Istio 是一個開源的服務網格,可為分布式微服務架構提供所需的基礎運作和管理要素。随着各組織越來越多地采用雲平台,開發者必須使用微服務設計架構以實作可移植性,而運維人員必須管理包含混合雲部署和多雲部署的大型分布式應用。Istio 采用一種一緻的方式來保護、連接配接和監控微服務,降低了管理微服務部署的複雜性。
從架構設計上來看,Istio 服務網格在邏輯上分為控制平面和資料平面兩部分。其中,控制平面 Pilot 負責管理和配置代理來路由流量,并配置 Mixer 以實施政策和收集遙測資料;資料平面由一組以 Sidecar 方式部署的智能代理(Envoy)組成,這些代理可以調節和控制微服務及 Mixer 之間所有的網絡通信。

一方面,我們在 Envoy 中看到,可以使用靜态配置檔案或使用一組發現服務來配置一組服務代理,以便在運作時發現監聽器、端點和叢集。Istio 在 Pilot 中實作了這些 Envoy 代理的 xDS API。
另一方面,Envoy 的服務發現依賴于某種服務系統資料庫來發現服務端點。Istio Pilot 實作了這個 API,但也将 Envoy 從任何特定的服務注冊實作中抽象出來。當 Istio 部署在 Kubernetes 上時,Kubernetes 的服務系統資料庫是 Istio 用于服務發現的。其它系統資料庫也可以像 HashiCorp 的 Consul 那樣使用。Envoy 資料平面完全不受這些實施細節的影響。
剖析 Istio 控制平面
Istio 的控制平面和 Envoy 的資料平面共同構成了一個引人注目的服務網格實作。兩者都擁有蓬勃發展和充滿活力的社群,并且面向下一代服務架構。Istio 是獨立于平台的,可運作于各種環境中,包括跨雲、内部部署、Kubernetes、Mesos 等。你可以在 Kubernetes 上部署 Istio 或在具有 Consul 的 Nomad 上部署。Istio 目前支援在 Kubernetes 上部署的服務、使用 Consul 注冊的服務以及在虛拟機上部署的服務。
其中,控制平面部分包括了 Pilot、Mixer、Citadel 和 Galley 四個元件。參見 Istio 架構一圖。
1. Pilot
Istio 的 Pilot 元件用于管理流量,可以控制服務之間的流量流動和 API 調用,通過 Pilot 可以更好地了解流量,以便在問題出現之前發現問題。這使得調用更加可靠、網絡更加強健,即使遇到不利條件也能讓應用穩如磐石。借助 Istio 的 Pilot,你能夠配置熔斷器、逾時和重試等服務級屬性,并設定常見的連續部署任務,如金絲雀釋出、A/B 測試和基于百分比拆分流量的分階段釋出。Pilot 為 Envoy 代理提供服務發現功能,為智能路由和彈性能力(如逾時、重試、熔斷器等)提供流量管理功能。Pilot 将控制流量行為的進階路由規則轉換為特定于 Envoy 代理的配置,并在運作時将它們傳播到 Envoy。此外,Istio 提供了強大的開箱即用故障恢複功能,包括逾時、支援逾時預算和變量抖動的重試機制、發往上遊服務的并發連接配接和請求數限制、對負載均衡池中的每個成員進行的定期主動運作狀況檢查,以及被動運作狀況檢查。
Pilot 将平台特定的服務發現機制抽象化并将其合成為标準格式,符合資料平面 API 的任何 Sidecar 都可以使用這種标準格式。這種松散耦合使得 Istio 能夠在多種環境下運作(例如 Kubernetes、Consul、Nomad),同時可保持用于流量管理的操作界面相同。
2. Mixer
Istio 的 Mixer 元件提供政策控制和遙測收集功能,将 Istio 的其餘部分與各個後端基礎設施後端的實作細節隔離開來。Mixer 是一個獨立于平台的元件,負責在服務網格上執行通路控制和使用政策,并從 Envoy 代理和其他服務收集遙測資料。代理提取請求級屬性,發送到 Mixer 進行評估。
Mixer 中包括一個靈活的插件模型,使其能夠接入到各種主機環境和後端基礎設施,從這些細節中抽象出 Envoy 代理和 Istio 管理的服務。利用 Mixer,你可以精細控制網格和後端基礎設施後端之間的所有互動。
與必須節省記憶體的 Sidecar 代理不同,Mixer 獨立運作,是以它可以使用相當大的緩存和輸出緩沖區,充當 Sidecar 的高度可伸縮且高度可用的二級緩存。
Mixer 旨在為每個執行個體提供高可用性。它的本地緩存和緩沖區可以減少延遲時間,還有助于屏蔽後端基礎設施後端故障,即使後端沒有響應也是如此。
3. Citadel
Istio Citadel 安全功能提供強大的身份驗證功能、強大的政策、透明的 TLS 加密以及用于保護服務和資料的身份驗證、授權和審計(AAA)工具,Envoy 可以終止或向網格中的服務發起 TLS 流量。為此,Citadel 需要支援建立、簽署和輪換證書。Istio Citadel 提供特定于應用程式的證書,可用于建立雙向 TLS 以保護服務之間的流量。
借助 Istio Citadel,確定隻能從經過嚴格身份驗證和授權的用戶端通路包含敏感資料的服務。Citadel 通過内置身份和憑證管理提供了強大的服務間和最終使用者身份驗證。可用于更新服務網格中未加密的流量,并為運維人員提供基于服務辨別而不是網絡控制的強制執行政策的能力。Istio 的配置政策在伺服器端配置平台身份驗證,但不在用戶端強制實施該政策,同時允許你指定服務的身份驗證要求。Istio 的密鑰管理系統可自動生成、分發、輪換與撤銷密鑰和證書。
Istio RBAC 為 Istio 網格中的服務提供命名空間級别、服務級别和方法級别的通路權限控制,包括易于使用的基于角色的語義、服務到服務和最終使用者到服務的授權,并在角色和角色綁定方面提供靈活的自定義屬性支援。
Istio 可以增強微服務及其通信(包括服務到服務和最終使用者到服務的通信)的安全性,且不需要更改服務代碼。它為每個服務提供基于角色的強大身份機制,以實作跨叢集、跨雲端的互動操作。
4. Galley
Galley 用于驗證使用者編寫的 Istio API 配置。随着時間的推移,Galley 将接管 Istio 擷取配置、處理和配置設定元件的頂級責任。它負責将其他的 Istio 元件與從底層平台(例如 Kubernetes)擷取使用者配置的細節中隔離開來。
總而言之,通過 Pilot,Istio 可在部署規模逐漸擴大的過程中幫助你簡化流量管理。通過 Mixer,借助強健且易于使用的監控功能,能夠快速有效地檢測和修複問題。通過 Citadel,減輕安全負擔,讓開發者可以專注于其他關鍵任務。
Istio 的架構設計中有幾個關鍵目标,這些目标對于系統應對大規模流量和高性能的服務處理至關重要。
- 最大化透明度:要采用 Istio,應該讓運維和開發人員隻需付出很少的代價就可以從中獲得實際價值。為此,Istio 将自身自動注入到服務間所有的網絡路徑中。Istio 使用 Envoy 代理來捕獲流量,并且在可能的情況下自動對網絡層進行程式設計,以便通過這些代理路由流量,而無需對已部署的應用程式代碼進行太多的更改,甚至不需要任何更改。在 Kubernetes 中,Envoy 代理被注入到 pod 中,通過 iptables 規則來捕獲流量。一旦注入 Envoy 代理到 pod 中并且修改路由規則,Istio 就能夠調節所有流量。這個原則也适用于性能。當将 Istio 用于部署時,運維人員可以發現,為提供這些功能而增加的資源開銷是很小的。所有元件和 API 在設計時都必須考慮性能和規模。
- 可擴充性:随着運維人員和開發人員越來越依賴 Istio 提供的功能,系統必然和他們的需求一起成長。在我們繼續添加新功能的同時,最需要的是能夠擴充政策系統,內建其他政策和控制來源,并将網格行為信号傳播到其他系統進行分析。政策運作時支援标準擴充機制以便插入到其他服務中。此外,它允許擴充詞彙表,以允許基于網格生成的新信号來強制執行政策。
- 可移植性:使用 Istio 的生态系統在很多方面都有所不同。Istio 必須能夠以最少的代價運作在任何雲或本地環境中。将基于 Istio 的服務移植到新環境應該是輕而易舉的,而使用 Istio 将一個服務同時部署到多個環境中也是可行的,例如可以在混合雲上部署以實作備援災備。
- 政策一緻性:政策應用于服務之間的 API 調用,可以很好地控制網格行為。但對于無需在 API 級别表達的資源來說,對資源應用政策也同樣重要。例如,将配額應用到機器學習訓練任務消耗的 CPU 數量上,比将配額應用到啟動這個工作的調用上更為有用。是以,Istio 将政策系統維護為具有自己的 API 的獨特服務,而不是将其放到代理中,這允許服務根據需要直接與其內建。
剖析 Istio 資料平面
當介紹服務網格的概念時,提到了服務代理的概念以及如何使用代理建構一個服務網格,以調節和控制微服務之間的所有網絡通信。Istio 使用 Envoy 代理作為預設的開箱即用服務代理,這些 Envoy 代理與參與服務網格的所有應用程式執行個體一起運作,但不在同一個容器程序中,形成了服務網格的資料平面。隻要應用程式想要與其他服務通信,就會通過服務代理 Envoy 進行。由此可見,Envoy 代理是資料平面和整個服務網格架構中的關鍵組成部分。
1. Envoy 代理
Envoy 最初是由 Lyft 開發的,用于解決建構分布式系統時出現的一些複雜的網絡問題。它于 2016 年 9 月作為開源項目提供,一年後加入了雲原生計算基金會(CNCF)。Envoy 是用 C++ 語言實作的,具有很高的性能,更重要的是,它在高負載運作時也非常穩定和可靠。網絡對應用程式來說應該是透明的,當網絡和應用程式出現問題時,應該很容易确定問題的根源。正是基于這樣的一種設計理念,将 Envoy 設計為一個面向服務架構的七層代理和通信總線。
為了更好地了解 Envoy,我們需要先搞清楚相關的幾個基本術語:
- 程序外(Out of Process)架構:Envoy 是一個獨立程序,Envoy 之間形成一個透明的通信網格,每個應用程式發送消息到本地主機或從本地主機接收消息,但無需關心網絡拓撲。
- 單程序多線程模型:Envoy 使用了單程序多線程的架構模型。一個主線程管理各種瑣碎的任務,而一些工作子線程則負責執行監聽、過濾和轉發功能。
- 下遊(Downstream):連接配接到 Envoy 并發送請求、接收響應的主機叫下遊主機,也就是說下遊主機代表的是發送請求的主機。
- 上遊(Upstream):與下遊相對,接收請求的主機叫上遊主機。
- 監聽器(Listener):監聽器是命名網絡位址,包括端口、unix domain socket 等,可以被下遊主機連接配接。Envoy 暴露一個或者多個監聽器給下遊主機連接配接。每個監聽器都獨立配置一些網絡級别(即三層或四層)的過濾器。當監聽器接收到新連接配接時,配置好的本地過濾器将被執行個體化,并開始處理後續事件。一般來說監聽器架構用于執行絕大多數不同的代理任務,例如限速、TLS 用戶端認證、HTTP 連接配接管理、MongoDB sniff?ing、原始 TCP 代理等。
- 叢集(Cluster):叢集是指 Envoy 連接配接的一組邏輯相同的上遊主機。
-
xDS 協定:在 Envoy 中 xDS 協定代表的是多個發現服務協定,包括叢集發現服務(CDS,
Cluster Discovery Service)、監聽器發現服務(LDS,Listener Discovery Service)、路由發現服務(RDS,Route Discovery Service)、端點發現服務(EDS,Endpoint Discovery Service),以及密鑰發現服務(SDS,Secret Discovery Service)。
Envoy 代理有許多功能可用于服務間通信,例如,暴露一個或者多個監聽器給下遊主機連接配接,通過端口暴露給外部的應用程式;通過定義路由規則處理監聽器中傳輸的流量,并将該流量定向到目标叢集,等等。後續章節會進一步分析這幾個發現服務在 Istio 中的角色和作用。
在了解了 Envoy 的術語之後,你可能想盡快知道 Envoy 到底起到了什麼作用?
首先,Envoy 是一種代理,在網絡體系架構中扮演着中介的角色,可以為網絡中的流量管理添加額外的功能,包括提供安全性、隐私保護或政策等。在服務間調用的場景中,代理可以為用戶端隐藏服務後端的拓撲細節,簡化互動的複雜性,并保護後端服務不會過載。例如,後端服務實際上是運作的一組相同執行個體,每個執行個體能夠處理一定量的負載。
其次,Envoy 中的叢集(Cluster)本質上是指 Envoy 連接配接到的邏輯上相同的一組上遊主機。那麼用戶端如何知道在與後端服務互動時要使用哪個執行個體或 IP 位址?Envoy 作為代理起到了路由選擇的作用,通過服務發現(SDS,Service Discovery Service),Envoy 代理發現叢集中的所有成員,然後通過主動健康檢查來确定叢集成員的健康狀态,并根據健康狀态,通過負載均衡政策決定将請求路由到哪個叢集成員。而在 Envoy 代理處理跨服務執行個體的負載均衡過程中,用戶端不需要知道實際部署的任何細節。
2. Envoy 的啟動配置
Envoy 目前提供了兩個版本的 API,即 v1 和 v2,從 Envoy 1.5.0 起就有 v2 API 了,為了能夠讓使用者順利地向 v2 版本 API 遷移,Envoy 啟動的時候設定了一個參數--v2-conf?ig-only。通過這個參數,可以明确指定 Envoy 使用 v2 API 的協定。幸運的是,v2 API 是 v1 的一個超集,相容 v1 的 API。在目前的 Istio 1.0 之後的版本中,明确指定了其支援 v2 的 API。通過檢視使用 Envoy 作為 Sidecar 代理的容器啟動指令,可以看到如下類似的啟動參數,其中指定了參數--v2-config-only:
$ /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
其中,參數 -c 表示的是基于版本 v2 的引導配置檔案的路徑,格式為 JSON,也支援其他格式,如 YAML、Proto3等。它會首先作為版本 v2 的引導配置檔案進行解析,若解析失敗,會根據 [--v2-conf?ig-only] 選項決定是否作為版本 v1 的 JSON 配置檔案進行解析。其他參數解釋如下,以便讀者及時了解 Envoy 代理啟動時的配置資訊:
- restart-epoch 表示熱重新開機周期,對于第一次啟動預設為 0,每次熱重新開機後都應該增加它。
- service-cluster 定義 Envoy 運作的本地服務叢集名稱。
- service-node 定義 Envoy 運作的本地服務節點名稱。
- drain-time-s 表示熱重新開機期間 Envoy 将耗盡連接配接的時間(秒),預設為 600 秒(10 分鐘)。通常耗盡時間應小于通過 --parent-shutdown-time-s 選項設定的父程序關閉時間。
- parent-shutdown-time-s 表示 Envoy 在熱重新開機時關閉父程序之前等待的時間(秒)。
- max-obj-name-len 描述的是叢集 cluster、路由配置 route_conf?ig 以及監聽器 listener 中名稱字段的最大長度,以位元組為機關。此選項通常用于自動生成叢集名稱的場景,通常會超過 60 個字元的内部限制。預設為 60。
- Envoy 的啟動配置檔案分為兩種方式:靜态配置和動态配置。具體表現為:
- 靜态配置是将所有資訊都放在配置檔案中,啟動的時候直接加載。
- 動态配置需要提供一個 Envoy 的服務端,用于動态生成 Envoy 需要的服務發現接口,也就是通常說的 xDS,通過發現服務來動态調整配置資訊,Istio 實作了 v2 的 xDS API。
3. Envoy 靜态與動态配置
Envoy 是由 JSON 或 YAML 格式的配置檔案驅動的智能代理,對于已經熟悉 Envoy 或 Envoy 配置的使用者來說,相信應該已經知道了 Envoy 的配置也有不同的版本。初始版本 v1 是 Envoy 啟動時配置 Envoy 的原始方式。此版本已被棄用,以支援 Envoy 配置的 v2 版本。Envoy 的參考文檔(
https://www.envoyproxy.io/docs)還提供了明确區分 v1 和 v2 的文檔。本文将隻關注 v2 配置,因為它是最新的版本,也是 Istio 使用的版本。
Envoy 版本 v2 的配置 API 建立在 gRPC 之上,v2 API 的一個重要特性是可以在調用 API 時利用流功能來減少 Envoy 代理彙聚配置所需的時間。實際上,這也消除了輪詢 API 的弊端,允許伺服器将更新推送到 Envoy 代理,而不是定期輪詢代理。
Envoy 的架構使得使用不同類型的配置管理方法成為可能。部署中采用的方法将取決于實作者的需求。簡單部署可以通過全靜态配置來實作,更複雜的部署可以遞增地添加更複雜的動态配置。主要分為以下幾種情況:
- 全靜态:在全靜态配置中,實作者提供一組監聽器和過濾器鍊、叢集和可選的 HTTP 路由配置。動态主機發現僅能通過基于 DNS 的服務發現。配置重載必須通過内置的熱重新開機機制進行。
- 僅SDS/EDS:在靜态配置之上,Envoy 可以通過該機制發現上遊叢集中的成員。
- SDS/EDS 和 CDS:Envoy 可以通過該機制發現使用的上遊叢集。
- SDS/EDS、CDS 和 RDS:RDS 可以在運作時發現用于 HTTP 連接配接管理器過濾器的整個路由配置。
- SDS/EDS、CDS、RDS 和 LDS:LDS 可以在運作時發現整個監聽器。這包括所有的過濾器堆棧,包括帶有内嵌到 RDS 的應用的 HTTP 過濾器。
靜态配置
我們可以使用 Envoy 的配置檔案指定監聽器、路由規則和叢集。如下示例提供了一個非常簡單的 Envoy 配置:
static_resources:
listeners:
- name: httpbin-demo
address:
socket_address: { address: 0.0.0.0, port_value: 15001 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: egress_http
route_config:
name: httpbin_local_route
virtual_hosts:
- name: httpbin_local_service
domains: ["*"]
routes:
- match: { prefix: "/"
}
route:
auto_host_rewrite: true
cluster: httpbin_service
http_filters:
- name: envoy.router
clusters:
- name: httpbin_service
connect_timeout: 5s
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
hosts: [{ socket_address: { address: httpbin, port_value: 8000 }}]
在這個簡單的 Envoy 配置檔案中,我們聲明了一個監聽器,它在端口 15001 上打開一個套接字并為其附加一個過濾器鍊。過濾器 http_connection_manager 在 Envoy 配置中使用路由指令(在此示例中看到的簡單路由指令是比對所有虛拟主機的通配符),并将所有流量路由到 httpbin_service 叢集。配置的最後一部分定義了 httpbin_service 叢集的連接配接屬性。在此示例中,我們指定端點服務發現的類型為 LOGICAL_DNS、與上遊 httpbin 服務通信時的負載均衡算法為 ROUND_ROBIN。
這是一個簡單的配置檔案,用于建立監聽器傳入的流量,并将所有流量路由到 httpbin 叢集。它還指定要使用的負載均衡算法的設定以及要使用的連接配接逾時配置。
你會注意到很多配置是明确指定的,例如指定了哪些監聽器,路由規則是什麼,我們可以路由到哪些叢集等。這是完全靜态配置檔案的示例。
有關這些參數更多資訊的解釋,請參閱 Envoy 的文檔(www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/service_discovery#logical-dns)。
在前面的部分中,我們指出 Envoy 能夠動态配置其各種設定。下面将介紹 Envoy 的動态配置以及 Envoy 如何使用 xDS API 進行動态配置。
動态配置
Envoy 可以利用一組 API 進行配置更新,而無需任何停機或重新開機。Envoy 隻需要一個簡單的引導配置檔案,該配置檔案将配置指向正确的發現服務 API,其餘動态配置。Envoy 進行動态配置的 API 通常統稱為 xDS 服務,具體包括如下:
- 監聽器發現服務(LDS):一種允許 Envoy 查詢整個監聽器的機制,通過調用該 API 可以動态添加、修改或删除已知監聽器;每個監聽器都必須具有唯一的名稱。如果未提供名稱,Envoy 将建立一個 UUID。
- 路由發現服務(RDS):Envoy 動态擷取路由配置的機制,路由配置包括 HTTP 标頭修改、虛拟主機以及每個虛拟主機中包含的單個路由規則。每個 HTTP 連接配接管理器都可以通過 API 獨立地擷取自身的路由配置。RDS 配置隸屬于監聽器發現服務 LDS 的一部分,是 LDS 的一個子集,用于指定何時應使用靜态和動态配置,以及指定使用哪個路由。
- 叢集發現服務(CDS):一個可選的 API,Envoy 将調用該 API 來動态擷取叢集管理成員。Envoy 還将根據 API 響應協調叢集管理,根據需要添加、修改或删除已知的叢集。在 Envoy 配置中靜态定義的任何叢集都不能通過 CDS API 進行修改或删除。
- 端點發現服務(EDS):一種允許 Envoy 擷取叢集成員的機制,基于 gRPC 或 RESTJSON 的 API,它是 CDS 的一個子集;叢集成員在 Envoy 術語中稱為端點(Endpoint)。對于每個叢集,Envoy 從發現服務擷取端點。EDS 是首選的服務發現機制。
- 密鑰發現服務(SDS):用于分發證書的 API;SDS 最重要的好處是簡化證書管理。如果沒有此功能,在 Kubernetes 部署中,必須将證書建立為密鑰并挂載到 Envoy 代理容器中。如果證書過期,則需要更新密鑰并且需要重新部署代理容器。使用密鑰發現服務 SDS,那麼 SDS 伺服器會将證書推送到所有 Envoy 執行個體。如果證書過期,伺服器隻需将新證書推送到 Envoy 執行個體,Envoy 将立即使用新證書而無需重新部署。
-
聚合發現服務(ADS):上述其他 API 的所有更改的序列化流;你可以使用此單個 API 按順序擷取所有更改;ADS 并不是一個實際意義上的 xDS,它提供了一個彙聚的功能,在需要多個同步 xDS 通路的時候,ADS 可以在一個流中完成。
配置可以使用上述服務中的一個或其中幾個的組合,不必全部使用它們。需要注意的一點是,Envoy 的 xDS API 是建立在最終一緻性的前提下,正确的配置最終會收斂。例如,Envoy 最終可能會使用新路由擷取RDS的更新,該路由将流量路由到尚未在 CDS 中更新的叢集。這意味着,路由可能會引入路由錯誤,直到更新 CDS。Envoy 引入了聚合發現服務 ADS 來解決這種問題,而 Istio 實作了聚合發現服務 ADS,并使用 ADS 進行代理配置的更改。
例如,Envoy 代理要動态發現監聽器,可以使用如下配置:
dynamic_resources:
lds_config:
api_config_source:
api_type: GRPC
grpc_services:
- envoy_grpc:
cluster_name: xds_cluster
clusters:
- name: xds_cluster
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
hosts: [{ socket_address: { address: 127.0.0.3, port_value: 5678 }}]
通過上面的配置,我們不需要在配置檔案中顯式配置每個監聽器。我們告訴 Envoy 使用 LDS API 在運作時發現正确的監聽器配置值。但是,我們需要明确配置一個叢集,這個叢集就是 LDS API 所在的位置,也就是該示例中定義的叢集 xds_cluster。
在靜态配置的基礎上,比較直覺地表示出各個發現服務所提供的資訊。
本文摘自于《Istio 服務網格解析與實戰》,經出版方授權釋出。本書由阿裡雲進階技術專家王夕甯撰寫,詳細介紹 Istio 的基本原理與開發實戰,包含大量精選案例和參考代碼可以下載下傳,可快速入門 Istio 開發。Gartner 認為,2020 年服務網格将成為所有領先的容器管理系統的标配技術。本書适合所有對微服務和雲原生感興趣的讀者,推薦大家對本書進行深入的閱讀。