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

語言: CN / TW / HK

Slime是網易數帆旗下輕舟微服務團隊開源的服務網格元件,它可以作為Istio的CRD管理器,旨在通過更為簡單的配置實現Istio/Envoy的高階功能。目前slime包含三個非常實用的子模組:

  1. 配置懶載入: 無須手動配置SidecarScope,按需載入配置和服務發現資訊
  2. Http外掛管理: 使用新的的CRD pluginmanager/envoyplugin包裝了可讀性,可維護性較差的envoyfilter,使得外掛擴充套件更為便捷
  3. 自適應限流: 結合監控資訊自動調整限流策略

1. 背景

服務網格作為新一代微服務架構,採用sidecar模式,實現了業務邏輯和微服務治理邏輯的物理解耦,降低微服務框架的開發與運維成本。權責清晰,易維護,可觀測,多語言支援等一些列優勢使其逐漸成為微服務話題中的焦點。而Istio+Envoy作為其使用最為廣泛的實現一直佔據著C位,背靠Google的大樹,Istio已隱隱具備了成為業界標準的趨勢。

欲戴皇冠,必承其重。站在風口浪尖的Istio,獲得讚譽的同時也引來了不少非議。專案的優勢自不必說,Istio有著一套行之有效的上層抽象,通過配置VirtualService,DestinationRule等CR可以實現版本分流,灰度釋出,負載均衡等功能,但是在面對本地限流,黑白名單,降級等微服務治理的高階功能時,這套抽象顯得力有不逮,起初Istio給出的解決方案是Mixer,將這些原本屬於資料面的功能上升到Mixer Adapter中,雖然解決了功能擴充套件的問題,但其集中式的架構遭到了不少關注者對其效能的質疑。最終,Istio在新版本中自斷其臂,棄用了Mixer,這就使得高階功能的擴充套件成為目前版本的一塊空白。另一方面Istio配置是全量推送的,這就意味著在大規模的網格場景下需推送海量配置,為了減少推送配置量,使用者不得不事先搞清楚服務間的依賴關係,配置SidecarScope做配置隔離,而這無疑增加了運維人員的心智負擔,易用性和效能成為不可兼得的魚和熊掌。

針對Istio目前的一些弊端,我們團隊開啟了slime專案。該專案是基於k8s-operator實現的,作為Istio的CRD管理器,可以無縫對接Istio,無需任何的定製化改造。slime內部採用了模組化的架構,目前包含了三個非常實用的子模組:

  1. 配置懶載入:無須手動配置SidecarScope,按需載入配置資訊和服務發現資訊,解決了全量推送的問題。
  2. Http外掛管理:使用新的的CRD pluginmanager/envoyplugin包裝了可讀性,可維護性較差的envoyfilter,使得外掛擴充套件更為便捷。
  3. 自適應限流:可結合監控資訊自動調整限流策略,填補了Istio限流功能的短板。

2. 配置懶載入

隨著服務網格上業務規模的逐步變大,我們遇到的第一個問題就是配置全量下發導致的效能問題,它對資料面和控制面都有較為嚴重的效能影響:

  1. 使資料面收到大量冗餘配置 a) Envoy啟動時間變長 b) Envoy記憶體開銷增加 c) 佔據Envoy主執行緒,阻塞Pilot事件推送
  2. 增加控制面處理推送事件複雜度 a) Pilot推送時記憶體增加,易引發OOM b) 配置下發時延增加 為了使Istio能支援一定量級的叢集,我們不得不事先要求業務方在服務釋出時,告知該服務所依賴的服務,並以此設定SidecarScope遮蔽無關服務的配置和服務發現資訊。但是在推行過程中卻遇到了阻力,一方面是依賴服務的資訊不好獲取,另一方面一旦業務方配置有誤,會導致調用出現問題。這個規定使得原本想要上網格的業務變得望而卻步。 image.png

有什麼辦法可以使服務按需獲取配置呢?最容易想到的是從服務呼叫關係中獲取該資訊,但是在缺失被呼叫方服務發現資訊的情況下,是無法成功訪問的,這就會導致一些容錯率低的服務不能接受這種方案,另一方面訪問不成功時獲得的服務呼叫關係也並不可靠。換而言之,如果有辦法使服務在不具備被呼叫方配置資訊和服務發現資訊的情況下能夠成功呼叫,就可以通過自動生成SidecarScope的方式實現配置懶載入(按需載入)。

我們想到的辦法是構建一條兜底路由,這條兜底路由的backend是一個全域性共享的sidecar,我們稱之為global-sidecar,它擁有全量的配置和服務發現資訊。缺失服務發現資訊的呼叫,都會被兜底路由劫持到global-sidecar,global-sidecar為其做二次代理,轉發到對應的後端服務。 lazyload-bookinfo-p1.png global-sidecar在完成代理後會將服務呼叫資訊上報給slime,slime根據呼叫資訊更新Scope,首次呼叫後,服務便可感知到被呼叫方的資訊,不再需要global-sidecar轉發,如下圖所示。 lazyload-bookinfo-p2.png

在實現配置懶載入的過程中,我們也遇到了另外一個問題,當被呼叫服務服務名被vs中的路由規則導向另一個服務時,slime只能將被呼叫服務新增到Scope中,被導向服務的服務發現資訊依然缺失,導致再次呼叫時出現503。為了解決這個問題,我們引入了自研CRD–ServiceFence,通過它可以構建起服務名和後端服務的對映關係。slime根據其,將被呼叫服務和被導向服務同時加入到Scope中,避免了上述問題。 ll.png Servicefence也可以對生成的SidecarScope的生命週期做管理,可以自動清理長時間不用的呼叫關係。 當然上述這些CRD的生成和維護都是自動的,使用者即不需要關心ServiceFence資源也不需要關心SidecarScope資源,只需要在SVC上打上istio.dependency.servicefence/status: "true"的標籤,表面該服務需要開啟配置懶載入即可。

