自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Istio Envoy 配置解讀,看這篇就夠了

開發(fā) 前端
Gateway 資源是用來配置允許外部流量進(jìn)入 Istio 服務(wù)網(wǎng)格的流量入口,用于接收傳入的 HTTP/TCP 連接。它會配置暴露的端口、協(xié)議等,但與 Kubernetes Ingress 資源不同,不會包括任何流量路由配置,真正的路由規(guī)則是通過 VirtualService 來配置的。

前面我們創(chuàng)建了一個 Gateway 和 VirtualService 對象,用來對外暴露應(yīng)用,然后我們就可以通過 ingressgateway 來訪問 Bookinfo 應(yīng)用了。那么這兩個資源對象是如何實現(xiàn)的呢?

Gateway 資源是用來配置允許外部流量進(jìn)入 Istio 服務(wù)網(wǎng)格的流量入口,用于接收傳入的 HTTP/TCP 連接。它會配置暴露的端口、協(xié)議等,但與 Kubernetes Ingress 資源不同,不會包括任何流量路由配置,真正的路由規(guī)則是通過 VirtualService 來配置的。

我們再查看一下前面創(chuàng)建的 Gateway 對象的定義:

# samples/bookinfo/networking/bookinfo-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector: # 如果使用的是 Helm 方式安裝,則默認(rèn)應(yīng)該是 istio=ingress 標(biāo)簽
    istio: ingressgateway # 匹配 ingress gateway pod 的標(biāo)簽(kubectl get pods -l istio=ingressgateway -n istio-system)
  servers:
    - port:
        number: 8080
        name: http
        protocol: HTTP
      hosts:
        - "*"

這里定義的 Gateway 對象中有一個 selector 標(biāo)簽選擇器,它會匹配 istio=ingressgateway 標(biāo)簽的 Pod,其實就是 istio-ingressgateway 這個組件。

其實本質(zhì)上 istio-ingressgateway 也是一個 Envoy 代理,用來作為 Istio 的統(tǒng)一入口網(wǎng)關(guān),它會接收外部流量,然后根據(jù) VirtualService 中定義的路由規(guī)則來進(jìn)行流量的轉(zhuǎn)發(fā)。

我們可以查看下 istio-ingressgateway 的 Envoy 配置來驗證下:

# 進(jìn)入 ingressgateway 組件所在的 Pod 中
$ kubectl exec -it istio-ingressgateway-9c8b9b586-s6s48 -n istio-system -- /bin/bash
istio-proxy@istio-ingressgateway-9c8b9b586-s6s48:/$ ll /etc/istio/proxy
total 20
drwxrwsrwx 2 root        istio-proxy    66 Nov  3 02:16 ./
drwxr-xr-x 7 root        root          103 Nov  3 02:16 ../
srw-rw-rw- 1 istio-proxy istio-proxy     0 Nov  3 02:16 XDS=
-rw-r--r-- 1 istio-proxy istio-proxy 14130 Nov  3 02:16 envoy-rev.json
-rw-r--r-- 1 istio-proxy istio-proxy  2699 Nov  3 02:16 grpc-bootstrap.json
istio-proxy@istio-ingressgateway-9c8b9b586-s6s48:/$

在 istio-ingressgateway 組件的 Pod 目錄中有一個配置文件 envoy-rev.json,這個文件就是 Envoy 的配置文件,該文件通過 istio 為 sidecar 注入的參數(shù)在啟動的時候修改或生成,由于這里采用的是 xDS 動態(tài)配置的方式,所以直接看不到前面我們添加的 Gateway 相關(guān)信息的,但是我們可以利用 Envoy 的 Admin 提供的 config_dump 來查看下配置文件:

kubectl exec istio-ingressgateway-9c8b9b586-s6s48 -c istio-proxy -n istio-system  -- curl 'localhost:15000/config_dump' > ingressgateway_envoy_conf.json

istio envoy 默認(rèn)配置為 json 格式,導(dǎo)出來的配置文件非常長(有 10000+行),我們可以先只看上層內(nèi)容:

istio envoy 配置

我們可以看到這個配置文件中其實就一個 configs 數(shù)組,每個元素都是一項配置,每個配置都指定了一個獨特的 @type 字段,來指定該配置是是干嘛的。接下來我們就來看下這個配置文件中的每個配置項都是干嘛的。

BootStrapConfigDump

用于在 Envoy 啟動時加載的一些靜態(tài)配置,包括類似 Sidecar 的環(huán)境變量等信息。我們也可以使用 istioctl proxy-config bootstrap 命令來查看這部分配置:

$ istioctl proxy-config bootstrap istio-ingressgateway-9c8b9b586-s6s48 -n istio-system -o yaml
bootstrap:
  admin:
    address:
      socketAddress:
        address: 127.0.0.1
        portValue: 15000
    profilePath: /var/lib/istio/data/envoy.prof
  dynamicResources:  # 動態(tài)配置發(fā)現(xiàn)服務(wù)信息
    adsConfig:
      apiType: GRPC
      grpcServices:
      - envoyGrpc:
          clusterName: xds-grpc
      setNodeOnFirstMessageOnly: true
      transportApiVersion: V3
    cdsConfig:
      ads: {}
      initialFetchTimeout: 0s
      resourceApiVersion: V3
    ldsConfig:
      ads: {}
      initialFetchTimeout: 0s
      resourceApiVersion: V3
  node:  # 節(jié)點信息
    cluster: istio-ingressgateway.istio-system
    id: router~10.244.2.52~istio-ingressgateway-9c8b9b586-s6s48.istio-system~istio-system.svc.cluster.local
    # ......
  staticResources:
    clusters:
    - connectTimeout: 0.250s  # prometheus cluster
      loadAssignment:
        clusterName: prometheus_stats
        endpoints:
        - lbEndpoints:
          - endpoint:
              address:
                socketAddress:
                  address: 127.0.0.1
                  portValue: 15000
      name: prometheus_stats
      type: STATIC
    - connectTimeout: 0.250s  # agent cluster
      loadAssignment:
        clusterName: agent
        endpoints:
        - lbEndpoints:
          - endpoint:
              address:
                socketAddress:
                  address: 127.0.0.1
                  portValue: 15020
      name: agent
      type: STATIC
    - connectTimeout: 1s
      loadAssignment:
        clusterName: sds-grpc
        endpoints:
        - lbEndpoints:
          - endpoint:
              address:
                pipe:
                  path: ./var/run/secrets/workload-spiffe-uds/socket
      name: sds-grpc
      type: STATIC
      typedExtensionProtocolOptions:
        envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
          '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
          explicitHttpConfig:
            http2ProtocolOptions: {}
    - circuitBreakers:
        thresholds:
        - maxConnections: 100000
          maxPendingRequests: 100000
          maxRequests: 100000
        - maxConnections: 100000
          maxPendingRequests: 100000
          maxRequests: 100000
          priority: HIGH
      connectTimeout: 1s
      loadAssignment:  # xds-grpc cluster
        clusterName: xds-grpc
        endpoints:
        - lbEndpoints:
          - endpoint:
              address:
                pipe:
                  path: ./etc/istio/proxy/XDS
      maxRequestsPerConnection: 1
      name: xds-grpc
      type: STATIC
      typedExtensionProtocolOptions:
        envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
          '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
          explicitHttpConfig:
            http2ProtocolOptions: {}
      upstreamConnectionOptions:
        tcpKeepalive:
          keepaliveTime: 300
    - connectTimeout: 1s
      dnsLookupFamily: V4_ONLY
      dnsRefreshRate: 30s
      loadAssignment:  # zipkin cluster
        clusterName: zipkin
        endpoints:
        - lbEndpoints:
          - endpoint:
              address:
                socketAddress:
                  address: zipkin.istio-system
                  portValue: 9411
      name: zipkin
      respectDnsTtl: true
      type: STRICT_DNS
    listeners:
    - address:
        socketAddress:
          address: 0.0.0.0
          portValue: 15090  # prometheus listener
      filterChains:
      - filters:
        - name: envoy.filters.network.http_connection_manager
          typedConfig:
            '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
            httpFilters:
            - name: envoy.filters.http.router
              typedConfig:
                '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
            routeConfig:
              virtualHosts:
              - domains:
                - '*'
                name: backend
                routes:
                - match:
                    prefix: /stats/prometheus
                  route:
                    cluster: prometheus_stats
            statPrefix: stats
    - address:
        socketAddress:
          address: 0.0.0.0
          portValue: 15021  # agent listener(健康檢查)
      filterChains:
      - filters:
        - name: envoy.filters.network.http_connection_manager
          typedConfig:
            '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
            httpFilters:
            - name: envoy.filters.http.router
              typedConfig:
                '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
            routeConfig:
              virtualHosts:
              - domains:
                - '*'
                name: backend
                routes:
                - match:
                    prefix: /healthz/ready
                  route:
                    cluster: agent
            statPrefix: agent
  statsConfig:
  # ......
  tracing: # 鏈路追蹤
    http:
      name: envoy.tracers.zipkin
      typedConfig:
        '@type': type.googleapis.com/envoy.config.trace.v3.ZipkinConfig
        collectorCluster: zipkin
        collectorEndpoint: /api/v2/spans
        collectorEndpointVersion: HTTP_JSON
        sharedSpanContext: false
        traceId128bit: true

