Istio 網關中的 Gateway 和 VirtualService 配置深度解析

原文連接:請求都去哪了?html

經過前幾篇文章的學習與實踐,咱們對 Gateway、VirtualService 和 Destinationrule 的概念和原理有了初步的認知,本篇將對這幾個對象資源的配置文件進行深度地解析,具體細節將會深刻到每個配置項與 Envoy 配置項的映射關係。node

在開始以前,須要先搞清楚咱們建立的這些對象資源最後都交給誰來處理了,負責處理這些資源的就是 pilot。git

1. pilot整體架構

首先咱們回顧一下 pilot 整體架構,上面是官方關於pilot的架構圖,由於是 old_pilot_repo 目錄下,可能與最新架構有出入,僅供參考。所謂的 pilot 包含兩個組件:pilot-agentpilot-discovery。圖裏的 agent 對應 pilot-agent 二進制,proxy 對應 Envoy 二進制,它們兩個在同一個容器中,discovery service 對應 pilot-discovery 二進制,在另一個跟應用分開部署的單獨的 Deployment 中。github

  • discovery service : 從 Kubernetes apiserver list/watch serviceendpointpodnode 等資源信息,監聽 istio 控制平面配置信息(如VirtualService、DestinationRule等), 翻譯爲 Envoy 能夠直接理解的配置格式。
  • proxy : 也就是 Envoy,直接鏈接 discovery service,間接地從 Kubernetes 等服務註冊中心獲取集羣中微服務的註冊狀況。
  • agent : 生成 Envoy 配置文件,管理 Envoy 生命週期。
  • service A/B : 使用了 Istio 的應用,如 Service A/B,的進出網絡流量會被 proxy 接管。

簡單來講 Istio 作爲管理面,集合了配置中心和服務中心兩個功能,並把配置發現和服務發現以一組統一的 xDS 接口提供出來,數據面的 Envoy 經過 xDS 獲取須要的信息來作服務間通訊和服務治理。json

2. pilot-discovery 爲 Envoy 提供的 xds 服務

所謂 xds

pilot-discovery 爲數據面(運行在 sidecar 中的 Envoy 等 proxy 組件)提供控制信息服務,也就是所謂的 discovery service 或者 xds 服務。這裏的 x 是一個代詞,相似雲計算裏的 XaaS 能夠指代 IaaS、PaaS、SaaS 等。在 Istio 中,xds 包括 cds(cluster discovery service)、lds(listener discovery service)、rds(route discovery service)、eds(endpoint discovery service),而 ads(aggregated discovery service) 是對這些服務的一個統一封裝。api

以上 cluster、endpoint、route 等概念的詳細介紹和實現細節能夠參考 Envoy 在社區推廣的 data plane api(github.com/envoyproxy/…),這裏只作簡單介紹:bash

  • endpoint : 一個具體的「應用實例」,對應 ip 和端口號,相似 Kubernetes 中的一個 Pod。
  • cluster : 一個 cluster 是一個「應用集羣」,它對應提供相同服務的一個或多個 endpoint。cluster 相似 Kubernetes 中 Service 的概念,即一個 Kubernetes Service 對應一個或多個用同一鏡像啓動,提供相同服務的 Pod。
  • route : 當咱們作灰度發佈、金絲雀發佈時,同一個服務會同時運行多個版本,每一個版本對應一個 cluster。這時須要經過 route 規則規定請求如何路由到其中的某個版本的 cluster 上。

以上這些內容實際上都是對 Envoy 等 proxy 的配置信息,而所謂的 cluster discovery service、route discovery service 等 xxx discovery service 就是 Envoy 等從 pilot-discovery 動態獲取 endpoint、cluster 等配置信息的協議和實現。爲何要作動態配置加載,天然是爲了使用 istioctl 等工具統1、靈活地配置 service mesh。至於如何經過 istioctl 來查看 xds 信息,下文將會詳細介紹。網絡

而爲何要用 ads 來「聚合」一系列 xds,並不是僅爲了在同一個 gRPC 鏈接上實現多種 xds 來省下幾個網絡鏈接,ads 還有一個很是重要的做用是解決 cdsrds 信息更新順序依賴的問題,從而保證以必定的順序同步各種配置信息,這方面的討論能夠詳見 Envoy官網數據結構

如何查看 xds

pilot-discovery 在初始化階段依次 init 了各類模塊,其中 discovery service 就是 xDS 相關實現。envoy API reference 能夠查到 v1 和 v2 兩個版本的 API 文檔。envoy control plane 給了 v2 grpc 接口相關的數據結構和接口。架構

那麼如何查看 xds 的信息呢?雖然 v2 是 grpc 的接口,可是 pilot 提供了 InitDebug,能夠經過 debug 接口查詢服務和 routes 等服務和配置信息。

查看 eds

首先找到 Service istio-pilot 的 Cluster IP