自動依賴.png

3. Http外掛管理

在閘道器場景下,流量管理比較複雜,需要使用定製化外掛來處理流量,在開發slime的外掛模組之前,外掛擴充套件只能通過EnvoyFilter來實現,EnvoyFilter是xDS層面的配置,管理和維護這樣的配置需要耗費大量的精力,同時出錯率也極高。

為了簡化外掛管理的難度,我們決定在EnvoyFilter上層做一層面向外掛管理的抽象。xDS中關於HTTP外掛的配置有兩段,一部分在LDS中,作為HttpConnectionManager的SubFilter,它決定了哪些外掛將被載入以及外掛的執行順序。另一部分在RDS中,並且有兩個粒度,分別是virtualHost粒度的perFilterConfig以及route粒度的perFilterConfig,這部分決定了當前Host或者是路由需要進行的外掛行為。

LDS中的部分被我們抽象為PluginManager,我們可以通過enable選項啟停外掛。通過PluginManager也可以管理外掛的執行優先順序,其中的外掛順序和LDS外掛鏈中的順序是一致的,越靠前的外掛執行優先順序越高,如下圖所示:

plugin_pluginmanager.png

RDS中的部分被抽象為EnvoyPlugin,通過EnvoyPlugin的Host/Route欄位可以設定外掛配置的生效範圍。EnvoyPlugin更加貼合閘道器的配置模型,在閘道器的控制檯上,後端服務往往被對映為某個Host下的某幾個API介面,例如我們需要為服務A配置自研的黑白名單外掛以及trace取樣外掛,A服務在閘道器上的介面為/abc/xyz,針對該服務的外掛配置就會被對映為:

apiVersion: microservice.netease.com/v1alpha1
kind: EnvoyPlugin
metadata:
  name: gateway-proxy-svc-a 
  namespace: gateway-system
spec:
  gateway:
  - gateway-system/gateway-proxy
  host:
  - gwtest.com
  route:
 - name: abc
 - name: xyz
  plugins:
  - name: com.netease.iprestriction
    inline
      settings:
        list:
        - 1.1.1.1
        type: BLACK 
  - name: com.netease.resty
    inline
      settings:
        plugins:
        - config:
            sample_rate: 0.001
            whitelist:
            - aaa
          name: neTraceSample 

EnvoyPlugin不關心每個外掛的具體配置(具體配置會被放在type.struct結構中透傳處理),它更關心的是外掛生效範圍,使用者可以將外掛配置在需要的維度中做聚合,這樣做一方面更加貼合外掛使用者的習慣,另一方面也降低了上層配置的冗餘,下圖表面了EnvoyPluing在xDS層面的對映關係,雖然xDS層面仍舊會展開,但至少在管理它們的時候,我們面對的是一個有序聚合的陣列,而非一顆龐大的外掛樹:

plugin_envoyplugin.png

4. 自適應限流

隨著Mixer的移除,要實現服務網格中的限流變得非常複雜。全侷限流需要配置額外部署 RLS (Ratelimit Server),即使是本地限流也需要藉助Envoy內建外掛– envoy.local.ratelimit,為此使用者不得不再次面對複雜的EnvoyFilter配置。相較於二代微服務框架中成熟的限流元件而言,Envoy的本地限流元件功能也略顯簡單,例如,無法做到自適應限流,只能以例項維度配置限流值等。

為了解決Istio中服務限流的短板,我們開發了自適應限流模組,在易用性方面,我們也為其設計了一套新的API – SmartLimiter。自適應限流的主體架構分為兩部分,一部分為SmartLimiter到EnvoyFilter的轉換邏輯,另一部分為監控資料獲取。目前slime支援從K8S metric-server獲取服務的CPU,Memory,副本數等資料,當然我們也對外提供了一套監控資料對接介面(Metric Discovery Server),通過MDS,可以將自定義的監控指標同步給限流元件。

limit_arch.png

SmartLimiter的配置是比較接近自然語義的,例如希望在CPU超過80%時觸發服務A的訪問限制,限額為30QPS,對應的SmartLimiter定義如下:

apiVersion: microservice.netease.com/v1alpha1
kind: SmartLimiter
metadata:
  name: a
  namespace: default
spec:
  descriptors:
  - action:
      fill_interval:
        seconds: 1
      quota: "30/{pod}"    # 30為該服務的額度,將其均分給每個pod
    condition: "{cpu}>0.8" # 根據監控項{cpu}的值自動填充該模板

最終產生的限流行為,如下圖所示:

limit.png

5. 如何獲取和使用slime

slime的原始碼已經開放,你可以在這裡獲得slime的最新動態,後續我們團隊會開放更多實用功能在slime中。你也可以閱讀使用指引快速上手slime。在使用指引中,我們基於bookinfo為slime編寫了簡單的例子,希望能幫助到你們。

最後,slime仍處於早期階段,希望有更多的mesher加入我們或為我們提出建議,幫助我們完善它。

希望slime能幫助使用者更好的駕馭Istio這艘帆船!

作者簡介

楊笛航,Istio社群成員,網易數帆架構師,負責輕舟Service Mesh配置管理,並主導slime元件設計與研發,參與網易嚴選和網易傳媒的Service Mesh建設。具有三年Istio控制面功能拓展和效能優化經驗。