上面的配置和之前我們介紹的 Envoy 配置基本一致,在上面配置中定義了一個 Prometheus 監(jiān)聽器,用來暴露 Prometheus 監(jiān)控指標(biāo),還定義了一個 Agent 監(jiān)聽器,用來暴露健康檢查接口,另外還定義了一個 zipkin 集群,用來定義鏈路追蹤的配置。另外通過 dynamicResources 定義了動態(tài)配置發(fā)現(xiàn)服務(wù)信息,xds-grpc 就是用來定義 Envoy 與 Pilot 之間的 xDS 通信的。

ListenersConfigDump

這里存儲著 Envoy 的 listeners 配置,也就是 Envoy 的監(jiān)聽器。Envoy 在攔截到請求后,會根據(jù)請求的地址與端口,將請求交給匹配的 listener 處理。

我們看到這個 ListenersConfigDump 中的 listener 配置分成了 static_listners 和 dynamic_listeners,分別對應(yīng) Envoy 的靜態(tài)配置和動態(tài)配置,靜態(tài)配置,是 Envoy 配置文件中直接指定的,而 dynamic_listeners的 listener 則是 istiod 通過 xDS 協(xié)議為 Envoy 下發(fā)的。

同樣我們也可以使用 istioctl proxy-config listener 命令來查看這部分配置:

istioctl proxy-config listener istio-ingressgateway-9c8b9b586-s6s48 -n istio-system -o yaml

對應(yīng)的配置文件如下所示:

- accessLog:
    - filter:
        responseFlagFilter:
          flags:
            - NR
      name: envoy.access_loggers.file
      typedConfig:
        "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
        logFormat:
          textFormatSource:
            inlineString: |
              [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %RESPONSE_CODE_DETAILS% %CONNECTION_TERMINATION_DETAILS% "%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%
        path: /dev/stdout
  address:
    socketAddress:
      address: 0.0.0.0
      portValue: 8080
  continueOnListenerFiltersTimeout: true
  filterChains:
    - filters:
        - name: istio_authn
          typedConfig:
            "@type": type.googleapis.com/udpa.type.v1.TypedStruct
            typeUrl: type.googleapis.com/io.istio.network.authn.Config
        - name: envoy.filters.network.http_connection_manager
          typedConfig:
            "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
            accessLog:
              - name: envoy.access_loggers.file
                typedConfig:
                  "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
                  logFormat:
                    textFormatSource:
                      inlineString: |
                        [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %RESPONSE_CODE_DETAILS% %CONNECTION_TERMINATION_DETAILS% "%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%
                  path: /dev/stdout
            forwardClientCertDetails: SANITIZE_SET
            httpFilters:
              - name: istio.metadata_exchange
                typedConfig:
                  "@type": type.googleapis.com/udpa.type.v1.TypedStruct
                  typeUrl: type.googleapis.com/io.istio.http.peer_metadata.Config
                  value:
                    upstream_discovery:
                      - istio_headers: {}
                      - workload_discovery: {}
                    upstream_propagation:
                      - istio_headers: {}
              - name: envoy.filters.http.grpc_stats
                typedConfig:
                  "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_stats.v3.FilterConfig
                  emitFilterState: true
                  statsForAllMethods: false
              - name: istio.alpn
                typedConfig:
                  "@type": type.googleapis.com/istio.envoy.config.filter.http.alpn.v2alpha1.FilterConfig
                  alpnOverride:
                    - alpnOverride:
                        - istio-http/1.0
                        - istio
                        - http/1.0
                    - alpnOverride:
                        - istio-http/1.1
                        - istio
                        - http/1.1
                      upstreamProtocol: HTTP11
                    - alpnOverride:
                        - istio-h2
                        - istio
                        - h2
                      upstreamProtocol: HTTP2
              - name: envoy.filters.http.fault
                typedConfig:
                  "@type": type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault
              - name: envoy.filters.http.cors
                typedConfig:
                  "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
              - name: istio.stats
                typedConfig:
                  "@type": type.googleapis.com/stats.PluginConfig
                  disableHostHeaderFallback: true
              - name: envoy.filters.http.router
                typedConfig:
                  "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
            httpProtocolOptions: {}
            normalizePath: true
            pathWithEscapedSlashesAction: KEEP_UNCHANGED
            rds:
              configSource:
                ads: {}
                initialFetchTimeout: 0s
                resourceApiVersion: V3
              routeConfigName: http.8080
            requestIdExtension:k
              typedConfig:
                "@type": type.googleapis.com/envoy.extensions.request_id.uuid.v3.UuidRequestIdConfig
                useRequestIdForTraceSampling: true
            serverName: istio-envoy
            setCurrentClientCertDetails:
              cert: true
              dns: true
              subject: true
              uri: true
            statPrefix: outbound_0.0.0.0_8080
            streamIdleTimeout: 0s
            tracing:
              # ......
            upgradeConfigs:
              - upgradeType: websocket
            useRemoteAddress: true
  name: 0.0.0.0_8080
  trafficDirection: OUTBOUND
- address:
    socketAddress:
      address: 0.0.0.0
      portValue: 15090
  # ......
- address:
    socketAddress:
      address: 0.0.0.0
      portValue: 15021
  # ......

雖然上面看到的 listener 配置還是很長,但是我們應(yīng)該也還是非常熟悉的,本質(zhì)就是 Envoy 的配置文件中的 listener 配置。我們這里重點看下動態(tài)配置對應(yīng)的配置,靜態(tài)的就是前面指定 prometheus 和 agent 對應(yīng)的監(jiān)聽器配置。

我們可以看到上面的動態(tài)配置對應(yīng)的監(jiān)聽器名稱為 0.0.0.0_8080,對應(yīng)的監(jiān)聽地址為 0.0.0.0:8080,也就是說在 Envoy 中監(jiān)聽了 8080 端口:

address:
  socketAddress:
    address: 0.0.0.0
    portValue: 8080

而前面我們是不是創(chuàng)建了一個 Gateway 資源對象,并指定了 8080 端口,其實這個端口就是我們前面創(chuàng)建的 Gateway 對象中定義的端口,這個監(jiān)聽器的配置就是通過 istiod 通過 xDS 協(xié)議下發(fā)的。

那么請求是如何到達(dá)這個監(jiān)聽器的呢?我們可以查看下 istio-ingressgateway 組建的 Service 數(shù)據(jù):

$ kubectl get svc istio-ingressgateway -n istio-system
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                      AGE
istio-ingressgateway   LoadBalancer   10.103.227.57   <pending>     15021:32459/TCP,80:31896/TCP,443:30808/TCP,31400:31535/TCP,15443:30761/TCP   46h

我們可以看到 istio-ingressgateway 組件的 Service 中定義了 5 個端口,還記得前面我們?nèi)ピL問 Productpage 的時候是如何訪問的嗎?是不是通過 http://$GATEWAY_URL/productpage 訪問的,而我們這里沒有 LoadBalancer,所以直接使用 NodePort 形式訪問就行,最終我們是通過 http://NodeIP:31896/productpage 來訪問應(yīng)用的,而這個 31896 端口對應(yīng) istio-ingressgateway 組件的 Service 中定義的 80 端口,也就是說我們的請求是通過 80 端口到達(dá) istio-ingressgateway 組件的,那么這個 80 端口是如何到達(dá) istio-ingressgateway 組件的呢?

$ kubectl describe svc istio-ingressgateway -n istio-system
Name:                     istio-ingressgateway
Namespace:                istio-system
# ......
Port:                     http2  80/TCP
TargetPort:               8080/TCP
NodePort:                 http2  31896/TCP
Endpoints:                10.244.2.52:8080

我們查看 Service 的定義就明白了,實際上 istio-ingressgateway 這個 Service 定義的 80 端口對應(yīng)的是 istio-ingressgateway 組件 Pod 的 8080 端口,也就是說我們的請求是通過 80 端口到達(dá) istio-ingressgateway 組件的 8080 端口的,而這個 8080 端口就是我們前面在 Envoy 配置中看到的監(jiān)聽器的端口了,所以當(dāng)我們訪問 http://$GATEWAY_URL/productpage 的時候請求到達(dá)了 istio-ingressgateway 這個組件的 8080 端口了。

當(dāng)請求到達(dá) istio-ingressgateway 組件時,就會被這個監(jiān)聽器所匹配,然后將請求交給 http_connection_manager 這個 filter 來處理,當(dāng)然后面就是用各種具體的 filter 來處理請求了,比如 envoy.filters.http.fault 這個 filter 就是用來處理故障注入的,envoy.filters.http.router 則是用來處理路由轉(zhuǎn)發(fā)的、envoy.filters.http.cors 則是用來處理跨域請求的等等。

但是我們的請求進(jìn)到 Envoy 后是又該如何路由呢?我應(yīng)該將請求轉(zhuǎn)發(fā)到哪里去呢?這個是不是就是 Envoy 中的路由配置來決定的了,對于靜態(tài)配置我們清楚直接在 Envoy 配置文件中就可以看到,比如:

routeConfig:
  virtualHosts:
    - domains:
        - "*"
      name: backend
      routes:
        - match:
            prefix: /healthz/ready
          route:
            cluster: agent

但是我們這里的路由配置是動態(tài)配置的,我們看到對應(yīng)的配置中有一個 rds 字段,這個字段就是用來指定動態(tài)路由配置的,其中的 routeConfigName 字段就是用來指定對應(yīng)的路由配置名稱的:

rds:
  configSource:
    ads: {}
    initialFetchTimeout: 0s
    resourceApiVersion: V3
  routeConfigName: http.8080

RoutesConfigDump

這里面保存著 Envoy 的路由配置,和 listeners 一樣,RoutesConfigDump 也分為 static_route_configs 和 dynamic_route_configs,分別對應(yīng)著靜態(tài)的路由配置和動態(tài)下發(fā)的路由配置。

同樣我們也可以使用 istioctl proxy-config route 命令來查看這部分配置:

istioctl proxy-config route istio-ingressgateway-9c8b9b586-s6s48 -n istio-system -o yaml

對應(yīng)的配置如下所示:

- ignorePortInHostMatching: true
  maxDirectResponseBodySizeBytes: 1048576
  name: http.8080
  validateClusters: false
  virtualHosts:
    - domains:
        - "*"
      includeRequestAttemptCount: true
      name: "*:8080"
      routes:
        - decorator:
            operation: productpage.default.svc.cluster.local:9080/productpage
          match:
            caseSensitive: true
            path: /productpage
          metadata:
            filterMetadata:
              istio:
                config: /apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo
          route:
            cluster: outbound|9080||productpage.default.svc.cluster.local
            maxGrpcTimeout: 0s
            retryPolicy:
              hostSelectionRetryMaxAttempts: "5"
              numRetries: 2
              retriableStatusCodes:
                - 503
              retryHostPredicate:
                - name: envoy.retry_host_predicates.previous_hosts
                  typedConfig:
                    "@type": type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
              retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes
            timeout: 0s
        - decorator:
            operation: productpage.default.svc.cluster.local:9080/static*
          match:
            caseSensitive: true
            prefix: /static
          metadata:
            filterMetadata:
              istio:
                config: /apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo
          route:
            cluster: outbound|9080||productpage.default.svc.cluster.local
            maxGrpcTimeout: 0s
            retryPolicy:
              hostSelectionRetryMaxAttempts: "5"
              numRetries: 2
              retriableStatusCodes:
                - 503
              retryHostPredicate:
                - name: envoy.retry_host_predicates.previous_hosts
                  typedConfig:
                    "@type": type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
              retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes
            timeout: 0s
        - decorator:
            operation: productpage.default.svc.cluster.local:9080/login
          match:
            caseSensitive: true
            path: /login
          metadata:
            filterMetadata:
              istio:
                config: /apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo
          route:
            cluster: outbound|9080||productpage.default.svc.cluster.local
            maxGrpcTimeout: 0s
            retryPolicy:
              hostSelectionRetryMaxAttempts: "5"
              numRetries: 2
              retriableStatusCodes:
                - 503
              retryHostPredicate:
                - name: envoy.retry_host_predicates.previous_hosts
                  typedConfig:
                    "@type": type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
              retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes
            timeout: 0s
        - decorator:
            operation: productpage.default.svc.cluster.local:9080/logout
          match:
            caseSensitive: true
            path: /logout
          metadata:
            filterMetadata:
              istio:
                config: /apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo
          route:
            cluster: outbound|9080||productpage.default.svc.cluster.local
            maxGrpcTimeout: 0s
            retryPolicy:
              hostSelectionRetryMaxAttempts: "5"
              numRetries: 2
              retriableStatusCodes:
                - 503
              retryHostPredicate:
                - name: envoy.retry_host_predicates.previous_hosts
                  typedConfig:
                    "@type": type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
              retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes
            timeout: 0s
        - decorator:
            operation: productpage.default.svc.cluster.local:9080/api/v1/products*
          match:
            caseSensitive: true
            prefix: /api/v1/products
          metadata:
            filterMetadata:
              istio:
                config: /apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo
          route:
            cluster: outbound|9080||productpage.default.svc.cluster.local
            maxGrpcTimeout: 0s
            retryPolicy:
              hostSelectionRetryMaxAttempts: "5"
              numRetries: 2
              retriableStatusCodes:
                - 503
              retryHostPredicate:
                - name: envoy.retry_host_predicates.previous_hosts
                  typedConfig:
                    "@type": type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
              retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes
            timeout: 0s
- virtualHosts:
    - domains:
        - "*"
      name: backend
      routes:
        - match:
            prefix: /stats/prometheus
          route:
            cluster: prometheus_stats
- virtualHosts:
    - domains:
        - "*"
      name: backend
      routes:
        - match:
            prefix: /healthz/ready
          route:
            cluster: agent

后面的兩個 virtualHosts 就是我們的靜態(tài)路由配置,第一個是動態(tài)的路由配置,我們可以看到該配置的名稱就是 http.8080,是不是和前面的 routeConfigName 是一致的。那么這個配置又是什么地方定義的呢?

其實仔細(xì)看這里面的配置和前面我們創(chuàng)建的 VirtualService 這個資源對象是不是很像,我們再看下前面創(chuàng)建的 VirtualService 對象的定義:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
    - "*"
  gateways:
    - bookinfo-gateway
  http:
    - match:
        - uri:
            exact: /productpage
        - uri:
            prefix: /static
        - uri:
            exact: /login
        - uri:
            exact: /logout
        - uri:
            prefix: /api/v1/products
      route:
        - destination:
            host: productpage
            port:
              number: 9080

我們可以看到在 VirtualService 對象中定義了 5 個路由規(guī)則,而這里的 RoutesConfigDump 中也定義了 5 個路由規(guī)則,VirtualService 中定義的 5 個路由分別為 /productpage、/static、/login、/logout、/api/v1/products,而 RoutesConfigDump 中定義的 5 個路由分別為 /productpage、/static、/login、/logout、/api/v1/products,是不是一一對應(yīng)的。最終匹配這些路由規(guī)則的請求是被轉(zhuǎn)發(fā)到 productpage 這個服務(wù)的 9080 端口的。

比如 /productpage 這個路由規(guī)則對應(yīng)的 Envoy 配置如下所示:

- domains:
    - "*"
  includeRequestAttemptCount: true
  name: "*:8080"
  routes:
    - decorator:
        operation: productpage.default.svc.cluster.local:9080/productpage
      match:
        caseSensitive: true
        path: /productpage
      metadata:
        filterMetadata:
          istio:
            config: /apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo
      route:
        cluster: outbound|9080||productpage.default.svc.cluster.local
        maxGrpcTimeout: 0s
        retryPolicy:
          hostSelectionRetryMaxAttempts: "5"
          numRetries: 2
          retriableStatusCodes:
            - 503
          retryHostPredicate:
            - name: envoy.retry_host_predicates.previous_hosts
              typedConfig:
                "@type": type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
          retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes
        timeout: 0s

這個配置就是我們熟悉的 Envoy 的關(guān)于虛擬主機(jī)部分的配置,比如當(dāng)我們請求的路徑為 /productpage 時,就會被這個路由規(guī)則匹配到,然后就用通過 route 字段來描述我們的路由目標(biāo)了,針對這個目錄,可以看到有一些類似于 retry_policy、timeout等字段來配置這個目標(biāo)的超時、重試策略等,不過最重要的還是 cluster 這個字段,它指定了這個路由目標(biāo)對應(yīng)著哪個上游集群,Envoy 最終將請求發(fā)送到這個 Cluster,比如我們這里的集群名稱為 outbound|9080||productpage.default.svc.cluster.local,關(guān)于其具體配置我們就要去查看 ClustersConfigDump 中的配置了。

ClustersConfigDump

該部分是用來存儲 Envoy 的集群配置的,同樣也分為 static_clusters 和 dynamic_active_clusters,分別對應(yīng)著靜態(tài)配置和動態(tài)下發(fā)的配置。這里的 Cluster 集群是 Envoy 內(nèi)部的概念,它是指 Envoy 連接的一組邏輯相同的上游主機(jī),并不是說 K8s 集群,只是大多數(shù)情況下我們可以把這個集群理解為 K8s 集群中的一個 Service,一個 Service 通常對應(yīng)著一組 Pod,由這組 Pod 響應(yīng)請求并提供同一種服務(wù),而 Envoy 的這個集群實際可以理解成這種Pod 集合。不過 Envoy 的一個集群也不一定就對應(yīng)著一個 Service,因為集群是一組邏輯相同的上游主機(jī),所以也有可能是別的符合定義的東西,比如說是服務(wù)的一個特定版本(如只是 v2 版本的 reviews 服務(wù))。istio 的版本灰度能力就是基于這個做的,因為兩個版本的同一服務(wù)實際上可以分成兩個集群。

同樣我們可以使用 istioctl proxy-config cluster 命令來查看這部分配置:

istioctl proxy-config cluster istio-ingressgateway-9c8b9b586-s6s48 -n istio-system -o yaml

該配置文件會非常長,它會將 K8s 集群中的 Service 都轉(zhuǎn)換成 Envoy 的 Cluster,這里我們只看下 productpage 這個服務(wù)對應(yīng)的 Cluster 配置,如下所示:

- circuitBreakers: #
    thresholds:
      - maxConnections: 4294967295
        maxPendingRequests: 4294967295
        maxRequests: 4294967295
        maxRetries: 4294967295
        trackRemaining: true
  commonLbConfig:
    localityWeightedLbConfig: {}
  connectTimeout: 10s
  edsClusterConfig:
    edsConfig:
      ads: {}
      initialFetchTimeout: 0s
      resourceApiVersion: V3
    serviceName: outbound|9080||productpage.default.svc.cluster.local
  filters:
    - name: istio.metadata_exchange
      typedConfig:
        "@type": type.googleapis.com/envoy.tcp.metadataexchange.config.MetadataExchange
        protocol: istio-peer-exchange
  lbPolicy: LEAST_REQUEST
  metadata:
    filterMetadata:
      istio:
        services:
          - host: productpage.default.svc.cluster.local
            name: productpage
            namespace: default
  name: outbound|9080||productpage.default.svc.cluster.local
  transportSocketMatches:
    - match:
        tlsMode: istio
      name: tlsMode-istio
      transportSocket:
        name: envoy.transport_sockets.tls
        typedConfig:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
          commonTlsContext:
            alpnProtocols:
              - istio-peer-exchange
              - istio
            combinedValidationContext:
              defaultValidationContext:
                matchSubjectAltNames:
                  - exact: spiffe://cluster.local/ns/default/sa/bookinfo-productpage
              validationContextSdsSecretConfig:
                name: ROOTCA
                sdsConfig:
                  apiConfigSource:
                    apiType: GRPC
                    grpcServices:
                      - envoyGrpc:
                          clusterName: sds-grpc
                    setNodeOnFirstMessageOnly: true
                    transportApiVersion: V3
                  initialFetchTimeout: 0s
                  resourceApiVersion: V3
            tlsCertificateSdsSecretConfigs:
              - name: default
                sdsConfig:
                  apiConfigSource:
                    apiType: GRPC
                    grpcServices:
                      - envoyGrpc:
                          clusterName: sds-grpc
                    setNodeOnFirstMessageOnly: true
                    transportApiVersion: V3
                  initialFetchTimeout: 0s
                  resourceApiVersion: V3
            tlsParams:
              tlsMaximumProtocolVersion: TLSv1_3
              tlsMinimumProtocolVersion: TLSv1_2
          sni: outbound_.9080_._.productpage.default.svc.cluster.local
    - match: {}
      name: tlsMode-disabled
      transportSocket:
        name: envoy.transport_sockets.raw_buffer
        typedConfig:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer
  type: EDS

我們可以看到這個 Envoy Cluster 的名稱為 outbound|9080||productpage.default.svc.cluster.local,和前面的路由配置中的 cluster 字段是一致的,名稱大多數(shù)會由 | 分成四個部分,分別是 inbound 或 outbound 代表入向流量或出向流量、端口號、subcluster 名稱(就是對應(yīng)著 destination rule 里的 subset)、Service FQDN,由 istio 的服務(wù)發(fā)現(xiàn)進(jìn)行配置,通過這個 name 我們很容易就能看出來這個集群對應(yīng)的是 K8s 集群的哪個服務(wù)。

然后配置的負(fù)載均衡策略是 LEAST_REQUEST,另外比較重要的這里的配置的類型為 type: EDS,也就是會通過 EDS 來發(fā)現(xiàn)上游的主機(jī)服務(wù),這個 EDS 的配置如下所示:

edsClusterConfig:
  edsConfig:
    ads: {}
    initialFetchTimeout: 0s
    resourceApiVersion: V3
  serviceName: outbound|9080||productpage.default.svc.cluster.local

基于 EDS 去動態(tài)發(fā)現(xiàn)上游主機(jī)的配置,其實在前面的 Envoy 章節(jié)我們已經(jīng)介紹過了,和這里是不是幾乎是一致的,serviceName 其實就對應(yīng)著 K8s 集群中的 productpage 這個 Service 對象的 9080 端口,而這個 Service 對象對應(yīng)著一組 Pod,這組 Pod 就是我們的上游主機(jī)了。當(dāng)然這是通過 xDS 協(xié)議下發(fā)的,我們可以通過 istioctl proxy-config endpoint 命令來查看這部分配置:

istioctl proxy-config endpoint istio-ingressgateway-9c8b9b586-s6s48 -n istio-system -o yaml

該部分?jǐn)?shù)據(jù)非常多,下面只截取 productpage 相關(guān)的數(shù)據(jù),如下所示:

- addedViaApi: true
  circuitBreakers:
    thresholds:
    - maxConnections: 4294967295
      maxPendingRequests: 4294967295
      maxRequests: 4294967295
      maxRetries: 4294967295
    - maxConnections: 1024
      maxPendingRequests: 1024
      maxRequests: 1024
      maxRetries: 3
      priority: HIGH
  edsServiceName: outbound|9080||productpage.default.svc.cluster.local
  hostStatuses:
  - address:
      socketAddress:
        address: 10.244.2.62
        portValue: 9080
    healthStatus:
      edsHealthStatus: HEALTHY
    locality: {}
    stats:
    - name: cx_connect_fail
    - name: cx_total
      value: "1"
    - name: rq_error
    - name: rq_success
      value: "4"
    - name: rq_timeout
    - name: rq_total
      value: "4"
    - name: cx_active
      type: GAUGE
    - name: rq_active
      type: GAUGE
    weight: 1
  name: outbound|9080||productpage.default.svc.cluster.local
  observabilityName: outbound|9080||productpage.default.svc.cluster.local

可以看到上面的配置中就包含一個真正的后端服務(wù)地址:

address:
  socketAddress:
    address: 10.244.2.62
    portValue: 9080

這個地址其實就是 productpage 這個 K8s Service 關(guān)聯(lián)的 Pod 的地址。這樣一個請求從進(jìn)入到 Envoy 到最終轉(zhuǎn)發(fā)到后端服務(wù)的過程就清楚了。

