Beyond Istio OSS —— Istio 服務網格的現狀及未來

語言: CN / TW / HK

關於本文

本文根據筆者在 GIAC 深圳 2022 年大會上的的演講 《Beyond Istio OSS —— Istio 的現狀及未來》 整理而成,演講幻燈片見 騰訊文檔

本文回顧了 Istio 開源近五年來的發展,並展望了 Istio 服務網格的未來方向。本文的主要觀點如下:

  • 因為 Kubernetes、微服務、DevOps 及雲原生架構的流行,導致服務網格技術的興起;
  • Kubernetes 和可編程代理,為 Istio 的出現打下了堅實的基礎;
  • 雖然 eBPF 可以加速 Istio 中的透明流量劫持,但無法取代服務網格中的 sidecar;
  • Istio 的未來在於構建基於混合雲的零信任網絡;

Istio 誕生的前夜

2013 年起,隨着移動互聯網的爆發,企業對應用迭代的效率要求更高,應用程序架構開始從單體轉向微服務,DevOps 也開始變得流行。同年隨着 Docker 的開源,解決了應用封裝和隔離的問題,使得應用在編排系統中調度變得更容易。2014 年 Kubernetes、Spring Boot 開源,Spring 框架開發微服務應用開始流行,在接下來的幾年間大批的 RPC 中間件開源項目出現,如 Google 在 2016 年發佈 gRPC 1.0,螞蟻在 2018 年開源 SOFAStack 等,微服務框架百花齊放。為了節約成本,增加開發效率,使應用更具彈性,越來越多的企業正在遷移上雲,但這不僅僅是將應用搬到雲上那麼簡單,為了更高效地利用雲計算,一套「雲原生」方法和理念也呼之欲出。

Istio 開源時間線

Istio 開源發展時間線如下圖所示。

Istio 開源發展時間線示意圖

下面我們來簡單回顧下 Istio 開源大事件:

  • 2016 年 9 月:因為 Envoy 是 Istio 中的重要組成,Istio 的開源時間線應該有 Envoy 一部分。起初 Envoy 在 Lyft 內部僅作為邊緣代理,開源前已在 Lyft 內部得到大規模生產驗證並受到了 Google 工程師的注意,那時候 Google 正打算推出一個服務網格的開源項目。2017 年,Lyft 將 Envoy 捐獻給了 CNCF
  • 2017 年 5 月:Istio 由 Google、IBM 和 Lyft 聯合宣佈開源。一開始就使用了微服務架構,確定了數據平面和控制平面的組成以及 Sidecar 模式。
  • 2018 年 3 月:Kubernetes 順利的成為從 CNCF 中第一個畢業的項目,變得越來越「無聊」,基礎 API 已經定型,CNCF 正式將服務網格(Service Mesh)寫入到了雲原生的第二版定義中。筆者當前就職的公司 Tetrate ,也是在那時由 Google Istio 初創團隊創業成立的。服務網格在中國開始爆發,ServiceMesher 社區也在螞蟻集團的支持下成立,在中國佈道服務網格技術。
  • 2018 年 7 月:Istio 1.0 發佈,號稱「生產可用」,Istio 團隊重組。
  • 2020 年 3 月:Istio 1.5 發佈,架構迴歸單體,發佈週期確定,每三個月發佈一個大版本,API 趨於穩定。
  • 2020 年至今:Istio 的發展主要着重於 Day 2 Operation、性能優化和擴展性發面,多個圍繞 Istio 生態的開源項目開始出現,例如 SlimeAreakiMerbridge

為什麼 Istio 會在 Kubernetes 之後出現?

微服務和容器化之後,異構語言使用的增加,服務的數量激增,容器的生命週期變短是導致服務網格出現的根本原因。

我們先來看下服務從部署在 Kubernetes 到 Istio 中架構的變遷,然後再探討架構演進過程中 Istio 的需求,下文假定讀者已瞭解Kubernetes 和 Istio 的架構

Kubernetes 到 Istio 的架構改變示意圖

從 Kubernetes 到 Istio,概括的講應用的部署架構有如下特點:

  • Kubernetes 管理應用的生命週期,具體來説,就是應用的部署和管理(擴縮容、自動恢復、發佈策略);

  • 基於 Kubernetes 的自動 sidecar 注入,實現了透明流量攔截。先通過 sidecar 代理攔截到微服務間流量,再通過控制平面配置管理微服務的行為。如今服務網格的部署模式也迎來了新的挑戰,sidecar 已經不是 Istio 服務網格所必須的,基於 gRPC 的無代理的服務網格也在測試中。

  • 服務網格將流量管理從 Kubernetes 中解耦,服務網格內部的流量無須 kube-proxy 組件的支持, 通過類似於微服務應用層的抽象,管理服務間的流量,實現安全性和可觀察性功能。

  • 控制平面通過 xDS 協議發放代理配置給數據平面,已實現 xDS 的代理有 Envoy 和螞蟻開源的 MOSN

  • Kubernetes 集羣外部的客户端訪問集羣內部服務時,原先是通過 KubernetesIngress ,在有了 Istio 之後,會通過 Gateway 來訪問。

Kubernetes 容器編排與可編程代理 Envoy 為 Istio 的出現打下了堅實的基礎。

從上面 Kubernetes 到 Istio 的架構的轉變的描述中,我們可以看到為了讓開發者最小成本地管理服務間的流量,Istio 需要解決三個問題:

  1. 透明劫持應用間的流量 :Istio 開源最初的目標是成為網絡基礎設施,就像水和電人類的基礎設施一樣,我們使用水電不需要關心如何取水和發電,只需要打開水龍頭,按下開關即可。透明流量劫持對於開發者來説,就像使用水和電,不需要修改應用程序就可以快速使用 Istio 帶來的流量管理能力;
  2. 代理集羣的運維 :如何為每個應用注入一個代理,同時高效地管理這些分佈式的 sidecar 代理;
  3. 可編程代理 :代理可以通過 API 動態配置,還要有出色的性能與可擴展性;

以上三個條件對於 Istio 服務網格來説缺一不可,而且,從中我們可以看到,這些要求基本都是對於 sidecar 代理的要求,這個代理的選擇將直接影響該項目的走向與成敗。為了解決以上三個問題,Istio 選擇了 Kubernetes 容器編排和可編程代理 Envoy。

