本系列文章由余凱執筆創作,聯合作者:阿里雲容器服務 謝石 對本文亦有貢獻
近幾年,企業基礎設施雲原生化的趨勢越來越明顯,從最開始的IaaS化到現在的微服務化,客戶的顆粒度精細化和可觀測性的需求也更加強烈。容器網絡爲了滿足客戶更高性能和更高密度的需求,也一直在高速的發展和演進中,這必然給客戶對雲原生網絡的可觀測性帶來了極高的門檻和挑戰。爲了提高雲原生網絡的可觀測性,同時便於客戶和前後線同學增加對業務鏈路的可讀性,ACK產研和AES聯合共建,合作開發了ack net-exporter和雲原生網絡數據面可觀測性系列,幫助客戶和前後線同學瞭解雲原生網絡架構體系,簡化對雲原生網絡的可觀測性的門檻,優化客戶運維和售後同學處理疑難問題的體驗 ,提高雲原生網絡的鏈路的穩定性。
△ 服務網格示例
△ Istio數據面示意圖
Kubernetes的橫空出現打破了底層服務器、底層網絡等計算資源的界限,給業務的靈活部署、快速恢復、彈性伸縮、資源效率最大化帶來了無限可能。但是業務場景的‘貪婪’是無限的,隨着微服務趨勢大肆發展,業務上對於同一個service,不同版本和流量控制有着更精細化的顆粒度的需求,最好能實現Pod維度的流量控制,可觀測性等等。這些在Kubernetes上是無法實現的:
-
從流量角度,K8s最小的控制維度是service, 其他比如金絲雀等發佈,藉助各種ingress controller或者其他組件實現,並且這些也無法實現Pod之間的流量和連接狀態的可觀測性。
-
K8s給服務微型化,小型化創造了條件, 如果前後端服務存在調用關心,他們如果使用共享通信庫,則會在開發階段就要求所有微服務使用相同的邏輯語言和堆棧,這從某種程度上又大大限制微服務的獨立化,無法實現完全的‘漠不關心’
-
將原來集成在同一個ECS上的服務拆分成不同的模塊,這些模塊之間調用涉及跨ECS等,那麼必然需要在代碼開發階段需要考慮超時,重試,連接失敗等邏輯機制,而這些與微服務最核心的服務應用其實沒有太大關係,但是開發工作往往耗費大量的經歷在邏輯設計上。
那麼,有沒有辦法實現上述和微服務的業務完全隔離呢?Istio的出現給這個帶來了相對完美的解決方案,讓應用這和開發者更加關注業務本身的開發迭代。Istio利用了K8s的Pod概念,會根據使用者的配置,在每個被注入的Pod部署時,自動注入istio-proxy 容器和initial 容器。initial容器的目的是通過修改Pod單獨網絡命名空間的iptables規則,讓需要代理的流量進入到istio-proxy 監聽的端口, istio-proxy監聽出入 兩個端口,根據網格配置,來實現對出入流量的代理實現和干預。而被同一個istio注入的載體,都被視爲同一個服務網格之內,他們之間的調用已經脫離了service的層面,會命中相關的istio cluster配置的endpoint,這樣我們就可以實現Pod維度的流量管理、觀測性、安全性等配置。
本文是[全景剖析容器網絡數據鏈路]第六部分,主要介紹ASM Istio模式下,數據面鏈路的轉發鏈路,一是通過了解不同場景下的數據面轉發鏈路,從而探知客戶在不同的場景下訪問結果表現的原因,幫助客戶進一步優化業務架構;另一方面,通過深入瞭解轉發鏈路,在遇到容器網絡抖動時候,客戶運維以及阿里雲同學可以知道在哪些鏈路點進行部署觀測手動,從而進一步定界問題方向和原因。
系列一:全景剖析阿里雲容器網絡數據鏈路(一):Flannel
系列二:全景剖析阿里雲容器網絡數據鏈路(二):Terway ENI
系列三:全景剖析阿里雲容器網絡數據鏈路(三):Terway ENIIP
系列四:全景剖析阿里雲容器網絡數據鏈路(四):Terway IPVLAN+EBPF
系列五:全景剖析阿里雲容器網絡數據鏈路(五):Terway ENI-Trunking
ASM Istio 流量代理
1.1 Pod注入
ASM默認提供了一個Webhook控制器,可以將Sidecar代理自動添加到可用的Pod中。通過下面的命令可以看到ASM注入的集羣有個istio-sidecar-injector-1-15-3的mutatingwebhookconfiguration, 查看webhook內容,可以看到其中一條就是有 istio-inject:enabled 標籤的namespace裏的pod創建時候會自動注入。
除了命名空間維度,還有Pod維度,其他註解方式等多種維度實現K8s集羣是否被加入到Istio服務網格中。爲了充分利用服務網格的所有特性,服務網格中ACK集羣的應用Pod必須包含一個Sidecar代理。除了手動注入方式外,通常建議啓用自動注入的方式來簡化部署,ASM已經實現了注入配置的可視化操作,具體請見**多種方式靈活開啓自動注入 [ 1] **。
1.2 Pod流量轉發
通過describe被注入的Pod, 可以發現Pod中除了設置好的業務container,還多出兩個容器:istio-proxy和init container:istio-init。這兩個容器的鏡像是一樣的,只是運行的命令的不一樣,這樣的好處是隻需要拉取一份鏡像,節省了拉取鏡像的時間。
Init Container
Init container 利用的是K8s的特性,一種具有特權的特殊容器,在Pod內的應用容器啓動之前運行。Init容器可以包括一些應用鏡像中不存在的實用工具和安裝腳本。每個Pod中可以包含多個容器和多個Init容器。他與普通容器很像,但是有自己獨特點:
-
多個init容器是串行運行的。也就是說多個init容器會依序運行,等上一個init容器運行完畢結束後,纔會開始運行下一個容器。
-
只有等到所有的init容器全部運行結束退出後,業務容器纔開始啓動,在這之前,pod不會處於ready。
-
如果Pod的Init容器失敗,kubelet根據pod設置的restartPolicy進行相應的action。
既然現在瞭解了Init container的作用,那我們來看一下istio-init在啓動的過程中做了哪些事情,可以通過下面的命令:
kubectl logs -n istio-inject productpage-v1-797d845774-dndmk -c istio-init
可以看到istio-init在啓動過程中進行了一連串的iptables規則的生成和配置,比如出方向轉發到15001端口;入方向轉發到15006端口;訪問15008端口,直接return不進行流量劫持等等。那有什麼辦法可以自定義配置麼?查看pod的信息可以看到相關配置的啓動參數,也就通過相關規則實現了出入流量重定向到設置的端口。
-p: 所有出方向的流量被iptables重定向到15001端口
-z: 所有入方向的流量被iptables重定向到15006端口
-u: 用於排除用戶ID爲1337,可以視爲envoy應用本身使用UID 1337
-m: 流量重定向模式,“REDIRECT” 或 “TPROXY”
-i: 重定向出方向的地址範圍, “*” 表示重定向所有出站流量。
-x: 指將從重定向出方向中排除的 IP 地址範圍
-b: 重定向入站端口列表
-d: 重定向入站端口中排除的端口列表
我們從Pod的視角去觀察,將Pod視爲一個整體,裏面有istio-proxy容器和業務容器APP container
入方向流量轉發
根據上文的iptables規則,我們可以歸納出被入方向代理轉發的端口,比如80等,在Pod的網絡命名空間netfilter模塊經過流程是Client -> RREROUTING -> ISTIO_INBOUND -> ISTIO_IN_REDIRECT -> INPUT -> Envoy 15006(Inbound)-> OUTPUT -> ISTIO_OUTPUT -> POSTROUTING -> APP 。這樣就實現了入方向流量先被轉發到sidecar容器後,在轉發到業務容器的監聽端口。其中在步驟5和6之間,流量會按照設置好的istio規則進行處理。
出方向流量轉發
根據上文的iptables規則,我們可以歸納出被入方向代理轉發的端口,比如80等,在Pod的網絡命名空間netfilter模塊經過流程是APP > OUTPUT -> ISTIO_OUTPUT -> ISTIO_REDIRECT -> Envoy 15001(Outbound)-> OUTPUT -> ISTIO_OUTPUT -> POSTROUTING -> DST。這樣就實現了出方向流量先被轉發到sidecar容器後,在轉發到目的監聽端口。其中在步驟d和e之間,流量會按照設置好的istio規則進行處理。
入方向流量免轉發
對於入方向的某些端口或者自定義端口,我們不需要它經過sidecar容器,iptables規則會設置將符合條件的入方向流量避免轉發到15006端口,直接轉發到業務容器監聽端口 RREROUTING -> ISTIO_INBOUND -> INPUT -> APP。
出方向流量免轉發
對於出方向的某些端口或者自定義端口,我們不需要它經過sidecar容器,iptables規則會設置將符合條件的入方向流量避免轉發到15001端口,直接離開Pod的網絡命名空間 APP -> OUTPUT -> ISTIO_OUTPUT -> POSTROUTING -> DST。
Istio-proxy
可以看到15001和15006被envoy應用所監聽,而envoy應用就是istio-proxy容器程序。Init容器啓動的時候根據所設置的參數中指定將出入站流量重定向到Envoy的模式爲 “REDIRECT”或者“TPROXY”。使用REDIRECT方式,一旦Pod注入了Sidecar代理之後,所有入站流量都是從Envoy重定向,Envoy將流量發送到綁定了本地地址(127.0.0.1)的應用程序,所以應用看不到真正的原始IP。在服務網格環境下如何保持服務訪問時的客戶端源IP呢?可以使用TPROXY模式,目前ASM已經支持了TPROXY模式,具體詳情請見:
https://help.aliyun.com/document_detail/464794.html
在TPROXY模式下,Pod的網絡命名空間的iptables會有mangle配置。
ADS聚合服務發現
我們已經知道了服務網格會在每個注入的Pod內注入兩個容器:istio-init和istio-proxy。一旦在網格控制面進行相關配置的修改,會通過pilot下發到每個istio-proxy容器去生效。而istio是通過xDS服務接口去實現相關配置的動態下發的,其中xDS包含了LDS(Listener Discover Service)、CDS(Cluster Discover Service)、EDS(Endpoint Discovery Service)和RDS(Route Discover Service)。一般情況下,在更新配置過程中應該先更新Cluster-> 之後CLuster的Endpoint 開始更新-> 開始更新Cluster和Endpoint相對應的Listener -> Route開始更新新配置的Listener信息 -> 最後刪除不在使用 Cluster 和Endpoint 以保證更新過程中流量無損。但是這些xDS接口是相互獨立,所以在配置下發的時候,存在某些依賴關係的DS因配置生效前後關係造成了部分流量被丟棄,這在某些生產環境中是無法接受的。
爲了保證數據面配置的一致性,服務網格利用gRPC流來進行ADS聚合發現服務,通過一個gRPC流來保證各個xDS接口的調用順序,避免各個接口獨立性造成數據配置的不匹配。詳細信息可以參考:
https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol
envoy-rev.json
可以看到istio-proxy啓動了pilot-agent程序,pilot-agent作爲父進程啓動了子進程/usr/local/bin/envoy。其中/etc/istio/proxy/envoy-rev.json是envoy初始化的配置文件。
Node
包含了istio-proxy所在節點,當前Pod,istio版本、ACK集羣ID、ASM版本、必要端口等相關信息。
admin
istio-proxy相關日誌,管理端口等信息
dynamic_resources
ADS相關配置信息,比如api協議,版本,超時時間等
static_resources
包含了prometheus_stats、agent、sds-grpc、xds-grpc和zipkin五個cluster和一個在15090上監聽的listener,xds-grpc cluster對應前面dynamic_resources中ADS配置。prometheus_stats cluster和15090用於對外提供prometheus採集端口。zipkin cluster是外部的zipkin服務器調用地址。
tracing
分佈式鏈路跟蹤,這裏的collector_cluster是前面static_resources裏面定義的zipkin cluster。
訪問日誌分析
通過前文,我們已經知道兩個互相被注入的pod訪問,流量會被各自的istio-proxy所劫持並處理,那麼只要分析客戶端和服務端的istio-proxy日誌並進行加工,就可以對流量進行可觀測性解讀。我們在這裏還是以官方例子來舉例。訪問http://
productpage-v1-797d845774-dndmk IP是10.0.1.130,details應用的svc的名稱是details,svc地址是192.168.1.125,svc端口是9080
請求發送方productpage-v1-797d845774-dndmk的istio-proxy日誌
{"upstream_host":"10.0.1.127:9080","downstream_remote_address":"10.0.1.130:49586","downstream_local_address":"192.168.1.125:9080","duration":6,"upstream_cluster":"outbound|9080||details.istio-inject.svc.cluster.local","path":"/details/0","protocol":"HTTP/1.1","upstream_local_address":"10.0.1.130:50026","method":"GET","user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36","route_name":"default","request_id":"834147c2-435f-94a7-af11-8491df9ab4f8","start_time":"2023-01-31T14:23:20.603Z","upstream_transport_failure_reason":null,"upstream_service_time":"5","response_flags":"-","bytes_received":0,"authority_for":"details:9080","authority":"details:9080","requested_server_name":null,"istio_policy_status":null,"trace_id":"9712c9f3da936a8c927f227bfe536c16","response_code":200,"x_forwarded_for":null,"bytes_sent":178}
請求接受方details-v1-6758dd9d8d-dtbdc 的istio-proxy日誌
{"x_forwarded_for":null,"start_time":"2023-01-31T14:23:20.608Z","method":"GET","response_flags":"-","route_name":"default","istio_policy_status":null,"requested_server_name":"outbound_.9080_._.details.istio-inject.svc.cluster.local","bytes_received":0,"request_id":"834147c2-435f-94a7-af11-8491df9ab4f8","response_code":200,"upstream_host":"10.0.1.127:9080","trace_id":"9712c9f3da936a8c927f227bfe536c16","downstream_remote_address":"10.0.1.130:50026","protocol":"HTTP/1.1","bytes_sent":178,"upstream_transport_failure_reason":null,"downstream_local_address":"10.0.1.127:9080","upstream_local_address":"127.0.0.6:46225","authority":"details:9080","authority_for":"details:9080","upstream_service_time":"0","upstream_cluster":"inbound|9080||","duration":1,"path":"/details/0","user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"}
日誌內容解讀
"upstream_host":"10.0.1.127:9080",————對於outbound,此是上游某個Endpoint的地址和端口
downstream_remote_address":"10.0.1.130:49586"," ————對於outbound,此爲本pod-ip:隨機端口1
downstream_local_address":"192.168.1.125:9080","————對於outbound,此爲目的svc-ip:svc-port
duration":6," ———— 整個請求時間,單位ms
upstream_cluster":"outbound|9080||details.istio-inject.svc.cluster.local",———— cluster信息
"path":"/details/0",
"protocol":"HTTP/1.1",
"upstream_local_address":"10.0.1.130:50026", ————對於outbound,此爲本pod-ip:隨機端口2
"method":"GET",
"user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
"route_name":"default",———— 路由名稱
"request_id":"834147c2-435f-94a7-af11-8491df9ab4f8",
"start_time":"2023-01-31T14:23:20.603Z",
"upstream_transport_failure_reason":null,
"upstream_service_time":"5",———— 上游返回請求時間,單位ms
"response_flags":"-",———— 返回標誌,關於連接或返回的詳細信息
"bytes_received":0,
"authority_for":"details:9080",
"authority":"details:9080",
"requested_server_name":null,
"istio_policy_status":null,
"trace_id":"9712c9f3da936a8c927f227bfe536c16",———— 此ID爲唯一值,可以在上游istio-proxy對應
"response_code":200,———— 返回狀態碼
"x_forwarded_for":null
,"bytes_sent":178
日誌解讀可以詳細見官方連接:
https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage
UPSTREAM_HOST
上游主機的host,表示從envoy發出的請求的目的端
通常來說,對於outbound cluster,此值是「上游pod-ip : pod-port」 ,而對於 inbound cluster,此值是「本pod-ip : pod-port」
UPSTREAM_LOCAL_ADDRESS
上游連接中,當前envoy的本地地址
通常來說,對於outbound cluster,此值是「本pod-ip : 隨機端口2」 ,而對於inbound cluster,此值是「127.0.0.6: 隨機端口3」,此處的127.0.0.6對應了 【1.2 Pod流量轉發-Init Container】 中的iptables會將來自127.0.0.6的流量免於istio代理,因爲這個流量是從sidecar本身發出的
DONSTREAM_LOCAL_ADDRESS
下游連接中,當前envoy的本地地址
通常來說,對於outbound cluster,此值是「目的service-ip : service-port 」,而對於 inbound cluster,此值是「當前pod-ip : pod-port,此處和下游的upstream_host應該相對應。
DOWNSTREAM_REMOTE_ADDRESS
通常來說,對於outbound cluster,此值是「當前pod-ip : 隨機端口 」,而對於 inbound cluster,此值是「下游pod-ip : 隨機端口2」,此處和下游的upstream_local_address相對應
1.3 Envoy配置簡讀(數據鏈路)
背景
還是用官方的示例, 以productpage訪問reviews服務來舉例。
通過Kubernetes集羣資源,我們可一看到reviews有三個版本分別爲v1,v2,v3, pod數量各一個。SVC reviews是ClusterIP模式,svc端口是9080, targetport是pod的9080端口,v1,v2,v3 都被加到了reviews SVC的endpointslice。在未被istio注入的情況下, 集羣內productpage pod訪問reviews.istio-inject服務, 會被netfilter以round-robin的方式平均轉發到v1,v2,v3三個pod上, 每個pod應該承受1/3的流量。
在傳統的K8s集羣中,是無法通過K8s的resource控制不同版本的流量分配的。但是實際的生產環境,我們是有這方面的需求的。比如v1版本是線上業務版本,承載了主要業務流量, v2版本是開發完畢預上線版本, 本質上是不希望影響線上流量的,可能需要引流線上流量的5%到預發版本進行一段時間的觀察,來判斷新版本是否有問題,之後再進一步擴大引流比例至100%之後,v1版本才下線,從而實現業務角度的平滑遷移。或者比如v3是測試版本,我們希望觀察流量在網絡波動超時情況下,業務的自我容災和恢復情況的行爲是否符合預期,以前這種需求需要通過在業務代碼中寫好熔斷代碼,不同熔斷環境都需要重新發版,那麼像這種流量控制在ASM Istio就可以很容易的實現。
下面就是一個ASM Istio中的vs和dr的配置。
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
creationTimestamp: '2023-01-30T06:25:21Z'
generation: 1
name: reviews
namespace: istio-inject
resourceVersion: '651722274'
uid: 63f715c9-b253-4fbb-8351-5313371df14e
spec:
hosts:
- reviews.istio-inject.svc.cluster.local
http:
- name: route
route:
- destination:
host: reviews
subset: v1
weight: 10
- destination:
host: reviews
subset: v2
weight: 40
- destination:
host: reviews
subset: v3
weight: 50
其中在reviews vs的定義了集羣內訪問reviews.istio-inject.svc.cluster.local是的http協議的規則。其中指明瞭v1版本權重10%,v2版本權重40%,v3版本權重 50%
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
creationTimestamp: '2023-01-30T06:28:46Z'
generation: 2
name: reviews
namespace: istio-inject
resourceVersion: '654863578'
uid: fdbdfcea-1fcd-453e-96fb-ce41c91ded9b
spec:
host: reviews
subsets:
- labels:
version: v1
name: v1
- labels:
version: v2
name: v2
- labels:
version: v3
name: v3
trafficPolicy:
connectionPool:
http:
http2MaxRequests: 1000
maxRequestsPerConnection: 10
tcp:
maxConnections: 100
outlierDetection:
baseEjectionTime: 15m
consecutive5xxErrors: 7
interval: 5m
reviews dr的定義了集羣內reviews的幾個版本,並定義了相關流量策略。其中http2MaxRequests表明http最大的請求數。maxRequestsPerConnection表明每個連接最大的請求數。tcp最大連接數是100。在熔斷配置中,每隔5min中檢測一次,連續7次5xx,把後端移除endpoint 15min。
通過前文我們知道pilot通過xDS接口將服務網格的配置下發到每個被注入的pod中的istio-proxy中。那麼對於每個pod中的istio-proxy,我們是否有辦法去查看相關的加載的配置信息呢?istio-proxy通過15000端口對外暴露管理端口,我們可以通過如圖所示的命令獲取到相關的配置信息。
其中可以通過curl 127.0.0.1:15000/config_dump可以獲取到完整的配置信息,由於此配置信息超過1萬多行,我們就不在這裏做全部的展示,感興趣的同學可以自行研究下,下文會針對此config_dump信息中的cluster,Listener,endpoint,route等關鍵信息做個相關展示和簡要說明,同時也和前文的xDS做個呼應。
kubectl exec -n istio-inject productpage-v1-797d845774-dndmk -c istio-proxy -it -- curl 127.0.0.1:15000/config_dump
Bootstrap
Envoy的初始化配置,與前文中的envoy-rev0.json是一致的。其中drainDuration —— 熱重啓期間Envoy關閉連接的時間(以秒爲單位),默認是45s。
parentShutdownDuration —— 熱重啓期間,在完全關閉父進程之前,等到的最大時間,默認60s。此數值應該大於drainDuration。
terminationDrainDuration —— 默認5s。proxy在關閉之前,允許連接關閉時間。通過前文,我們知道pod流量先過istio再被轉發到業務容器。當應用發版時候,如果保證現有連接優雅關閉,保證istio-proxy容器在業務容器完全關閉現有連接後,再退出是發版更新時候需要考慮的問題,此值是實現業務平滑更新需要考慮的。
static_resources
config_dump中靜態資源,是來自envoy-rev0.json, 裏面包含了prometheus_stats、agent、sds-grpc、xds-grpc和zipkin等配置
dynamic_resources
動態資源,是通過xDS接口下發到每個istio-proxy容器生效的ASM Istio的配置。也是上述reviews dr,vs配置後通過ASM管控側下發的。我們接下來關注動態資源配置
Listeners
Envoy採用的listener來接受到達Envoy的流量請求。Listener和IP Sock、Unix Domain Socket綁定,也可以不綁定到具體的端口,而是接收從其他listener轉發來的流量。ASM Istio就是利用了Envoy listener的這一特性來實現轉發給不同的服務請求交給不同的Listeners來處理。
還是以productpage訪問reviews來舉例, productpage訪問的是reviews的9080端口,根據上文我們知道productpage container訪問外部的9080端口會被先轉發到15001端口,所以我們先看下15001的端口listeners。
VirtualOutbound
Envoy在15001端口創建了Listeners,所有被iptables轉發的對外請求都會被轉到envoy的15001端口。可以從配置看到,envoy接受到了流量後,並不會做相關的業務動作,而是根據 "use_original_dst": true, 這個配置,依據請求的目的端口轉發到相應的listeners 進行處理。
那麼肯定有個疑問了,如果請求的目的端口並沒有配置相關的listeners設置,流量該如何進行處理呢?這個取決於outboundTrafficPolicy的配置,詳情請見:
https://istio.io/latest/docs/reference/config/istio.mesh.v1alpha1/#MeshConfig-OutboundTrafficPolicy-Mode
Outbound
outbound監聽命名是0.0.0.0_9080, 表明發向任何IP地址的9080端口都被此監聽涵蓋。"bind_to_port”: false此值表明監聽沒有綁定到tcp端口,流量是有virtualOutbound轉發而來的。那麼首先我們需要區別這個監聽是爲了入方向還是出方向呢?對於入方向,流量會經過15006端口的virtualInbound 的listeners,是不會進入0.0.0.0_9080的listeners。
從配置上可以看到filter中並沒有特殊的志敏篩選條件,說明接受任何流量,其中config_source 爲ads,表明這個是來自動態發現。
根據前文可以可看到revirews,ratings,details幾個service都是9080端口,這些應用都被同一個服務網格注入,那麼productpage訪問的目的地址的9080,Envoy如何剛知道是哪個service?是如何判斷如果目的端口未9080的地址不是網格內,該如何處理呢?通過上圖"route_config_name": "9080" 可以看到存在一個‘9080’的路由規則,在這個路由規則中規定不同的請求目的地的不同的處理結果,下一小節我們將討論。
Route
前文我們已經知道productpage應用訪問reviews的9080端口會被listeners outbound 0.0.0.0_9080路由規則到9080的路由。以下是‘9080’ 路由的全部信息。我們可以看到一共有5個virtual_hosts, 分別是allow_any、details、productpage、ratings、和reviews。其中後面4個對應4個不同outbound的cluster, allow_any對應的是PassthroughCluster,當出方向請求找到相應的Cluster規則時候,會採用默認直接通過。
可能有小夥伴很奇怪productpage爲什麼不直接調用ratings服務,爲什麼productpage envoy配置會包含ratings的信息。這是因爲ASM Istio控制面是無法感知到數據面各個服務之間是如何調用的,因此會將所有的配置信息都下發到被注入的envoy裏面,這樣保證了網格配置的一致性,但是隨着網格服務配置的增多,每個envoy接受和本envoy不相關的配置信息就會變多,這樣對envoy資源使用會有一定影響,如果小夥伴有很好的envoy開發能力,並且對業務之間調用非常熟悉,想去除掉本pod中envoy無關的規則,可以通過sidecar規則自定義配置對egress和ingress進行調整,詳情請見:
https://istio.io/latest/docs/reference/config/networking/sidecar/
{
"version_info": "2023-01-30T06:25:21Z/19",
"route_config": {
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"name": "9080",
"virtual_hosts": [
{
"name": "allow_any",
"domains": [
"*"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "PassthroughCluster",
"timeout": "0s",
"max_grpc_timeout": "0s"
},
"name": "allow_any"
}
],
"include_request_attempt_count": true
},
{
"name": "details.istio-inject.svc.cluster.local:9080",
"domains": [
"details.istio-inject.svc.cluster.local",
"details.istio-inject.svc.cluster.local:9080",
"details",
"details:9080",
"details.istio-inject.svc",
"details.istio-inject.svc:9080",
"details.istio-inject",
"details.istio-inject:9080",
"192.168.1.125",
"192.168.1.125:9080"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "outbound|9080||details.istio-inject.svc.cluster.local",
"timeout": "0s",
"retry_policy": {
"retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
"num_retries": 2,
"retry_host_predicate": [
{
"name": "envoy.retry_host_predicates.previous_hosts",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
}
}
],
"host_selection_retry_max_attempts": "5",
"retriable_status_codes": [
503
]
},
"max_stream_duration": {
"max_stream_duration": "0s",
"grpc_timeout_header_max": "0s"
}
},
"decorator": {
"operation": "details.istio-inject.svc.cluster.local:9080/*"
},
"name": "default"
}
],
"include_request_attempt_count": true
},
{
"name": "istio-ingressgateway.istio-system.svc.cluster.local:9080",
"domains": [
"istio-ingressgateway.istio-system.svc.cluster.local",
"istio-ingressgateway.istio-system.svc.cluster.local:9080",
"istio-ingressgateway.istio-system",
"istio-ingressgateway.istio-system:9080",
"istio-ingressgateway.istio-system.svc",
"istio-ingressgateway.istio-system.svc:9080",
"192.168.1.250",
"192.168.1.250:9080"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "outbound|9080||istio-ingressgateway.istio-system.svc.cluster.local",
"timeout": "0s",
"retry_policy": {
"retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
"num_retries": 2,
"retry_host_predicate": [
{
"name": "envoy.retry_host_predicates.previous_hosts",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
}
}
],
"host_selection_retry_max_attempts": "5",
"retriable_status_codes": [
503
]
},
"max_stream_duration": {
"max_stream_duration": "0s",
"grpc_timeout_header_max": "0s"
}
},
"decorator": {
"operation": "istio-ingressgateway.istio-system.svc.cluster.local:9080/*"
"name": "default"
}
],
"include_request_attempt_count": true
},
},
{
"name": "productpage.istio-inject.svc.cluster.local:9080",
"domains": [
"productpage.istio-inject.svc.cluster.local",
"productpage.istio-inject.svc.cluster.local:9080",
"productpage",
"productpage:9080",
"productpage.istio-inject.svc",
"productpage.istio-inject.svc:9080",
"productpage.istio-inject",
"productpage.istio-inject:9080",
"192.168.6.226",
"192.168.6.226:9080"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "outbound|9080||productpage.istio-inject.svc.cluster.local",
"timeout": "0s",
"retry_policy": {
"retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
"num_retries": 2,
"retry_host_predicate": [
{
"name": "envoy.retry_host_predicates.previous_hosts",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
}
}
],
"host_selection_retry_max_attempts": "5",
"retriable_status_codes": [
503
]
},
"max_stream_duration": {
"max_stream_duration": "0s",
"grpc_timeout_header_max": "0s"
}
},
"decorator": {
"operation": "productpage.istio-inject.svc.cluster.local:9080/*"
},
"name": "default"
}
],
"include_request_attempt_count": true
},
{
"name": "ratings.istio-inject.svc.cluster.local:9080",
"domains": [
"ratings.istio-inject.svc.cluster.local",
"ratings.istio-inject.svc.cluster.local:9080",
"ratings",
"ratings:9080",
"ratings.istio-inject.svc",
"ratings.istio-inject.svc:9080",
"ratings.istio-inject",
"ratings.istio-inject:9080",
"192.168.1.172",
"192.168.1.172:9080"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "outbound|9080||ratings.istio-inject.svc.cluster.local",
"timeout": "0s",
"retry_policy": {
"retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
"num_retries": 2,
"retry_host_predicate": [
{
"name": "envoy.retry_host_predicates.previous_hosts",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
}
}
],
"host_selection_retry_max_attempts": "5",
"retriable_status_codes": [
503
]
},
"max_stream_duration": {
"max_stream_duration": "0s",
"grpc_timeout_header_max": "0s"
}
},
"decorator": {
"operation": "ratings.istio-inject.svc.cluster.local:9080/*"
},
"name": "default"
}
],
"include_request_attempt_count": true
},
{
"name": "reviews.istio-inject.svc.cluster.local:9080",
"domains": [
"reviews.istio-inject.svc.cluster.local",
"reviews.istio-inject.svc.cluster.local:9080",
"reviews",
"reviews:9080",
"reviews.istio-inject.svc",
"reviews.istio-inject.svc:9080",
"reviews.istio-inject",
"reviews.istio-inject:9080",
"192.168.4.113",
"192.168.4.113:9080"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"weighted_clusters": {
"clusters": [
{
"name": "outbound|9080|v1|reviews.istio-inject.svc.cluster.local",
"weight": 10
},
{
"name": "outbound|9080|v2|reviews.istio-inject.svc.cluster.local",
"weight": 40
},
{
"name": "outbound|9080|v3|reviews.istio-inject.svc.cluster.local",
"weight": 50
}
],
"total_weight": 100
},
"timeout": "0s",
"retry_policy": {
"retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
"num_retries": 2,
"retry_host_predicate": [
{
"name": "envoy.retry_host_predicates.previous_hosts",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
}
}
],
"host_selection_retry_max_attempts": "5",
"retriable_status_codes": [
503
]
},
"max_stream_duration": {
"max_stream_duration": "0s",
"grpc_timeout_header_max": "0s"
}
},
"metadata": {
"filter_metadata": {
"istio": {
"config": "/apis/networking.istio.io/v1alpha3/namespaces/istio-inject/virtual-service/reviews"
}
}
},
"decorator": {
"operation": "reviews:9080/*"
},
"name": "route"
}
],
"include_request_attempt_count": true
}
],
"validate_clusters": false
},
"last_updated": "2023-01-30T06:25:21.804Z"
},
我們還是以productpage調用reviews來舉例, Envoy會根據 HTTP header 中domains 來匹配VirtualHost中domain,所以可以看到domains中列舉了reviews的集羣內的長短域名,和svc的地址。match “/” 會路由到三個cluster "outbound|9080|v1|reviews.istio-inject.svc.cluster.local"、"outbound|9080|v2|reviews.istio-inject.svc.cluster.local"和"outbound|9080|v3|reviews.istio-inject.svc.cluster.local",權重分別爲10,40,50,名稱是‘route’,看到這裏是不是有點熟悉?對的,這和前文 [1.3 Envoy配置簡讀-背景]中reviews的VS中的設置,故我們在vs中的相關配置信息,最終envoy會轉爲route的配置加載後生效。
通過前面我們還可以看到默認超時是0s,默認重試次數是2次,重試條件是"connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes"。
Cluster
outbound cluster
根據上一下小節,productpage訪問reviews,會被productpage中的istio-proxy匹配到‘9080’路由-> 依據vs配置的相關信息,進入三個cluster( "outbound|9080|v1|reviews.istio-inject.svc.cluster.local"、"outbound|9080|v2|reviews.istio-inject.svc.cluster.local"和"outbound|9080|v3|reviews.istio-inject.svc.cluster.local")中一個。這裏我們就以"outbound|9080|v1|reviews.istio-inject.svc.cluster.local"cluster 爲例
outbound|9080|v1|reviews.istio-inject.svc.cluster.local cluster配置中可以看到,其類型爲EDS,即表示該Cluster的endpoint來自於動態發現,動態發現中eds_config則表明是由ads下發的。同樣可以看到與前文[1.3 Envoy配置簡讀-背景]中reviews的dr中的設置熟悉的配置,比如connectionpool,outlierDetection等這些相關配置信息,最終envoy會轉爲cluster的配置加載後生效。
接下來我們稍微探討下其他幾個特殊的cluster。
BlackHoleCluster
Envoy對於找不到後端處理請求的會默認丟棄,所以會有統一的一個blackholecluster,沒有任何指明的後端svc,任何無匹配後端的流量會被轉發到這個cluster。
PassthroughCluster
和BlackHoleCluter正好相反,發向PassthroughCluster的請求會被直接發送到器請求連接中的原始目地的,type類型是"type": "ORIGINAL_DST",表明其發到原始的目的地址:端口
Inbound Cluster
inbound Cluster是爲了pod上的入方向的請求,對於reviews來說,其對應的Inbound Cluster只有一個inbound|9080。
Endpoint
從endpoint文件內容可以看到,reviews cluster “outbound|9080|v1|reviews.istio-inject.svc.cluster.local”只有1個endpoint地址,是reviews-v1-74fb8fdbd8-qwsjq的pod ip 10.0.3.29。
至此,我們大概梳理完畢服務網格內兩個服務之間訪問,istio-proxy日誌解讀和配置對應關係。
Tips
前文的config_dump文件太長,解讀起來其實比較費力,服務網格提供了asmctl工具可以很方便的去解讀listeners,route,cluster,endpoint等,詳細信息請見:
https://help.aliyun.com/document_detail/313187.html
[root@shycmain ~]# asmctl --asmconfig asmconfig pc listeners productpage-v1-797d845774-dndmk.istio-inject --port 9080
ADDRESS PORT MATCH DESTINATION
0.0.0.0 9080 Trans: raw_buffer; App: HTTP Route: 9080
0.0.0.0 9080 ALL PassthroughCluster
[root@shycmain ~]# asmctl --asmconfig asmconfig pc routes productpage-v1-797d845774-dndmk.istio-inject --name 9080
NOTE: This output only contains routes loaded via RDS.
NAME DOMAINS MATCH VIRTUAL SERVICE
9080 details /*
9080 istio-ingressgateway.istio-system /*
9080 productpage /*
9080 ratings /*
9080 reviews /* reviews.istio-inject
[root@shycmain ~]# asmctl --asmconfig asmconfig pc cluster productpage-v1-797d845774-dndmk.istio-inject --fqdn reviews.istio-inject.svc.cluster.local
SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE
reviews.istio-inject.svc.cluster.local 9080 - outbound EDS reviews.istio-inject
reviews.istio-inject.svc.cluster.local 9080 v1 outbound EDS reviews.istio-inject
reviews.istio-inject.svc.cluster.local 9080 v2 outbound EDS reviews.istio-inject
reviews.istio-inject.svc.cluster.local 9080 v3 outbound EDS reviews.istio-inject
[root@shycmain ~]# asmctl --asmconfig asmconfig pc endpoint productpage-v1-797d845774-dndmk.istio-inject --cluster "outbound|9080|v1|reviews.istio-inject.svc.cluster.local"
ENDPOINT STATUS OUTLIER CHECK CLUSTER
10.0.3.29:9080 HEALTHY OK outbound|9080|v1|reviews.istio-inject.svc.cluster.local
企業擁抱容器化總結
本篇文章主要聚焦在ASM Istio服務網格模式下,被注入pod的數據面流量轉發鏈路情況。istio靈活注入實現了在Pod維度對流量的定製化配置和觀測性,帶來了業務鏈路角度實現的更多種的可能。在服務網格中配置gateway,virtualservice,destinationrule等規則在通過xDS下發到envoy後,會通過listeners, route、cluster、endpoint等一個環節一個環節最終體現在流量轉發規則上。那麼在運用ASM遇到不符合預期情況時,這些環節都是需要考慮的方向。ASM除了流量管理,還有安全,鑑權,可觀測方面的便捷運用,這些方面的配置,最終也會體現在相關的網格服務資源配置上,感興趣的小夥伴可以參考**ASM官方文檔 [ 2] **。
參考鏈接
https://istio.io/latest/docs/reference/config/networking/sidecar/
https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage
[1] 多種方式靈活開啓自動注入
https://help.aliyun.com/document_detail/186136.html
[2] 官方文檔