Istio 的數據平面 Envoy Proxy 配置詳解
本文轉載自:Istio 的數據平面 Envoy Proxy 配置詳解 - jimmysong.io
Envoy 是 Istio Service Mesh 中默認的 Sidecar,Istio 在 Enovy 的基礎上按照 Envoy 的 xDS 協議擴展了其控制平面,在講到 Envoy xDS 協議之前還需要我們先熟悉下 Envoy 的基本術語。下面列舉了 Envoy 里的基本術語及其數據結構解析,關於 Envoy 的詳細介紹請參考 Envoy 官方文檔,至於 Envoy 在 Service Mesh(不僅限於 Istio) 中是如何作為轉發代理工作的請參考網易雲劉超的這篇深入解讀 Service Mesh 背後的技術細節 以及理解 Istio Service Mesh 中 Envoy 代理 Sidecar 注入及流量劫持,本文引用其中的一些觀點,詳細內容不再贅述。

基本術語
下面是您應該了解的 Enovy 里的基本術語:
- Downstream(下游):下游主機連接到 Envoy,發送請求並接收響應,即發送請求的主機。
- Upstream(上游):上游主機接收來自 Envoy 的連接和請求,並返迴響應,即接受請求的主機。
- Listener(監聽器):監聽器是命名網地址(例如,埠、unix domain socket 等),下遊客戶端可以連接這些監聽器。Envoy 暴露一個或者多個監聽器給下游主機連接。
- Cluster(集群):集群是指 Envoy 連接的一組邏輯相同的上游主機。Envoy 通過服務發現來發現集群的成員。可以選擇通過主動健康檢查來確定集群成員的健康狀態。Envoy 通過負載均衡策略決定將請求路由到集群的哪個成員。
我將在本文的後半部分解釋以上術語與 Kubernetes、Istio 中概念之間的聯繫。
關於 xDS 的版本
有一點需要大家注意,就是 Envoy 的 API 有 v1 和 v2 兩個版本,從 Envoy 1.5.0 起 v2 API 就已經生產就緒了,為了能夠讓用戶順利的向 v2 版本的額 API 過度,Envoy 啟動的時候設置了一個 --v2-config-only
的標誌,Enovy 不同版本對 v1/v2 API 的支持詳情請參考 Envoy v1 配置廢棄時間表。
Envoy 的作者 Matt Klein 在 Service Mesh 中的通用數據平面 API 設計這篇文章中說明了 Envoy API v1 的歷史及其缺點,還有 v2 的引入。v2 API 是 v1 的演進,而不是革命,它是 v1 功能的超集。
在 Istio 1.0 及以上版本中使用的是 Envoy 1.8.0-dev 版本,其支持 v2 的 API,同時在 Envoy 作為 Sidecar proxy 啟動的使用使用了例如下面的命令:
$ /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 ratings --service-node sidecar~172.33.14.2~ratings-v1-8558d4458d-ld8x9.default~default.svc.cluster.local --max-obj-name-len 189 --allow-unknown-fields -l warn --v2-config-only
上面是都 Bookinfo 示例中的 rating pod 中的 sidecar 啟動的分析,可以看到其中指定了 --v2-config-only
,表明 Istio 1.0+ 只支持 xDS v2 的 API。
Istio sidecar proxy 配置
假如您使用 kubernetes-vagrant-centos-cluster 部署了 Kubernetes 集群並開啟了 Istio Service Mesh,再部署 bookinfo 示例,那麼在 default
命名空間下有一個名字類似於 ratings-v1-7c9949d479-dwkr4
的 Pod,使用下面的命令查看該 Pod 的 Envoy sidecar 的全量配置:
kubectl -n default exec ratings-v1-7c9949d479-dwkr4 -c istio-proxy curl http://localhost:15000/config_dump > dump-rating.json
將 Envoy 的運行時配置 dump 出來之後你將看到一個長 6000 余行的配置文件。關於該配置文件的介紹請參考 Envoy v2 API 概覽。

