天天看點

将 Kubernetes 擴充到超過 4k 個節點和 200k個Pod

作者 | Abdul Qadeer

譯者 | 平川

策劃 | Tina

在 PayPal,我們最近開始試水 Kubernetes。我們大部分的工作負載都運作在 Apache Mesos 上,而作為遷移的一部分,我們需要從性能方面了解下運作 Kubernetes 叢集以及 PayPal 特有的控制平面。其中最主要的是了解平台的可擴充性,以及通過調整叢集找出可以改進的地方。

本文最初釋出于 PayPal 技術部落格。

與 Apache Mesos 不同的是,前者無需任何修改即可擴充到 10,000 個節點,而擴充 Kubernetes 則非常具有挑戰性。Kubernetes 的可擴充性不僅僅展現在節點和 Pod 的數量上,還有其他多個方面,如建立的資源數量、每個 Pod 的容器數量、服務總數和 Pod 部署的吞吐量。本文描述了我們在擴充過程中遇到的一些挑戰,以及我們如何解決這些問題。

叢集拓撲

我們的生産環境中有各種不同規模的叢集,包含數千個節點。我們的設定包括三個主節點和一個外部的三節點 etcd 叢集,所有這些都運作在谷歌雲平台(GCP)上。控制平面前面有一個負載平衡器,所有資料節點都與控制平面屬于相同的區域。

工作負載

為了進行性能測試,我們使用了一個開源的工作負載生成器 k-bench,并針對我們的場景做了修改。我們使用的資源對象是簡單的 Pod 和部署。我們按不同的批次大小和部署間隔時間,分批次連續對它們進行部署。

擴 展

開始時,Pod 和節點數量都比較少。通過壓力測試,我們發現可以改進的地方,并繼續擴大叢集的規模,因為我們觀察到性能有所改善。每個工作節點有四個 CPU 核心,最多可容納 40 個 Pod。我們擴充到大約 4100 個節點。用于基準測試的應用程式是一個無狀态的服務,運作在 100 個服務品質(QoS)有保證的毫核(millicores )上。

我們從 1000 個節點、2000 個 Pod 開始,接着是 16000 個 Pod,然後是 32000 個 Pod。之後,我們躍升到 4100 個節點、15 萬個 Pod,接着是 20 萬個 Pod。我們不得不增加每個節點的核數,以容納更多的 Pod。

API 伺服器

事實證明,API 伺服器是一個瓶頸,有幾個到 API 伺服器的連接配接傳回 504 網關逾時,此外還有本地用戶端限流(指數退避)。這些問題在擴充過程中呈指數級增長:

I0504 17:54:55.731559 1 request.go:655] Throttling request took 1.005397106s, request: POST:https://:443/api/v1/namespaces/kbench-deployment-namespace-14/Pods..

I0504 17:55:05.741655 1 request.go:655] Throttling request took 7.38390786s, request: POST:https://:443/api/v1/namespaces/kbench-deployment-namespace-13/Pods..

I0504 17:55:15.749891 1 request.go:655] Throttling request took 13.522138087s, request: POST:https://:443/api/v1/namespaces/kbench-deployment-namespace-13/Pods..

I0504 17:55:25.759662 1 request.go:655] Throttling request took 19.202229311s, request: POST:https://:443/api/v1/namespaces/kbench-deployment-namespace-20/Pods..

I0504 17:55:35.760088 1 request.go:655] Throttling request took 25.409325008s, request: POST:https://:443/api/v1/namespaces/kbench-deployment-namespace-13/Pods..

I0504 17:55:45.769922 1 request.go:655] Throttling request took 31.613720059s, request: POST:https://:443/api/v1/namespaces/kbench-deployment-namespace-6/Pods..

API 伺服器上限制速率的隊列的大小是通過 max-mutating-requests-inflight 和 max-requests-inflight 更新的。1.20 版本中引入的優先級和公平性特性測試版,就是在 API 伺服器上這兩個标記的控制下将隊列的總大小在不同的隊列類别之間進行劃分。例如,群首選舉請求的優先級比 Pod 請求高。在每個優先級中,都有可配置隊列的公平性。未來還可以通過 PriorityLevelConfiguration&FlowSchema API 對象做進一步調優。

控制器管理器

控制器管理器負責為副本集、命名空間等本地資源以及數量衆多的部署(由副本集管理)提供控制器。控制器管理器與 API 伺服器同步其狀态的速度是有限的。有多個調節器用于調整這一行為:

kube-api-qps—— 控制器管理器在一秒鐘内可以向 API 伺服器進行查詢的次數。

kube-api-burst—— 控制器管理器突發流量峰值,是kube-api-qps之上另一個并發調用數。

concurrent-deployment-syncs—— 部署、複制集等對象同步調用的并發性。

排程器

當作為一個獨立的元件單獨測試時,排程器可以支援每秒 1000 個 Pod 的高吞吐率。然而,在将排程器部署到一個線上叢集中時,我們注意到,實際的吞吐量有所降低。etcd 執行個體速度慢導緻排程器的綁定延遲增加,使得待處理隊列的大小增加到數千個 Pod 的程度。我們的想法是在測試運作期間将這個數值保持在 100 以下,因為數量比較大的話會影響 Pod 的啟動延遲。此外,我們最後還調整了群首選舉參數,以應對短暫的網絡分區或網絡擁堵引發的虛假重新開機。

etcd

etcd 是 Kubernetes 叢集中最關鍵的一部分。這一點從 etcd 在整個叢集中引發的、以不同方式表現出來的大量問題可以看出來。經過非常仔細的研究,我們才找到根本原因,并擴充 etcd 以比對我們預期的規模。

在擴充過程中,許多 Raft proposal 開始失敗

