Istio 流量管理機制技術實踐-Istio商業環境實戰

專一於大數據及容器雲核心技術解密,若有任何學術交流,可隨時聯繫。更多內容請關注《數據雲技術社區》公衆號,或請轉發郵件至1120746959@qq.comnode

1 整體

  • Pilot 定義了網格中服務的標準模型,這個標準模型獨立於各類底層平臺,Pilot 還能夠從 Mesos, Cloud Foundry, Consul 等平臺中獲取服務信息,還能夠開發適配器將其餘提供服務發現的組件集成到 Pilot 中。
  • Istio 和 Envoy 項目聯合制定了 Envoy V2 API, 並採用該 API 做爲 Istio 控制平面和數據平面流量管理的標準接口。
  • 用戶也能夠根據本身的業務場景選擇不一樣的 sidecar 和控制平面集成,如高吞吐量的,低延遲的,高安全性的等等。除 Istio 目前集成的 Envoy 外,還能夠和 Linkerd, Nginmesh 等

2 Discovery Services

2.1 控制平面-pilot-discovery

  • 從 Service provider(如kubernetes或者consul)中獲取服務信息
  • 從 K8S API Server 中獲取流量規則(K8S CRD Resource)
  • 將服務信息和流量規則轉化爲數據平面能夠理解的格式,經過標準的數據平面 API 下發到網格中的各個 sidecar 中
  • 對應的 docker 鏡像爲 gcr.io/istio-release/pilot

2.2 數據平面

  • 在數據平面有兩個進程 Pilot-agent 和 envoy

2.2.1 Pilot-agent

  • 根據 K8S API Server 中的配置信息生成 Envoy 的配置文件
  • 啓動 Envoy 進程(Envoy 的大部分配置信息都是經過 xDS 接口從 Pilot 中動態獲取的)
  • Pilot-agent 在啓動 Envoy 進程時傳入了 pilot 地址和 zipkin 地址,併爲 Envoy 生成了一個初始化配置文件 envoy-rev0.json

2.2.1.1 envoy-rev0.json結構說明

  • Node 包含了 Envoy 所在節點相關信息

  • Admin 配置 Envoy 的日誌路徑以及管理端口
  • Dynamic_resources 配置動態資源,這裏配置了 ADS 服務器。
  • Static_resources 配置靜態資源,包括了 xds-grpc 和 zipkin 兩個 cluster。其中 xds-grpc cluster 對應前面 dynamic_resources 中 ADS 配置,指明瞭 Envoy 用於獲取動態資源的服務器地址。

  • Tracing 配置分佈式鏈路跟蹤
  • Stats_sinks 這裏配置的是和 Envoy 直連的 metrics 收集 sink,和 Mixer telemetry 沒有關係。Envoy 自帶 stats 格式的 metrics 上報。

2.2.2 envoy

  • 經過 K8s 的 Admission webhook 機制實現了 sidecar 的自動注入,Mesh 中的每一個微服務會被加入 Envoy 相關的容器。下面是 Productpage 微服務的 Pod 內容,可見除 productpage 以外,Istio 還在該 Pod 中注入了兩個容器 gcr.io/istio-release/proxy_init 和 gcr.io/istio-release/proxyv2
[root@hadoop ~]# kubectl exec productpage-v1-99b4c9954-vj6hf -c istio-proxy -- ps -ef                              
UID        PID  PPID  C STIME TTY          TIME CMD
istio-p+     1     0  0 03:26 ?        00:00:05 

/usr/local/bin/pilot-agent proxy sidecar --domain default.svc.cluster.local 
--configPath /etc/istio/proxy 
--binaryPath /usr/local/bin/envoy 
--serviceCluster productpage.default 
--drainDuration 45s --parentShutdownDuration 1m0s 
--discoveryAddress istiod.istio-system.svc:15012 
--zipkinAddress zipkin.istio-system:9411 
--proxyLogLevel=warning 
--proxyComponentLogLevel=misc:error 
--connectTimeout 10s 
--proxyAdminPort 15000 
--concurrency 2 
--controlPlaneAuthPolicy NONE 
--dnsRefreshRate 300s 
--statusPort 15020 
--trust-domain=cluster.local 
--controlPlaneBootstrap=false

