Slime 2022 展望:把 Istio 的複雜性塞入智慧的黑盒

語言: CN / TW / HK

一、導讀

網易數帆輕舟微服務團隊很早就開始使用 Istio 做服務網格。在實踐過程中,我們開發了很多 Istio 周邊模組,方便了自身及網易集團內部客戶使用 Istio。為了回饋社群,我們系統整理了這些模組,並選擇了一部分,在2021年初開源出 Slime 專案

Slime 專案旨在解決 Istio 使用上的痛點,方便使用者使用 Istio 的高階功能,並始終堅持可以無縫對接 Istio,無需任何的定製化改造的原則,極大降低了使用門檻。

這一年多來,Slime 在架構、功能、工程方面做了很多變化和嘗試,得到了很大提升,並且在2021年12月受邀加入 Istio 生態,正式成為 Istio Ecosystem - integrations 的成員。

今天,本文將會介紹 Slime 現階段的主要能力,以懶載入和智慧限流模組為主,並展望未來發展,希望能讓更多的 ServiceMesher 瞭解 Slime,參與 Slime,一起更輕鬆地使用服務網格。

二、懶載入

2.1 背景

Istio 的全量推送效能問題,是所有 Istio 使用者都要面對的問題。

眾所周知,早期 Istio 的配置下發非常粗糙,直接全量推送。這意味著,隨著服務網格中業務規模的不斷擴大,控制面需要下發的內容越來越多,資料面需要接收的內容也不斷增長,這必然帶來效能問題。叢集中往往有多個業務系統。一個業務系統的應用感知所有業務系統的配置,這意味著推送大量冗餘配置,也是不合理的。如下圖左側所示,A 只和 B 有關係,卻被推送了 C 和 D 的配置。 另一個問題是,推送的頻率會很高。當一個服務發生變化,控制面要通知資料面所有SidecarProxy。

於是,Istio 1.1版本提供瞭解決方案 - Sidecar CRD (本文會稱之為 SidecarScope,以和 Envoy 實現的 SidecarProxy 做區分)。使用者可以在 SidecarScope 中描述 SidecarProxy 需要關心的服務資訊,藉此遮蔽無關的服務配置下發。結果如上圖右側所示,服務配置了 SidecarScope 後,收到的配置精簡了,不再包含無關的配置。同時,不相關服務的配置變化也不會再通知,減少了推送頻率。

一個典型的 SidecarScope 樣例如下,樣例表示允許匹配的 SidecarProxy 感知 Namespace prod1 和 istio-system 中的所有服務以及 Namespace prod2 中 ratings 服務配置資訊。

apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
  name: default
  namespace: prod1
