天天看點

螞蟻集團萬級規模 K8s 叢集 etcd 高可用建設之路

螞蟻集團運維着可能是全球最大的 K8s 叢集:K8s 官方以 5k node 作為 K8s 規模化的頂峰,而螞蟻集團事實上運維着規模達到 10k node 規模的 K8s 叢集。一個形象的比喻就是,如果官方以及跟着官方的 K8s 使用者能想象到的 K8s 的叢集規模是泰山,那麼螞蟻集團在官方的解決方案之上已經實作了一個珠穆朗瑪峰,引領了 K8s 規模化技術的提升。

這個量級的差異,不僅僅是量的差異,更是 K8s 管理維護的質的提升。能維護有如此巨大挑戰巨量規模的 K8s 叢集,其背後原因是螞蟻集團付出了遠大于 K8s 官方的優化努力。

所謂萬丈高樓平地起,本文着重讨論下螞蟻集團的在 K8s 的基石 --- etcd 層面做出的高可用建設工作:隻有 etcd 這個基石穩當了,K8s 這棟高樓大廈才保持穩定性,有 tidb 大佬黃東旭朋友圈佐證【圖檔已獲黃總授權】。

螞蟻集團萬級規模 K8s 叢集 etcd 高可用建設之路

面臨的挑戰

etcd 首先是 K8s 叢集的 KV 資料庫。

從資料庫的角度來看,K8s 整體叢集架構各個角色如下:

  1. etcd 叢集的資料庫
  2. kube-apiserver etcd 的 API 接口代理、資料緩存層
  3. kubelet 資料的生産者和消費者
  4. kube-controller-manager 資料的消費者和生産者
  5. 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 整體叢集架構各個角色如下:

  1. etcd 消息路由器
  2. kube-apiserver etcd 生産者消息代理和消息廣播【或者成為次級消息路由器、消費者代理】
  3. kubelet 消息的生産者和消費者
  4. kube-controller-manager 消息的消費者和生産者
  5. 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 消息暴增以及消息寫放大的三種壓力。

為了證明所言非虛,特引用部分資料為佐證:

  1. etcd KV 資料量級在 100 萬以上;
  2. etcd event 資料量在 10 萬以上;
  3. etcd 讀流量壓力峰值在 30 萬 pqm 以上,其中讀 event 在 10k qpm 以上;
  4. etcd 寫流量壓力峰值在 20 萬 pqm 以上,其中寫 event 在 15k qpm 以上;
  5. etcd CPU 經常性飙升到 900% 以上;
  6. etcd 記憶體 RSS 在 60 GiB 以上;
  7. etcd 磁盤使用量可達 100 GiB 以上;
  8. etcd 自身的 goroutine 數量 9k 以上;
  9. etcd 使用的使用者态線程達 1.6k 以上;
  10. etcd gc 單次耗時常态下可達 15ms。

使用 Go 語言實作的 etcd 管理這些資料非常吃力,無論是 CPU、記憶體、gc、goroutine 數量還是線程使用量,基本上都接近 go runtime 管理能力極限:經常在 CPU profile 中觀測到 go runtime 和 gc 占用資源超過 50% 以上。

螞蟻的 K8s 叢集在經曆高可用項目維護之前,當叢集規模突破 7 千節點規模時,曾出現如下性能瓶頸問題:

  1. etcd 出現大量的讀寫延遲,延遲甚至可達分鐘級;
  2. kube-apiserver 查詢 pods / nodes / configmap / crd 延時很高,導緻 etcd oom;
  3. etcd list-all pods 時長可達 30 分鐘以上;
  4. 2020 年 etcd 叢集曾因 list-all 壓力被打垮導緻的事故就達好幾起;
  5. 控制器無法及時感覺資料變化,如出現 watch 資料延遲可達 30s 以上。

如果說這種狀況下的 etcd 叢集是在刀鋒上跳舞, 此時的整個 K8s 叢集就是一個活火山:稍不留神就有可能背個 P 級故障, 彼時的整個 K8s master 運維工作大概是整個螞蟻集團最危險的工種之一。

高可用政策

實作一個分布式系統高可用能力的提升,大概有如下手段:

  1. 提升自身穩定性與性能;
  2. 精細管理上遊流量;
  3. 保證服務下遊服務 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 可調整參數如下:

  1. write batch
  2. compaction

1.write batch

像其他正常的 DB 一樣,etcd 磁盤送出資料時也采用了定時批量送出、異步寫盤的方式提升吞吐,并通過記憶體緩存的方式平衡其延時。具體的調整參數接口如下:

  1. batch write number 批量寫 KV 數目,預設值是 10k;
  2. batch write interval 批量寫事件間隔,預設值是 100 ms。

etcd batch 這兩個預設值在大規模 K8s 叢集下是不合适的,需要針對具體的運作環境調整之,避免導緻記憶體使用 OOM。一般地規律是,叢集 node 數目越多,這兩個值就應該成比例減小。

2.compaction

