蚂蚁集团万级规模 k8s 集群 etcd 高可用建设之路
螞蟻集團運維著可能是全球最大的 k8s 集群:k8s 官方以 5k node 作為 k8s 規模化的頂峰,而螞蟻集團事實上運維著規模達到 10k node 規模的 k8s 集群。一個形象的比喻就是,如果官方以及跟著官方的 k8s 使用者能想象到的 k8s 的集群規模是泰山,那么螞蟻集團在官方的解決方案之上已經實現了一個珠穆朗瑪峰,引領了 k8s 規模化技術的提升。?
這個量級的差異,不僅僅是量的差異,更是 k8s 管理維護的質的提升。能維護有如此巨大挑戰巨量規模的 k8s 集群,其背后原因是螞蟻集團付出了遠大于 k8s 官方的優化努力。
所謂萬丈高樓平地起,本文著重討論下螞蟻集團的在 k8s 的基石?--- etcd 層面做出的高可用建設工作:只有 etcd 這個基石穩當了,k8s 這棟高樓大廈才保持穩定性,有 tidb 大佬黃東旭朋友圈佐證【圖片已獲黃總授權】。
面臨的挑戰
etcd 首先是 k8s 集群的 KV 數據庫。?
從數據庫的角度來看,k8s 整體集群架構各個角色如下:
etcd?集群的數據庫
kube-apiserver?etcd?的?API?接口代理、數據緩存層
kubelet?數據的生產者和消費者
kube-controller-manager?數據的消費者和生產者
kube-scheduler?數據的消費者和生產者
etcd 本質是一個 KV 數據庫,存儲了 k8s 自身資源 、用戶自定義的 CRD 以及 k8s 系統的 event 等數據。每種數據的一致性和數據安全性要求不一致,如 event 數據的安全性小于 k8s 自身的資源數據以及 CRD 數據。
k8s 的早期擁護者在推廣 k8s 時,宣稱其比 OpenStack 的優勢之一是 k8s 沒有使用消息隊列,其延遲比 OpenStack 低。這其實是一個誤解,無論是 etcd 提供的 watch 接口,還是 k8s client 包中的 informer 機制,無不表明 k8s 是把 etcd 當做了消息隊列,k8s 消息的載體很多,譬如 k8s event。
從消息隊列的角度來看,k8s 整體集群架構各個角色如下:
etcd?消息路由器
kube-apiserver?etcd?生產者消息代理和消息廣播【或者成為次級消息路由器、消費者代理】
kubelet?消息的生產者和消費者
kube-controller-manager?消息的消費者和生產者
kube-scheduler?消息的消費者和生產者
etcd 是推模式的消息隊列。etcd 是 k8s 集群的 KV 數據庫和消息路由器,充當了 OpenStack 集群中的 MySQL 和 MQ 兩個角色,這樣的實現貌似簡化了集群的結構,但其實不然。在 large scale 規模 k8s 集群中,一般經驗,首先都會使用一個單獨 etcd 集群存儲 event 數據:把 KV 數據和一部分 MQ 數據物理隔離開,實現了 KV 和 MQ 角色的部分分離。?如?參考文檔 2 中提到美團?“針對 etcd 的運營,通過拆分出獨立的 event 集群降低主庫的壓力”。
當 k8s 集群規模擴大時,etcd 承載著 KV 數據劇增、event 消息暴增以及消息寫放大的三種壓力。為了證明所言非虛,特引用部分數據為佐證:
etcd KV 數據量級在 100 萬以上;
etcd event 數據量在 10 萬以上;
etcd 讀流量壓力峰值在 30 萬 pqm 以上,其中讀 event 在 10k qpm 以上;
etcd 寫流量壓力峰值在 20 萬 pqm 以上,其中寫 event 在 15k qpm 以上;
etcd CPU 經常性飆升到 900%?以上;
etcd 內存 RSS 在 60 GiB 以上;
etcd 磁盤使用量可達 100 GiB 以上;
etcd 自身的 goroutine 數量 9k 以上;
etcd 使用的用戶態線程達 1.6k 以上;
etcd gc 單次耗時常態下可達 15ms。
使用 Go 語言實現的 etcd 管理這些數據非常吃力,無論是 CPU、內存、gc、goroutine 數量還是線程使用量,基本上都接近 go runtime 管理能力極限:經常在 CPU profile 中觀測到 go runtime 和 gc 占用資源超過 50%?以上。
螞蟻的 k8s 集群在經歷高可用項目維護之前,當集群規模突破 7 千節點規模時,曾出現如下性能瓶頸問題:
etcd 出現大量的讀寫延遲,延遲甚至可達分鐘級;
kube-apiserver 查詢 pods / nodes / configmap / crd 延時很高,導致 etcd oom;
etcd list-all pods 時長可達 30?分鐘以上;
2020 年 etcd 集群曾因 list-all 壓力被打垮導致的事故就達好幾起;
控制器無法及時感知數據變化,如出現 watch 數據延遲可達 30s 以上。
如果說這種狀況下的 etcd 集群是在刀鋒上跳舞,?此時的整個 k8s 集群就是一個活火山:稍不留神就有可能背個 P 級故障,?彼時的整個 k8s master 運維工作大概是整個螞蟻集團最危險的工種之一。
高可用策略
實現一個分布式系統高可用能力的提升,大概有如下手段:
提升自身穩定性與性能;
精細管理上游流量;
保證服務下游服務 SLO。
etcd 經過社區與各方使用者這么多年的錘煉,其自身的穩定性足夠。螞蟻人能做到的,無非是使出周扒皮的本事,提高集群資源整體利用率,scale out 和 scale up 兩種技術手段雙管齊下,盡可能的提升其性能。
etcd 自身作為 k8s 的基石,其并無下游服務。如果說有,那也是其自身所使用的物理 node 環境了。下面分別從 etcd 集群性能提升、請求流量管理等角度闡述我們在 etcd 層面所做出的高可用能力提升工作。
文件系統升級
在山窩里飛出金鳳凰,誠非易事。讓 etcd 跑的更快這件事,沒有什么手段比提供一個高性能的機器更短平快地見效了。?
1.使用 NVMe?ssd
etcd 自身?= etcd 程序?+?其運行環境。早期 etcd 服務器使用的磁盤是 SATA 盤,經過簡單地測試發現 etcd 讀磁盤速率非常慢,老板豪橫地把機器鳥槍換炮?---?升級到使用了 NVMe SSD 的 f53 規格的機器:etcd 使用 NVMe ssd 存儲 boltdb 數據后,隨機寫速率可提升到 70 MiB/s 以上。
參考文檔 2 中提到美團?“基于高配的 SSD 物理機器部署可以達到日常 5 倍的高流量訪問”,可見提升硬件性能是大廠的首選,能折騰機器千萬別浪費人力。
2.使用?tmpfs?
NVMe ssd 雖好,理論上其讀寫極限性能跟內存比還是差一個數量級。我們測試發現使用 tmpfs【未禁止 swap out】替換 NVMe ssd 后,etcd 在讀寫并發的情況下性能仍然能提升 20%?之多。考察 k8s 各種數據類型的特點后,考慮到 event 對數據的安全性要求不高但是對實時性要求較高的特點,我們毫不猶豫的把 event etcd 集群運行在了 tmpfs 文件系統之上,將 k8s 整體的性能提升了一個層次。?
3.磁盤文件系統
磁盤存儲介質升級后,存儲層面能夠進一步做的事情就是研究磁盤的文件系統格式。目前 etcd 使用的底層文件系統是 ext4 格式,其 block size 使用的是默認的 4 KiB。我們團隊曾對 etcd 進行單純的在單純寫并行壓測時發現,把文件系統升級為 xfs,且 block size 為 16 KiB【在測試的 KV size 總和 10 KiB 條件下】時,etcd 的寫性能仍然有提升空間。
但在讀寫并發的情況下,磁盤本身的寫隊列幾乎毫無壓力,又由于 etcd 3.4 版本實現了并行緩存讀,磁盤的讀壓力幾乎為零,這就意味著:繼續優化文件系統對 etcd 的性能提升空間幾乎毫無幫助。自此以后單節點 etcd scale up 的關鍵就從磁盤轉移到了內存:優化其內存索引讀寫速度。
4.磁盤透明大頁
在現代操作系統的內存管理中,有 huge page 和 transparent huge page 兩種技術,不過一般用戶采用 transparent huge page 實現內存 page 的動態管理。在 etcd 運行環境,關閉 transparent huge page 功能,否則 RT 以及 QPS 等經常性的監控指標會經常性的出現很多毛刺,導致性能不平穩。
etcd?調參
MySQL 運維工程師常被人稱為?“調參工程師”,另一個有名的 KV 數據庫 RocksDB 也不遑多讓,二者可調整的參數之多到了令人發指的地方:其關鍵就在于針對不同存儲和運行環境需要使用不同的參數,才能充分利用硬件的性能。etcd 隨不及之,但也不拉人后,預計以后其可調整參數也會越來越多。
etcd 自身也對外暴露了很多參數調整接口。除了阿里集團 k8s 團隊曾經做出的把 freelist 由 list 改進為 map 組織形式優化外,目前常規的 etcd 可調整參數如下:
write batch
compaction
1.write?batch
像其他常規的 DB 一樣,etcd 磁盤提交數據時也采用了定時批量提交、異步寫盤的方式提升吞吐,并通過內存緩存的方式平衡其延時。具體的調整參數接口如下:
batch write number 批量寫 KV 數目,默認值是 10k;
batch write interval 批量寫事件間隔,默認值是 100 ms。
etcd batch 這兩個默認值在大規模 k8s 集群下是不合適的,需要針對具體的運行環境調整之,避免導致內存使用 OOM。一般地規律是,集群 node 數目越多,這兩個值就應該成比例減小。
2.compaction
etcd 自身由于支持事務和消息通知,所以采用了 MVCC 機制保存了一個 key 的多版本數據,etcd 使用定時的 compaction 機制回收這些過時數據。etcd 對外提供的壓縮任務參數如下:
compaction interval 壓縮任務周期時長;
compaction sleep interval 單次壓縮批次間隔時長,默認 10 ms;
compaction batch limit 單次壓縮批次 KV 數目,默認 1000。
(1)壓縮任務周期
k8s 集群的 etcd compaction 可以有兩種途徑進行 compaction:
etcd 另外提供了 comapct 命令和 API 接口,k8s kube-apiserver 基于這個 API 接口也對外提供了 compact 周期參數;
etcd 自身會周期性地執行 compaction;
etcd 對外提供了自身周期性 compaction 參數調整接口,這個參數的取值范圍是?(0, 1 hour];
其意義是:etcd compaction 即只能打開不能關閉,如果設置的周期時長大于 1 hour,則 etcd 會截斷為 1 hour。
螞蟻 k8s 團隊在經過測試和線下環境驗證后,目前的壓縮周期取值經驗是:
在 etcd 層面把 compaction 周期盡可能地拉長,如取值 1 hour,形同在 etcd 自身層面關閉 compaction,把 compaction interval 的精細調整權交給 k8s kube-apiserver;
在 k8s kube-apiserver 層面,根據線上集群規模取值不同的 compaction interval。
之所以把 etcd compaction interval 精細調整權調整到 kube-apiserver 層面,是因為 etcd 是 KV 數據庫,不方便經常性地啟停進行測試,而 kube-apiserver 是 etcd 的緩存,其數據是弱狀態數據,相對來說啟停比較方便,方便調參。至于 compaction interval 的取值,一條經驗是:集群 node 越多 compaction interval 取值可以適當調大。compaction 本質是一次寫動作,在大規模集群中頻繁地執行 compaction 任務會影響集群讀寫任務的延時,集群規模越大,其延時影響越明顯,在 kube-apiserver 請求耗時監控上表現就是有頻繁出現地周期性的大毛刺。
更進一步,如果平臺上運行的任務有很明顯的波谷波峰特性,如每天的 8:30 am ~ 21:45 pm 是業務高峰期,其他時段是業務波峰期,那么可以這樣執行 compaction 任務:
在 etcd 層面設定 compaction 周期是 1 hour;
在 kube-apiserver 層面設定 comapction 周期是 30 minutes;
在 etcd 運維平臺上啟動一個周期性任務:當前時間段在業務波谷期,則啟動一個 10 minutes 周期的 compaction 任務。
其本質就是把 etcd compaction 任務交給 etcd 運維平臺,當發生電商大促銷等全天無波谷的特殊長周期時間段時,就可以在平臺上緊急關閉 compaction 任務,把 compaction 任務對正常的讀寫請求影響降低到最低。
(2)單次壓縮
即使是單次壓縮任務,etcd 也是分批執行的。因為 etcd 使用的存儲引擎 boltdb 的讀寫形式是多讀一寫:可以同時并行執行多個讀任務,但是同時刻只能執行一個寫任務。
為了防止單次 compaction 任務一直占用 boltdb 的讀寫鎖,每次執行一批固定量【compaction batch limit】的磁盤 KV 壓縮任務后,etcd 會釋放讀寫鎖 sleep 一段時間【compaction sleep interval】。
在 v3.5 之前,compaction sleep interval 固定為 10 ms,在 v3.5 之后 etcd 已經把這個參數開放出來方便大規模 k8s 集群進行調參。類似于 batch write 的 interval 和 number,單次 compaction 的 sleep interval 和 batch limit 也需要不同的集群規模設定不同的參數,以保證 etcd 平穩運行和 kube-apiserver 的讀寫 RT 指標平穩無毛刺。?
運維平臺
無論是 etcd 調參,還是升級其運行的文件系統,都是通過 scale up 的手段提升 etcd 的能力。還有兩種 scale up 手段尚未使用:
通過壓測或者在線獲取 etcd 運行 profile,分析 etcd 流程的瓶頸,然后優化代碼流程提升性能;
通過其他手段降低單節點 etcd 數據量。
通過代碼流程優化 etcd 性能,可以根據 etcd 使用方的人力情況進行之,更長期的工作應該是緊跟社區,及時獲取其版本升級帶來的技術紅利。通過降低 etcd 數據規模來獲取 etcd 性能的提升則必須依賴 etcd 使用方自身的能力建設了。
我們曾對 etcd 的單節點 RT 與 QPS 性能與 KV 數據量的關系進行過 benchmark 測試,得到的結論是:當 KV 數據量增加時,其 RT 會隨之線性增加,其 QPS 吞吐則會指數級下降。這一步測試結果帶來的啟示之一即是:通過分析 etcd 中的數據組成、外部流量特征以及數據訪問特點,盡可能地降低單 etcd 節點的數據規模。
目前螞蟻的 etcd 運維平臺具有如下數據分析功能:
longest?N?KV?--- 長度最長的?N?個?KV
top?N?KV?---?段時間內訪問次數最多的?N?個?KV
top?N?namespace?--- KV?數目最多的?N?個?namespace?
verb?+?resoure?---?外部訪問的動作和資源統計
連接數?---?每個?etcd?節點的長連接數目
client?來源統計 --- 每個?etcd?節點的外部請求來源統計
冗余數據分析 --- etcd?集群中近期無外部訪問的?KV?分布
根據數據分析結果,可以進行如下工作:
客戶限流
負載均衡
集群拆分
冗余數據刪除
業務流量精細分析
1.集群拆分
前文提到,etcd 集群性能提升的一個經典手段就是把 event 數據獨立拆分到一個獨立的 etcd 集群,因為 event 數據是 k8s 集群一中量級比較大、流動性很強、訪問量非常高的數據,拆分之后可以降低 etcd 的數據規模并減輕 etcd 單節點的外部客戶端流量。
一些經驗性的、常規性的 etcd 拆分手段有:
pod/cm
node/svc
event,?lease
這些數據拆分后,大概率能顯著提升 k8s 集群的 RT 與 QPS,但是更進一步的數據拆分工作還是有必要的。依據數據分析平臺提供的熱數據【top N KV】量級以及外部客戶訪問【verb + resource】情況,進行精細分析后可以作為 etcd 集群拆分工作的依據。
2.客戶數據分析
針對客戶數據的分析分為 longest N KV 分析、top N namespace。
一個顯然成立的事實是:單次讀寫訪問的 KV 數據越長,則 etcd 響應時間越長。通過獲取客戶寫入的 longest N KV 數據后,可以與平臺使用方研究其對平臺的使用方法是否合理,降低業務對 k8s 平臺的訪問流量壓力和 etcd 自身的存儲壓力。
一般地,k8s 平臺每個 namespace 都是分配給一個業務單獨使用。前面提到 k8s 可能因為 list-all 壓力導致被壓垮,這些數據訪問大部分情況下都是 namespace 級別的 list-all。從平臺獲取 top N namespace 后,重點監控這些數據量級比較大的業務的 list-all 長連接請求,在 kube-apiserver 層面對其采取限流措施,就可以基本上保證 k8s 集群不會被這些長連接請求打垮,保證集群的高可用。
3.冗余數據分析
etcd 中不僅有熱數據,還有冷數據。這些冷數據雖然不會帶來外部流量訪問壓力,但是會導致 etcd 內存索引鎖粒度的增大,進而導致每次 etcd 訪問 RT 時延增加和整體 QPS 的下降。
近期通過分析某大規模【7k node 以上】 k8s 集群 etcd 中的冗余數據,發現某業務數據在 etcd 中存儲了大量數據,其數據量大卻一周內沒有訪問過一次,與業務方詢問后獲悉:業務方把 k8s 集群的 etcd 當做其 crd 數據的冷備使用。與業務方溝通后把數據從 etcd 中遷移掉后,內存 key 數目立即下降 20%?左右,大部分 etcd KV RT P99 延時立即下降 50%?~ 60%?之多。
4.負載均衡
k8s 平臺運維人員一般都有這樣一條經驗:etcd 集群如果發生了啟停,需要盡快對所有 k8s kube-apiserver 進行一次重啟,以保證 kube-apiserver 與 etcd 之間連接數的均衡。其原因有二:
kube-apiserver 在啟動時可以通過隨機方式保證其與 etcd 集群中某個節點建立連接,但 etcd 發生啟停后,kube-apiserver 與 etcd 之間的連接數并無規律,導致每個 etcd 節點承擔的客戶端壓力不均衡;
kube-apiserver 與 etcd 連接數均衡時,其所有讀寫請求有 2/3 概率是經過 follower 轉發到 leader,保證整體 etcd 集群負載的均衡,如果連接不均衡則集群性能無法評估。
通過 etcd 運維平臺提供的每個 etcd 的連接負載壓力,可以實時獲取集群連接的均衡性,進而決定運維介入的時機,保證 etcd 集群整體的健康度。
其實最新的 etcd v3.5 版本已經提供了 etcd 客戶端和 etcd 節點之間的自動負載均衡功能,但這個版本才發布沒多久,目前最新版本的 k8s 尚未支持這個版本,可以及時跟進 k8s 社區對這個版本的支持進度以及時獲取這一技術紅利,減輕平臺運維壓力。?
未來之路
通過一年多的包括 kube-apiserver 和 etcd 在內的 k8s 高可用建設,目前 k8s 集群已經穩定下來,一個顯著的特征是半年內 k8s 集群沒有發生過一次 P 級故障,但其高可用建設工作不可能停歇?---?作為全球 k8s 規模化建設領導力象限的螞蟻集團正在挑戰 node 量級更大規模的 k8s 集群,這一工作將推動 etcd 集群建設能力的進一步提升。
前面提到的很多 etcd 能力提升工作都是圍繞其 scale up 能力提升展開的,這方面的能力還需要更深層次的加強:
etcd?最新?feature?地及時跟進,及時把社區技術進步帶來的開源價值轉換為螞蟻?k8s?平臺上的客戶價值
及時跟進阿里集團在?etcd?compact?算法優化、etcd?單節點多?multiboltdb?的架構優化以及 kube-apiserver?的服務端數據壓縮等?etcd?優化工作【見參考文檔?1】,對兄弟團隊的工作進行借鑒和反饋,協同作戰共同提升
跟進螞蟻自身?k8s?平臺上?etcd?的性能瓶頸,提出我們自己的解決方案,在提升我們平臺的技術價值的同時反哺開源
除了關注 etcd 單節點性能的提升,我們下一步的工作將圍繞分布式 etcd 集群這一 scale out 方向展開。前面提到的 etcd 集群拆分工作,其本質就是通過分布式 etcd 集群的方式提升 etcd 集群整體的性能:該集群的數據劃分方式是依據 k8s 業務層面的數據類型進行的。
該工作可以進一步拓展為:不區分 KV 的業務意義,從單純的 KV 層面對把數據根據某種路由方式把數據寫入后端多 etcd 子集群,實現 etcd 集群整體的冷熱負載均衡。
分布式 etcd 集群的實現有兩種方式:proxyless 和 proxy based:proxy based etcd 分布式集群的請求鏈路是 client[kube-apiserver]?-> proxy -> etcd server,而謂的 proxyless 分布式 etcd 集群的請求鏈路是 client[kube-apiserver]?-> etcd server。
proxy based etcd 分布式集群的好處是可以直接基于 etcd 社區提供的 etcd proxy 進行開發,后期亦可回饋社區,實現其開源價值、技術價值和客戶價值的統一。但經過測試:按照測試發現,kube-apiserver 經過 proxy 向 etcd 發起讀寫請求后 RT ?和 QPS 降低 20%?~ 25%。所以下一步的工作重點是開發 proxyless etcd 集群。
目前的拆分后的 etcd 分布式集群本質或者 67%?的概率是 proxy based 分布式集群:kube-apiserver 的請求大概有三分之二的概率是經過 follower 轉發到 leader,此處的 follower 本質就是一個 proxy。如果 kube-apiserver 所有請求都是與 leader 直連后被處理,理論上當前的 k8s 集群的 RT 和 QPS 就有 67%?* 20%?≈ 13.4%?的性能收益。
proxyless etcd 分布式集群的缺點是如果把 proxy 的路由邏輯放入 kube-apiserver 中,會造成 kube-apiserver 版本升級成本增加,但相比于至少 20%?【將來通過 etcd 集群規模擴充這個收益肯定會更大】的收益,這個僅僅影響了 kube-apiserver 單個組件的版本升級的成本是值得的。
除了 multiple etcd clusters 的思路外,數據中間件團隊基于 OBKV 之上實現了 etcd ?V3 API ,算是另一種比較好的技術路線,頗類似于本文開頭黃東旭提到的在 tikv 之上 etcd ?V3 API 接口層,可以稱之為類 etcd 系統,目前相關工作也在推進中。
總之,隨著我們 k8s 規模越來越大,螞蟻集團 etcd 整體工作的重要性就日益凸顯。?如果說前期 etcd 的高可用建設之路是在泥濘小道上蹣跚前行,那么以后的 etcd 高可用建設之路必是康莊大道?---?道路越走越寬廣!
參看文檔
參考文檔 1:
https://www.kubernetes.org.cn/9284.html
參考文檔 2:
https://tech.meituan.com/2020/08/13/openstack-to-kubernetes-in-meituan.html
作者簡介
于雨(github @AlexStocks),dubbogo 社區負責人,一個有十一年服務端基礎架構和中間件研發一線工作經驗的程序員。
陸續參與和改進過 Redis/Pika/Pika-Port/etcd/Muduo/Dubbo/dubbo-go/Sentinel-go 等知名項目,目前在螞蟻集團可信原生技術部大規模 k8s 集群調度團隊從事容器編排工作,參與維護全球規模最大的 Kubernetes 生產集群之一,致力于打造規模化、金融級、可信的云原生基礎設施。
歡迎對 Serverless 自動伸縮技術、自適應混合部署技術以及 Kata/Nanovisor 等安全容器技術感興趣的同行或者 2022 屆應屆畢業生加入我們。
聯系郵箱 xiaoyun.maoxy@antgroup.com 或者 yuyu.zx@antgroup.com。
本周推薦
我們做出了一個分布式注冊中心
2021-07-27
還在為多集群管理煩惱嗎?OCM來啦!
2021-07-20
RFC8998+BabaSSL---讓國密駛向更遠的星辰大海
2021-07-13
MOSN 子項目 Layotto:開啟服務網格+應用運行時新篇章
2021-06-21
總結
以上是生活随笔為你收集整理的蚂蚁集团万级规模 k8s 集群 etcd 高可用建设之路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个95分位延迟要求5ms的场景,如何做
- 下一篇: 解决 GraphQL 的限流难题