透明流量劫持

如果你使用的是如 gRPC 這類中間件開發微服務,在程序中集成 SDK 後,SDK 中的攔截器會自動為你攔截流量,如下圖所示。

gRPC 的攔截器示意圖

如何讓 Kubernetes pod 中的流量都通過代理呢?答案是在每個應用程序 pod 中注入一個代理,與應用共享網絡空間,再通過修改 pod 內的流量路徑,讓所有進出 pod 的流量都經過 sidecar,其架構如下圖所示。

Istio 中的透明流量劫持示意圖

從圖中我們可以看到其中有一套非常複雜的 iptables 流量劫持邏輯(詳見 Istio 中的 Sidecar 注入、透明流量劫持及流量路由過程詳解 ),使用 iptables 的好處是適用於任何 Linux 操作系統。但是這也帶來了一些副作用:

  1. Istio 網格中所有的服務都需要在進出 pod 時都增加了一個網絡跳躍點(hop),雖然每次 hop 可能只有兩三毫秒,但是隨着網格中服務和服務間的依賴增加,這種延遲可能會顯著增加,對於那種追求低延遲的服務可能就不適用於服務網格了;
  2. 因為 Istio 向數據平面中注入了大量的 sidecar,尤其是當服務數量增大時,控制平面需要下發更多的 Envoy 代理配置到數據平面,這樣會使數據平面佔用大量的系統內存和網絡資源;

針對這兩個問題,如何優化服務網格呢?

  1. 使用 proxyless 模式:取消 sidecar 代理,重新回到 SDK;
  2. 優化數據平面:減少下發到數據平面的配置的頻率和大小;
  3. eBPF:使用 eBPF 優化網絡劫持;

本文將在後面一節講解這些細節。

Sidecar 運維管理

Istio 是在 Kubernetes 的基礎上構建的,它可以利用 Kubernetes 的容器編排和生命週期管理,在 Kubernetes 創建 pod 時,通過准入控制器自動向 pod 中注入 sidecar。

為了解決 Sidecar 的資源消耗問題,有人為服務網格提出了有四種部署模式,如下圖所示。

服務網格的四種部署模式示意圖

下表中詳細對比了這四種部署方式,它們各有優劣,具體選擇哪種根據實際情況而定。

模式 內存開銷 安全性 故障域 運維
Sidecar 代理 因為為每個 pod 都注入一個代理,所以開銷最大。 由於 sidecar 必須與工作負載一起部署,工作負載有可能繞過 sidecar。 Pod 級別隔離,如果有代理出現故障,隻影響到 Pod 中的工作負載。 可以單獨升級某個工作負載的 sidecar 而不影響其他工作負載。
節點共享代理 每個節點上只有一個代理,為該節點上的所有工作負載所共享,開銷小。 對加密內容和私鑰的管理存在安全隱患。 節點級別隔離,如果共享代理升級時出現版本衝突、配置衝突或擴展不兼容等問題,則可能會影響該節點上的所有工作負載。 不需要考慮注入 Sidecar 的問題。
Service Account / 節點共享代理 服務賬户 / 身份下的所有工作負載都使用共享代理,開銷小。 工作負載和代理之間的連接的認證及安全性無法保障。 節點和服務賬號之間級別隔離,故障同 “節點共享代理”。 同 “節點共享代理”。
帶有微代理的共享遠程代理 因為為每個 pod 都注入一個微代理,開銷比較大。 微代理專門處理 mTLS,不負責 L7 路由,可以保障安全性。 當需要應用 7 層策略時,工作負載實例的流量會被重定向到 L7 代理上,若不需要,則可以直接繞過。該 L7 代理可以採用共享節點代理、每個服務賬户代理,或者遠程代理的方式運行。 同 “Sidecar 代理”。
服務網格的四種部署模式對比

可編程代理

Flomesh 的張曉輝曾在 為什麼需要可編程代理 博客中詳細説明了代理軟件的發展演化過程,我下面將引用他的一些觀點,説明可編程代理 Envoy 在 Istio 中的關鍵作用。

下圖展示了代理從配置到可編程模式的演化過程,及每個階段中的代表性代理軟件。

代理軟件的演化示意圖

整個代理演化過程都是隨着應用從本地和單體,越來越走向大規模和分佈式。下面我將簡要概括代理軟件的發展過程:

  • 配置文件時代 :幾乎所有軟件都有配置文件,代理軟件因為其相對複雜的功能,更離不開配置文件。該階段的代理主要使用 C 語言開發,包括其擴展模塊,突出的代理本身的能力。這也是我們使用代理最原始最基礎的形式,這些代理包括 Nginx、Apache HTTP Server、 Squid 等;
  • 配置語言時代 :這個時代的代理,更具擴展性和靈活性,比如動態數據獲取和配套的邏輯判斷。代表性代理包括擴 Varnish 和 HAProxy;
  • 腳本語言時代 :從腳本語言的引入開始,代理軟件才真正走向的可編程,我們可以更方便的使用腳本在代理中增加動態邏輯,增加了開發效率。代表性的代理是 Nginx 及其支持的腳本語言;
  • 集羣時代 :隨着雲計算的普及,大規模部署和動態配置 API 成了代理所必需的能力,而且隨着網絡流量的增加,大規模代理集羣也應運而生。這個時代的代表性代理有 Envoy、Kong 等;
  • 雲原生時代 :多租户、彈性、異構混合雲、多集羣、安全和可觀測,這些都是雲原生時代對代理所提出的更高要求,代表性軟件有 Istio、Linkerd、 Pypi ,它們都為代理構建了控制平面。

這些都是服務網格嗎?

現在我將列舉一些流行的服務網格開源項目,讓我們一起探索服務網格的發展規律和本質。下表對比了當前流行的服務網格開源項目。