etcd 自身由于支援事務和消息通知,是以采用了 MVCC 機制儲存了一個 key 的多版本資料,etcd 使用定時的 compaction 機制回收這些過時資料。etcd 對外提供的壓縮任務參數如下:

  1. compaction interval 壓縮任務周期時長;
  2. compaction sleep interval 單次壓縮批次間隔時長,預設 10 ms;
  3. compaction batch limit 單次壓縮批次 KV 數目,預設 1000。

(1)壓縮任務周期

K8s 叢集的 etcd compaction 可以有兩種途徑進行 compaction:

  1. etcd 另外提供了 comapct 指令和 API 接口,K8s kube-apiserver 基于這個 API 接口也對外提供了 compact 周期參數;
  2. etcd 自身會周期性地執行 compaction;
  3. etcd 對外提供了自身周期性 compaction 參數調整接口,這個參數的取值範圍是 (0, 1 hour];
  4. 其意義是:etcd compaction 即隻能打開不能關閉,如果設定的周期時長大于 1 hour,則 etcd 會截斷為 1 hour。

螞蟻 K8s 團隊在經過測試和線下環境驗證後,目前的壓縮周期取值經驗是:

  1. 在 etcd 層面把 compaction 周期盡可能地拉長,如取值 1 hour,形同在 etcd 自身層面關閉 compaction,把 compaction interval 的精細調整權交給 K8s kube-apiserver;
  2. 在 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 任務:

  1. 在 etcd 層面設定 compaction 周期是 1 hour;
  2. 在 kube-apiserver 層面設定 comapction 周期是 30 minutes;
  3. 在 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 手段尚未使用:

  1. 通過壓測或者線上擷取 etcd 運作 profile,分析 etcd 流程的瓶頸,然後優化代碼流程提升性能;
  2. 通過其他手段降低單節點 etcd 資料量。

通過代碼流程優化 etcd 性能,可以根據 etcd 使用方的人力情況進行之,更長期的工作應該是緊跟社群,及時擷取其版本更新帶來的技術紅利。通過降低 etcd 資料規模來擷取 etcd 性能的提升則必須依賴 etcd 使用方自身的能力建設了。

我們曾對 etcd 的單節點 RT 與 QPS 性能與 KV 資料量的關系進行過 benchmark 測試,得到的結論是:當 KV 資料量增加時,其 RT 會随之線性增加,其 QPS 吞吐則會指數級下降。這一步測試結果帶來的啟示之一即是:通過分析 etcd 中的資料組成、外部流量特征以及資料通路特點,盡可能地降低單 etcd 節點的資料規模。

目前螞蟻的 etcd 運維平台具有如下資料分析功能:

  1. longest N KV --- 長度最長的 N 個 KV
  2. top N KV --- 段時間内通路次數最多的 N 個 KV
  3. top N namespace --- KV 數目最多的 N 個 namespace
  4. verb + resoure --- 外部通路的動作和資源統計
  5. 連接配接數 --- 每個 etcd 節點的長連接配接數目
  6. client 來源統計 --- 每個 etcd 節點的外部請求來源統計
  7. 備援資料分析 --- etcd 叢集中近期無外部通路的 KV 分布

根據資料分析結果,可以進行如下工作:

  1. 客戶限流
  2. 負載均衡
  3. 叢集拆分
  4. 備援資料删除
  5. 業務流量精細分析

1.叢集拆分

前文提到,etcd 叢集性能提升的一個經典手段就是把 event 資料獨立拆分到一個獨立的 etcd 叢集,因為 event 資料是 K8s 叢集一中量級比較大、流動性很強、通路量非常高的資料,拆分之後可以降低 etcd 的資料規模并減輕 etcd 單節點的外部用戶端流量。

一些經驗性的、正常性的 etcd 拆分手段有:

  1. pod/cm
  2. node/svc
  3. 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 之間連接配接數的均衡。其原因有二:

  1. kube-apiserver 在啟動時可以通過随機方式保證其與 etcd 叢集中某個節點建立連接配接,但 etcd 發生啟停後,kube-apiserver 與 etcd 之間的連接配接數并無規律,導緻每個 etcd 節點承擔的用戶端壓力不均衡;
  2. 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 能力提升展開的,這方面的能力還需要更深層次的加強:

  1. etcd 最新 feature 地及時跟進,及時把社群技術進步帶來的開源價值轉換為螞蟻 K8s 平台上的客戶價值

2.及時跟進阿裡集團在 etcd compact 算法優化、etcd 單節點多 multiboltdb 的架構優化以及 kube-apiserver 的服務端資料壓縮等 etcd 優化工作【見參考文檔 1】,對兄弟團隊的工作進行借鑒和回報,協同作戰共同提升

  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 屆應屆畢業生加入我們。

聯系郵箱 [email protected] 或者 [email protected]

本周推薦閱讀

更多文章請掃碼關注“金融級分布式架構”公衆号

螞蟻集團萬級規模 K8s 叢集 etcd 高可用建設之路