做者 | 墨封
來源 | 阿里巴巴雲原生公衆號node
一週前,咱們介紹了《面對大規模 K8s 集羣,如何先於用戶發現問題》。web
本篇文章,咱們將繼續爲你們介紹 ASI SRE(ASI,Alibaba Serverless infrastructure,阿里巴巴針對雲原生應用設計的統一基礎設施) 是如何探索在 Kubernetes 體系下,建設 ASI 自身基礎設施在大規模集羣場景下的變動灰度能力的。api
ASI 誕生於阿里巴巴集團全面上雲之際,承載着集團大量基礎設施全面雲原生化的同時,自身的架構、形態也在不斷地演進。網絡
ASI 總體上主要採用 Kube-on-Kube 的架構,底層維護了一個核心的 Kubernetes 元集羣,並在該集羣部署各個租戶集羣的 master 管控組件:apiserver、controller-manager、scheduler,以及 etcd。而在每一個業務集羣中,則部署着各種 controller、webhook 等 addon 組件,共同支撐 ASI 的各項能力。而在數據面組件層面,部分 ASI 組件以 DaemonSet 的形式部署在節點上,也有另外一部分採用 RPM 包的部署形式。架構
同時,ASI 承載了集團、售賣區場景下數百個集羣,幾十萬的節點。即使在 ASI 建設初期,其管轄的節點也達到了數萬的級別。在 ASI 自身架構快速發展的過程當中,組件及線上變動至關頻繁,早期時單日 ASI 的組件變動能夠達到數百次。而 ASI 的核心基礎組件諸如 CNI 插件、CSI 插件、etcd、Pouch 等,不管任意之一的錯誤變動均可能會引發整個集羣級別的故障,形成上層業務不可挽回的損失。app
簡而言之,集羣規模大、組件數量多,變動頻繁以及業務形態複雜是在 ASI,或其餘 Kubernetes 基礎設施層建設灰度能力和變動系統的幾大嚴峻挑戰。當時在阿里巴巴內部,ASI/Sigma 已有數套現有的變動系統,但都存在必定的侷限性。less
由此,咱們但願借鑑前面幾代 sigma/ASI 的發佈平臺歷史,從變動時入手,以系統能力爲主,再輔以流程規範,逐步構建 ASI 體系下的灰度體系,建設 Kubernetes 技術棧下的運維變動平臺,保障數以千計的大規模集羣的穩定性。運維
ASI 自身架構和形態的發展會極大地影響其自身的灰度體系建設方式,所以在 ASI 發展的早期,咱們對 ASI 將來的形態作了以下大膽的預設:ide
基於以上幾個假設,咱們可以總結在 ASI 建設初期,亟待解決的幾個問題:單元測試
咱們轉換一下視角,脫離集羣的維度,嘗試從組件的角度來解決變動的複雜性。對於每一個組件,它的生命週期能夠大致劃分爲需求和設計階段,研發階段和發佈階段。對於每一個階段咱們都但願進行規範化,並解決 Kubernetes 自己的特色,將固定的規範落到系統中,以系統能力去保證灰度過程。
結合 ASI 的形態和變動場景的特殊性,咱們從如下幾點思路出發去系統化建設 ASI 的灰度體系:
需求和設計階段
組件研發階段
ASI 核心組件的研發流程能夠總結爲如下幾個流程:
針對 ASI 自身的核心組件,咱們與質量技術團隊的同窗共同建設了 ASI 組件的 e2e 測試流程。除了組件自身的單元測試、集成測試外,咱們單獨搭建了單獨的 e2e 集羣,用做常態化進行的 ASI 總體的功能性驗證和 e2e 測試。
從單個組件視角入手,每一個組件的新功能通過研發後,進行 Code Review 經過併合入 develop 分支,則當即觸發進行 e2e 流程,經過 chorus(雲原生測試平臺) 系統構建鏡像後,由 ASIOps(ASI 運維管控平臺) 部署到對應的 e2e 集羣,執行標準的 Kubernetes Conformance 套件測試任務,驗證 Kubernetes 範圍內的功能是否正常。僅當全部測試 case 經過,該組件的版本纔可標記爲可推平版本,不然後續的發佈將會受到管控限制。
然而正如上文提到,Kubernetes 開放的架構意味着它不只僅包含管控、調度等核心組件,集羣的功能還很大程度依賴於上層的 operator 來共同實現。所以 Kubernetes 範圍內的白盒測試並不能覆蓋全部的 ASI 的適用場景。底層組件功能的改變頗有大程度會影響到上層 operator 的使用,所以咱們在白盒 Conformance 的基礎上增長了黑盒測試用例,它包含對各種 operator 自身的功能驗證,例如從上層 paas 發起的擴縮容,校驗發佈鏈路的 quota 驗證等能力,常態化運行在集羣中。
針對 ASI 組件多、集羣多的特色,咱們在原有 asi-deploy 功能之上進行拓展,以組件爲切入點,加強組件在多集羣間的管理能力,從鏡像管理演進成了YAML 管理。
基於 Helm Template 的能力,咱們將一個組件的 YAML 抽離成模板、鏡像和配置三部分,分別表示如下幾部分信息:
所以,一個完整的 YAML 則由模板、鏡像和配置共同渲染而成。而 ASIOps 則再會對鏡像信息和配置信息這部分 YAML 分別進行集羣維度和時間維度(多版本)進行管理,計算組件當前版本信息在衆多集羣衆多分佈情況以及組件在單集羣中版本的一致性情況。
針對鏡像版本,咱們從系統上促使其版本統一,以保證不會因版本太低而致使線上問題;而針對配置版本,咱們則從管理上簡化它的複雜性,防止配置錯誤發入集羣。
有了組件的基礎原型後,咱們但願發佈不只僅是「替換 workload 裏的 image 字段」這樣簡單的一件事。咱們當前維護了整個 YAML 信息,包含了除了鏡像以外的其餘配置內容,須要支持除了鏡像變更外的變動內容。所以咱們嘗試以儘量接近 kubectl apply 的方式去進行 YAML 下發。
咱們會記錄三部分的 YAML Specification 信息:
對於一個由鏡像、配置和模板共同構建的 YAML,咱們會採集上述三種 Spec 信息,並進行一次 diff,從而得到到資源 diff patch,再進行一次 filter out,篩去不容許變動的危險的字段,最後將總體的 patch 以 strategic merge patch 或者 merge patch 的形式發送給 APIServer,觸發使得 workload 從新進入 reconcile 過程,以改變集羣中該 workload 的實際情況。
除此以外,因爲 ASI 組件之間具備較強的相關性,存在許多場景須要同時一次性發布多個組件。例如當咱們初始化一個集羣,或者對集羣作一次總體的 release 時。所以咱們在單個組件部署的基礎上增長了 Addon Release 的概念,以組件的集合來代表整個 ASI 的 release 版本,而且根據每一個組件的依賴關係自動生成部署流,保證總體發佈的過程當中不會出現循環依賴。
在雲原生的環境下,咱們以終態的形式去描述應用的部署形態,而 Kubernetes 提供了維護各種 Workload 終態的能力,Operator 對比 workload 當前狀態與終態的差距並進行狀態協調。這個協調的過程,換言之 workload 發佈或者回滾的過程,能夠由 Operator 定義的發佈策略來處理這個「面向終態場景內的面向過程的流程」。
相比 Kubernetes 上層的應用負載,底層的基礎設施組件在發佈的過程當中更關心組件自身的灰度發佈策略和灰度暫停能力,即不論任何類型的組件,都須要能在發佈過程當中具有及時中止發佈的能力,以提供更多的時間進行功能檢測、決策以及回滾。具體而言,這些能力能夠概括爲以下幾類:
ASI 中針對 Kubernetes 原生 workload 能力、節點能力都進行了加強。依託於集羣中 Kruise 和 KubeNode 這類 operator 的能力以及上層管控平臺 ASIOps 的共同協做,咱們對 Kubernetes 基礎設施組件實現了上述灰度能力的支持。對於 Deployment / StatefulSet / DaemonSet / Dataplane 類型的組件,在單集羣中發佈時支持的能力以下:
後文將簡要介紹咱們針對不一樣 Workload 類型的組件進行灰度的實現,詳細的實現細節能夠關注咱們開源的項目 OpenKruise 以及後續準備開源的 KubeNode。
大多數 Kubernetes 的 operator 以 Deployment 或者 StatefulSet 的方式部署,在 Operator 發佈的過程當中,一旦鏡像字段變更,全部 Operator 副本均會被升級。這個過程一旦新版本存在問題,則會形成不可挽回的問題。
針對此類 operator,咱們將 controller-runtime 從 operator 中剝離出來,構建一箇中心化的組件 operator-manager(OpenKruise 開源實現中爲 controller-mesh)。同時每一個 operator pod 中會增長一個 operator-runtime 的 sidecar 容器,經過 gRPC 接口爲組件的主容器提供 operator 的核心能力。
operator 向 APIServer 創建 Watch 鏈接後,監聽到事件並被轉化爲待 operator 協調處理的任務流(即 operator 的流量),operator-manager 負責中心化管控全部 operator 的流量,並根據規則進行流量分片,分發到不一樣的 operator-runtime,runtime 中的 workerqueue 再觸發實際 operator 的協調任務。
在灰度過程當中,operator-manager 支持按照 namespace 級別,哈希分片方式,將 operator 的流量分攤給新舊版本的兩個副本,從而能夠從兩個副本處理的負載 workload 來驗證此次灰度發佈是否存在問題。
社區原生的 DaemonSet 支持了 RollingUpdate,可是其滾動升級的能力上僅支持 maxUnavailable 一種,這對於單集羣數千上萬節點的 ASI 而言是沒法接受的,一旦更新鏡像後全部 DaemonSet Pod 將會被升級,而且沒法暫停,僅能經過 maxUnavailable 策略進行保護。一旦 DaemonSet 發佈了一個 Bug 版本,而且進程可以正常啓動,那麼 maxUnavailable 也沒法生效。
此外社區提供 onDelete 方式,能夠在手動刪除 Pod 建立新 Pod,由發佈平臺中心端控制發佈順序和灰度,這種模式沒法作到單集羣中的自閉環,全部的壓力都上升到發佈平臺上。讓上層發佈平臺來進行Pod驅逐,風險比較大。最好的方式就是 Workload 能自閉環提供組件更新的能力。所以咱們在 Kruise 中增強了 DaemonSet 的能力使其支持上述幾種重要的灰度能力。
以下是一個基本的 Kruise Advanced DaemonSet 的例子:
apiVersion: apps.kruise.io/v1alpha1 kind: DaemonSet spec: # ... updateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 5 partition: 100 paused: false
其中 partition 意爲保留老版本鏡像的 Pod 副本數,滾升級過程當中一旦指定副本數 Pod 升級完成,將再也不對新的 Pod 進行鏡像升級。咱們在上層 ASIOps 中控制 partition 的數值來滾動升級 DaemonSet,並配合其餘 UpdateStrategy 參數來保證灰度進度,同時在新建立的 Pod 上進行一些定向驗證。
MachineComponentSet 是 KubeNode 體系內的 Workload,ASI 中在 Kubernetes 以外的節點組件(沒法用 Kubernetes 自身的 Workload 發佈的組件),例如 Pouch,Containerd,Kubelet 等均是經過該 Workload 進行發佈。
節點組件以 Kubernetes 內部的自定義資源 MachineComponent 進行表示,包含一個指定版本的節點組件(例如 pouch-1.0.0.81)的安裝腳本,安裝環境變量等信息;而 MachineComponentSet 則是節點組件與節點集合的映射,代表該批機器須要安裝該版本的節點組件。而中心端的 Machine-Operator 則會去協調這個映射關係,以終態的形式,比對節點上的組件版本以及目標版本的差別,並嘗試去安裝指定版本的節點組件。
在灰度發佈這一部分,MachineComponentSet 的設計與 Advanced DaemonSet 相似,提供了包括 partition,maxUnavailable 的 RollingUpdate 特性,例如如下是一個 MachineComponentSet 的示例:
apiVersion: kubenode.alibabacloud.com/v1 kind: MachineComponentSet metadata: labels: alibabacloud.com/akubelet-component-version: 1.18.6.238-20201116190105-cluster-202011241059-d380368.conf component: akubelet name: akubelet-machine-component-set spec: componentName: akubelet selector: {} updateStrategy: maxUnavailable: 20% partition: 55 pause: false
一樣上層 ASIOps 在控制灰度升級節點組件時,與集羣側的 Machine-Operator 進行交互,修改指定 MachineComponentSet 的 partition 等字段進行滾動升級。
相比於傳統的節點組件發佈模式,KubeNode 體系將節點組件的生命週期也閉環至 Kubernetes 集羣內,並將灰度發佈的控制下沉到集羣側,減小中心側對節點元數據管理的壓力。
阿里巴巴內部針對雲產品、基礎產品制定了變動紅線 3.0,對管控面組件、數據面組件的變動操做的分批灰度、控制間隔、可觀測、可暫停、可回滾進行了要求。但變動對象以 region 的單元進行灰度不知足 ASI 的複雜場景,所以咱們嘗試去細化 ASI 上管控面、數據面的變動所屬的變動單元的類型。
咱們圍繞集羣這一基礎單元向上,向下分別進行抽象,獲得如下幾個基本單元:
針對每種發佈模式(管控組件、節點組件),咱們以最小爆炸半徑爲原則,將他們所對應的灰度單元編排串聯在一塊兒,以使得灰度流程可以固化到系統中,組件開發在發佈中必須遵照流程,逐個單元進行部署。編排過程當中,咱們主要考慮如下幾個因素:
同時咱們對每一個單元進行權重打分,並對單元間的依賴關係進行編排。例如如下是一條 ASI 監控組件的發佈流水線,因爲該監控組件在全部 ASI 場景都會使用同一套方案,它將推平至全部 ASI 集羣。而且在推平過程當中,它首先會通過泛電商交易集羣的驗證,再進行集團 VPC 內二方的發佈,最後進行售賣區集羣的發佈。
而在每一個集羣中,該組件則會按照上一節中咱們討論的單集羣內的灰度方式進行 1/5/10 批次的分批,逐批進行發佈。
進行了灰度單元編排以後,咱們則能夠得到到一次組件推平流水線的基礎骨架。而對於骨架上的每一個灰度單元,咱們嘗試去豐富它的前置檢查和後置校驗,從而可以在每次發佈後確認灰度的成功性,並進行有效的變動阻斷。同時對於單個批次咱們設置必定的靜默期去使得後置校驗可以有足夠的時間運行完,而且提供給組件開發足夠的時間進行驗證。目前單批次前置後置校驗內容包括:
將整個多集羣發佈的流程串聯在一塊兒,咱們能夠獲得一個組件從研發,測試至上線發佈,整個流程經歷的事件以下圖:
在流水線編排的實現方面,咱們對社區已有的 tekton 和 argo 進行了選型調研,但考慮到咱們在發佈流程中較多的邏輯不適合單獨放在容器中執行,同時咱們在發佈過程當中的需求不只僅是 CI/CD,以及在設計初期這兩個項目在社區中並不穩定。於是咱們參考了 tekton 的基礎設計(task / taskrun / pipeline / pipelinerun)進行了實現,而且保持着和社區共同的設計方向,在將來會調整與社區更接近,更雲原生的方式。
通過近一年半的建設,ASIOps 目前承載了近百個管控集羣,近千個業務集羣(包括 ASI 集羣、Virtual Cluster 多租虛擬集羣,Sigma 2.0 虛擬集羣等),400 多個組件(包括 ASI 核心組件、二方組件等)。同時 ASIOps 上包含了近 30 餘條推平流水線,適用於 ASI 自身以及 ASI 承載的業務方的不一樣發佈場景。
同時天天有近 400 次的組件變動(包括鏡像變動和配置變動),經過流水線推平的此時達 7900+。同時爲了提升發佈效率,咱們在先後置檢查完善的條件下開啓了單集羣內自動灰度的能力,目前該能力被大多數 ASI 數據面的組件所使用。
以下是一個組件經過 ASIOps 進行版本推平的示例:
同時咱們在 ASIOps 上的分批灰度以及後置檢查變動阻斷,也幫助咱們攔住了必定因爲組件變動引發的故障。例如 Pouch 組件在進行灰度時,因爲版本不兼容致使了集羣不可用,經過發佈後觸發的後置巡檢發現了這一現象,並阻斷了灰度進程。
ASIOps 上的組件大多數都是 ASI/Kubernetes 底層的基礎設施組件,近一年半以來沒有由於由組件變動所引發的故障。咱們努力將指定的規範經過系統能力固化下來,以減小和杜絕違反變動紅線的變動,從而將故障的發生逐步右移,從變動引起的低級故障逐步轉變至代碼 Bug 自身引發的複雜故障。
隨着 ASI 的覆蓋的場景逐步擴大,ASIOps 做爲其中的管控平臺須要迎接更復雜的場景,規模更大的集羣數、組件數的挑戰。
首先咱們亟待解決穩定性和效率這一權衡問題,當 ASIOps 納管的集羣數量到達必定量級後,進行一次組件推平的耗時將至關大。咱們但願在建設了足夠的先後置校驗能力後,提供變動全託管的能力,由平臺自動進行發佈範圍內的組件推平,並執行有效的變動阻斷,在 Kubernetes 基礎設施這一層真正作到 CI/CD 自動化。
同時目前咱們須要手動對灰度單元進行編排,肯定灰度順序,在將來咱們但願建設徹底整個 ASI 的元數據,並自動對每次發佈範圍內的全部單元進行過濾、打分和編排。
最後,目前 ASIOps 暫時只作到針對組件相關的變動進行灰度的能力,而 ASI 範圍內的變動遠不止組件這一點。灰度體系應該是一個通用的範疇,灰度流水線須要被賦能到注入資源運維、預案執行的其餘的場景中。此外,整個管控平臺的灰度能力沒有與阿里巴巴有任何緊耦合,徹底基於 Kruise / KubeNode 等 Workload 進行打造,將來咱們會探索開源整套能力輸出到社區中。