對比項 Istio Linkerd Consul Connect Traefik Mesh Kuma Open Service Mesh (OSM)
當前版本 1.14 2.11 1.12 1.4 1.5 1.0
許可證 Apache License 2.0 Apache License 2.0 Mozilla License Apache License 2.0 Apache License 2.0 Apache License 2.0
發起者 Google、IBM、Lyft Buoyant HashiCorp Traefik Labs Kong Microsoft
服務代理 Envoy,支持 gRPC 的 proxyless 模式 Linkerd2-proxy 默認為 Envoy ,可替換 Traefik Proxy Envoy Envoy
入口控制器 Envoy,自定義的 Ingress,支持 Kubernetes Gateway API 無內置 Envoy,支持 Kubernetes Gateway API 無內置 Kong 支持 Contour、Nginx,兼容其他
治理 Istio Community 和 Open Usage Commons,已提議捐獻給 CNCF CNCF 查看 貢獻指南 查看 貢獻指南 CNCF CNCF
服務網格開源項目對比表

上表中列出的都是服務網格,下面再簡單評論一下這些項目:

  • Istio :目前最流行的服務網格項目之一,在中國幾乎成為了服務網格的代名詞;
  • Linkerd :最早出現的服務網格,「Service Mesh」概念提出者,第一個進入 CNCF 的服務網格項目,使用自研的 Rust 語言編寫輕量級 sidecar 代理;
  • Traefik Mesh :由 Traefik 推出的服務網格項目,使用 Treafik proxy 作為 sidecar,支持 SMI(接下來會提到),它的特點是對應用的無侵入性,不會在 pod 中注入 sidecar;
  • Kuma :由 Kong 推出的服務網格項目,使用 Envoy 作為 Sidecar 代理,特色是使用 Kong 自家的網關作為入口網關;
  • Consul Connect :Consul 服務網格,使用 Envoy 作為 sidecar 代理;
  • Open Service Mesh :由微軟開源的服務網格,使用 Envoy 作為 sidecar,兼容 SMI(同樣是微軟提出);

另外還有幾個項目,也服務網格領域也經常被提及,但它們都不是服務網格:

  • Envoy :Envoy 本身只是代理,也經常被作為其他基於 Envoy 的服務網格的 sidecar,也經常被用來構建 API Gateway;
  • Service Mesh Performance(SMP) :標準化了服務網格值的指標,通過捕獲基礎設施容量、服務網格配置和工作負載元數據的細節來描述任何部署的性能;
  • Service Mesh Interface(SMI) :它不是服務網格,而只是一套服務網格實現標準,與 OAM、SPIFFE、CNI、CSI 等類似都是定義接口標準,具體實現就不一而足了。目前 Traefik Mesh 和 Open Service Mesh 聲明支持該規範;
  • Network Service Mesh :有必要提一下這個項目,因為經常有人把它錯認為是一個服務網格。實際上,它面向的是三層網絡,使用它可以在不更換 CNI 插件的前提下,連接多雲/混合雲。它並不是我們所定義的「服務網格」,而是服務網格的一個有力補充(雖然名字裏帶有服務網格比較有迷惑性)。

縱觀以上項目,我們可以看出大部分服務網格項目的發起者都是根據代理起家,然後做控制平面。而且 Istio、Consul Connect、Open Service Mesh、Kuma 都是使用 Envoy 作為 sidecar 代理。只有 Linkerd 和 Traefik Mesh 推出了自己的代理。而所有的服務網格項目都支持 sidecar 模式。除了 Istio、Linkerd、Consul Connect 已應用於生產上,其他服務網格項目還沒有看到被大規模在生產上使用。

Istio 的性能優化

在 Istio 1.5 版本確定了穩定的架構之後,社區的主要精力在於優化 Istio 的性能。下面我將向你詳細介紹 Istio 中的性能優化方法,包括:

  • 採用 Proxyless 模式;
  • 使用 eBPF 優化流量劫持;
  • 控制平面性能優化;
  • 數據平面性能優化;

Proxyless 模式

Proxyless 模式是 Istio 在 1.11 版本中提出的實驗特性 —— 基於 gRPC 和 Istio 的無 sidecar 代理的服務網格 。使用該模式可以直接將 gRPC 服務添加到 Istio 中,而不需要再向 Pod 中注入 Envoy 代理。下圖展示了 sidecar 模式與 proxyless 模式的對比圖。

Sidecar 模式 vs Proxyless 模式

從上圖中我們可以看到,雖然 proxyless 模式不使用 proxy 進行數據平面通信,但仍然需要一個 agent(即 pilot-agent ) 來進行初始化和與控制平面的通信。首先,agent 在啟動時生成一個 引導文件 ,與為 Envoy 生成引導文件的方式相同。這告訴 gRPC 庫如何連接到 istiod ,在哪裏可以找到用於數據平面通信的證書,向控制平面發送什麼元數據。接下來,agent 作為 xDS proxy,代表應用程序與 istiod 進行連接和認證。最後,agent 獲取並輪換數據平面通信中使用的證書,這其實與 Sidecar 模式的流程是一樣的,只是將 Envoy 代理的功能內置到 SDK 中了。

服務網格的本質不是 Sidecar 模式,也不是配置中心或透明流量攔截,而是標準化的服務間通信標準。

有人説 proxyless 模式又回到了基於 SDK 開發微服務的老路,服務網格的優勢喪失殆盡,那還能叫做服務網格嗎?其實這也是一種對性能的妥協 —— 如果你主要使用 gRPC 來開發微服務的話,只需要維護不同語言的 gRPC 版本,即可以通過控制平面來管理微服務了。

Envoy xDS 已經成為服務網格中服務間通信的事實標準。

使用 eBPF 優化流量劫持

在一節,我們可以看到一個服務間的流量在到達目的地 pod 時經過的 iptables 規則和路徑,其中需要經過多條 iptables 規則,如 PREROUTINGISTIO_INBOUNDISTIO_IN_REDIRECTOUTPUTISTIO_OUTPUTPOSTROUTING 等。假設現在有一個服務 A 想要調用非本地主機上的另一個 pod 中的服務 B,經過的網絡堆棧如下圖所示。

非同主機 Pod 間的服務訪問路徑(iptables 模式)

從圖中我們可以看到整個調用流程中經過四次 iptables,其中 Pod A 中的從 Envoy 的出站(iptables2)和 Pod B 中的從 eth0 的入站(iptables3)的 iptables 路由是無法避免的,那麼剩下的兩個 iptables1 和 iptables4 是否可以優化呢?讓兩個 socket 直接通信,不就可以縮短網絡路徑了嗎?這就需要通過 eBPF 編程,使得:

  • Service A 的流量從直接發送到 Envoy 的 Inbound socket 上;
  • Pod B 中 Envoy 接收到入站流量後,已經確定流量是要發送給本地的服務,直接對接 Outbound socket 與 Service B;