$ export PILOT_SVC_IP=$(kubectl -n istio-system get svc istio-pilot -o go-template='{{.spec.clusterIP}}')
複製代碼

而後查看 eds:

$ curl http://$PILOT_SVC_IP:8080/debug/edsz
複製代碼
[{
    "clusterName": "outbound|9080||reviews.nino.svc.cluster.local",
    "endpoints": [{
        "lbEndpoints": [{
            "endpoint": {
                "address": {
                    "socketAddress": {
                        "address": "10.244.0.56",
                        "portValue": 9080
                    }
                }
            }
        }, {
            "endpoint": {
                "address": {
                    "socketAddress": {
                        "address": "10.244.0.58",
                        "portValue": 9080
                    }
                }
            }
        }, {
            "endpoint": {
                "address": {
                    "socketAddress": {
                        "address": "10.244.2.25",
                        "portValue": 9080
                    }
                }
            }
        }]
    }]
}, {
    "clusterName": "outbound|9080|v3|reviews.nino.svc.cluster.local",
    "endpoints": [{
        "lbEndpoints": [{
            "endpoint": {
                "address": {
                    "socketAddress": {
                        "address": "10.244.0.58",
                        "portValue": 9080
                    }
                }
            }
        }]
    }]
}]
複製代碼

查看 cds

$ curl http://$PILOT_SVC_IP:8080/debug/cdsz
複製代碼
[{"node": "sidecar~172.30.104.45~fortio-deploy-56dcc85457-b2pkc.default~default.svc.cluster.local-10", "addr": "172.30.104.45:43876", "connect": "2018-08-07 06:31:08.161483005 +0000 UTC m=+54.337448884","Clusters":[{
  "name": "outbound|9080||details.default.svc.cluster.local",
  "type": "EDS",
  "edsClusterConfig": {
    "edsConfig": {
      "ads": {

      }
    },
    "serviceName": "outbound|9080||details.default.svc.cluster.local"
  },
  "connectTimeout": "1.000s",
  "circuitBreakers": {
    "thresholds": [
      {

      }
    ]
  }
},
...
{
  "name": "outbound|9090||prometheus-k8s.monitoring.svc.cluster.local",
  "type": "EDS",
  "edsClusterConfig": {
    "edsConfig": {
      "ads": {

      }
    },
    "serviceName": "outbound|9090||prometheus-k8s.monitoring.svc.cluster.local"
  },
  "connectTimeout": "1.000s",
  "circuitBreakers": {
    "thresholds": [
      {

      }
    ]
  }
},
{
  "name": "BlackHoleCluster",
  "connectTimeout": "5.000s"
}]}
]
複製代碼

查看 ads

$ curl http://$PILOT_SVC_IP:8080/debug/adsz
複製代碼

3. Envoy 基本術語回顧

爲了讓你們更容易理解後面所講的內容,先來回顧一下 Envoy 的基本術語。

  • Listener : 監聽器(listener)是服務(程序)監聽者,就是真正幹活的。 它是能夠由下游客戶端鏈接的命名網絡位置(例如,端口、unix域套接字等)。Envoy 公開一個或多個下游主機鏈接的偵聽器。通常是每臺主機運行一個 Envoy,使用單進程運行,可是每一個進程中能夠啓動任意數量的 Listener(監聽器),目前只監聽 TCP,每一個監聽器都獨立配置必定數量的(L3/L4)網絡過濾器。Listenter 也能夠經過 Listener Discovery Service(LDS)動態獲取。
  • Listener filter : Listener 使用 listener filter(監聽器過濾器)來操做連接的元數據。它的做用是在不更改 Envoy 的核心功能的狀況下添加更多的集成功能。Listener filter 的 API 相對簡單,由於這些過濾器最終是在新接受的套接字上運行。在鏈中能夠互相銜接以支持更復雜的場景,例如調用速率限制。Envoy 已經包含了多個監聽器過濾器。
  • Http Route Table : HTTP 的路由規則,例如請求的域名,Path 符合什麼規則,轉發給哪一個 Cluster。
  • Cluster : 集羣(cluster)是 Envoy 鏈接到的一組邏輯上類似的上游主機。Envoy 經過服務發現發現集羣中的成員。Envoy 能夠經過主動運行情況檢查來肯定集羣成員的健康情況。Envoy 如何將請求路由到集羣成員由負載均衡策略肯定。

更多詳細信息能夠參考 Envoy 的架構與基本術語,本文重點突出 ListenerRouteCluster 這三個基本術語,同時須要注意流量通過這些術語的前後順序,請求首先到達 Listener,而後經過 Http Route Table 轉到具體的 Cluster,最後由具體的 Cluster 對請求作出響應。

4. Gateway 和 VirtualService 配置解析