Istio 會在為 Service Mesh 中的每個 Pod 注入 Sidecar 的時候同時為 Envoy 注入 Bootstrap 配置,其餘的配置是通過 Pilot 下發的,注意整個數據平面即 Service Mesh 中的 Envoy 的動態配置應該是相同的。您也可以使用上面的命令檢查其他 sidecar 的 Envoy 配置是否跟最上面的那個相同。
使用下面的命令檢查 Service Mesh 中的所有有 Sidecar 注入的 Pod 中的 proxy 配置是否同步。
$ istioctl proxy-status
PROXY CDS LDS EDS RDS PILOT VERSION
details-v1-876bf485f-sx7df.default SYNCED SYNCED SYNCED (100%) SYNCED istio-pilot-5bf6d97f79-6lz4x 1.0.0
...
istioctl 這個命令行工具就像 kubectl 一樣有很多神器的魔法,通過它可以高效的管理 Istio 和 debug。
Envoy proxy 配置解析
Istio envoy sidecar proxy 配置中包含以下四個部分。
- bootstrap:Envoy proxy 啟動時候載入的靜態配置。
- listeners:監聽器配置,使用 LDS 下發。
- clusters:集群配置,靜態配置中包括 xds-grpc 和 zipkin 地址,動態配置使用 CDS 下發。
- routes:路由配置,靜態配置中包括了本地監聽的服務的集群信息,其中引用了 cluster,動態配置使用 RDS 下發。
每個部分中都包含靜態配置與動態配置,其中 bootstrap 配置又是在集群啟動的時候通過 sidecar 啟動參數注入的,配置文件在 /etc/istio/proxy/envoy-rev0.json
。
Enovy 的配置 dump 出來後的結構如下圖所示。

由於 bootstrap 中的配置是來自 Envoy 啟動時載入的靜態文件,主要配置了節點信息、tracing、admin 和統計信息收集等信息,這不是本文的重點,大家可以自行研究。

