K8s 集群節點在線率達到 99.9% 以上,擴容效率提升 50%,我們做了這 3 個深度改造


作者 | 張振(守辰)阿里雲雲原生應用平台高級技術專家

導讀:2019 年阿里巴巴核心系統 100% 以雲原生方式上雲,完美地支撐了 雙11 大促。這次上雲的姿勢很不一般,不僅是擁抱了 Kubernetes,而且還以擁抱 Kubernetes 為契機進行了一系列對運維體系的深度改造。

Kubernetes 作為雲原生的最佳實踐,已經成為了事實上的容器編排引擎標准,Kubernetes 在阿里巴巴集團落地主要經歷了四個階段:

  • 研發和探索:2017 年下半年阿里巴巴集團開始嘗試使用 Kubernetes api 來改造內部自研平台,並開始了對應用交付鏈路的改造,以適配 Kubernetes;
  • 初步灰度:  2018 年下半年阿里巴巴集團和螞蟻金服共同投入 Kubernetes 技術生態的研發,力求通過 Kubernetes 替換內部自研平台,實現了小規模的驗證,支撐了當年部分 雙11 的流量;
  • 雲化灰度:  2019 年初阿里巴巴經濟體開始進行全面上雲改造,阿里巴巴集團通過重新設計 Kubernetes 落地方案,適配雲化環境,改造落后運維習慣,在 618 前完成了雲化機房的小規模驗證;
  • 規模化落地:2019 年 618 之后,阿里巴巴集團內部開始全面推動 Kubernetes 落地,在大促之前完成了全部核心應用運行在 Kubernetes 的目標,並完美支撐了 雙11 大考。

在這幾年的實踐中,一個問題始終縈繞在各個架構師的頭腦中: 在阿里巴巴這么大體量、這么復雜的業務下, 遺留了大量傳統的運維習慣以及支撐這些習慣的運維體系,落地 Kubernetes 到底要堅持什么?要妥協什么?要改變什么?

本文將分享阿里巴巴這幾年對於這些問題的思考。答案很明顯:擁抱 Kubernetes 本身並不是目的,而是通過擁抱 Kubernetes 撬動業務的雲原生改造,通過 Kubernetes 的能力,治理傳統運維體系下的沉痾頑疾,釋放雲彈性的能力,為業務的應用交付解綁提速。

在阿里巴巴的 Kubernetes 落地實踐中,關注了下面幾個關鍵的雲原生改造:

面向終態改造

在阿里巴巴傳統的運維體系下,應用的變更都是 PaaS 通過創建操作工單,發起工作流,繼而對容器平台發起一個個的變更來完成的。

當應用發布時, PaaS 會從數據庫中查到應用所有相關的容器,並針對每個容器,向容器平台發起修改容器鏡像的變更。每一個變更實際上也是一個工作流,涉及到鏡像的拉取、舊容器的停止和新容器的創建。工作流中一旦發生錯誤或者超時,都需要 PaaS 進行重試。一般而言,為了保證工單及時的完成,重試僅會執行幾次,幾次重試失敗后,只能依靠人工處理。

當應用縮容時,PaaS 會根據運維人員的輸入,指定容器列表進行刪除,一旦其中有容器因為宿主機異常的情況下刪除失敗或者超時,PaaS 只能反復重試,為了保證工單的結束,在重試一定次數后只能認為容器刪除成功。如果宿主機后續恢復正常,被“刪除”的容器很有可能依然運行着。

傳統的面向過程的容器變更一直存在如下問題無法解決:

  • 單個變更失敗無法保證最終成功

例如,一旦容器鏡像變更失敗,PaaS 無法保證容器鏡像的最終一致;一旦刪除容器失敗,
也無法保證容器最后真的被刪除干凈。兩個例子都需要通過巡檢來處理不一致的容器。而巡檢任務因為執行較少,其正確性和及時性都很難保證;

  • 多個變更會發生沖突

例如應用的發布和應用的擴容過程需要加鎖,否則會出現新擴的容器鏡像未更新的情況。而一旦對變更進行加鎖,變更的效率又會大幅下降。

Kubernetes 的能力提供了解決這個問題的機會。Kubernetes 的 workload 提供了聲明式的 API 來修改應用的實例數和版本,workload 的控制器可以監聽 pod 的實際情況,保證應用 pod 的實例數量和版本符合終態,避免了並發擴容和發布的沖突問題。Kubernetes 的 kubelet 會依據 pod 的 spec,反復嘗試啟動 pod,直到 pod 符合 spec 描述的終態。重試由容器平台內部實現,不再和應用的工單狀態綁定。