使用 eBPF 模式的透明流量攔截網絡路徑如下圖所示。

非同主機 Pod 間的服務訪問路徑(eBPF 模式)

如果要訪問的服務 A 和服務 B 在同一個節點上,那麼網絡路徑將更短。

同主機 Pod 間的網絡訪問路徑(eBPF 模式)

同一個節點中的服務間訪問完全繞過了 TCP/IP 堆棧,變成了 socket 間的直接訪問。

什麼是 eBPF?

我們知道修改 Linux 內核代碼很難,新特性發布到內核中需要很長的週期。eBPF 是一個框架,允許用户在操作系統的內核內加載和運行自定義程序。也就是説,有了 eBPF,你不需要直接修改內核,就可以擴展和改變內核的行為。下面我將簡要的為大家介紹一下 eBPF:

  • eBPF 程序加載到內核中後需要通過驗證器的驗證才可以運行,驗證器可以防止 eBPF 程序超越權限的訪問,這樣可以確保內核的安全;
  • eBPF 程序是附着於內核事件上的,當有進入或退出內核函數時被觸發;
  • 內核空間的 eBPF 程序必須使用能夠支持生成 eBPF 字節碼格式的編譯器的語言編寫,目前你可以用 C 和 Rust 語言編寫 eBPF 程序;
  • eBPF 程序對於不同的 Linux 版本存在兼容性問題;

由於 eBPF 程序可以直接監聽和操作 Linux 內核,具有對系統最底層的透視,就可以在流量管理、可觀測性和安全發揮作用。有關 eBPF 的詳細介紹請參考筆者翻譯的《什麼是 eBPF》 電子書。

開源項目 Merbridge 正是利用 eBPF 縮短了透明流量劫持的路徑,優化了服務網格的性能。關於 Merbridge 實現的一些細節,請參考 Istio 博客

注意

Merbridge 使用的 eBPF 函數需要 Linux 內核版本 ≥ 5.7。

乍看上去 eBPF 似乎從更底層實現了 Istio 的功能,更大有取代 sidecar 的趨勢。但是 eBPF 也存在很多侷限性,導致在可以預見的未來無法取代服務網格和 Sidecar。如果取消 sidecar 轉而使用每個主機一個代理的模式,會導致:

  1. 代理失敗的爆炸半徑擴大到整個節點,即一個代理失敗了,代理所在節點上的所有工作負載都會受到影響;
  2. 使得安全問題更加複雜,因為一個節點上保存在太多負載的證書,一旦被攻擊,會存在祕鑰泄露的風險;
  3. 主機上的 Pod 之間的流量爭搶問題,即節點上如果有一個工作負載消耗掉代理的所有資源,其他工作負載將無法獲得流量;

而且 eBPF 主要負責三/四層流量,可以與 CNI 一起運行,但是七層流量使用 eBPF 來處理就不太合適了。

在可以預見的未來 eBPF 技術無法取代服務網格和 Sidecar。

關於 eBPF 與服務網格的關係的更詳細介紹請參考博客 請暫時拋棄使用 eBPF 取代服務網格和 Sidecar 模式的幻想

控制平面性能優化

以上兩種優化都是針對數據平面進行的,我們再來看下控制平面的性能優化。你可以把服務網格想象成是一場演出,控制平面是總導演,數據平面是所有演員,導演不參與演出,但是負責指揮演員。如果這場演出的情節很簡單,時長又很短,那要每個演員分配的戲份就會很少,排練起來就會很容易;如果是一個大型演出,演員的數量多,情節有很複雜,要想排練好這場演出,一個導演可能是不夠的,他指揮不了這麼多演員,因此我們需要多名副導演(擴大控制平面實例數量);我們還需要給演員準備好台詞和腳本,如果演員也可以一個鏡頭完成一連串的台詞和場景的表演(減少都數據平面的打擾,批量推送更新),那我們的排練是不是更加高效?

從上面的類比中,你應該可以找到控制平面性能優化的方向了,那就是:

  • 減少需要推送的配置大小;
  • 批處理代理推送;
  • 擴大控制平面規模;

減少需要推送的配置

控制平面性能優化最直接的方式就是減少要向數據平面推送的代理配置大小。假設有工作負載 A,如果僅將與 A 相關的代理配置(即 A 依賴的服務)推送給 A,而不是將網格內所有服務的配置都推送給 A,這樣就可以大大壓縮要推送的工作負載範圍及配置大小。Istio 中的 Sidecar 資源可以幫助我們實現這一點。下面是 Sidecar 配置示例:

apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
  name: default
  namespace: cn-bj
spec:
  workloadSelector:
    labels:
      app: app-a
  egress:
  - hosts:
    - "cn-bj/*"

我們通過 workloadSelector 字段可以限制該 Sidecar 配置適用的工作負載範圍,而 egress 字段可以確定該工作負載依賴的服務範圍,這樣控制平面就可以僅向服務 A 推送其依賴的服務配置,大大減低要向數據平面推送的配置大小,減少了服務網格的內存和網絡消耗。

批處理代理配置推送

控制平面 Istiod 向數據平面推送代理配置的過程比較複雜,下圖展示了其中的流程。

Istiod 向數據平面推送代理配置的流程圖

管理員配置 Istio 網格後,Istiod 中推送代理配置的流程是這樣的:

DiscoveryServer

從以上流程中我們可以看出,優化配置推送的關鍵就是步驟 2 中去抖動週期和步驟 4 中的限流設置。有這樣幾個環境變量可以幫助你設置控制平面的推送:

PILOT_DEBOUNCE_AFTER
PILOT_DEBOUNCE_MAX
PILOT_ENABLE_EDS_DEBOUNCE
PILOT_PUSH_THROTTLE

關於這些環境變量的默認值和具體配置請參考 Istio 文檔

這些值究竟如何設置,可以遵循以下原則:

  • 如果控制平面資源空閒,為了加快配置更新的傳播速度,你可以:
    • 縮短去抖動週期,增加推送次數;
    • 增加同時處理的推送請求數量;
  • 如果控制平面飽和,為了降低性能瓶頸,你可以:
    • 延遲去抖動週期,減少推送次數;
    • 增加同時處理的推送請求的數量;

