天天看點

螞蟻金服 Service Mesh 大規模落地系列 - Operator 篇

螞蟻金服 Service Mesh 大規模落地系列 - Operator 篇

本文為《螞蟻金服 Service Mesh 大規模落地系列》第六篇 - Operator 篇,該系列将會從核心、RPC、消息、無線網關、控制面、安全、運維、測試等子產品對 Service Mesh 雙十一大規模落地實踐進行詳細解析。文末包含往期系列文章。

引言

Service Mesh 是螞蟻金服下一代技術架構的核心,也是螞蟻金服内部雙十一應用雲化的重要一環,本文主要分享在螞蟻金服目前的體量下,如何支撐應用從現有微服務體系大規模演進到 Service Mesh 架構并平穩落地。

本文作者:杜宏偉(花名:應明),螞蟻金服技術專家,關注 API 網關,Service Mesh 和容器網絡,螞蟻金服 Service Mesh 核心成員。

為什麼需要 Service Mesh

在此之前,SOFAStack 作為螞蟻金服微服務體系下服務治理的核心技術棧,通過提供 Cloud Engine 應用容器、SOFABoot 程式設計架構(已開源)、SOFARPC(已開源) 等中間件,來實作服務發現和流量管控等能力。經過若幹年的嚴苛金融場景的錘煉,SOFAStack 已經具備極高的可靠性和可擴充性,通過開源共建,也已形成了良好的社群生态,能夠與其他開源元件互相替換和內建。在研發疊代上,中間件類庫已經與業務解耦,不過避免不了的是,運作時兩者在同一個程序内,意味着基礎庫的更新需要推動業務方更新對應的中間件版本。

我們一直在探索更好的技術實作方式。我們發現,Service Mesh 通過将原先通過類庫形式提供的服務治理能力進行提煉和優化後,下沉到與業務程序協同,但獨立運作的 Sidecar Proxy 程序中,大量的 Sidecar Proxy 構成了一張規模龐大的服務網絡,為業務提供一緻的,高品質的使用者體驗的同時,也實作了服務治理能力在業務無感的條件下獨立進行版本疊代的目标。

應用 Service Mesh 的挑戰

Service Mesh 帶給我們的能力很美好,但現實為我們帶來的挑戰同樣很多。比方說資料面技術選型和私有協定支援,控制面與螞蟻金服内部現有系統對接,配套監控運維體系建設,以及在調用鍊路增加兩跳的情況下如何優化請求延遲和資源使用率等等。

本文着重從 MOSN(Sidecar Proxy)的運維和風險管控方面,分享我們的實踐經驗,其他方面的挑戰及應對方案,請參考系列分享中的其他文章。

MOSN:

https://github.com/sofastack/sofa-mosn

Sidecar 注入

建立注入

已經完成容器化改造,運作在 Kubernetes 中的應用,如何接入到 Service Mesh 體系中?最簡單的方式,也是以 Istio 為代表的 Service Mesh 社群方案所采用的方式,即是在應用釋出階段,通過 mutating webhook 攔截 Pod 建立請求,在原始 Pod Spec 的基礎上,為 Pod 注入一個新的 MOSN 容器。

值得注意的是,在資源配置設定上,起初我們依據經驗值,在應用 8G 記憶體的場景下,為 Sidecar 配置設定 512M 記憶體,即

App: req=8G, limit=8G

Sidecar: req=512M, limit=512M

很快我們就發現了這種配置設定方案帶來的問題,一方面部分流量比較高的應用的 MOSN 容器,出現了嚴重的記憶體不足甚至 OOM;另一方面注入進去的 Sidecar 容器額外向排程器申請了一部分記憶體資源,這部分資源脫離了業務的 quota 管控。

是以,為了消除記憶體 OOM 風險和避免業務資源容量規劃上的偏差,我們制定了新的“共享記憶體”政策。在這個政策下,Sidecar 的記憶體 request 被置為0,不再向排程器額外申請資源;同時 limit 被設定為應用的 1/4,保障 Sidecar 在正常運作的情況下,有充足的記憶體可用。為了确實達到“共享”的效果,螞蟻金服 sigma 團隊針對 kubelet 做了調整,使之在設定 Sidecar 容器 cgroups limit 為應用 1/4 的同時,保證整個 Pod 的 limit 沒有額外增加(細節這裡不展開)。

當然,Sidecar 與應用“共享”配置設定到的記憶體資源,也導緻了在異常情況(比如記憶體洩露)下,sidecar 跟應用搶記憶體資源的風險。如何應對這個風險?我們的做法是,通過擴充 Pod Spec(及相應的 apiserver, kubelet 鍊路),我們為 Sidecar 容器額外設定了 Linux oom_score_adj 這個屬性,以保障在記憶體耗盡的情況下,Sidecar 容器會被 OOM Killer 更優先選中,以發揮 sidecar 比應用能夠更快速重新開機,進而更快恢複到正常服務的優勢。

此外,在 CPU 資源的配置設定上,我們也遇到過在一些場景下,MOSN 搶占不到 CPU 資源進而導緻請求延遲大幅抖動,解決方案是確定在注入 Sidecar 時,根據 Pod 内的容器數量,為每個 Sidecar 容器計算出相應的 cpushare 權重,并通過工具掃描并修複全站所有未正确設定的 Pod。

原地注入