自愈能力改造

在阿里巴巴傳統的運維體系下,容器平台僅生產資源,應用的啟動以及服務發現是在容器啟動后由 PaaS 系統來執行的,這種分層的方法給了 PaaS 系統最大的自由,也在容器化后促進了阿里巴巴第一波容器生態的繁榮。但是這種方式有一個嚴重的問題,即:
容器平台無法獨立地去觸發容器的擴縮容,需要和一個個 PaaS 來做復雜的聯動,上層 PaaS 也需要做很多重復的工作。這妨礙了容器平台在宿主機發生故障、重啟、容器中進程發生異常、卡住時的高效自愈修復,也讓彈性擴縮容變得非常復雜。

在 Kubernetes 中通過容器的命令以及生命周期鈎子,可以將 PaaS 啟動應用以及檢查應用啟動狀態的流程內置在了 pod 中;另外,通過創建 service 對象,可以將容器和對應的服務發現機制關聯起來,從而實現容器、應用、服務生命周期的統一。容器平台不再僅僅生產資源,而是交付可以直接為業務使用的服務。這極大地簡化了上雲之后故障自愈以及自動彈性擴縮容能力的建設,
真正地發揮了雲的彈性能力。

另外,在宿主機發生故障的情況下,PaaS 傳統上需要先對應用進行擴容,然后才刪除宿主機上的容器。然而在大規模的集群下,我們發現往往會卡在應用擴容這步。應用資源額度可能不夠,集群內滿足應用調度限制的空閑資源也可能不夠,無法擴容就無法對宿主機上的容器進行驅逐,進而也無法對異常的宿主機進行送修,久而久之,整個集群很容易就陷入故障機器一大堆,想修修不了、想騰騰不動的困境之中。

在 Kubernetes 中對於故障機的處理要“簡單和粗暴”得多,不再要求對應用先擴容,而是直接把故障機上的容器進行刪除,刪除后才由負載控制器進行擴容。這種方案乍一聽簡直膽大妄為,落地 Kubernetes 的時候很多 PaaS 的同學都非常排斥這種方法,認為這會嚴重影響業務的穩定性。事實上,絕大多數核心的業務應用都維護着一定的冗余容量,以便全局的流量切換或者應對突發的業務流量,臨時刪除一定量的容器根本不會造成業務的容量不足。

我們所面臨的關鍵問題是如何確定業務的可用容量,當然這是個更難的問題,但對於自愈的場景完全不需要准確的容量評估,只需要一個可以推動自愈運轉的悲觀估計就可以。在 Kubernetes 中可以通過 PodDisruptionBudget 來定量地描述對應用的可遷移量,例如可以設置對應用進行驅逐的並發數量或者比例。這個值可以參考發布時的每批數量占比來設置。假如應用發布一般分 10 批,那么可以設置 PodDisruptionBudget 中的 maxUnavailable 為 10%(對於比例,如果應用只有 10 個以內的實例,Kubernetes 還是認為可以驅逐 1 個實例)。萬一應用真的一個實例都不允許驅逐呢?那么對不起,這樣的應用是需要改造之后才能享受上雲的收益的。一般應用可以通過改造自身架構,或者通過 operator 來自動化應用的運維操作,從而允許實例的遷移。<

不可變基礎設施改造

Docker 的出現提供了一種統一的應用交付形式,通過把應用的二進制、配置、依賴統一在構建過程中打到了鏡像中,
通過使用新的鏡像創建容器,並刪除掉舊容器就完成了應用的變更。Docker 在交付應用時和傳統基於軟件包或者腳本的交付方式有一個重大區別,就是強制了容器的不可變,想要變更容器只能通過新創建容器來完成,而每個新容器都是從應用同一個鏡像創建而來,確保了一致性,從而避免了配置漂移,或者雪花服務器的問題。

Kubernetes 進一步強化了不可變基礎設施的理念,在默認的滾動升級過程中不但不會變更容器,而且還不會變更pod。每次發布,都是通過創建新的 pod,並刪除舊的 pod 來完成,這不僅保證了應用的鏡像統一,還保證了數據卷、資源規格以及系統參數配置都是和應用模板的 spec 保持一致。