spec:
  egress:
  - hosts:
    - "prod1/*"
    - "prod2/ratings.prod2.svc.cluster.local"
    - istio-system/*

Istio 提供的 SidecarScope 可以解決配置全量下發的問題,看起來問題解決了。但是在實踐中,手動管理 SidecarScope 很困難。一方面是服務依賴的資訊不好整理,另一方面一旦配置有誤,會導致調用出現問題。這非常不利於服務網格大規模落地。我們迫切希望能夠更智慧地管理 SidecarScope。

2.2 價值

懶載入模組就是用於解決上述問題。懶載入可自動對接服務網格,在轉發過程中支援 Istio 所有網路治理能力,無效能問題。它可以幫助業務人員使用了 SidecarScope,而無需直接管理它。

我們認為,服務依賴關係可以分成執行過程中不斷變化的動態服務依賴和業務人員可以提前知曉的靜態服務依賴兩類。對於動態依賴,我們設計了一套機制,實時獲取服務依賴並修改 SidecarScope ;對於靜態依賴,我們著重簡化了配置規則,使其更加人性化。

2.3 動態配置更新

懶載入包含 Global-sidecar 和 Lazyload Controller 兩個元件。

  • Global-sidecar: 兜底元件,當源服務無法找到目標服務時,進行兜底轉發,並生成相應的服務依賴 Metric
  • Lazyload Controller: 控制組件,處理 Global-sidecar 上報的 Metric,修改源服務的 SidecarScope,為其增加相應配置

簡化的動態配置更新流程如下所示

  • 服務 A 的 SidecarScope 初始是空白,沒有服務 B 的配置資訊
  • 服務 A 發起對服務 B 的第一次訪問,由於服務 A 的 SidecarProxy 沒有服務 B 的配置資訊,請求傳送到兜底元件 Global-Sidecar
  • 兜底元件 Global-Sidecar 擁有全量服務配置資訊,自然包含服務 B ,轉發請求到服務 B,首次請求成功,並生成 Metric(A->B)
  • Lazyload Controller 感知到 Metric(A->B) ,修改 SidecarScope A,為其增加服務 B 的配置
  • 服務 A 第二次訪問服務 B 時,服務 A 的 SidecarProxy 已經有服務 B 的配置,請求直達服務 B

詳細的流程圖如下

其中 ServiceFence 是懶載入中引入的 CRD,作用是儲存與服務相關的 Metric,實現 SidecarScope 的更新。詳細的介紹可參考 懶載入教程-架構

2.4 靜態配置增強

懶載入發展早期,我們聚焦於動態服務依賴關係的獲取,這看起來智慧又省心。然而在實踐中,我們發現很多使用者出於安全考慮,往往希望直接配置一些規則到 SidecarScope 中,即配置靜態服務依賴關係。於是,我們開始思考如何靈活配置靜態依賴關係。

於是,我們設計出一套很好用的靜態規則,並將其寫到 ServiceFence 中(是的,就是動態配置更新中用於儲存 Metric 的 CRD,它在這裡發揮了新作用)。之後 Lazyload Controller 根據這些規則更新相應的 SidecarScope 。

現在我們提供的靜態配置規則有三類:

  • 依賴某些 Namespace 所有服務
  • 依賴具有某些 label 的所有服務
  • 依賴某個特定服務

此處以 label 匹配舉個例子,假如應用部署如下圖所示

現在為服務 productpage 啟用懶載入,已知 productpage 的依賴規則為

  • 具有 label app: details的所有服務
  • 具有 label app: reviews version: v2的所有服務

那麼對應的 ServiceFence 寫法如下

---
apiVersion: microservice.slime.io/v1alpha1
kind: ServiceFence
metadata:
  name: productpage
  namespace: default
spec:
  enable: true
  labelSelector: # Match service label, multiple selectors are 'or' relationship
    - selector:
        app: details
    - selector: # labels in one selector are 'and' relationship
        app: reviews
        version: v2

Lazyload Controller 會根據實際匹配結果,填充 SidecarScope。 實際得到的 SidecarScope 如下,上圖中綠色的服務全部選中

apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: productpage
  namespace: default
spec:
  egress:
  - hosts:
    - '*/details.ns1.svc.cluster.local'
    - '*/details.ns2.svc.cluster.local'
    - '*/details.ns3.svc.cluster.local'
    - '*/reviews.ns2.svc.cluster.local'
    - istio-system/* # istio部署的ns
    - mesh-operator/* # lazyload部署的ns
  workloadSelector:
    labels:
      app: productpage

終於,我們不用在上線前反覆確認,是否填齊了所有服務依賴,更不用在服務依賴關係變更時,手動修改 SidecarScope。配置兩三條 ServiceFence 的規則就可搞定一切。

詳細的介紹可參考 懶載入教程 - 靜態服務依賴關係新增

2.5 Metric型別

在2.3章節中,我們解釋了 Metric 是動態依賴關係生成的根本。目前懶載入支援的 Metric 型別有兩個:Prometheus 和 AccessLog 。

使用 Prometheus Mode,指標由各個業務應用的 SidecarProxy 產生。Lazyload Controller 查詢 Prometheus 獲取指標。此模式需要服務網格對接 Prometheus。

使用 AccessLog Mode,指標來源是 Global-sidecar 的 AccessLog。Global-sidecar 在兜底轉發的同時,會生成固定格式的 AccessLog,發給 Lazyload Controller 進行處理。此模式無需外部依賴,更為輕便。

2.6 使用模式

懶載入模組有兩種使用模式,Namespace 模式和 Cluster 模式。兩種模式中,Lazyload Controller 都是全域性唯一的,不同點在於前者的 Global-sidecar 是 Namespace 級別,後者是 Cluster 級別。如下圖所示

對於 N 個 Namespace,Namespace 模式的懶載入元件數是 O(N),Cluster 模式則是 O(1)。現在我們更推薦使用 Cluster 模式。如上圖所示,每個叢集只需要部署兩個 Deployment,簡潔明瞭。

詳細的介紹可參考 懶載入教程 - 安裝和使用

三、智慧限流

3.1 背景

隨著 Istio 移除 Mixer,在服務網格中實現限流變得困難起來。

  • 場景少:Envoy 的本地限流元件功能簡單,不能實現全域性均分、全域性共享限流等高階用法
  • 配置複雜:本地限流需要藉助 Envoy 內建外掛 envoy.local.ratelimit,使用者不得不面對複雜的 EnvoyFilter 配置
  • 條件固定:沒有根據資源使用率等實際情況,自動調整限流配置的能力

3.2 價值

為了解決這個問題,我們推出了智慧限流模組。智慧限流模組具有很多優勢,具體來說

  • 場景多:支援本地限流、全域性均分限流、全域性共享限流
  • 配置方便:配置簡單,可讀性好,無需配置 EnvoyFilter
  • 條件自適應:限流觸發的條件可結合 Prometheus Metric,動態計算,實現自適應限流效果

3.3 實現

我們設計了一個新的CRD - SmartLimiter,其配置規則接近自然語義。模組的邏輯分成兩部分

  • SmartLimiter Controller 獲取監控資料,更新 SmartLimiter CR
  • SmartLimiter CR 到 EnvoyFilter 的轉換

限流模組架構如下

紅色是本地限流,綠色是全域性均分限流,而藍色是全域性共享限流。詳細的介紹可參考 智慧限流教程 - 架構

3.4 本地限流

本地限流是最基礎的使用場景。SmartLimiter 替服務的每個 Pod 設定固定的限流數值。底層依賴 Envoy 內建外掛 envoy.local.ratelimit。標識欄位是action.strategy: single

一個樣例如下,表示 reviews 服務每個 Pod 的9080埠每分鐘限流 60次。

apiVersion: microservice.slime.io/v1alpha2
kind: SmartLimiter
metadata:
  name: reviews
  namespace: default
spec:
  sets:
    _base:   # 匹配所有服務,關鍵詞 _base ,也可以是你定義的 subset ,如 v1 
      descriptor:   
      - action:    # 限流規則
          fill_interval:
            seconds: 60
          quota: '100'
          strategy: 'single'  
        condition: 'true'  # 永遠執行該限流
        target:
          port: 9080

3.5 全域性均分限流

全域性均分限功能根據使用者設定的總的限流數,然後平均分配到各個 Pod。底層依賴 Envoy 內建外掛 envoy.local.ratelimit。標識欄位是action.strategy: average

一個樣例如下,表示 reviews 服務所有 Pod 的9080埠每分鐘一共限流 60次,具體到每個 Pod 的限流次數由 action.quota計算得出。

apiVersion: microservice.slime.io/v1alpha2
kind: SmartLimiter
metadata:
  name: reviews
  namespace: default
spec:
  sets:
    _base:
      descriptor:
      - action:
          fill_interval:
            seconds: 60
          quota: '100/{{._base.pod}}' # 如果reviews例項數是2,則每個Pod限流每分鐘50次
          strategy: 'average'  
        condition: 'true'
        target:
          port: 9080

3.6 全域性共享限流

全域性共享限流限制目標服務的所有 Pod 可訪問的總次數,不會像全域性均分限流限制在平均值,比較適用於訪問不均的場景。此場景會維護一個全域性計數器,底層依賴 Envoy 外掛 envoy.filters.http.ratelimit和 RLS 服務提供的全域性計數能力。標識欄位是action.strategy: global

一個樣例如下,表示 reviews 服務所有 Pod 的9080埠每分鐘一共限流 60次,不均分到每個 Pod 。

apiVersion: microservice.slime.io/v1alpha2
kind: SmartLimiter
metadata:
  name: reviews
  namespace: default
spec:
  sets:
    _base:
      #rls: 'outbound|18081||rate-limit.istio-system.svc.cluster.local' 如果不指定預設是該地址
      descriptor:
      - action:
          fill_interval:
            seconds: 60
          quota: '100'
          strategy: 'global'
        condition: 'true'
        target:
          port: 9080 

3.7 自適應限流

上述三個場景中,觸發限流的條件欄位condition除了可以是固定數值(true),還可以是 Prometheus Query 的計算結果。後者就是自適應限流。此場景和上述三個場景是交叉關係。

使用者可以自定義需要獲取的監控指標,比如定義一個handler cpu.sum,其數值等於 sum(container_cpu_usage_seconds_total{namespace="$namespace",pod=~"$pod_name",image=""}),後面就可以將觸發限流的condition設定為{{._base.cpu.sum}}>100 的形式,實現自適應限流。

一個樣例如下,表示 reviews 服務每個 Pod 的9080埠只有當CPU使用值大於100時,每分鐘限流 60次。對比3.4中的例子,condition不再永遠為真,是否觸發限流,由 SmartLimiter Controller 根據應用實際狀態判斷,更加智慧化。

apiVersion: microservice.slime.io/v1alpha2
kind: SmartLimiter
metadata:
  name: reviews
  namespace: default
spec:
  sets:
    _base:   # 匹配所有服務,關鍵詞 _base ,也可以是你定義的 subset ,如 v1 
      descriptor:   
      - action:    # 限流規則
          fill_interval:
            seconds: 60
          quota: '100'
          strategy: 'single'  
        condition: '{{._base.cpu.sum}}>100'  如果服務的所有負載大於100,則執行該限流
        target:
          port: 9080

四、專案架構

這一節簡單介紹下 Slime 的專案架構,幫助大家理解 Slime 多模組場景下的程式碼倉庫和部署形態。架構如下圖所示

Slime 的專案架構遵守“高內聚,低耦合”的設計思想,包含三塊

  • Modules: 提供某個功能的獨立模組,像懶載入、智慧限流都屬於 Modules

  • Framework: 模組的底座,提供 Modules 需要的基礎能力,比如日誌輸出、監控指標獲取

  • Slime-boot: 啟動元件,負責拉起 Framework 和指定的 Modules

整個程式碼倉庫分成 1+N。Slime-boot 和 Framework 位於 slime 主倉庫 slime-io/slime,懶載入等模組全部位於獨立倉庫。

部署形式也是 1+N 的,即一個 Slime Deployment 包含一個公用的 Framework 和使用者想要的 N 個模組。好處是,無論使用多少 Slime 模組,部署時都是一個 Deployment,很好解決了微服務元件過多的維護痛點。

五、展望

Slime 開源至今一年多的時間,除了模組層面新功能的新增、已有功能的完善,還經過了一次架構大調整和 Metric 體系的重構,可以說,現在 Slime 的發展已經走上正軌,進入新的階段。未來規劃可以分為已有模組的提升和新模組的引入,下面詳細說明。

5.1 懶載入規劃

特性 特性說明 性質 計劃釋出時間
災備兜底能力 改造 Global-Sidecar 元件,完善其兜底能力,使懶載入可用於一些災備場景 確定性 2022.Q2
多服務註冊中心支援 懶載入目前主要適配 Kubernetes 場景,計劃通過對 ServiceEntry 的支援,適配多服務註冊中心場景 確定性 2022.Q2
更靈活的靜態配置 通過更高維度的抽象,實現 ServiceFence 的自動配置,支援更高階的靜態規則 確定性 2022.Q3
多協議懶載入 懶載入目前支援 Http 服務,計劃支援其他協議服務的懶載入,比如 Dubbo 等 探索性 2022.H2
跨叢集懶載入 懶載入目前支援同叢集服務,計劃支援多叢集服務網格場景下的跨叢集服務懶載入 探索性 2022.H2

5.2 智慧限流規劃

特性 特性說明 性質 計劃釋出時間
多服務註冊中心支援 智慧限流目前主要適配 Kubernetes 場景,計劃通過對 ServiceEntry 的支援,適配多服務註冊中心場景 確定性 2022.Q2
出流量側限流 智慧限流目前支援入流量側限流,這可以滿足大多數場景,但是從能力完備度上,計劃支援出流量側限流 確定性 2022.Q3
多協議智慧限流 智慧限流目前支援 Http 服務,計劃支援其他協議服務的智慧限流,比如 Dubbo 等 探索性 2022.H2
跨叢集智慧限流 智慧限流目前支援同叢集服務,計劃支援多叢集服務網格場景下的跨叢集智慧限流 探索性 2022.H2

5.3 新模組規劃

模組名(規劃) 模組說明 性質 計劃釋出時間
IPerf 專為 Istio 打造的效能測試工具集,整合 Istio 測試框架,新增自定義測試用例,可直觀對比不同版本的效能變化 確定性 2022.H2
Tracetio 服務網格的全鏈路自動化運維,提高排障效率,給出智慧化判斷 確定性 2022.H2
I9s 類似 K9s ,適用於服務網格場景的黑屏半命令列半圖形化運維工具 確定性 2022.H2

希望上面的這些規劃能儘快與大家見面。Slime 相關資訊可以查閱 Slime - Home,歡迎大家多多與我們交流。

相關閱讀

Slime:讓 Istio 服務網格變得更加高效與智慧

作者簡介: 王晨宇,網易數帆資深服務端開發工程師,Istio 社群成員,Slime Maintainer,熟悉 Istio 與 Kubernetes,主要負責 Slime 及網易數帆輕舟服務網格的設計與研發,具有多年雲原生相關領域的實踐經驗。