Kubernetes 4000節(jié)點(diǎn)運(yùn)維經(jīng)驗(yàn)分享
在 PayPal,我們最近開始試水 Kubernetes。我們大部分的工作負(fù)載都運(yùn)行在 Apache Mesos 上,而作為遷移的一部分,我們需要從性能方面了解下運(yùn)行 Kubernetes 集群以及 PayPal 特有的控制平面。其中最主要的是了解平臺(tái)的可擴(kuò)展性,以及通過(guò)調(diào)整集群找出可以改進(jìn)的地方。
本文最初發(fā)布于 PayPal 技術(shù)博客。
在 PayPal,我們最近開始試水 Kubernetes。我們大部分的工作負(fù)載都運(yùn)行在 Apache Mesos 上,而作為遷移的一部分,我們需要從性能方面了解下運(yùn)行 Kubernetes 集群以及 PayPal 特有的控制平面。其中最主要的是了解平臺(tái)的可擴(kuò)展性,以及通過(guò)調(diào)整集群找出可以改進(jìn)的地方。
與 Apache Mesos 不同的是,前者無(wú)需任何修改即可擴(kuò)展到 10,000 個(gè)節(jié)點(diǎn),而擴(kuò)展 Kubernetes 則非常具有挑戰(zhàn)性。Kubernetes 的可擴(kuò)展性不僅僅體現(xiàn)在節(jié)點(diǎn)和 Pod 的數(shù)量上,還有其他多個(gè)方面,如創(chuàng)建的資源數(shù)量、每個(gè) Pod 的容器數(shù)量、服務(wù)總數(shù)和 Pod 部署的吞吐量。本文描述了我們?cè)跀U(kuò)展過(guò)程中遇到的一些挑戰(zhàn),以及我們?nèi)绾谓鉀Q這些問題。
我們的生產(chǎn)環(huán)境中有各種不同規(guī)模的集群,包含數(shù)千個(gè)節(jié)點(diǎn)。我們的設(shè)置包括三個(gè)主節(jié)點(diǎn)和一個(gè)外部的三節(jié)點(diǎn) etcd 集群,所有這些都運(yùn)行在谷歌云平臺(tái)(GCP)上??刂破矫媲懊嬗幸粋€(gè)負(fù)載平衡器,所有數(shù)據(jù)節(jié)點(diǎn)都與控制平面屬于相同的區(qū)域。
為了進(jìn)行性能測(cè)試,我們使用了一個(gè)開源的工作負(fù)載生成器 k-bench,并針對(duì)我們的場(chǎng)景做了修改。我們使用的資源對(duì)象是簡(jiǎn)單的 Pod 和部署。我們按不同的批次大小和部署間隔時(shí)間,分批次連續(xù)對(duì)它們進(jìn)行部署。
開始時(shí),Pod 和節(jié)點(diǎn)數(shù)量都比較少。通過(guò)壓力測(cè)試,我們發(fā)現(xiàn)可以改進(jìn)的地方,并繼續(xù)擴(kuò)大集群的規(guī)模,因?yàn)槲覀冇^察到性能有所改善。每個(gè)工作節(jié)點(diǎn)有四個(gè) CPU 內(nèi)核,最多可容納 40 個(gè) Pod。我們擴(kuò)展到大約 4100 個(gè)節(jié)點(diǎn)。用于基準(zhǔn)測(cè)試的應(yīng)用程序是一個(gè)無(wú)狀態(tài)的服務(wù),運(yùn)行在 100 個(gè)服務(wù)質(zhì)量(QoS)有保證的毫核(millicores )上。
我們從 1000 個(gè)節(jié)點(diǎn)、2000 個(gè) Pod 開始,接著是 16000 個(gè) Pod,然后是 32000 個(gè) Pod。之后,我們躍升到 4100 個(gè)節(jié)點(diǎn)、15 萬(wàn)個(gè) Pod,接著是 20 萬(wàn)個(gè) Pod。我們不得不增加每個(gè)節(jié)點(diǎn)的核數(shù),以容納更多的 Pod。
事實(shí)證明,API 服務(wù)器是一個(gè)瓶頸,有幾個(gè)到 API 服務(wù)器的連接返回 504 網(wǎng)關(guān)超時(shí),此外還有本地客戶端限流(指數(shù)退避)。這些問題在擴(kuò)展過(guò)程中 呈指數(shù)級(jí) 增長(zhǎng):
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 服務(wù)器上限制速率的隊(duì)列的大小是通過(guò) max-mutating-requests-inflight 和 max-requests-inflight 更新的。1.20 版本中引入的 優(yōu)先級(jí)和公平性 特性測(cè)試版,就是在 API 服務(wù)器上這兩個(gè)標(biāo)記的控制下將隊(duì)列的總大小在不同的隊(duì)列類別之間進(jìn)行劃分。例如,群首選舉請(qǐng)求的優(yōu)先級(jí)比 Pod 請(qǐng)求高。在每個(gè)優(yōu)先級(jí)中,都有可配置隊(duì)列的公平性。未來(lái)還可以通過(guò) PriorityLevelConfiguration&FlowSchema API 對(duì)象做進(jìn)一步調(diào)優(yōu)。
控制器管理器負(fù)責(zé)為副本集、命名空間等本地資源以及數(shù)量眾多的部署(由副本集管理)提供控制器。控制器管理器與 API 服務(wù)器同步其狀態(tài)的速度是有限的。有多個(gè)調(diào)節(jié)器用于調(diào)整這一行為:
kube-api-qps -- 控制器管理器在一秒鐘內(nèi)可以向 API 服務(wù)器進(jìn)行查詢的次數(shù)。
kube-api-burst -- 控制器管理器突發(fā)流量峰值,是 kube-api-qps 之上另一個(gè)并發(fā)調(diào)用數(shù)。
concurrent-deployment-syncs -- 部署、復(fù)制集等對(duì)象同步調(diào)用的并發(fā)性。
當(dāng)作為一個(gè)獨(dú)立的組件單獨(dú)測(cè)試時(shí),調(diào)度器可以支持每秒 1000 個(gè) Pod 的高吞吐率。然而,在將調(diào)度器部署到一個(gè)在線集群中時(shí),我們注意到,實(shí)際的吞吐量有所降低。etcd 實(shí)例速度慢導(dǎo)致調(diào)度器的綁定延遲增加,使得待處理隊(duì)列的大小增加到數(shù)千個(gè) Pod 的程度。我們的想法是在測(cè)試運(yùn)行期間將這個(gè)數(shù)值保持在 100 以下,因?yàn)閿?shù)量比較大的話會(huì)影響 Pod 的啟動(dòng)延遲。此外,我們最后還調(diào)整了群首選舉參數(shù),以應(yīng)對(duì)短暫的網(wǎng)絡(luò)分區(qū)或網(wǎng)絡(luò)擁堵引發(fā)的虛假重啟。
etcd 是 Kubernetes 集群中最關(guān)鍵的一部分。這一點(diǎn)從 etcd 在整個(gè)集群中引發(fā)的、以不同方式表現(xiàn)出來(lái)的大量問題可以看出來(lái)。經(jīng)過(guò)非常仔細(xì)的研究,我們才找到根本原因,并擴(kuò)展 etcd 以匹配我們預(yù)期的規(guī)模。
在擴(kuò)展過(guò)程中,許多 Raft proposal 開始失?。?/p>