另外,不少應用都有比較復雜的結構,一個應用實例可能同時包含多個團隊獨立開發的組件。 比如一個應用可能包括了業務相關的應用程序服務器,也包括了基礎設施團隊開發的日志采集進程,甚至還包括了第三方的中間件組件。這些進程、組件如果想要獨立發布就不能放在一個應用鏡像中,為此 Kubernetes 提供了多容器 pod 的能力,可以在一個 pod 中編排多個容器,想要發布單個組件,只需要修改對應容器的鏡像即可。

不過,阿里巴巴傳統的容器形態是富容器,即應用程序服務器,以及日志采集進程等相關的組件都部署在一個大的系統容器中,這造成了日志采集等組件的資源消耗無法單獨限制,也無法方便地進行獨立升級。因此,在阿里巴巴這次上雲中,開始把系統容器中除業務應用外的其他組件都拆分到獨立的 sidecar 容器,我們稱之為輕量化容器改造。改造后,一個 pod 內會包括一個運行業務的主容器、一個運行着各種基礎設施 agent 的運維容器,以及服務網格等的sidecar容器。輕量化容器之后, 業務的主容器就能以比較低的開銷運行業務服務,從而更方便進行 serverless 的相關改造。

不過,Kubernetes 默認的滾動升級過程過於僵硬地執行了不可變基礎設施的理念,導致對多容器 pod 的能力支持有嚴重的缺失。雖然可以在一個 pod 中編排多個容器,但如果要發布 pod 中的一個容器,在實際執行發布時,不僅會重建待發布的容器,還會把整個 pod 都刪除,而后重調度、再重建。這意味着假如要升級基礎設施的日志采集組件,會導致其他組件,特別是業務的應用服務器被一起刪除重啟,從而干擾到正常的業務運行。因此,多個組件的變更依然沒有解耦。

對業務而言,假如 pod 中有本地緩存的組件,而每次業務的發布都會重啟緩存進程,這會導致業務發布期間緩存的命中率大幅下降,影響性能甚至用戶的體驗;另外,如果基礎設施、中間件等團隊的組件升級都和業務的組件升級綁定在一起,這會給技術的迭代更新帶來巨大的阻礙。假設負責中間件的團隊推出了新的 service mesh 版本, 而為了升級 mesh 還需要央求一個個業務發布才能更新 mesh 組件,那么中間件的技術升級就會大大減速。

因此,相比 pod 層次的不可變,我們認為堅持容器級別的不可變原則,更能發揮 Kubernetes 多容器 pod 的技術優勢。為此,我們建設了支持應用發布時只原地修改 pod 中部分容器的能力,特別地建設了支持容器原地升級的工作負載控制器,並替換 Kubernetes 默認的 deployment 和 statefulset 控制器作為內部的主要工作負載。

另外,還建設了支持跨應用進行 sidecar 容器升級的 sidecarset, 方便進行基礎設施以及中間件組件的升級。此外,通過支持原地升級還帶來了集群分布確定性、加速鏡像下載等的額外優勢。這部分能力,我們已經通過 OpenKruise 項目開源出來。OpenKruise 中的 Kruise 是 cruise的諧音,'K' for Kubernetes, 寓意 Kubernetes 上應用的自動巡航,滿載着阿里巴巴多年應用部署管理經驗和阿里巴巴經濟體雲原生化歷程的最佳實踐。目前,OpenKruise 正在計划發布更多的 Controller 來覆蓋更多的場景和功能,比如豐富的發布策略、金絲雀發布、藍綠發布、分批發布等等。

總結

今年我們實現了 Kubernetes 的規模化落地,經受了 雙11 大促真實場景的考驗。像阿里巴巴這樣有着大量存量應用的場景,落地 K8s 並沒有捷徑,我們經受住了快速規模化落地的誘惑,沒有選擇兼容和妥協落后的運維習慣,而是選擇深蹲打好基礎、選擇深挖雲原生價值。接下來,我們將繼續推動更多應用的雲原生改造,特別是有狀態應用的改造,讓有狀態應用的部署和運維更加高效;另外,還將推動整個應用交付鏈路的雲原生改造,讓應用交付更加高效和標准化。

本書亮點

  • 雙11 超大規模 K8s 集群實踐中,遇到的問題及解決方法詳述
  • 雲原生化最佳組合:Kubernetes+容器+神龍,實現核心系統 100% 上雲的技術細節
  • 雙 11 Service Mesh 超大規模落地解決方案

“ 阿里巴巴雲原生微信公眾號(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公眾號。”

更多相關信息,關注“阿里巴巴雲原生”


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM