原文連接:請求都去哪了?html
經過前幾篇文章的學習與實踐,咱們對 Gateway、VirtualService 和 Destinationrule 的概念和原理有了初步的認知,本篇將對這幾個對象資源的配置文件進行深度地解析,具體細節將會深刻到每個配置項與 Envoy 配置項的映射關係。node
在開始以前,須要先搞清楚咱們建立的這些對象資源最後都交給誰來處理了,負責處理這些資源的就是 pilot。git
首先咱們回顧一下 pilot 整體架構,上面是官方關於pilot的架構圖,由於是 old_pilot_repo 目錄下,可能與最新架構有出入,僅供參考。所謂的 pilot 包含兩個組件:pilot-agent
和 pilot-discovery
。圖裏的 agent
對應 pilot-agent 二進制,proxy
對應 Envoy 二進制,它們兩個在同一個容器中,discovery service
對應 pilot-discovery 二進制,在另一個跟應用分開部署的單獨的 Deployment 中。github
service
、endpoint
、pod
、node
等資源信息,監聽 istio 控制平面配置信息(如VirtualService、DestinationRule等), 翻譯爲 Envoy 能夠直接理解的配置格式。簡單來講 Istio 作爲管理面,集合了配置中心和服務中心兩個功能,並把配置發現和服務發現以一組統一的 xDS
接口提供出來,數據面的 Envoy 經過 xDS 獲取須要的信息來作服務間通訊和服務治理。json
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
cluster
是一個「應用集羣」,它對應提供相同服務的一個或多個 endpoint
。cluster 相似 Kubernetes 中 Service
的概念,即一個 Kubernetes Service 對應一個或多個用同一鏡像啓動,提供相同服務的 Pod。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 還有一個很是重要的做用是解決 cds
、rds
信息更新順序依賴的問題,從而保證以必定的順序同步各種配置信息,這方面的討論能夠詳見 Envoy官網。數據結構
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
複製代碼
爲了讓你們更容易理解後面所講的內容,先來回顧一下 Envoy 的基本術語。
更多詳細信息能夠參考 Envoy 的架構與基本術語,本文重點突出 Listener
、Route
和 Cluster
這三個基本術語,同時須要注意流量通過這些術語的前後順序,請求首先到達 Listener
,而後經過 Http Route Table
轉到具體的 Cluster
,最後由具體的 Cluster 對請求作出響應。
仍是拿以前 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 是綁在 ingressgateway
的 Listener
中的;若是沒有這個字段,就表示這個 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
}
]
複製代碼
hosts
字段對應 Http Route Table 中 virtualHosts
配置項的 domains
字段。這裏表示可使用任何域名來經過 ingressgateway 訪問服務(也能夠直接經過 IP 來訪問)。exact
字段對應 Http Route Table 中 routes.match
配置項的 path
字段。prefix
字段對應 Http Route Table 中 routes.match
配置項的 prefix
字段。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 了,那麼接下來這個請求將會如何走完整個旅程呢?請聽下回分解!