至於如何設置最優解,需要結合你的可觀測系統來調試。

擴大控制平面規模

如果設置去抖動批處理和 Sidecar 還無法優化控制平面性能的話,最後的選擇就是擴大控制平面的規模,包括擴大單個 Istiod 實例的資源和增加 Istiod 的實例個數,究竟採用哪種擴展方式視情況而定:

  • 當單個 Istiod 的資源佔用飽和時,優先推薦你擴大 Istiod 的實例大小,這通常是因為服務網格中有太多的資源(Istio 的自定義資源,如 VirtualService、DestinationRule 等)需要處理;
  • 如果增加 Istiod 實例的 CPU 和內存依然不起效的話,增加 Istiod 的實例個數,這樣可以分散單個實例要管理的工作負載數量;

數據平面性能優化

Apache SkyWalking 可以作為 Istio 提供可觀測性工具,還可以幫助我們在進行服務動態調試和故障排除剖析服務的性能,其最新推出的 Apache SkyWalking Rover 組件可以利用 eBPF 技術來準確定位 Istio 的關鍵性能問題。在數據平面,我們可以通過以下方式來增加 Envoy 的吞吐量以優化 Istio 的性能:

  • 禁用 Zipkin 追蹤或減少採樣率
  • 簡化訪問日誌格式
  • 禁用 Envoy 的訪問日誌服務(ALS)

以上優化方式對 Envoy 吞吐量的影響數據請參閲 使用 eBPF 準確定位服務網格的關鍵性能問題

Envoy —— 服務網格的領銜主演

我們知道服務網格是由數據平面和控制平面組成的,從上面的服務網格開源項目列表中我們可以看到,服務網格開源項目大部分都是基於 Envoy,然後開發自己的控制平面。還記得我在本文前面將服務網格比作演出嗎?在這場服務網格的演出中,毫無疑問 Envoy 就是領銜主演 —— Envoy 發明的 xDS 協議,基本成為服務網格的通用 API。下面展示的是 Envoy 的架構圖。

Envoy 架構圖

xDS 是 Envoy 區別於其他代理的關鍵,它的代碼和解析流程十分複雜,直接擴展起來也很有難度。下面展示的是 Istio 組件拓撲圖,從圖中我們可以看到 Istio 數據平面的 Sidecar 容器中不止有 envoy 這一個進程,還有一個 pilot-agent 進程。

Istio 組件拓撲圖

pilot-agent 進程的作用如下:

envoy

從以上功能中我們可以看出 pilot-agent 進程主要是用於與 Istiod 交互,為 Envoy 起到指揮和輔助的作用,Istio 的核心組件是 Envoy。那麼 Envoy 會不會「演而優則導」,不再配合 Istio,構建一套自己的控制平面呢?

在 Sidecar 容器中, pilot-agent 就像是 Envoy 的 “Sidecar”。

請讀者思考一下

pilot-agent 的功能能否直接內置到 Envoy 中,從而取消 pilot-agent 呢?

Envoy Gateway 統一服務網格網關

在 Kubernetes 中,除 Service 資源對象之外,最早用來暴露集羣中服務的資源對象是 Ingress。使用 Ingress 你只需要為集羣開放一個對外的訪問點即可,通過 HTTP Hosts 和 path 來路由流量到具體的服務。相對於直接在 service 資源上暴露服務來説,可以減少集羣的網絡訪問點(PEP),降低集羣被網絡攻擊的風險。使用 Ingress 訪問集羣內的服務流程如下圖所示。

Kubernetes Ingress 流量訪問流程圖

在 Kubernetes 之前,API Gateway 軟件就已經被廣泛用作邊緣路由了,在引用 Istio 時又增加了 Istio 自定義的 Gateway 資源,使得訪問 Istio 服務網格中的資源又多了一種選擇,如下圖所示。

訪問 Istio 網格中的服務的方式

現在,要想暴露單個 Istio 網格中的服務, NodePortLoadBalance 、Istio 自定義 Gateway、Kubernetes Ingress 和 API Gateway 軟件,如何選擇?如果是多集羣服務網格,客户端如何訪問網格內的服務?我們的服務網格領銜主演 Envoy 已經在這方面做足了功夫,被以多種形式使用:

  • Sidecar Proxy:正如在提到的,Istio、Kuma、Consul Connect 都使用了 Envoy 作為 sidecar 代理;
  • Kubernetes Ingress Controller/API Gateway: ContourEmissaryHangoGloo 等;

這些項目利用 Envoy 來實現服務網格和 API 網關,其中有很多功能重疊,同時又有很多專有功能,或者缺乏社區多樣性,這種現狀由於 Envoy 社區沒有提供控制平面實現而導致的。為了改變現狀,Envoy 社區發起了 Envoy Gateway 項目,該項目旨在結合現有的基於 Envoy 的 API Gateway 相關項目的經驗,利用帶有一些 Envoy 特定擴展的 Kubernetes Gateway API 降低 Envoy 用户使用網關的門檻。因為 Envoy Gateway 仍然通過 xDS 下發配置給 Envoy 代理,因此你還可以用它來管理支持 xDS 的網關,如 Istio Gateway。

我們現在所見的網關基本都是在單集羣中作為入口網關,對於多集羣和多網格就無能為力了。為了應對多集羣,我們需要在 Istio 之上再添加一層網關,和一個全局的控制平面以在多集羣間路由流量,如下圖所示。

多集羣多網格的兩級網關示意圖

關於兩級網關的簡要介紹

  • 一級網關(下文簡稱 T1)位於應用邊緣,用於多集羣環境。同一應用會同時託管在不同的集羣上,T1 網關將對該應用的請求流量在這些集羣之間路由。
  • 二級網關(下文簡稱 T2)位於一個的集羣邊緣,用於將流量路由到該集羣內由服務網格管理的服務。

通過在 Istio 控制平面以外增加一層全局控制平面和 API,來實現多集羣服務網格管理。將 T1 網關部署為集羣,可以防止單點故障。想要了解關於兩級網關的更多內容,請參考 通過兩級網關設計來路由服務網格流量

