从零到破万节点!支撑618大促背后的蚂蚁金服Kubernetes集群
2019年天貓618大促,螞蟻金服首次在大促中對調度系統和技術棧全面應用Kubernetes,突破了Kubernetes單集群萬節點的規模,總節點數達到數十萬個,這是世界最大規模的 Kubernetes 集群之一,而這距離開發團隊下載Kubernetes代碼僅一年之久。
背景
去年6月份,螞蟻金服的 Kubernetes開發團隊剛剛下載Kubernetes代碼,從零開始嘗試在內部落地Kubernetes集群,并推動云原生實踐。2019年天貓618大促,螞蟻金服首次在調度系統和技術棧全量應用Kubernetes,平穩度過大促并突破 Kubernetes 單集群萬節點規模,機房和集群數量達到數十個,總節點達到數十萬個,這是世界最大規模的 Kubernetes 集群之一。
螞蟻金服的 Kubernetes開發團隊是一個僅有十幾人組成的小隊團,他們用短短一年時間,通過擴展 Kubernetes 的方式,將螞蟻金服老的調度系統的功能對齊,還將一系列令人興奮的Kubernetes 功能帶回并落地到螞蟻金服。開發團隊能在如此短時間內取得如此成績,所依靠的正是云原生技術:開發團隊使用云原生技術讓開發和迭代敏捷化,同時讓一切過程自動化。開發團隊在落地 Kubernetes 過程中,就已經嘗試將云原生的理念實踐并執行,取得的優秀成果將為整個螞蟻金服后續的全面云原生化提供優秀范本。
本文將分享螞蟻金服的 Kubernetes開發團隊如何使用云原生的技術去推進 Kubernetes 在螞蟻金服的大規模落地,同時也會分享一些對于大規模 Kubernetes集群性能優化的經驗。
將Kubernetes運行在Kubernetes上
云原生的核心理念是讓應用無差別運行在任何一朵云上,即將應用變成云的 “原住民”。而螞蟻金服的 Kubernetes 開發團隊在項目開始時需要思考的是如何將 Kubernetes 云原生化的運行在各個機房,并在沒有任何基礎設施的云機房也能無差別運行Kubernetes。
首先,新建一個Kubernetes是一項繁瑣的事情:初始化一堆證書,包括安全的存儲證書;有序拉起二十幾個組件,同時組織好彼此之間的引用關系,保證后續能夠方便地升級;最后還要做自動化故障恢復。
一旦擁有Kubernetes,如果某個應用提出應用發布、自動化運維等需求,可以很簡單的完成。但是,如何才能讓Kubernetes本身也享受到 Kubernetes 帶來的強大功能呢?
那么,這句話應該如何理解呢?在螞蟻金服啟動落地 Kubernetes 時,就已經預見內部對“自動化運維和交付”的巨大依賴。螞蟻金服需要管理幾十個集群、幾十萬計算節點。同時,隨著迭代的進行,擴展組件越來越多(到目前已經有三十多個)。
因此,在經過一系列討論之后,螞蟻金服決定將 Kubernetes運行在Kubernetes 之上,并且將組件和節點的交付過程封裝成Operator。有了 Kubernetes 和 Operator 之后,想要交付一個 Kubernetes 集群給應用方就像提交Pod一樣容易,并且集群的各種組件都能自動故障自愈。這可以理解為,用Operator這種云原生的方式交付整個基礎設施。如果不是當初的這個決定,很難想象組件和節點在離開 Kubernetes 之后如何運維和自動化恢復。
為此,開發團隊設計出了 Kube-on-Kube-Operator,它的用途是將各個機房交付給用戶的 Kubernetes 集群(稱為 “業務集群”) 的組件和服務也都跑在一個 Kubernetes (稱為 “元集群”) 上。下圖是Kube-on-Kube-Operator的核心架構圖:
在擁有Kube-on-Kube-Operator 之后,螞蟻金服能在分鐘級甚至秒級創建一個 Kubernetes 集群。同時,所有業務集群 Kubernetes Master 組件都運行在元集群 Kubernetes 上,憑借 Kubernetes 的能力可以做到秒級故障恢復。
Kube-on-Kube-Operator 用云原生方式搞定了 Kubernetes Master 組件,那么 kubelet 和 Worker 節點呢?
眾所周知,將一個 Worker 節點加入集群也需要一堆復雜的事情:生成隨機證書,安裝 Runtime 以及各種底層軟件,設置 kubelet 啟動參數等。對此,螞蟻金服團隊在思考能否像 Kubernetes 自動化維護 Pod 一樣自動化維護 Worker 節點呢?
Node-Operator 正是在這樣的背景下誕生的,其使用聲明式編程、面向終態的特性去管理 Worker節點。Node-Operator 的職責涵蓋從云廠商購買計算節點開始到接管 Kubenertes 節點整個生命周期:從初始化證書、kubelet 以及節點必要組件,到自動化升級組件版本,再到最后的節點下線。同時,Node-Opeator 也會訂閱 NPD(Node Problem Detector) 上報的節點故障信息,進行一系列自動化修復。下圖是 Node-Operator 的核心架構圖:
Kube-on-Kube-Operator 和Node-Operator 在生產環境中表現穩定,經歷了數十個大迭代以及無數小迭代。同時,Kube-on-Kube-Operator自動化運維了數十個生產集群,為每日自動化功能測試和回歸測試快速創建和銷毀臨時測試集群。Node-Operator接管了幾乎整個螞蟻金服的物理機生命周期,這種思想也繼承到了下一代運維管控系統。
除了 Kube-on-Kube-Operator 和Node-Operator,在 “automate everything”信念的驅動下,螞蟻金服還開發出了各種 Operator去交付螞蟻金服的所有基礎設施和應用。
自動化流水線與GitOps
自動化和 CI/CD 是云原生非常重視的理念,螞蟻金服開發團隊將其應用到研發過程中,讓研發、測試和發布的一系列過程都自動化。
上線 Kubernetes 初期,在保持飛速迭代的情況下,也需要確保代碼質量,團隊的規定是:任何組件都要有單獨的 e2e 測試集或者測試用例,同時每天晚上都會將最新代碼放入全新的沙箱集群測試,另外還會定期或者觸發式在生產集群運行功能驗證測試。
為了提高效率并更好完成測試,開發團隊建立了自動化流水線。最開始的自動化流水線是為了服務測試,在自動化流水線建立工作集,幾乎所有工作集都通過調用 Kube-on-Kube-Operator 和 Node-Operator 建立了全新的沙箱集群,然后開始運行各自測試集的測試,最后銷毀集群發送測試結果。
為了在自動化流水線上更方便的建立工作集,團隊將所有組件和測試集都進行鏡像化,使用Kube-on-Kube-Operator 和 Kubernetes 元集群可以方便、快速地建立測試用的沙箱集群,然后使用容器和配置注入的方式讓測試集(Job on Kubernetes)運行在任何環境、指定任何集群跑測試。
后來,團隊在流水線加上自動化發布:流水線將期望的組件發布完成,然后自動觸發功能驗證測試。如果發布或者測試失敗,通過釘釘機器人通知對應負責人,如果成功,幾乎可以做到無人值守發布。
Kubernetes 一個令人興奮的特性就是將各種部署資源的聲明統一和標準化:如果需要一個在線無狀態服務,只需向 Kubernetes提交一個 Deployment;如果需要負載均衡,只需提交一個 Service;如果需要持久化存儲卷,就提交一個PVC(PersistentVolumeClaim);如果需要保存配置文件或者密碼存儲,只需提交ConfigMap 或者 Secret,然后在 Pod 里面引用就可以。Kube-on-Kube-Operator 就是用這些標準的資源定義Kubernetes元集群發布各個 Kubernetes 業務集群的組件。
但是,在Kube-on-Kube-Operator 的使用過程中,團隊發現Kubernetes 發布還是不夠透明:Kube-on-Kube-Operator 承包了發布過程,即使它是一個非常輕量的觸發器,用來將業務集群部署資源文件提交到 Kubernetes 元集群,但執行一次發布也需要比較繁瑣的操作。這在快速的迭代團隊中,教會新同事使用 Kube-on-Kube-Operator 聲明版本、修改 Cluster(代表一個集群的 CRD) 版本引用,看起來不夠 “敏捷”。Kube-on-Kube-Operator 儼然變成了一個 PaaS,但業務團隊為什么要花精力學習一個 “非標準 PaaS” 的使用呢?
事實上,Kubernetes 已經將一切標準化,使用Git 自帶的版本管理、 PR Review 功能就可以實現部署資源文件管理系統。
因此,開發團隊引入 GitOps 理念, GitOps 基于經過驗證的 DevOps 技術——大體類似于 CI/CD和聲明式基礎設施即代碼,而構建為Kubernetes應用程序提供了一套聯合的、可自動生成的生命周期框架。
GitOps將原先包裹在 PaaS 或者 Kube-on-Kube-Operator 的黑盒全部公開化和民主化。每個人都能從名為 kubernetes-resources 的 Git 倉庫看到 Kubernets 組件目前在線上的部署狀態:版本、規格、副本數、引用的資源。每個人都能參與發布,只要提交 PR,經過事先設定的管理員 Review 和 Approve 之后,就可以自動發布到線上并開始跑測試。團隊使用 kustomize 的 base/overlays 功能做到各集群的差異化部署,同時又能避免同一個組件在各個Kubernetes 集群重復編寫。
開發團隊還將 GitOps 能力開放給業務方,為此建立了名為 partner-resources 的 Git 倉庫,任何業務應用的開發同學都能訪問該倉庫并提交 PR,經過 Review 合并到Master 后,自動化流水線會將部署資源生效到指定 Kubernetes 集群,從而進行業務應用的云原生實踐和落地。
可能很多同學會不由自主的將透明、民主和 “不安全”掛上鉤。恰恰相反,kubernetes-resources 和 partner-resources 里面沒有任何一個秘鑰文件,只有權限聲明(RBAC)文件。權限聲明需要 Review 才能合入,而身份識別使用了 Kubernetes 自動注入秘鑰(ServiceAccount)功能。在ServiceMesh 普及之后,秘鑰文件更加被弱化,所有身份識別都依賴 Mesh。同時,螞蟻金服運行的 Kubernetes 集群采用了最高的安全標準,所有鏈路都使用 TLS 雙向加密和身份認證。
在云原生時代,Kubernetes集群其實已經是最好的元數據系統。同時,Kubernetes 各種 Workload 配合工作讓用戶提交的部署資源一直維持在期望狀態;而 GitOps 擁有的版本記錄、PR Review 功能等是最好的部署資源文件管理系統。即使目前還有一些路要走,比如目前Kubernetes 缺少灰度發布等更高級功能的 Workload,但是在不久的將來肯定能看到這些特性被放入Workload。
全面云原生化
Kubernetes 是云原生的基礎,螞蟻金服在過去一年從零到全面落地 Kubernetes ,并在期望的規模下做到優秀的吞吐量??梢哉f,過去一年完成了云原生的基礎建設。
同時,非常多其它云原生技術在Kubernetes 集群內并行探索和落地,達到生產級別,甚至支持大促,比如ServiceMesh等。簡單來看,螞蟻金服落地云原生的目的可以總結為三點:標準化交付、提高研發效率和提高資源利用率。
其中,標準化交付比較好理解,螞蟻金服圍繞 Kubernetes 建設 PaaS 和應用交付體系,使用 Kubernetes 統一的資源聲明方式交付所有應用,同時讓所有應用自動化注入基礎服務,如ServiceMesh,統一日志,Tracing 等;提高研發效率注重提高每個應用開發者的工作效率,讓其使用 Serverless、CI/CD展開日常工作,讓開發者背靠云原生技術棧做更敏捷的開發和迭代;最后,使用 Kubernetes 統一資源和調度,讓所有的應用、Job、GPU 調度都使用 Kubernetes 集群統一的資源池。開發團隊在 Kubernetes 統一資源池內做了一系列調度優化和混布技術,讓資源利用率有質的提升。
大規模集群性能優化
如果按照Kubernetes最新版本提供的能力,做到單集群萬節點并不是特別困難。但是,在這種背景下,讓整個集群保持較高吞吐量和性能,并在618大促時依舊對各種響應做到及時反饋是不容易的。
螞蟻金服通過系列壓測和迭代將單集群做到了上萬規模。然而,在這個過程中,整個團隊發現不能太迷信壓測數據,壓測場景其實非常片面,而生產環境和壓測環境有非常大差異,主要體現在 Kubernetes 擴展組件的客戶端行為和一些極端情況。
舉例來說,一個是 Daemonset,螞蟻金服內部一個集群已經擁有十個左右的 Daemonset 系統 Agent,在如此大規模集群內上線一個使用 Kubernetes 客戶端的不規范Daemonset 都可能使API Server 陷入崩潰狀態;另一個是 Webhook,螞蟻金服擁有一系列Webhook Server 擴展功能,它們的響應速度都會影響到 API Server 的性能,甚至引起內存和 goruntine 泄露。從上述示例不難發現,其實要將大規模集群維持在健康狀態需要全鏈路優化和調優,而不僅僅局限于 API Server和etcd,更不是僅僅停留在壓測數據。
開發團隊將集群節點規模上升到萬級別的時候,發現更多的瓶頸在 Kubernetes API Server。相對來說,etcd 的表現比較穩定。團隊做了系列優化和調整,來讓API Server 滿足性能需求。
1、優先滿足和保證 API Server 計算資源需求在常規部署模式下,API Server 會和Controller Manager、Scheduler 等核心組件一起部署在一臺節點上,在Kube-on-Kube 架構下也是采用這種部署模式,以達到合理使用資源的目的。在這種部署架構下,將 API Server 的資源優先級設置到最高級別,也就是在 Kubernetes 資源級別表達里的 Guaranteed 級別,并且盡可能將物理節點所有資源都占用;同時將其他組件的優先級相對降低,即將其設置成 Burstable 級別,以保證API Server的資源需求。
2、均衡 API Server 負載
在 Kubernetes 架構下,所有組件均面向APIServer展開工作,因此組件對API Server的請求鏈路健康非常敏感,只要API Server發生升級、重啟,所有組件幾乎都會在秒級內發起新的一系列List/Watch請求。如果使用滾動升級模式逐個升級 API Server 的模式去升級 API Server,那么很有可能在升級之后,絕大多數客戶端請求都會打在一個API Server實例上。
如果負載不均衡,使得API Server進入 “一人工作,多人圍觀” 的場面,那么很可能導致 API Server 發生雪崩效應。更糟糕的情況是,因為 Kubernetes 的 client-go 代碼庫使用了 TLS 鏈路復用的特性,客戶端不會隨著運行時間增長,因為鏈接重建將負載均衡掉。
開發團隊研究后發現,這個問題可以通過優化客戶端將請求平衡掉來解決,當前正在著手研發,成功后也會回饋給開源社區。在這個特性還未發布之前,可以通過設置升級 API Server 的策略使用 “先擴后縮” 來緩解該問題,即先將新版本的API Server全部創建出來,然后再下線老版本的 API Server。使用 Kubernetes 的 Deployment 表達三副本的 API Server 升級策略如下:
3、開啟 NodeLease Feature
對于提升 Kubernetes 集群規模來說,NodeLease 是一個非常重要的 Feature 。在沒有開啟 NodeLease 之前,Kubelet 會使用 Update Node Status 的方式更新節點心跳,而一次這樣的心跳會向 API Server 發送大約10 KB數據量。
在大規模場景下,API Server 處理心跳請求是非常大的開銷。而開啟 NodeLease 之后,Kubelet 會使用非常輕量的 NodeLease 對象(0.1 KB)更新請求替換老的 Update Node Status 方式,這大大減輕了 API Server 的負擔。在上線NodeLease 功能之后,集群API Server 開銷的 CPU 大約降低了一半。
4、修復請求鏈路中丟失 Context 的場景
眾所周知,Go語言標準庫的HTTP請求使用 request.Context() 方法獲取的 Context 來判斷客戶端請求是否結束。如果客戶端已經退出請求,而 API Server 還在處理請求,那么就可能導致請求處理 goruntine 殘留和積壓。
在API Server陷入性能瓶頸時,APIServer 已經來不及處理請求,而客戶端發起的重試請求,會將 API Server帶入雪崩效應:處理已取消請求的 goruntine 會積壓的越多,直到 API Server 陷入OOM。開發團隊找出了一系列在處理請求沒有使用 Context 的場景,并向上游社區提交了修復方案,包括使用 client-go 可能導致的 goruntine;Admission 和 Webhook 可能引起的 goruntine 積壓。
5、優化客戶端行為
目前,API Server 限流功能只限制最大讀和寫并發數,而沒有限制特定客戶端請求并發量的功能。因此,API Server 其實是比較脆弱的,一個客戶端頻繁的 List 數目較大資源(如 Pod, Node 等)都有可能會讓 API Server 陷入性能瓶頸。開發團隊強制要求所有客戶端使用 Informer 去 List/Watch 資源,并且禁止在處理邏輯里面直接調用 Client 去向 API Server List 資源。而社區開始重視這方面,通過引入 Priority and Fairness 特性去更加細粒度的控制客戶端的請求限制。
在后續越來越多的系統 Daemonset 上線之后,團隊發現,即使做到了所有客戶端使用 Informer,集群內的 List Pod 請求依舊很多。這主要是 Kubelet 和 Daemonset 帶來的,可以用 Bookmark 特性來解決這個問題,在未上線 Bookmark 的集群內,可以調整 API Server 對資源的更新事件緩存量來緩解該問題 (如 --watch-cache-sizes=node#1000,pod#5000),同時調整 Daemonset Re-Watch 的時間間隔:
結束語
在螞蟻金服云原生實踐和落地的過程,開發團隊認識到,項目順利實踐與開源社區的幫助密切相關。除了Kubernetes,螞蟻金服團隊還使用了其它開源項目,比如 Prometheus。Prometheus 的意義在于標準化Metrics 和其查詢語句,任何應用都可以使用 Prometheus 埋點并記錄 Metrics,而螞蟻金服通過自研采集任務調度系統,以及數據持久化方案,使得Prometheus 數據不會有任何 “斷點” ,同時還支持永久歷史數據查詢。
目前,螞蟻金服已向 Kubernetes 社區提交了許多大規模場景下的性能提升和優化方案,上面提到在Kubernetes API Server性能優化過程中發現的問題,以及修復最新Kubernetes版本中的許多 Daemonset 的 bug ,將Daemonset的生產可用性提高一個層級。同時,螞蟻金服也將眾多技術開源給社區,包括金融級分布式框架SOFAStack。
可以說,全面實現云原生是每個開發者都需要參與的“革命”,而螞蟻金服也將作為發起者和分享者參與其中。
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的从零到破万节点!支撑618大促背后的蚂蚁金服Kubernetes集群的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蚂蚁金服OceanBase挑战TPCC丨
- 下一篇: 技术架构演进|0到千万DAU,微淘如何走