SecretsConfigDump

由于網(wǎng)格中的 Envoy 之間互相通信會使用 mTLS 模式,因此每個 Envoy 通信時都需要提供本工作負(fù)載的證書,同時為了簽發(fā)證書還需要 istio ca 的根證書,這些證書的信息保存在該配置項之下。

總結(jié)

到這里我們就把 Envoy 的整個配置文件都理解了一遍,它們分別是 Bootstrap、Listeners、Routes、Clusters、Secrets 幾個配置,其中又涉及到 VirtualHost 等細(xì)分概念。

整體上一個請求在 Envoy 內(nèi)部的處理與轉(zhuǎn)發(fā)過程中,listener、route、cluster 這幾個配置是緊密相連的,它們通過配置的 name 一層又一層地向下引用(listener 內(nèi)的 filter 引用 route、route 內(nèi)的 virtual_host 引用 cluster),形成了一條引用鏈,最終將請求從 listener 遞交到具體的 cluster。

我們可以使用 envoyui.solo.io 這個在線的 Envoy 配置可視化工具來查看 Envoy 的配置,只需要將我們的 Envoy 配置 dump 出來上傳上來即可:

Envoy config dump

經(jīng)過上面的分析我們也明白了其實 Istio 并沒有實現(xiàn)很多復(fù)雜的邏輯,服務(wù)治理相關(guān)的功能比如負(fù)載均衡、故障注入、權(quán)重路由等都是 Envoy 本身就有的能力,Istio 只是將這些能力抽象成了一個個資源對象,然后通過 Envoy 的 xDS 協(xié)議下發(fā)到 Envoy 中,這樣就能夠?qū)崿F(xiàn)對 Envoy 的流量治理了。所以重點還是需要我們先理解 Envoy 的配置,然后再去理解 Istio 的配置,這樣才能更好的理解 Istio,不然你就不清楚 Gateway、VirtualService 等這些資源對象到底是干什么的,它們是如何影響 Envoy 的配置的。

當(dāng)然我們這里還只是分析的 Istio Ingress Gateway 的配置,而對于 Sidecar 模式的 Envoy 代理又是如何去配置的呢?它又是如何將 Pod 的流量進(jìn)行攔截的呢?這些我們后面會繼續(xù)分析。

責(zé)任編輯:姜華 來源: k8s技術(shù)圈
點贊
收藏

51CTO技術(shù)棧公眾號