T1 網關的配置如下所示:

apiVersion: gateway.tsb.tetrate.io/v2
kind: Tier1Gateway
metadata:
  name: service1-tier1
  group: demo-gw-group
  organization: demo-org
  tenant: demo-tenant
  workspace: demo-ws
spec:
  workloadSelector:
    namespace: t1
    labels:
      app: gateway-t1
      istio: ingressgateway
  externalServers:
  - name: service1
    hostname: servicea.example.com
    port: 80
    tls: {}
    clusters:
    - name: cluster1
      weight: 75
    - name: cluster2
      weight: 25 

該配置將 servicea.example.com 通過 T1 網關暴露到網格外,並將網格外訪問該服務的流量的 75% 轉發到 cluster125% 的流量轉發到 cluster2 ,另外為了應對多集羣中的流量、服務和安全配置,Tetrate 旗艦產品 Tetrate Service Bridge 中還增加了 一系列 Group API,詳見 TSB 文檔

Istio 開源生態

Istio 開源在至今已經五年多了,近兩年來出現了很多基於 Istio 的開源項目,其中比較代表性的有:

  • 網易開源的 Slime
  • 騰訊開源的 Aeraki
  • Istio 官方對 Wasm 插件的支持

它們的出現使得 Istio 更加智能化並擴展了 Istio 的適用範圍。

Slime

Slime 是由網易數帆微服務團隊開源的一款基於 Istio 的智能網格管理器。Slime 基於 Kubernetes Operator 實現,可作為 Istio 的 CRD 管理器,無須對 Istio 做任何定製化改造,就可以定義動態的服務治理策略,從而達到自動便捷使用 Istio 和 Envoy 高階功能的目的。

我們在前文的中提到了通過「減少需要推送的配置」的方式來優化 Istio 的性能,但是 Istio 無法做到自動識別無法依賴以最優化需要推送到每個 sidecar 的代理配置,Slime 提供了 lazyload 控制器,可以幫助我們實現配置懶加載,用户無須手動配置 SidecarScope ,Istio 可以按需加載服務配置和服務發現信息。

下圖展示的是 Slime 作為 Istio 的管理平面更新數據平面配置的流程圖。

使用 Slime 更新 Istio 數據平面配置的流程圖

其中,Global Proxy 使用 Envoy 構建,在每個需要啟動配置懶加載的命名空間中部署一個或在整個網格中只部署一個,所有缺失服務發現信息的調用(你也可以手動配置服務調用關係),都會被兜底路由劫持到 Global Proxy,經過其首次轉發後,Slime 便可感知到被調用方的信息,然後根據其對應服務的 VirtualService,找到服務名和真實後端的映射關係,將兩者的都加入 SidecarScope,以後該服務的調用就不再需要經過 Global Proxy 了。

數據平面配置更新的具體步驟如下:

  1. Slime Operator 根據管理員的配置在 Kubernetes 中完成 Slime 組件的初始化,開發者創建符合 Slime CRD 規範的配置並應用到 Kubernetes 集羣中;
  2. Slime 持續監聽 Slime CRD 的創建;
  3. Slime 查詢 Prometheus 中保存的相關服務的監控數據,結合 Slime CRD 中自適應部分的配置,將 Slime CRD 轉換為 Istio CRD,同時將其推送到 Global Proxy 中;
  4. Istio 監聽 Istio CRD 的創建;
  5. Istio 將代理的配置信息推送到數據平面相應的 Sidecar Proxy 中;

因為數據平面中的所有服務的首次調用都通過 Global Proxy,該 Proxy 可以記錄所有服務的調用和依賴信息,根據該依賴信息更新 Istio 中 Sidecar 資源的配置;當某個服務的調用鏈被 VirtualService 中的路由信息重新定義時, Global Proxy 原有記錄就失效了,需要一個新的數據結構來維護該服務的調用關係。Slime 創建了名為 ServiceFence 的 CRD 來維護服務調用關係以解決服務信息缺失問題,詳見Slime 簡介 。

Aeraki

Aeraki Mesh 是騰訊雲在 2021 年 3 月開源的一個服務網格領域的項目,基於 Istio 擴展其對七層協議的支持,專注於解決 Istio 中的 非 HTTP 協議 的服務治理,已於 2022 年 6 月進入 CNCF Sandbox。

下圖展示了 Aeraki 將非 HTTP 協議納入到 Istio 網格中的流程圖。

Aeraki 將非 HTTP 協議納入到 Istio 網格中的流程圖

其詳細流程如下:

tcp-metaprotocol-dubbo

在 Istio 中接入非 HTTP 服務的整個流程中的關鍵是 MetaProtocol Proxy 。Istio 默認支持 HTTP/HTTP2、TCP 和 gRPC 協議,實驗性支持 Mongo、MySQL 和 Redis 協議。若要使用 Istio 路由其他協議的流量,不僅需要修改 Istio 控制平面並擴展 Envoy,這將帶來巨大的工作量,而且不同協議共享通用的控制邏輯,這還會帶來很多重複性工作。MetaProtocol Proxy 是在 Envoy 代碼基礎上的擴展,為七層協議統一實現了服務發現、負載均衡、RDS 動態路由、流量鏡像、故障注入、本地/全侷限流等基礎能力,大大降低了在 Envoy 上開發第三方協議的難度。

下圖展示的 MetaProtocol Proxy 的架構圖。

MetaProtocol Proxy 架構圖

當我們想擴展 Istio 使其支持 Kafka、Dubbo、Thrift 等其他七層協議時,只需要實現上圖中的編解碼的接口(Decode 和 Encode),就可以基於 MetaProtocol 快速開發一個第三方協議插件。MetaProtocol Proxy 是在 Envoy 基礎上的擴展,因此你仍然可以使用多種語言為其開發過濾器,並使用 EnvoyFilter 資源將配置下發到數據平面。

WasmPlugin API

WasmPlugin 是 Istio 1.12 版本引入的 API,作為代理擴展機制,我們可以使用它將自定義和第三方的 Wasm 模塊添加到數據平面中。下圖中展示瞭如何在 Istio 中使用 WasmPlugin。

在 Istio 中使用 WasmPlugin 的流程圖

