天天看點

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 并沒有捷徑,我們經受住了快速規模化落地的誘惑,沒有選擇相容和妥協落後的運維習慣,而是選擇深蹲打好基礎、選擇深挖雲原生價值。接下來,我們将繼續推動更多應用的雲原生改造,特别是有狀态應用的改造,讓有狀态應用的部署和運維更加高效;另外,還将推動整個應用傳遞鍊路的雲原生改造,讓應用傳遞更加高效和标準化。

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

本書亮點

  • 雙11 超大規模 K8s 叢集實踐中,遇到的問題及解決方法詳述
  • 雲原生化最佳組合:Kubernetes+容器+神龍,實作核心系統 100% 上雲的技術細節
  • 雙 11 Service Mesh 超大規模落地解決方案
“ 阿裡巴巴雲原生微信公衆号(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公衆号。”

更多相關資訊,關注

“阿裡巴巴雲原生”