istio-p+    20     1  0 03:26 ?        00:00:21 /usr/local/bin/envoy -c /etc/istio/proxy/envoy-rev0.json 
--restart-epoch 0 
--drain-time-s 45 
--parent-shutdown-time-s 60 
--service-cluster productpage.default 
--service-node sidecar~10.42.0.69~productpage-v1-99b4c9954-vj6hf.default~default.svc.cluster.local 
--max-obj-name-len 189 
--local-address-ip-version v4 
--log-format [Envoy (Epoch 0)] [%Y-%m-%d %T.%e][%t][%l][%n] %v -l warning 
--component-log-level misc:error 
--concurrency 2
istio-p+    36     0  0 06:32 ?        00:00:00 ps -ef
複製代碼
  • 從 Envoy 初始化配置文件中,咱們能夠大體看到 Istio 經過 Envoy 來實現服務發現和流量管理的基本原理。即控制平面將 xDS server 信息經過 static resource 的方式配置到 Envoy 的初始化配置文件中,Envoy 啓動後經過 xDS server 獲取到 dynamic resource,包括網格中的 service 信息及路由規則

  • Envoy 中實際生效的配置是由初始化配置文件中的靜態配置和從 Pilot 獲取的動態配置一塊兒組成的。

3 envoy原理

Envoy 支持 靜態(StaticRoute)、 動態(RDS)兩種方式進行路由配置。靜態路由配置,是指寫死在 Envoy 啓動配置文件裏。動態路由配置,又稱 RDS(Route Discovery Service,路由發現服務),是指 Envoy 在運行時向控制面發送請求,由控制面下發路由配置。 從架構圖看出,Envoy的路由機制可粗略劃分爲五步:web

  • ① 請求路由配置
  • ② 接收路由配置
  • ③ 收到並解析HTTP請求
  • ④ 路由匹配
  • ⑤ 轉發HTTP請求

3.0 Envoy 啓動配置文件的內容分析

  • 定義了一個網絡監聽器,名爲 listener_0,監聽地址 10.85.25.87:60001。
  • 監聽器下面掛載了一條過濾器鏈(filter chain),只包含了一個過濾器 envoy.httpconnectionmanager,官方的 HTTP 鏈接管理器。
  • 過濾器下面配置了動態路由發現 rds,表示路由配置將在運行時從某處數據源動態獲取。
  • routeconfigname:惟一標識了一項路由配置,以便向數據源獲取指定的路由配置。
  • configsource: 表示路由配置的數據源,值 ads:{} 表示數據源是 adsconfig 指定的集羣。
  • adsconfig: 指定了全部 xDS 資源(包括路由配置)都將從集羣 xdscluster 獲取,且通信協議爲 GRPC。
  • xds_cluster: 指定了集羣下面的主機只有一個 10.85.25.87:61001。

kubectl exec -it productpage-v1-54b8b9f55-bx2dq -c istio-proxy curl http://127.0.0.1:15000/config_dump > config_dump
複製代碼

3.1 Outbound Cluster

  • 以 details 爲例,對於 Productpage 來講,details 是一個外部服務,所以其 Cluster 名稱中包含 outbound 字樣。
  • 從 details 服務對應的 cluster 配置中能夠看到,其類型爲 EDS,即表示該 Cluster 的 endpoint 來自於動態發現,動態發現中 eds_config 則指向了 ads,最終指向 static Resource 中配置的 xds-grpc cluster,即 Pilot 的地址。