在建立 Pod 的時候注入 Sidecar,是一件相對比較“舒服“的接入方式,因為這種做法,操作起來相對比較簡單,應用隻需先擴容,再縮容,就可以逐漸用帶有 Sidecar 的 Pod,替換掉舊的沒有 Sidecar 的 Pod。可問題是,在大量應用,大規模接入的時候,需要叢集有較大的資源 buffer 來供應用執行個體進行滾動替換,否則替換過程将變得十分艱難且漫長。而螞蟻金服走向雲原生的目标之一則是,雙十一大促不加機器,提高機器使用率。如果說我們要花更多的錢購買更多的機器來支援雲原生,就多少有點事與願違了。

為了解決這個問題,我們提出了“原地注入”的概念,也就是說在 Pod 不銷毀,不重建的情況下,原地把 Sidecar 注入進去。

如圖所示,原地注入由以下步驟構成:

  1. 在 PaaS 送出工單,選擇一批需要原地注入的 Pod;
  2. PaaS 調用中間件接口,關閉業務流量并停止應用容器;
  3. PaaS 以 annotation 的形式打開 Pod 上的原地注入開關;
  4. Operator 觀察到 Pod 原地注入開關打開,渲染 sidecar 模版,注入到 Pod 中并調整 cpu/memory 等參數;
  5. Operator 将 Pod 内容器期望狀态置為運作;
  6. kubelet 将 Pod 内容器重新拉起;
  7. PaaS 調用中間件接口,打開業務流量;

Sidecar 更新

我們将 RPC 等能力從基礎庫下沉到 Sidecar 之後,基礎庫更新與業務綁定的問題雖然消除了,但是這部分能力的疊代需求依然存在,隻是從更新基礎庫變成了如何更新 Sidecar。

最簡單的更新就是替換,即銷毀 Pod 重新建立出一個新的,這樣建立出來的 Pod 所注入的 Sidecar 自然就是新版本了。但通過替換的更新方式,與建立注入存在相似的問題,就是需要大量的資源 buffer,并且,這種更新方式對業務的影響最大,也最慢。

非平滑更新

為了避免銷毀重建 Pod,我們通過 Operator 實作了“非平滑更新”能力。

如圖所示,非平滑更新需要:

  1. PaaS 關流量,停容器;
  2. Operator 替換 MOSN 容器為新版本,重新拉起容器;
  3. PaaS 重新打開流量;

可以想到,原地更新 Pod 打破了 Kubernetes immutable infrastructure 的設計,為了能夠實作我們的目标,sigma 團隊修改了 apiserver validation 和 admission 相關的邏輯以允許修改運作中的 Pod Spec,也修改了 kubelet 的執行邏輯以實作容器的增删啟停操作。

平滑更新

為了進一步降低 Sidecar 更新對應用帶來的影響,我們針對 MOSN Sidecar 開發了“平滑更新”能力,以做到在 Pod 不重建,流量不關停,應用無感覺的條件下對 MOSN 進行版本更新。

從上圖可見,Operator 通過注入新 MOSN,等待 MOSN 自身進行連接配接和 Metrics 資料的遷移完成,再停止并移除舊 MOSN,來達到應用無感,流量無損的效果。整個過程看似沒有很複雜,實則在各個環節上充斥着各種細節上的配合,目前為止,在平滑更新能力上,我們仍需在成功率方面努力,也需要改進 Operator 的狀态機來提升性能。關于 MOSN 自身的連接配接遷移過程,讀者如有興趣,可參閱系列分享中的對應篇章。

Sidecar 復原

為了確定大促活動萬無一失,我們還提供了 Sidecar 復原的保底方案,以備在識别到 Service Mesh 出現嚴重問題的情況下,迅速将應用復原到未接入 Sidecar 的狀态,使用應用原先的能力繼續提供業務服務。

風險管控

從技術風險角度來看,關于 Sidecar 的所有運維操作,都要具備三闆斧能力。在灰階能力上,Operator 為更新等運維動作增加了顯式的開關,確定每個執行動作符合使用者(SRE)的期望,避免不受控制地,“偷偷地“自動執行變更操作。

監控方面,在基本的操作成功率統計,操作耗時統計,資源消耗等名額之外,仍需以快速發現問題,快速止血為目标,繼續完善精細化監控。

Operator 目前對外提供的幾個運維能力,細節上都比較複雜,一旦出錯,影響面又很大,是以單元測試覆寫率和內建測試場景覆寫率,也會是後續 Service Mesh 穩定性建設的一個重要的點去努力完善。

未來的思考

演進到 Service Mesh 架構後,保障 Sidecar 自身能夠快速,穩定的疊代十分重要。相信在未來,除了繼續增強 Operator 的能力,也需要通過以下幾個可能的優化手段,來做到更好的風險控制:

  1. 對 Sidecar 模版做版本控制,由 Service Mesh 控制面,而非使用者來決定某個叢集下某個應用的某個 Pod 應該使用哪個版本的 Sidecar。這樣既可以統一管控全站的 Sidecar 運作版本,又可以将 Sidecar 二進制和其 container 模版相綁定,避免出現意外的,不相容的更新。
  2. 提供更加豐富的模版函數,在保持靈活性的同時,簡化 Sidecar 模版的編寫複雜度,降低出錯率。
  3. 設計更完善的灰階機制,在 Operator 出現異常後,快速熔斷,避免故障範圍擴大。
  4. 持續思考,整個 Sidecar 的運維方式能否更加“雲原生”?

最後

雙十一的考驗強化了我們在雲原生道路上探索的信心,未來還有很長的路要走,任重而道遠。期望我們能夠與更多感興趣的同學交流,一起建設 Service Mesh 技術體系,繼續用技術幫助業務更好發展。

SOFAStack 部分開源項目位址:

螞蟻金服 Service Mesh 大規模落地系列文章