通過(guò)調(diào)查分析,我們發(fā)現(xiàn),GCP 將 PD-SSD 磁盤的吞吐量限制在每秒 100MB 左右(如下圖所示),我們的磁盤大小為 100G。GCP 沒有提供增加吞吐量限制的方法——它只 隨著磁盤的大小增加。盡管 etcd 節(jié)點(diǎn)只需要不到 10G 的空間,我們首先嘗試了 1TB PD-SSD。然而,當(dāng)所有 4k 個(gè)節(jié)點(diǎn)同時(shí)加入 Kubernetes 控制平面時(shí),磁盤再大也會(huì)成為一個(gè) 瓶頸。我們決定使用本地 SSD,它的吞吐量非常高,代價(jià)是在出現(xiàn)故障時(shí)丟失數(shù)據(jù)的幾率略高,因?yàn)樗?不是持久化的。

在遷移到本地 SSD 后,我們并沒有看到最快的 SSD 帶來(lái)了預(yù)期的性能。我們用 FIO 直接在磁盤上做了一些基準(zhǔn)測(cè)試,數(shù)值在意料之中。但是,對(duì)于所有成員的寫入并發(fā),etcd 基準(zhǔn)測(cè)試講述了一個(gè)不同的故事:
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.8374PD SSDSummary:Total: 4.6773 secs.Slowest: 0.3412 secs.Fastest: 0.0249 secs.Average: 0.0464 secs.Stddev: 0.0187 secs.: 21379.7235
本地 SSD 的表現(xiàn)更差!經(jīng)過(guò)深入調(diào)查,這是由 ext4 文件系統(tǒng)的寫屏障緩存提交導(dǎo)致的。由于 etcd 使用寫前日志,并在每次提交到 Raft 日志時(shí)調(diào)用 fsync,所以可以禁用寫屏障。此外,我們?cè)谖募到y(tǒng)級(jí)和應(yīng)用程序級(jí)有 DB 備份作業(yè),用于 DR。在這樣修改之后,使用本地 SSD 的數(shù)值提高到了與 PD-SSD 相當(dāng)?shù)某潭龋?/p>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
這一改進(jìn)的效果在 etcd 的 WAL 同步持續(xù)時(shí)間和后端提交延遲上體現(xiàn)了出來(lái),如下圖所示,在 15:55 左右這個(gè)時(shí)間點(diǎn)上,WAL 同步持續(xù)時(shí)間和后端提交延遲降低了 90% 以上:


etcd 中默認(rèn)的 MVCC 數(shù)據(jù)庫(kù)大小為 2GB。在 DB 空間不足的告警被觸發(fā)時(shí),這個(gè)大小最大會(huì)增加到 8GB。由于該數(shù)據(jù)庫(kù)的 利用率約為 60%,所以我們能夠擴(kuò)展到 20 萬(wàn)個(gè)無(wú)狀態(tài) Pod。
經(jīng)過(guò)上述這些優(yōu)化,在預(yù)期的規(guī)模下,集群更加穩(wěn)定了,然而,在 API 延遲方面,我們的 SLI 還差很多。
etcd 服務(wù)器還會(huì)偶爾重啟,僅一次重啟就會(huì)破壞基準(zhǔn)測(cè)試結(jié)果,尤其是 P99 值。仔細(xì)觀察發(fā)現(xiàn),v1.20 版的 etcd YAML 中有一個(gè)存活探針 Bug。為了解決這個(gè)問題,我們采用了一個(gè)變通辦法,即增加失敗閾值的計(jì)數(shù)。
在用盡所有方法對(duì) etcd 進(jìn)行了垂直擴(kuò)展之后,主要是在資源方面(CPU、內(nèi)存、磁盤),我們發(fā)現(xiàn),etcd 的性能受到范圍查詢的影響。當(dāng)范圍查詢很多時(shí),etcd 的表現(xiàn)并不好,對(duì) Raft 日志的寫入也受到影響,增加了集群的延遲。以下是一次測(cè)試運(yùn)行中影響性能的每個(gè) Kubernetes 資源的范圍查詢的數(shù)量:
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由于這些查詢很耗時(shí),etcd 的后端延遲受到了很大的影響。在事件資源上對(duì) etcd 服務(wù)器進(jìn)行分片管理后,我們看到,在 Pod 高度競(jìng)爭(zhēng)的情況下,集群的穩(wěn)定性有所提高。將來(lái),還可以進(jìn)一步在 Pod 資源上對(duì) etcd 集群進(jìn)行分片。配置 API 服務(wù)器聯(lián)系相關(guān)的 etcd 以與分片的資源進(jìn)行交互很容易。
在對(duì) Kubernetes 的各種組件做完優(yōu)化和調(diào)整后,我們觀察到,延遲有大幅改善。下圖展示了隨著時(shí)間的推移,為滿足 SLO 而實(shí)現(xiàn)的性能提升。其中,工作負(fù)載是 150k 個(gè) Pod,每個(gè)部署 250 個(gè)副本,10 個(gè)并發(fā)工作進(jìn)程。只要 Pod 啟動(dòng)的 P99 延遲在 5 秒之內(nèi),按照 Kubernetes SLO,我們就算是很好了。

下圖顯示了當(dāng)集群有 20 萬(wàn)個(gè) Pod 時(shí),API 調(diào)用延遲完全符合 SLO。
_ 我們還實(shí)現(xiàn)了 20 萬(wàn)個(gè) Pod P99 啟動(dòng)延遲為 5 秒左右,而 Pod 部署速率遠(yuǎn)遠(yuǎn)高于 K8s 針對(duì) 5k 節(jié)點(diǎn)測(cè)試時(shí)所聲稱的 3000 個(gè) Pod/ 分鐘。_

Kubernetes 是一個(gè)復(fù)雜的系統(tǒng),必須深入了解控制平面,才能知道如何擴(kuò)展每個(gè)組件。通過(guò)這次操練,我們學(xué)到了很多東西,并將繼續(xù)優(yōu)化我們的集群。
- END -
?推薦閱讀? Golang DevOps 運(yùn)維開發(fā)實(shí)戰(zhàn)從網(wǎng)管到架構(gòu)師,給你分享這10年的成長(zhǎng)感悟分享 5 個(gè)適用于IT工程師的面試技巧9個(gè)常用的Shell腳本,面試也常問!終于明白了 DevOps 與 SRE 的區(qū)別!Linux 性能全方位調(diào)優(yōu)經(jīng)驗(yàn)總結(jié)Kubernetes 生態(tài)架構(gòu)圖 基于Nginx實(shí)現(xiàn)灰度發(fā)布與AB測(cè)試 在Kubernetes上部署一套 Redis 集群 Linux 系統(tǒng)日常巡檢腳本 K8s kubectl 常用命令總結(jié)(建議收藏) 搭建一套完整的企業(yè)級(jí) K8s 集群(kubeadm方式)
點(diǎn)亮,服務(wù)器三年不宕機(jī)