具體步驟如下:

  1. 用户使用 Proxy-Wasm SDK (目前有 AssemblyScript、C++、Rust、Zig 和 Go 語言版本)來開發擴展,並構建成 OCI 鏡像(如 Docker 鏡像)上傳到鏡像倉庫;
  2. 用户編寫 WasmPlugin 配置並應用到 Istio;
  3. Istio 控制平面根據 WasmPlugin 配置中的工作負載選擇配置,將 Wasm 模塊注入到指定的 Pod 中;
  4. Sidecar 中的 pilot-agent 從遠程或本地文件中獲取 Wasm 模塊並將其加載到 Envoy 中運行;

誰應該使用 Istio?

好了,説了這麼説,這跟你有什麼關係呢?Istio 跟你的關係取決於你的角色:

  • 如果你是平台負責人,應用服務網格後,可能增強你的平台可觀測性,具有了一個統一的平台來管理微服務,你將是直接受益者,也應該是服務網格的主要實施者;
  • 如果是應用程序開發者,也會從服務網格中收益,因為你可以更加專屬於業務邏輯,而不用擔心重試策略、TLS 等其他非功能性問題;

下圖展示了服務網格的採用路徑。

服務網格的採用路徑

是否採用服務網格取決於你公司的技術發展階段,應用是否實現容器化和微服務,對多語言的需求,是否需要 mTLS 以及對性能損耗的接納度等。

服務網格在雲原生技術棧中的定位

技術的發展日新月異,近兩年來有一些新技術出現,似乎挑戰了服務網格的地位,更有人聲稱可以直接取代現有經典的 sidecar 模式的服務網格,我們不要被外界嘈雜的聲音所迷惑,認清服務網格在雲原生技術棧中的定位。

一味地推廣某項技術而忽略它的適用場景,就是耍流氓。

下圖展示的是雲原生技術堆棧。

雲原生技術堆棧示意圖

我們可以看到,在雲原生技術堆棧圖中的「雲基礎設施」、「中間件」和「應用」層都列舉了一些標誌性的開源項目,這些項目構建了它們所在領域的標準:

  • 在雲基礎設施領域,Kubernetes 統一了容器編排和應用生命週期管理的標準,Operator 模式奠定了擴展 Kubernetes API 及第三方應用接入的標準;
  • 在中間件領域,服務網格承擔起了雲原生技術棧中的七層網絡、可觀測性和安全等多個方面的部分或全部責任,它運行在應用程序下層,對於應用程序來説幾乎是無感知的;Dapr(分佈式應用程序運行時)定義雲原生中間件的能力模型,開發者可以在應用中集成 Dapr 的多語言 SDK,面向 Dapr 提供的分佈式能力編程,而不用關心應用所運行的環境及對接的後端基礎設施。因為在和應用程序運行在同一個 Pod 中的 Dapr 運行時(Sidecar 模式部署,其中包含各種構建塊)自動幫我們對接了後端組件(Component);
  • 在應用程序領域:OAM 旨在建立一個應用模型標準,通過組件、特徵、策略和工作流來一個應用程序,詳見雲原生資料庫 ;

下圖展示了 Istio 在雲原生部署中定位於七層網格管理。

Istio 在雲原生架構中定位在七層網絡

Dapr 與 Istio 是什麼關係?

在雲原生技術棧中,Istio 和 Dapr 同時位於中間件層,它們之間有很多區別和聯繫。

Istio 和 Dapr 之間的相同點:

  • Istio 和 Dapr 都可以使用 Sidecar 模式的部署模型;
  • 同屬於中間件,同樣可以管理服務間通信;

Istio 和 Dapr 之間的不同點:

  • 目標不同:Istio 的目標是構建零信任網絡,定義服務間通信標準,Dapr 目標是構建標準的中間件能力的 API;
  • 架構不同:Istio = Envoy + 透明流量劫持 + 控制平面,Dapr = 多語言 SDK + 標準化 API + 分佈式能力組件;
  • 面向的人羣不同:但是應用 Istio 對於開發者來説幾乎無感知,主要需要基礎設施運維團隊實施,而應用 Dapr 需要開發者自主選擇集成 Dapr SDK;

服務網格的未來

我在前文中介紹了 Istio 的發展脈絡及開源生態,接下來我將為大家介紹 Istio 服務網格的未來趨勢:

  • 構建零信任網絡
  • 成為混合雲管理平台的網絡基礎設施

服務網格的未來在於成為零信任網絡和混合雲的基礎設施。

這也是筆者所在的公司企業級服務網格提供商 Tetrate 的努力方向,我們致力於構建一個基於零信任的適用於任意環境、任意負載的應用感知網絡。下面展示的是 Tetrate 旗艦產品 Tetrate Service Bridge 的架構圖。

TSB 架構圖

Tetrate 公司是由 Istio 項目的發起人創立的,TSB 是基於開源的 Istio、Envoy 和 Apache SkyWalking 開發的。我們同時積極得貢獻上游社區,並參與了旨在簡化將 Envoy 網關使用的 Envoy Gateway 項目的創建(上圖中的 XCP 即使用 Envoy 構建的網關)。

零信任

零信任(Zero Trust)是 IstioCon 2022 裏的一個重要話題,Istio 正在成為零信任網絡的一個重要組成部分。

什麼是零信任?

零信任(Zero Trust)是一種安全理念,而不是一種所有安全團隊都要遵循的最佳實踐。零信任概念的提出是為了給雲原生世界帶來更安全的網絡。零信任是一種理論狀態,即網絡內的所有消費者不僅沒有任何權限,而且也不具備對周圍網絡的感知。零信任的主要挑戰是就越來越細化的授權和和對用户授權的時間限制。關於更多零信任的介紹,請閲讀這篇博客 。

身份認證

零信任網絡中最重要的是 面向身份的控制 而不是面向網絡的控制。Istio 1.14 中增加了對 SPIRE 的支持,SPIRE(SPIFFE Runtime Environment,CNCF 孵化項目) 是 SPIFFE(Secure Production Identity Framework For Everyone,CNCF 孵化項目) 的一個實現。在 Kubernetes 中我們使用ServiceAccount 為 Pod 中的工作負載提供身份信息,其核心是基於 Token(使用 Secret 資源存儲)來表示負載身份。而 Token 是 Kubernetes 集羣中的資源,對於多集羣及運行在非 Kubernetes 環境(例如虛擬機)中的負載,如何統一它們的身份?這就是 SPIFFE 要解決的問題。

