K8s 叢集節點線上率達到 99.9% 以上,擴容效率提升 50%,我們做了這 3 個深度改造
點選下載《不一樣的 雙11 技術:阿里巴巴經濟體雲原生實踐》
本文節選自《不一樣的 雙11 技術:阿里巴巴經濟體雲原生實踐》一書,點選上方圖片即可下載!
作者 | 張振(守辰)阿里云云原生應用平臺高階技術專家
導讀: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等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公眾號。”
更多相關資訊,關注“阿里巴巴雲原生”