3.2 Inbound Cluster

  • 該類 Cluster 對應於 Envoy 所在節點上的服務。若是該服務接收到請求,固然就是一個入站請求。對於 Productpage Pod 上的 Envoy,其對應的 Inbound Cluster 只有一個,即 productpage。該 cluster 對應的 host 爲 127.0.0.1,即環回地址上 productpage 的監聽端口。因爲 iptables 規則中排除了 127.0.0.1,入站請求經過該 Inbound cluster 處理後將跳過 Envoy,直接發送給 Productpage 進程處理。

3.3 BlackHoleCluster

  • 這是一個特殊的 Cluster,並無配置後端處理請求的 Host。如其名字所暗示的同樣,請求進入後將被直接丟棄掉。若是一個請求沒有找到其對的目的服務,則被髮到 BlackHoleCluster。

3.4 Listeners

  • Envoy 採用 listener 來接收並處理 downstream 發過來的請求,listener 的處理邏輯是插件式的,能夠經過配置不一樣的 filter 來插入不一樣的處理邏輯。

3.5 Virtual Listener

  • Envoy 建立了一個在 15001 端口監聽的入口監聽器。Iptables 將請求截取後發向 15001 端口,該監聽器接收後並不進行業務處理,而是根據請求目的地分發給其餘監聽器處理。
  • Envoy 是如何作到按服務分發的呢? 能夠看到該 Listener 的配置項 use_original_dest 設置爲 true,該配置要求監聽器將接收到的請求轉交給和請求原目的地址關聯的 listener 進行處理。

4 路程分析

  • 查看 productpage對應的全部監聽器,0.0.0.0:15001 上的監聽器接收進出 Pod 的全部流量,而後將請求移交給虛擬監聽器。
export PILOT_SVC_IP=$(kubectl -n istio-system get svc -l app=pilot -o go-template='{{range .items}}{{.spec.clusterIP}}{{end}}')

kubectl get pod -l app=productpage

istioctl proxy-config listeners productpage-v1-99b4c9954-vj6hf

ADDRESS           PORT      TYPE
10.42.0.69        9080      HTTP
10.42.0.69        15020     TCP
10.43.113.103     31400     TCP
10.43.113.103     15443     TCP
10.43.0.1         443       TCP
10.43.86.87       15011     TCP
10.43.39.94       443       TCP
10.43.113.103     443       TCP
10.43.108.37      15012     TCP
10.43.0.10        53        TCP
10.43.86.87       15012     TCP
10.43.86.87       443       TCP
10.43.39.94       15443     TCP
10.43.108.37      443       TCP
10.43.0.10        9153      TCP
10.43.113.103     15020     TCP
10.43.113.103     15031     TCP
10.43.113.103     15032     TCP
10.43.96.17       14250     TCP
0.0.0.0           20001     TCP
0.0.0.0           80        TCP
10.43.113.103     15029     TCP
10.43.96.17       14268     TCP
0.0.0.0           9411      TCP
10.43.113.103     15030     TCP
10.43.96.17       14267     TCP
0.0.0.0           8000      TCP
10.43.34.201      80        TCP
0.0.0.0           15014     TCP
10.43.137.123     443       TCP
0.0.0.0           8080      TCP
0.0.0.0           3000      TCP
10.43.130.148     16686     TCP
0.0.0.0           9090      TCP
0.0.0.0           15010     TCP
0.0.0.0           14250     TCP
0.0.0.0           9080      TCP
0.0.0.0           15001     TCP   
0.0.0.0           15006     TCP
0.0.0.0           443       TCP
0.0.0.0           15090     HTTP

istioctl proxy-config listeners productpage-v1-99b4c9954-vj6hf --address 10.43.0.10 --port 53 -o json

 {
                        "name": "envoy.tcp_proxy",
                        "typedConfig": {
                            "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
                            "statPrefix": "outbound|53||kube-dns.kube-system.svc.cluster.local",
                            "cluster": "outbound|53||kube-dns.kube-system.svc.cluster.local",
                            "accessLog": [
                                {
                                    "name": "envoy.file_access_log",
                                    "typedConfig": {
                                        "@type": "type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog",
                                        "path": "/dev/stdout",
                                        "format": "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% \"%DYNAMIC_METADATA(istio.mixer:status)%\" \"%UPSTREAM_TRANSPORT_FAILURE_REASON%\" %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\" %UPSTREAM_CLUSTER% %UPSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_REMOTE_ADDRESS% %REQUESTED_SERVER_NAME% %ROUTE_NAME%\n"
                                    }
                                }
                            ]
                        }
                    }
                  
                  
kubectl exec productpage-v1-99b4c9954-vj6hf  -c istio-proxy --  netstat -ln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:15090           0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:9080            0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:15000         0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:15001           0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:15006           0.0.0.0:*               LISTEN
tcp6       0      0 :::15020                :::*                    LISTEN 

9080: productpage進程對外提供的服務端口
15001: Envoy的Virtual Outbound監聽器,iptable會將productpage服務發出的出向流量導入該端口中由Envoy進行處理
15006: Envoy的Virtual Inbound監聽器,iptable會將發到productpage的入向流量導入該端口中由Envoy進行處理
15000: Envoy管理端口,該端口綁定在本地環回地址上,只能在Pod內訪問。
15090:指向127.0.0.1:15000/stats/prometheus, 用於對外提供Envoy的性能統計指標
複製代碼
  • 查看 eds
# 查看 eds
curl http://$PILOT_SVC_IP:8080/debug/edsz|grep "outbound|53||kube-dns.kube-system.svc.cluster.local" -A 27 -B 1

{
  "clusterName": "outbound|53||kube-dns.kube-system.svc.cluster.local",
  "endpoints": [
    {
      "lbEndpoints": [
        {
          "endpoint": {
            "address": {
              "socketAddress": {
                "address": "172.30.135.21",
                "portValue": 53
              }
            }
          },
          "metadata": {
            "filterMetadata": {
              "istio": {
                  "uid": "kubernetes://coredns-64b597b598-4rstj.kube-system"
                }
            }
          }
        }
      ]
    },

複製代碼
  • 各個微服務之間是直接經過 PodIP + Port 來通訊的,Service 只是作一個邏輯關聯用來定位 Pod,實際通訊的時候並無經過 Service

Envoy 本身實現了一套流量轉發機制, 當訪問 ClusterIP 時,Envoy 就把流量轉發到具體的 Pod 上去,不須要藉助 kube-proxy 的 iptables 或 ipvs 規則docker

curl http://$PILOT_SVC_IP:8080/debug/edsz|grep "outbound|9080||productpage.default.svc.cluster.local" -A 27 -B 1

{
  "clusterName": "outbound|9080||productpage.default.svc.cluster.local",
  "endpoints": [
    {
      "locality": {

      },
      "lbEndpoints": [
        {
          "endpoint": {
            "address": {
              "socketAddress": {
                "address": "10.42.0.69",
                "portValue": 9080
              }
            }
          },
          "metadata": {
            "filterMetadata": {
              "envoy.transport_socket_match": {
                  "tlsMode": "istio"
                }
            }
          },
          "loadBalancingWeight": 1
        }
      ],
      "loadBalancingWeight": 1
    }
複製代碼
  • 每一個 Sidecar 都有一個綁定到 0.0.0.0:15001 的監聽器,IP tables 將 pod 的全部入站和出站流量路由到這裏。此監聽器把 useOriginalDst 設置爲 true,

這意味着它將請求交給最符合請求原始目標的監聽器。若是找不到任何匹配的虛擬監聽器,它會將請求發送給返回 404 的 BlackHoleClusterjson

istioctl proxy-config listeners productpage-v1-99b4c9954-vj6hf --port 15001 -o json

[
    {
        "name": "virtual",
        "address": {
            "socketAddress": {
                "address": "0.0.0.0",
                "portValue": 15001
            }
        },
        "filterChains": [
            {
                "filters": [
                    {
                        "name": "envoy.tcp_proxy",
                        "config": {
                            "cluster": "BlackHoleCluster",
                            "stat_prefix": "BlackHoleCluster"
                        }
                    }
                ]
            }
        ],
        "useOriginalDst": true
    }
]
複製代碼
  • 咱們的請求是到 9080 端口的 HTTP 出站請求,這意味着它被切換到 0.0.0.0:9080 虛擬監聽器。而後,此監聽器在其配置的 RDS 中查找路由配置。在這種狀況下,它將查找由 Pilot 配置的 RDS 中的路由 9080
istioctl proxy-config listeners productpage-v1-99b4c9954-vj6hf --address 0.0.0.0 --port 9080 -o json

"filters": [
    {
        "name": "envoy.http_connection_manager",
        "typedConfig": {
            "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager",
            "statPrefix": "outbound_0.0.0.0_9080",
            "rds": {
                "configSource": {
                    "ads": {}
                },
                "routeConfigName": "9080"
            },
複製代碼
  • 9080 路由配置僅爲每一個服務提供虛擬主機。咱們的請求正在前往 reviews 服務,所以 Envoy 將選擇咱們的請求與域匹配的虛擬主機。一旦在域上匹配,Envoy 會查找與請求匹配的第一條路徑。在這種狀況下,咱們沒有任何高級路由,所以只有一條路由匹配全部內容。這條路由告訴 Envoy 將請求發送到 outbound|9080||reviews.default.svc.cluster.local 集羣。
istioctl proxy-config routes productpage-v1-99b4c9954-vj6hf --name 9080 -o json 

[
    {
        "name": "9080",
        "virtualHosts": [
            {
                "name": "reviews.default.svc.cluster.local:9080",
                "domains": [
                    "reviews.default.svc.cluster.local",
                    "reviews.default.svc.cluster.local:9080",
                    "reviews",
                    "reviews:9080",
                    "reviews.default.svc.cluster",
                    "reviews.default.svc.cluster:9080",
                    "reviews.default.svc",
                    "reviews.default.svc:9080",
                    "reviews.default",
                    "reviews.default:9080",
                    "172.21.152.34",
                    "172.21.152.34:9080"
                ],
                "routes": [
                    {
                        "match": {
                            "prefix": "/"
                        },
                        "route": {
                            "cluster": "outbound|9080||reviews.default.svc.cluster.local",
                            "timeout": "0.000s"
                        },
複製代碼
  • 此集羣配置爲從 Pilot(經過 ADS)檢索關聯的端點。所以,Envoy 將使用 serviceName 字段做爲密鑰來查找端點列表並將請求代理到其中一個端點。
istioctl proxy-config cluster productpage-v1-99b4c9954-vj6hf --fqdn reviews.default.svc.cluster.local -o json

[
    {
        "name": "outbound|9080||reviews.default.svc.cluster.local",
        "type": "EDS",
        "edsClusterConfig": {
            "edsConfig": {
                "ads": {}
            },
            "serviceName": "outbound|9080||reviews.default.svc.cluster.local"
        },
        "connectTimeout": "1.000s",
        "circuitBreakers": {
            "thresholds": [
                {}
            ]
        }
    }
]
    
複製代碼

5 備註

本文核心內容摘自Istio 流量管理實現機制深度解析,爲了加深我的理解,結合實踐,寫了筆記,方便查閱,原文內容請參考:fuckcloudnative.io/posts/istio…後端

專一於大數據及容器雲核心技術解密,若有任何學術交流,可隨時聯繫。更多內容請關注《數據雲技術社區》公衆號,或請轉發郵件至1120746959@qq.comapi

相關文章
相關標籤/搜索