上圖是 bootstrap 的配置信息。
Bootstrap 是 Envoy 中配置的根本來源,Bootstrap 消息中有一個關鍵的概念,就是靜態和動態資源的之間的區別。例如 Listener 或 Cluster 這些資源既可以從 static_resources 靜態的獲得也可以從 dynamic_resources 中配置的 LDS 或 CDS 之類的 xDS 服務獲取。
Listener
Listener 顧名思義,就是監聽器,監聽 IP 地址和埠,然後根據策略轉發。
Listener 的特點
- 每個 Envoy 進程中可以有多個 Listener,Envoy 與 Listener 之間是一對多的關係。
- 每個 Listener 中可以配置一條 filter 鏈表(filter_chains),Envoy 會根據 filter 順序執行過濾。
- Listener 可以監聽下游的埠,也可以接收來自其他 listener 的數據,形成鏈式處理。
- filter 是可擴展的。
- 可以靜態配置,也可以使用 LDS 動態配置。
- 目前只能監聽 TCP,UDP 還未支持。
Listener 的數據結構
Listener 的數據結構如下,除了 name
、address
和 filter_chains
為必須配置之外,其他都為可選的。
{
"name": "...",
"address": "{...}",
"filter_chains": [],
"use_original_dst": "{...}",
"per_connection_buffer_limit_bytes": "{...}",
"metadata": "{...}",
"drain_type": "...",
"listener_filters": [],
"transparent": "{...}",
"freebind": "{...}",
"socket_options": [],
"tcp_fast_open_queue_length": "{...}",
"bugfix_reverse_write_filter_order": "{...}"
}
下面是關於上述數據結構中的常用配置解析。
- name:該 listener 的 UUID,唯一限定名,默認60個字元,例如
10.254.74.159_15011
,可以使用命令參數指定長度限制。 - address:監聽的邏輯/物理地址和埠號,例如 "address": { "socket_address": { "address": "10.254.74.159", "port_value": 15011 } }
- filter_chains:這是一個列表,Envoy 中內置了一些通用的 filter,每種 filter 都有特定的數據結構,Enovy 會根據該配置順序執行 filter。Envoy 中內置的 filter 有:envoy.client_ssl_auth、envoy.echo、enovy.http_connection_manager、envoy.mongo_proxy、envoy.rate_limit、enovy.redis_proxy、envoy.tcp_proxy、http_filters、thrift_filters等。這些 filter 可以單獨使用也可以組合使用,還可以自定義擴展,例如使用 Istio 中的 EnvoyFilter 配置。
- use_original_dst:這是一個布爾值,如果使用 iptables 重定向連接,則代理接收的埠可能與原始目的地址的埠不一樣。當此標誌設置為 true 時,Listener 將重定向的連接切換到與原始目的地址關聯的 Listener。如果沒有與原始目的地址關聯的 Listener,則連接由接收它的 Listener 處理。默認為 false。注意:該參數將被廢棄,請使用原始目的地址的 Listener filter 替代。該參數的主要用途是:Envoy 通過監聽 15001 埠將應用的流量截取後再由其他 Listener 處理而不是直接轉發出去,詳情見 Virtual Listener。
關於 Listener 的詳細介紹請參考 Envoy v2 API reference - listener。
Route
我們在這裡所說的路由指的是 HTTP 路由,這也使得 Envoy 可以用來處理網格邊緣的流量。HTTP 路由轉發是通過路由過濾器實現的。該過濾器的主要職能就是執行路由表中的指令。除了可以做重定向和轉發,路由過濾器還需要處理重試、統計之類的任務。
HTTP 路由的特點
- 前綴和精確路徑匹配規則。
- 可跨越多個上游集群進行基於權重/百分比的路由。
- 基於優先順序的路由。
- 基於哈希策略的路由。
Route 的數據結構
{
"name": "...",
"virtual_hosts": [],
"internal_only_headers": [],
"response_headers_to_add": [],
"response_headers_to_remove": [],
"request_headers_to_add": [],
"request_headers_to_remove": [],
"validate_clusters": "{...}"
}
下面是關於上述數據結構中的常用配置解析。
- name:該名字跟
envoy.http_connection_manager
filter 中的http_filters.rds.route_config_name
一致,在 Istio Service Mesh 中為 Envoy 下發的配置中的 Route 是以監聽的埠號作為名字,而同一個名字下面的virtual_hosts
可以有多個值(數組形式)。 - virtual_hosts:因為 VirtualHosts 是 Envoy 中引入的一個重要概念,我們在下文將詳細說明
virtual_hosts
的數據結構。 - validate_clusters:這是一個布爾值,用來設置開啟使用 cluster manager 來檢測路由表引用的 cluster 是否有效。如果是路由表是通過 route_config 靜態配置的則該值默認設置為 true,如果是使用 rds 動態配置的話,則該值默認設置為 false。
關於 Route 的詳細介紹請參考 Envoy v2 API reference - HTTP route configuration。
route.VirtualHost
VirtualHost 即上文中 Route 配置中的 virtual_hosts
,VirtualHost 是路由配置中的頂級元素。每個虛擬主機都有一個邏輯名稱以及一組根據傳入請求的 host header 路由到它的域。這允許單個 Listener 為多個頂級域路徑樹提供服務。基於域選擇了虛擬主機後 Envoy 就會處理路由以查看要路由到哪個上游集群或是否執行重定向。
VirtualHost 的數據結構
下面是 VirtualHost 的數據結構,除了 name
和 domains
是必須配置項外,其他皆為可選項。
{
"name": "...",
"domains": [],
"routes": [],
"require_tls": "...",
"virtual_clusters": [],
"rate_limits": [],
"request_headers_to_add": [],
"request_headers_to_remove": [],
"response_headers_to_add": [],
"response_headers_to_remove": [],
"cors": "{...}",
"per_filter_config": "{...}",
"include_request_attempt_count": "..."
}
下面是關於上述數據結構中的常用配置解析。
- name:該 VirtualHost 的名字,一般是 FQDN 加埠,如
details.default.svc.cluster.local:9080
。 - domains:這是個用來匹配 VirtualHost 的域名(host/authority header)列表,也可以使用通配符,但是通配符不能匹配空字元,除了僅使用
*
作為 domains,注意列表中的值不能重複和存在交集,只要有一條 domain 被匹配上了,就會執行路由。Istio 會為該值配置所有地址解析形式,包括 IP 地址、FQDN 和短域名等。 - routes:針對入口流量的有序路由列表,第一個匹配上的路由將被執行。我們在下文將詳細說明 route 的數據結構。
下面是一個實際的 VirtualHost 的例子,該配置來自 Bookinfo 應用的 details 應用的 Sidecar 服務。
{
"name": "details.default.svc.cluster.local:9080",
"domains": [
"details.default.svc.cluster.local",
"details.default.svc.cluster.local:9080",
"details",
"details:9080",
"details.default.svc.cluster",
"details.default.svc.cluster:9080",
"details.default.svc",
"details.default.svc:9080",
"details.default",
"details.default:9080",
"10.254.4.113",
"10.254.4.113:9080"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "outbound|9080||details.default.svc.cluster.local",
"timeout": "0s",
"max_grpc_timeout": "0s"
},
"decorator": {
"operation": "details.default.svc.cluster.local:9080/*"
},
"per_filter_config": {
"mixer": {
"forward_attributes": {
"attributes": {
"destination.service.uid": {
"string_value": "istio://default/services/details"
},
"destination.service.host": {
"string_value": "details.default.svc.cluster.local"
},
"destination.service.namespace": {
"string_value": "default"
},
"destination.service.name": {
"string_value": "details"
},
"destination.service": {
"string_value": "details.default.svc.cluster.local"
}
}
},
"mixer_attributes": {
"attributes": {
"destination.service.host": {
"string_value": "details.default.svc.cluster.local"
},
"destination.service.uid": {
"string_value": "istio://default/services/details"
},
"destination.service.name": {
"string_value": "details"
},
"destination.service.namespace": {
"string_value": "default"
},
"destination.service": {
"string_value": "details.default.svc.cluster.local"
}
}
},
"disable_check_calls": true
}
}
}
]
}
關於 route.VirtualHost 的詳細介紹請參考 Envoy v2 API reference - route.VirtualHost。
route.Route
路由既是如何匹配請求的規範,也是對下一步做什麼的指示(例如,redirect、forward、rewrite等)。
route.Route 的數據結構
下面是是 route.Route 的數據結構,除了 match
之外其餘都是可選的。
{
"match": "{...}",
"route": "{...}",
"redirect": "{...}",
"direct_response": "{...}",
"metadata": "{...}",
"decorator": "{...}",
"per_filter_config": "{...}",
"request_headers_to_add": [],
"request_headers_to_remove": [],
"response_headers_to_add": [],
"response_headers_to_remove": []
}
下面是關於上述數據結構中的常用配置解析。
- match:路由匹配參數。例如 URL prefix(前綴)、path(URL 的完整路徑)、regex(規則表達式)等。
- route:這裡面配置路由的行為,可以是 route、redirect 和 direct_response,不過這裡面沒有專門的一個配置項用來配置以上三種行為,而是根據實際填充的配置項來確定的。例如在此處添加
cluster
配置則暗示路由動作為」route「,表示將流量路由到該 cluster。詳情請參考 route.RouteAction。 - decorator:被匹配的路由的修飾符,表示被匹配的虛擬主機和 URL。該配置里有且只有一個必須配置的項
operation
,例如details.default.svc.cluster.local:9080/*
。 - per_filter_config:這是一個 map 類型,
per_filter_config
欄位可用於為 filter 提供特定路由的配置。Map 的 key 應與 filleter 名稱匹配,例如用於 HTTP buffer filter 的envoy.buffer
。該欄位是特定於 filter 的,詳情請參考 HTTP filter。
關於 route.Route 的詳細介紹請參考 Envoy v2 API reference - route.Route。
Cluster
Cluster 是指 Envoy 連接的一組邏輯相同的上游主機。Envoy 通過服務發現來發現 cluster 的成員。可以選擇通過主動健康檢查來確定集群成員的健康狀態。Envoy 通過負載均衡策略決定將請求路由到 cluster 的哪個成員。
Cluster 的特點
- 一組邏輯上相同的主機構成一個 cluster。
- 可以在 cluster 中定義各種負載均衡策略。
- 新加入的 cluster 需要一個熱身的過程才可以給路由引用,該過程是原子的,即在 cluster 熱身之前對於 Envoy 及 Service Mesh 的其餘部分來說是不可見的。
- 可以通過多種方式來配置 cluster,例如靜態類型、嚴格限定 DNS、邏輯 DNS、EDS 等。
Cluster 的數據結構
Cluster 的數據結構如下,除了 name
欄位,其他都是可選的。
{
"name": "...",
"alt_stat_name": "...",
"type": "...",
"eds_cluster_config": "{...}",
"connect_timeout": "{...}",
"per_connection_buffer_limit_bytes": "{...}",
"lb_policy": "...",
"hosts": [],
"load_assignment": "{...}",
"health_checks": [],
"max_requests_per_connection": "{...}",
"circuit_breakers": "{...}",
"tls_context": "{...}",
"common_http_protocol_options": "{...}",
"http_protocol_options": "{...}",
"http2_protocol_options": "{...}",
"extension_protocol_options": "{...}",
"dns_refresh_rate": "{...}",
"dns_lookup_family": "...",
"dns_resolvers": [],
"outlier_detection": "{...}",
"cleanup_interval": "{...}",
"upstream_bind_config": "{...}",
"lb_subset_config": "{...}",
"ring_hash_lb_config": "{...}",
"original_dst_lb_config": "{...}",
"least_request_lb_config": "{...}",
"common_lb_config": "{...}",
"transport_socket": "{...}",
"metadata": "{...}",
"protocol_selection": "...",
"upstream_connection_options": "{...}",
"close_connections_on_host_health_failure": "...",
"drain_connections_on_host_removal": "..."
}
下面是關於上述數據結構中的常用配置解析。
- name:如果你留意到作為 Sidecar 啟動的 Envoy 的參數的會注意到
--max-obj-name-len 189
,該選項用來用來指定 cluster 的名字,例如inbound|9080||ratings.default.svc.cluster.local
。該名字字元串由|
分隔成四個部分,分別是inbound
或outbound
代表入向流量或出向流量、埠號、subcluster 名稱、FQDN,其中 subcluster 名稱將對應於 IstioDestinationRule
中配置的subnet
,如果是按照多版本按比例路由的話,該值可以是版本號。 - type:即服務發現類型,支持的參數有
STATIC
(預設值)、STRICT_DNS
、LOGICAL_DNS
、EDS
、ORIGINAL_DST
。 - hosts:這是個列表,配置負載均衡的 IP 地址和埠,只有使用了
STATIC
、STRICT_DNS
、LOGICAL_DNS
服務發現類型時才需要配置。 - eds_cluster_config:如果使用
EDS
做服務發現,則需要配置該項目,其中包括的配置有service_name
和ads
。
關於 Cluster 的詳細介紹請參考 Envoy v2 API reference - cluster。
參考
- Envoy v2 API 概覽 - servicemesher.com
- 監聽器發現服務(LDS)- servicemesher.com
- 路由發現服務(RDS)- servicemesher.com
- 集群發現服務(CDS)- servicemesher.com
- xDS 協議解析 - jimmysong.io
- 深入解讀 Service Mesh 背後的技術細節 - cnblogs.com
- 理解 Istio Service Mesh 中 Envoy 代理 Sidecar 注入及流量劫持 - jimmysong.io
推薦閱讀:
TAG:istio | ServiceMesh | 雲原生(CloudNative) |