将 Kubernetes 擴充到超過 4k 個節點和 200k個Pod

通過調查分析,我們發現,GCP 将 PD-SSD 磁盤的吞吐量限制在每秒 100MB 左右(如下圖所示),我們的磁盤大小為 100G。GCP 沒有提供增加吞吐量限制的方法——它隻随着磁盤的大小增加。盡管 etcd 節點隻需要不到 10G 的空間,我們首先嘗試了 1TB PD-SSD。然而,當所有 4k 個節點同時加入 Kubernetes 控制平面時,磁盤再大也會成為一個瓶頸。我們決定使用本地 SSD,它的吞吐量非常高,代價是在出現故障時丢失資料的幾率略高,因為它不是持久化的。

将 Kubernetes 擴充到超過 4k 個節點和 200k個Pod

在遷移到本地 SSD 後,我們并沒有看到最快的 SSD 帶來了預期的性能。我們用 FIO 直接在磁盤上做了一些基準測試,數值在意料之中。但是,對于所有成員的寫入并發,etcd 基準測試講述了一個不同的故事:

Plain TextLOCAL SSDSummary: Total: 8.1841 secs. Slowest: 0.5171 secs. Fastest: 0.0332 secs. Average: 0.0815 secs. Stddev: 0.0259 secs.Requests/sec: 12218.8374

PD SSD

Summary:

Total: 4.6773 secs.

Slowest: 0.3412 secs.

Fastest: 0.0249 secs.

Average: 0.0464 secs.

Stddev: 0.0187 secs.

Requests/sec: 21379.7235

本地 SSD 的表現更差!經過深入調查,這是由 ext4 檔案系統的寫屏障緩存送出導緻的。由于 etcd 使用寫前日志,并在每次送出到 Raft 日志時調用 fsync,是以可以禁用寫屏障。此外,我們在檔案系統級和應用程式級有 DB 備份作業,用于 DR。在這樣修改之後,使用本地 SSD 的數值提高到了與 PD-SSD 相當的程度:

Plain TextLOCAL SSDSummary: Total: 4.1823 secs. Slowest: 0.2182 secs. Fastest: 0.0266 secs. Average: 0.0416 secs. Stddev: 0.0153 secs. Requests/sec: 23910.3658

這一改進的效果在 etcd 的 WAL 同步持續時間和後端送出延遲上展現了出來,如下圖所示,在 15:55 左右這個時間點上,WAL 同步持續時間和後端送出延遲降低了 90% 以上

etcd 中預設的 MVCC 資料庫大小為 2GB。在 DB 空間不足的告警被觸發時,這個大小最大會增加到 8GB。由于該資料庫的使用率約為 60%,是以我們能夠擴充到 20 萬個無狀态 Pod。

經過上述這些優化,在預期的規模下,叢集更加穩定了,然而,在 API 延遲方面,我們的 SLI 還差很多。

etcd 伺服器還會偶爾重新開機,僅一次重新開機就會破壞基準測試結果,尤其是 P99 值。仔細觀察發現,v1.20 版的 etcd YAML 中有一個存活探針 Bug。為了解決這個問題,我們采用了一個變通辦法,即增加失敗門檻值的計數。

在用盡所有方法對 etcd 進行了垂直擴充之後,主要是在資源方面(CPU、記憶體、磁盤),我們發現,etcd 的性能受到範圍查詢的影響。當範圍查詢很多時,etcd 的表現并不好,對 Raft 日志的寫入也受到影響,增加了叢集的延遲。以下是一次測試運作中影響性能的每個 Kubernetes 資源的範圍查詢的數量:

Plain Textetcd$ sudo grep -ir "events" 0.log.20210525-035918 | wc -l130830etcd$ sudo grep -ir "Pods" 0.log.20210525-035918 | wc -l107737etcd$ sudo grep -ir "configmap" 0.log.20210525-035918 | wc -l86274etcd$ sudo grep -ir "deployments" 0.log.20210525-035918 | wc -l6755etcd$ sudo grep -ir "leases" 0.log.20210525-035918 | wc -l4853etcd$ sudo grep -ir "nodes" 0.log.20210525-035918 | wc -l

由于這些查詢很耗時,etcd 的後端延遲受到了很大的影響。在事件資源上對 etcd 伺服器進行分片管理後,我們看到,在 Pod 高度競争的情況下,叢集的穩定性有所提高。将來,還可以進一步在 Pod 資源上對 etcd 叢集進行分片。配置 API 伺服器聯系相關的 etcd 以與分片的資源進行互動很容易。

結 果

在對 Kubernetes 的各種元件做完優化和調整後,我們觀察到,延遲有大幅改善。下圖展示了随着時間的推移,為滿足 SLO 而實作的性能提升。其中,工作負載是 150k 個 Pod,每個部署 250 個副本,10 個并發工作程序。隻要 Pod 啟動的 P99 延遲在 5 秒之内,按照 Kubernetes SLO,我們就算是很好了

将 Kubernetes 擴充到超過 4k 個節點和 200k個Pod

下圖顯示了當叢集有 20 萬個 Pod 時,API 調用延遲完全符合 SLO。

我們還實作了 20 萬個 Pod P99 啟動延遲為 5 秒左右,而 Pod 部署速率遠遠高于 K8s 針對 5k 節點測試時所聲稱的 3000 個 Pod/ 分鐘。

将 Kubernetes 擴充到超過 4k 個節點和 200k個Pod

總 結

Kubernetes 是一個複雜的系統,必須深入了解控制平面,才能知道如何擴充每個元件。通過這次操練,我們學到了很多東西,并将繼續優化我們的叢集。

檢視英文原文:

https://medium.com/paypal-tech/scaling-kubernetes-to-over-4k-nodes-and-200k-pods-29988fad6ed?

繼續閱讀