SPIFFE 的目的是基於零信任的理念,建立一個開放、統一的工作負載身份標準,這有助於建立一個零信任的全面身份化的數據中心網絡。SPIFFE 的核心是通過簡單 API 定義了一個生命週期短暫的加密身份文件—— SVID(SPFFE Verifiable Identity Document),用作工作負載認證時使用的身份文件(基於 X.509 證書或 JWT 令牌)。SPIRE 可以根據管理員定義的策略自動輪換 SVID 證書和祕鑰,動態地提供工作負載標識,同時 Istio 可以通過 SPIRE 動態的消費這些工作負載標識。

基於 Kubernetes 的 SPIRE 架構圖如下所示。

SPIRE 部署在 Kubernetes 中的架構圖

Istio 中原先是使用 Istiod 中 Citadel 服務負責服務網格中證書管理,通過 xDS(準確的説是 SDS API)協議將證書下發給數據平面。有了 SPIRE 之後,證書管理的工作就交給了 SPIRE Server。SPIRE 同樣支持 Envoy SDS API,我們在 Istio 中啟用 SPIRE 之後,進入工作負載 Pod 中的流量在被透明攔截到 Sidecar 中後,會經過一次身份認證。身份認證的目的是對比該工作負載的身份,與它所運行的環境信息(所在的節點、Pod 的 ServiceAccount 和 Namespace 等)是否一致,以防止偽造身份。請參考 如何在 Istio 中集成 SPIRE 以瞭解如何在 Istio 中使用 SPIRE 做身份認證。

我們可以使用 Kubernetes Workload Registrar 在 Kubernetes 中部署 SPIRE,它會為我們自動註冊 Kubernetes 中的工作負載並生成 SVID。該註冊機是 Server-Agent 架構,它在每個 Node 上部署一個 SPIRE Agent,Agent 與工作負載通過共享的 UNIX Domain Socket 通信。零信任網絡中每個流量會話都需要經過身份認證,Istio 在透明流量劫持時,Sidecar 同時對流量請求進行身份認證。下圖展示了在 Istio 中使用 SPIRE 進行身份認證的過程。

Istio 中基於 SPIRE 的工作負載身份認證過程示意圖

Istio 中使用 SPIRE 進行工作負載認證的步驟如下:

pilot-agent

關於工作負載的註冊和認證的詳細過程請參考SPIRE 文檔 。

NGAC

當每個工作負載都有準確的身份之後,如何對這些身份的權限進行限制?Kubernetes 中默認使用 RBAC 來做訪問控制,正如其名,這種訪問控制是基於角色的,雖然使用起來比較簡單,但是對於大規模集羣,存在角色爆炸問題 —— 即存在太多角色,而且角色的類型不是一成不變的,難以對角色權限機型跟蹤和審計。另外 RBAC 中的角色的訪問權限是固定,沒有規定短暫的使用權限,也沒有考慮位置、時間或設備等屬性。使用 RBAC 的企業很難滿足複雜的訪問控制要求,以滿足其他組織需求的監管要求。

NGAC,即下一代訪問控制,採用將訪問決定數據建模為DAG (有向無環圖)的方法。NGAC 可以實現系統化、策略一致的訪問控制方法,以高精細度授予或拒絕用户管理能力。NGAC 由 NIST (美國國家標準與技術研究所)開發,目前已用於 Tetrate Service Bridge 中的權限管理。關於為什麼選擇 NGAC,而不是 ABAC 和 RBAC 的更多內容請參考博客 為什麼應該選擇使用 NGAC 作為權限控制模型

混合雲

在實際應用中,我們可能出於負載均衡、隔離開發和生產環境、解耦數據處理和數據存儲、跨雲備份和災難恢復以及避免廠商鎖定等原因,在多種環境下部署多個 Kubernetes 集羣。Kubernetes 社區提供了「集羣聯邦」功能可以幫助我們創建多集羣架構,例如下圖所示的一種常用的 Kubernetes 多集羣架構,其中 Host Cluster 作為控制平面,有兩個成員集羣,分別是 West 和 East。

Kubernetes 集羣聯邦架構

集羣聯邦要求 Host 集羣與成員集羣的之間的網絡能夠互通,對成員集羣之間的網絡連接性沒有要求。Host 集羣作為 API 入口,外界所有對 Host 集羣的資源請求會轉發到成員集羣中。Host 集羣中部署有集羣聯邦的控制平面,其中的 「Push Reconciler」會將聯邦中的身份、角色及角色綁定傳播到所有的成員集羣中。集羣聯邦只是簡單地將多個集羣簡單的「連接到了一起」,在多個集羣之間複製工作負載,而成員集羣之間的流量無法調度,也無法實現真正的多租户。

集羣聯邦不足以實現混合雲,為了實現真正意義上的混合雲,就要讓集羣之間做到互聯互通,同時實現多租户。TSB 在 Istio 之上構建一個多集羣管理的通用控制平面,然後再增加一個管理平面來管理多集羣,提供多租户、管理配置、可觀察性等功能。下面是 Istio 管理平面的多租户和 API 示意圖。

TSB 在 Istio 之上構建的管理平面示意圖

TSB 為管理混合雲,基於 Istio 構建了一個管理平面,新建了 Tenant 和 Workspace 的資源,並通過選擇器,將網關組、流量組和安全組應用到對應集羣中的工作負載上。關於 TSB 的詳細架構請參考 TSB 文檔

更多

如果你想了解更多關於 Istio 和雲原生的內容,下面有一些資料分享給你:

  • 為了幫助大家更好的瞭解 Istio 和雲原生,筆者在 2020 年發起了 雲原生社區 ,歡迎大家加入我們一起探索後 Kubernetes 時代的雲原生新範式;
  • 2022 年 6 月,雲原生社區著的 《深入理解 Istio —— 雲原生服務網格進階實戰》 已圖書由電子工業出版社出版,歡迎大家購買;
  • 筆者於 2022 年 5 月,將之前所作電子書、教程和譯文全部遷移到了雲原生資料庫 ,歡迎閲讀和留言評論。

參考