仍是拿以前 Istio 流量管理 這篇文章中的例子來解析吧,首先建立了一個 Gateway,配置文件以下:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: bookinfo-gateway
spec:
 selector:
 istio: ingressgateway # use istio default controller
 servers:
 - port:
 number: 80
 name: http
 protocol: HTTP
 hosts:
 - "*"
複製代碼

而後又建立了一個 VirtualService

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

VirtualService 映射的就是 Envoy 中的 Http Route Table,你們能夠注意到上面的 VirtualService 配置文件中有一個 gateways 字段,若是有這個字段,就表示這個 Http Route Table 是綁在 ingressgatewayListener 中的;若是沒有這個字段,就表示這個 Http Route Table 是綁在 Istio 所管理的全部微服務應用的 Pod 上的。

爲了分清主次,我決定將本文拆分紅兩篇文章來說解,本篇主要圍繞 ingressgateway 來解析 Gateway 和 VirtualService,而微服務應用自己的 VirtualService 和 DestinationRule 解析放到下一篇文章再說。

顯而易見,上面這個 VirtualService 映射的 Http Route Table 是被綁在 ingressgateway 中的,能夠經過 istioctl 來查看,istioctl 的具體用法請參考:調試 Envoy 和 Pilot

首先查看 Listener 的配置項:

$ istioctl -n istio-system pc listeners istio-ingressgateway-b6db8c46f-qcfks --port 80 -o json
複製代碼
[
    {
        "name": "0.0.0.0_80",
        "address": {
            "socketAddress": {
                "address": "0.0.0.0",
                "portValue": 80
            }
        },
        "filterChains": [
            {
                "filters": [
                    {
                        "name": "envoy.http_connection_manager",
                        "config": {
                            ...
                            "rds": {
                                "config_source": {
                                    "ads": {}
                                },
                                "route_config_name": "http.80"
                            },
                            ...
                        }
                    }
                ]
            }
        ]
    }
]
複製代碼

經過 rds 配置項的 route_config_name 字段能夠知道該 Listener 使用的 Http Route Table 的名字是 http.80

查看 Http Route Table 配置項:

$ istioctl -n istio-system pc routes istio-ingressgateway-b6db8c46f-qcfks --name http.80 -o json
複製代碼
[
    {
        "name": "http.80",
        "virtualHosts": [
            {
                "name": "bookinfo:80",
                "domains": [
                    "*"
                ],
                "routes": [
                    {
                        "match": {
                            "path": "/productpage"
                        },
                        "route": {
                            "cluster": "outbound|9080||productpage.default.svc.cluster.local",
                            "timeout": "0.000s",
                            "maxGrpcTimeout": "0.000s"
                        },
                        ...
                    },
                    ...
                    {
                        "match": {
                            "prefix": "/api/v1/products"
                        },
                        "route": {
                            "cluster": "outbound|9080||productpage.default.svc.cluster.local",
                            "timeout": "0.000s",
                            "maxGrpcTimeout": "0.000s"
                        },
                        ...
                    },
                    ...
                ]
            }
        ],
        "validateClusters": false
    }
]
複製代碼
  • VirtualService 中的 hosts 字段對應 Http Route Table 中 virtualHosts 配置項的 domains 字段。這裏表示可使用任何域名來經過 ingressgateway 訪問服務(也能夠直接經過 IP 來訪問)。
  • VirtualService 中的 exact 字段對應 Http Route Table 中 routes.match 配置項的 path 字段。
  • VirtualService 中的 prefix 字段對應 Http Route Table 中 routes.match 配置項的 prefix 字段。
  • VirtualService 中的 route.destination 配置項對應 Http Route Table 中 routes.route 配置項的 cluster 字段。

關於 Envoy 中的 HTTP 路由解析能夠參考我以前的文章:HTTP 路由解析

查看 Cluster 配置項:

$ istioctl -n istio-system pc clusters istio-ingressgateway-b6db8c46f-qcfks --fqdn productpage.default.svc.cluster.local --port 9080 -o json
複製代碼
[
    {
        "name": "outbound|9080||productpage.default.svc.cluster.local",
        "type": "EDS",
        "edsClusterConfig": {
            "edsConfig": {
                "ads": {}
            },
            "serviceName": "outbound|9080||productpage.default.svc.cluster.local"
        },
        "connectTimeout": "1.000s",
        "circuitBreakers": {
            "thresholds": [
                {}
            ]
        }
    }
]
複製代碼

能夠看到,Cluster 最終將集羣外經過 ingressgateway 發起的請求轉發給實際的 endpoint,也就是 Kubernetes 集羣中的 Service productpage 下面的 Pod(由 serviceName 字段指定)。

實際上 istioctl 正是經過 pilot 的 xds 接口來查看 Listener 、Route 和 Cluster 等信息的。

好了,如今請求已經轉交給 productpage 了,那麼接下來這個請求將會如何走完整個旅程呢?請聽下回分解!

5. 參考

相關文章
相關標籤/搜索