大規(guī)模場(chǎng)景下 kubernetes 集群的性能優(yōu)化

一、etcd 優(yōu)化

  • 1、etcd 采用本地 ssd 盤作為后端存儲(chǔ)存儲(chǔ)

  • 2、etcd 獨(dú)立部署在非 k8s node 上

  • 3、etcd 快照(snap)與預(yù)寫式日志(wal)分盤存儲(chǔ)

etcd 詳細(xì)的優(yōu)化操作可以參考上篇文章:etcd 性能測(cè)試與調(diào)優(yōu)。

二、apiserver 的優(yōu)化

1、參數(shù)調(diào)整

  • --max-mutating-requests-inflight :在給定時(shí)間內(nèi)的最大 mutating 請(qǐng)求數(shù),調(diào)整 apiserver 的流控 qos,可以調(diào)整至 3000,默認(rèn)為 200

  • --max-requests-inflight:在給定時(shí)間內(nèi)的最大 non-mutating 請(qǐng)求數(shù),默認(rèn) 400,可以調(diào)整至 1000

  • --watch-cache-sizes:調(diào)大 resources 的 watch size,默認(rèn)為 100,當(dāng)集群中 node 以及 pod 數(shù)量非常多時(shí)可以稍微調(diào)大,比如: --watch-cache-sizes=node#1000,pod#5000

2、etcd 多實(shí)例支持

對(duì)于不同 object 進(jìn)行分庫(kù)存儲(chǔ),首先應(yīng)該將數(shù)據(jù)與狀態(tài)分離,即將 events 放在單獨(dú)的 etcd 實(shí)例中,在 apiserver 的配置中加上--etcd-servers-overrides=/events#https://xxx:3379;https://xxx:3379;https://xxx:3379;https://xxxx:3379;https://xxx:3379,后期可以將 pod、node 等 object 也分離在單獨(dú)的 etcd 實(shí)例中。

3、apiserver 的負(fù)載均衡

通常為了保證集群的高可用,集群中一般會(huì)有多個(gè) master 節(jié)點(diǎn),kubelet 的連接也會(huì)被均分到不同的 apiserver,在 k8s v1.10 以前的版本中,kubelet 使用 HTTP/2,HTTP/2 為了提高網(wǎng)絡(luò)性能,一個(gè)主機(jī)只建立一個(gè)連接,所有的請(qǐng)求都通過(guò)該連接進(jìn)行,默認(rèn)情況下,即使網(wǎng)絡(luò)異常,它還是重用這個(gè)連接,直到操作系統(tǒng)將連接關(guān)閉,而操作系統(tǒng)關(guān)閉僵尸連接的時(shí)間默認(rèn)是十幾分鐘,所以在 v1.10 以前的版本中 kubelet 連接 apiserver 超時(shí)之后不會(huì)主動(dòng) reset 掉連接進(jìn)行重試,除非主動(dòng)重啟 kubelet 或者等待十多分鐘后其進(jìn)行重試。

此問(wèn)題在 v1.10 版本中被修復(fù)過(guò)(track/close kubelet->API connections on heartbeat failure #63492),代碼也被 merge 到了 v1.8 和 v1.9,但是該問(wèn)題并沒(méi)有完全修復(fù),直到 v1.14 版本才被完全修復(fù)( kubelet: fix fail to close kubelet->API connections on heartbeat failure #78016)。

所以為了保證 apiserver 的連接數(shù)均衡,請(qǐng)使用 v1.14 及以上版本。

4、使用 pprof 進(jìn)行性能分析

pprof 是 golang 的一大殺器,要想進(jìn)行源碼級(jí)別的性能分析,必須使用 pprof。

// 安裝相關(guān)包
$ brew install graphviz

// 啟動(dòng) pprof
$ go tool pprof http://localhost:8001/debug/pprof/profile
File: kube-apiserver
Type: cpu
Time: Oct 11, 2019 at 11:39am (CST)
Duration: 30s, Total samples = 620ms ( 2.07%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) web   // 使用 web 命令生成 svg 文件

然后打開(kāi) svg 文件:

apiserver

可以通過(guò) graph 以及交互式界面得到 cpu 耗時(shí)、goroutine 阻塞等信息,apiserver 中的對(duì)象比較多,序列化會(huì)消耗非常大的時(shí)間,golang 標(biāo)準(zhǔn)庫(kù)的 json 也有很嚴(yán)重的性能問(wèn)題,開(kāi)源的 json-iter 相比標(biāo)準(zhǔn)庫(kù)有不少性能上的提升,但 json-iter 有很多標(biāo)準(zhǔn)庫(kù)不兼容的問(wèn)題,此前也有相關(guān)的 issue 進(jìn)行反饋但并沒(méi)有合進(jìn)主線。

三、kube-controller-manager 的優(yōu)化

1、參數(shù)優(yōu)化

  • 調(diào)大 --kube-api-qps 值:可以調(diào)整至 100,默認(rèn)值為 20

  • 調(diào)大 --kube-api-burst 值:可以調(diào)整至 100,默認(rèn)值為 30

  • 禁用不需要的 controller:kubernetes v1.14 中已有 35 個(gè) controller,默認(rèn)啟動(dòng)為--controllers,即啟動(dòng)所有 controller,可以禁用不需要的 controller

  • 調(diào)整 controller 同步資源的周期:避免過(guò)多的資源同步導(dǎo)致集群資源的消耗,所有帶有 --concurrent 前綴的參數(shù)

2、kube-controller-manager 升級(jí)過(guò)程 informer 預(yù)加載

參考自 阿里巴巴云原生實(shí)踐 15 講

controller-manager 中存儲(chǔ)的對(duì)象非常多,每次升級(jí)過(guò)程中從 apiserver 獲取這些對(duì)象并反序列化的開(kāi)銷是無(wú)法忽略的,重啟 controller-manager 恢復(fù)時(shí)可能要花費(fèi)幾分鐘才能完成。我們需要盡量的減小 controller-manager 單次升級(jí)對(duì)系統(tǒng)的中斷時(shí)間,主要有以下兩處改造:

  • 預(yù)啟動(dòng)備 controller informer,提前加載 controller 需要的數(shù)據(jù)

  • 主 controller 升級(jí)時(shí),會(huì)主動(dòng)釋放 Leader Lease,觸發(fā)備立即接管工作

通過(guò)此方案 controller-manager 中斷時(shí)間降低到秒級(jí)別(升級(jí)時(shí) < 2s),即使在異常宕機(jī)時(shí),備僅需等待 leader lease 的過(guò)期(默認(rèn) 15s),無(wú)需要花費(fèi)幾分 鐘重新同步數(shù)據(jù)。通過(guò)這個(gè)增強(qiáng),顯著的降低了 controller-manager MTTR(平均恢復(fù)時(shí)間),同時(shí)降低了 controller-manager 恢復(fù)時(shí)對(duì) apiserver 的性能沖擊。

此方案需要對(duì) controller-manager 上面兩處的代碼進(jìn)行修改,controller-manager 默認(rèn)的啟動(dòng)方式是先拿到鎖然后 callback run 方法,在 run 方法中會(huì)啟動(dòng) informers 然后同步對(duì)象,在停止時(shí)也要改為主動(dòng)釋放 leader lease。

四、kube-scheduler 優(yōu)化

在 k8s 核心組件中,調(diào)度器的功能做的比較通用,大部分公司都不會(huì)局限于當(dāng)前調(diào)度器的功能而進(jìn)行一系列的改造,例如美團(tuán)就對(duì) kube-scheduler 進(jìn)行過(guò)一些優(yōu)化,并將預(yù)選失敗中斷機(jī)制(詳見(jiàn)PR)和將全局最優(yōu)解改為局部最優(yōu)解(詳見(jiàn)PR1,PR2)等重要 feature 回饋給了社區(qū)。

首先還是使用好調(diào)度器的基本功能:

然后再進(jìn)行一些必要的優(yōu)化。

1、參數(shù)優(yōu)化

調(diào)大--kube-api-qps 值:可以調(diào)整至 100,默認(rèn)值為 50

2、調(diào)度器優(yōu)化

  • 擴(kuò)展調(diào)度器功能:目前可以通過(guò) scheduler_extender 很方便的擴(kuò)展調(diào)度器,比如對(duì)于 GPU 的調(diào)度,可以通過(guò) scheduler_extender + device-plugins 來(lái)支持。

  • 多調(diào)度器支持:kubernetes 也支持在集群中運(yùn)行多個(gè)調(diào)度器調(diào)度不同作業(yè),例如可以在 pod 的 spec.schedulerName 指定對(duì)應(yīng)的調(diào)度器,也可以在 job 的 .spec.template.spec.schedulerName 指定調(diào)度器。

  • 動(dòng)態(tài)調(diào)度支持:由于 kubernetes 的默認(rèn)調(diào)度器只在 pod 創(chuàng)建過(guò)程中進(jìn)行一次性調(diào)度,后續(xù)不會(huì)重新去平衡 pod 在集群中的分布,導(dǎo)致實(shí)際的資源使用率不均衡,此時(shí)集群中會(huì)存在部分熱點(diǎn)宿主,為了解決默認(rèn)調(diào)度器的功能缺陷,kubernetes 孵化了一個(gè)工具 Descheduler 來(lái)對(duì)默認(rèn)調(diào)度器的功能進(jìn)行一些補(bǔ)充,詳細(xì)說(shuō)明可以參考官方文檔。

3、其他優(yōu)化策略

  • 根據(jù)實(shí)際資源使用率進(jìn)行調(diào)度:目前默認(rèn)的調(diào)度僅根據(jù) pod 的 request 值進(jìn)行調(diào)度,對(duì)于一些資源使用率非常不均衡的場(chǎng)景可以考慮直接以實(shí)際的使用率進(jìn)行調(diào)度。

  • 等價(jià)類劃分(Equivalence classes):典型的用戶擴(kuò)容請(qǐng)求為一次擴(kuò)容多個(gè)容器,因此我 們通過(guò)將 pending 隊(duì)列中的請(qǐng)求劃分等價(jià)類的方式,實(shí)現(xiàn)批處理,顯著的降 低 Predicates/Priorities 的次數(shù),這是阿里在今年的 kubeCon 上提出的一個(gè)優(yōu)化方式。

五、kubelet 優(yōu)化

1、使用 node lease 減少心跳上報(bào)頻率

在大規(guī)模場(chǎng)景下,大量 node 的心跳匯報(bào)嚴(yán)重影響了 node 的 watch,apiserver 處理心跳請(qǐng)求也需要非常大的開(kāi)銷。而開(kāi)啟 nodeLease 之后,kubelet 會(huì)使用非常輕量的 nodeLease 對(duì)象 (0.1 KB) 更新請(qǐng)求替換老的 Update Node Status 方式,這會(huì)大大減輕 apiserver 的負(fù)擔(dān)。

2、使用 bookmark 機(jī)制

kubernetes v1.15 支持 bookmark 機(jī)制,bookmark 主要作用是只將特定的事件發(fā)送給客戶端,從而避免增加 apiserver 的負(fù)載。bookmark 的核心思想概括起來(lái)就是在 client 與 server 之間保持一個(gè)“心跳”, 即使隊(duì)列中無(wú) client 需要感知的更新,reflector 內(nèi)部的版本號(hào)也需要及時(shí)的更新。

比如:每個(gè)節(jié)點(diǎn)上的 kubelet 僅關(guān)注 和自己節(jié)點(diǎn)相關(guān)的 pods,pod storage 隊(duì)列是有限的(FIFO),當(dāng) pods 的隊(duì)列更新時(shí),舊的變更就會(huì)從隊(duì)列中淘汰,當(dāng)隊(duì)列中的更新與某個(gè) kubelet client 無(wú)關(guān)時(shí),kubelet client watch 的 resourceVersion 仍然保持不變,若此時(shí) kubelet client 重連 apiserver 后,這時(shí)候 apiserver 無(wú)法判斷當(dāng)前隊(duì)列的最小值與 kubelet client 之間是否存在需要感知的變更,因此返回 client too old version err 觸發(fā) kubelet client 重新 list 所有的數(shù)據(jù)。

3、限制驅(qū)逐

kubelet 擁有節(jié)點(diǎn)自動(dòng)修復(fù)的能力,例如在發(fā)現(xiàn)異常容器或不合規(guī)容器后,會(huì)對(duì)它們進(jìn)行驅(qū)逐刪除操作,這對(duì)于有些場(chǎng)景來(lái)說(shuō)風(fēng)險(xiǎn)太大。例如當(dāng) kubelet 發(fā)現(xiàn)當(dāng)前宿主機(jī)上容器個(gè)數(shù)比設(shè)置的最大容器個(gè)數(shù)大時(shí),會(huì)挑選驅(qū)逐和刪除某些容器,雖然正常情況下不會(huì)輕易發(fā)生這種問(wèn)題,但是也需要對(duì)此進(jìn)行控制,降低此類風(fēng)險(xiǎn),配置 kubelet 的參數(shù) --eviction-hard= 來(lái)確保在任何情況 kubelet 都不會(huì)驅(qū)逐容器。

4、原地升級(jí)

kubernetes 默認(rèn)只要 pod 的 spec 信息有改動(dòng),例如鏡像信息,此時(shí) pod 的 hash 值就會(huì)改變,然后會(huì)導(dǎo)致 pod 的銷毀重建,一個(gè)Pod中可能包含了主業(yè)務(wù)容器,還有不可剝離的依賴業(yè)務(wù)容器,以及SideCar組件容器等,這在生產(chǎn)環(huán)境中代價(jià)是很大的,一方面 ip 和 hostname 可能會(huì)發(fā)生改變,pod 重啟也需要一定的時(shí)間,另一方面頻繁的重建也給集群管理帶來(lái)了更多的壓力,甚至還可能導(dǎo)致無(wú)法調(diào)度成功。為了解決該問(wèn)題,就需要支持容器的原地升級(jí)??梢蚤_(kāi)發(fā)一個(gè) operator 來(lái)實(shí)現(xiàn)相關(guān)的功能,這種方法需要重新實(shí)現(xiàn)一個(gè) resource 對(duì)應(yīng)于 k8s 中的應(yīng)用,然后當(dāng) pod 中的 image 改變后只更新 pod 不重建,kubelet 會(huì)重啟 container 的,可以參考阿里的 cafeDeployment,或者對(duì)原生 deployment/statefulset 中的控制器直接進(jìn)行修改。

六、kube-proxy 優(yōu)化

1、使用 ipvs 模式

由于 iptables 匹配時(shí)延和規(guī)則更新時(shí)延在大規(guī)模集群中呈指數(shù)增長(zhǎng),增加以及刪除規(guī)則非常耗時(shí),所以需要轉(zhuǎn)為 ipvs,ipvs 使用 hash 表,其增加或者刪除一條規(guī)則幾乎不受規(guī)則基數(shù)的影響。iptables 以及 ipvs 詳細(xì)的介紹會(huì)在后面的文章中介紹。

2、獨(dú)立部署

kube-proxy 默認(rèn)與 kubelet 同時(shí)部署在一臺(tái) node 上,可以將 kube-proxy 組件獨(dú)立部署在非 k8s node 上,避免在所有 node 上都產(chǎn)生大量 iptables 規(guī)則。

七、鏡像優(yōu)化

一個(gè)容器的鏡像平均 2G 左右,若頻繁的拉取鏡像可能會(huì)將宿主機(jī)的帶寬打滿,甚至影響鏡像倉(cāng)庫(kù)的使用,

  • 1、限制鏡像的大小

  • 2、鏡像緩存

  • 3、使用 P2P 進(jìn)行鏡像分發(fā),比如:dragonfly

  • 4、基礎(chǔ)鏡像預(yù)加載:一般鏡像會(huì)分為三層,第一層基礎(chǔ)鏡像即 os,第二層環(huán)境鏡像即帶有 nginx、tomcat 等服務(wù)的鏡像,第三層業(yè)務(wù)鏡像也就是帶有業(yè)務(wù)代碼的鏡像。基礎(chǔ)鏡像一般不會(huì)頻繁更新,可在所有宿主機(jī)上預(yù)先加載,環(huán)境鏡像可以定時(shí)進(jìn)行加載,業(yè)務(wù)鏡像則實(shí)時(shí)拉取。

八、客戶端優(yōu)化

在大規(guī)模場(chǎng)景下,集群中所有的 daemonset、webhook 以及 operator 等組件非常多,每個(gè)客戶端都要從 apiserver 中獲取資源,此時(shí)對(duì) apiserver 的壓力非常大,若客戶端使用不當(dāng)很可能導(dǎo)致 apiserver 或者 etcd 崩潰,此時(shí)對(duì)客戶端的行為進(jìn)行限制就非常有必要了。首先應(yīng)確保所有客戶端都使用 ListWatch 機(jī)制而不是只使用 List,并且在使用 ListWatch 機(jī)制時(shí)盡量不要覆蓋 ListOption,即直接從 apiserver 的緩存中獲取資源列表,避免請(qǐng)求直接命中 etcd。

k8s.io/apimachinery/pkg/apis/meta/v1/types.go

...

// ListOptions is the query options to a standard REST list call.
type ListOptions struct {
        ...

    // When specified with a watch call, shows changes that occur after that particular version of a resource.
    // Defaults to changes from the beginning of history.
    // When specified for list:
    // - if unset, then the result is returned from remote storage based on quorum-read flag;
    // - if it's 0, then we simply return what we currently have in cache, no guarantee;
    // - if set to non zero, then the result is at least as fresh as given rv.
    // +optional
    ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,4,opt,name=resourceVersion"`

    ...
}

...

九、資源使用率的提升

在大規(guī)模場(chǎng)景中,提高資源使用率是非常有必要的,否則會(huì)存在嚴(yán)重的資源浪費(fèi),資源使用率高即宿主的 cpu 利用率,但是不可能一個(gè)宿主上所有容器的資源利用率都非常高,容器和物理機(jī)不同,一個(gè)服務(wù)下容器的平均 cpu idle 一般到 50% 時(shí)此服務(wù)就該擴(kuò)容了,但物理機(jī) idle 在 50% 時(shí)還是處于穩(wěn)定運(yùn)行狀態(tài)的,而服務(wù)一般都會(huì)有潮汐現(xiàn)象,所以需要一些其他方法來(lái)提高整機(jī)的 cpu 使用率。

  • 1、pod 分配資源壓縮:為 pod 設(shè)置 request 和 limit 值,對(duì)應(yīng)的 pod qos 為 burstable。

  • 2、宿主資源超賣:比如將一個(gè)實(shí)際只有 48 核的宿主上報(bào)資源給 apiserver 時(shí)上報(bào)為 60 核,以此來(lái)對(duì)宿主進(jìn)行資源超賣。第一種方法就是給宿主機(jī)打上特定的資源超賣標(biāo)簽,然后直接修改 kubelet 的代碼上報(bào)時(shí)應(yīng)用指定的超賣系數(shù),或者使用 admission webhook 在 patch node status 時(shí)修改其資源中對(duì)應(yīng)的值,這種方法需要對(duì) kubelet 注冊(cè) apiserver 的原理有深入了解。

  • 3、在離線業(yè)務(wù)混部:大部分公司都會(huì)做在離線混部,在離線混部需要解決的問(wèn)題有:

    • 1、在線能及時(shí)搶占離線資源(目前內(nèi)核不支持)
    • 2、讓離線高效的利用空閑 CPU

? 騰訊云對(duì)在離線有深入研究,整機(jī)資源使用率已達(dá) 90%,可以借鑒其一些設(shè)計(jì)理念,參考:騰訊成本優(yōu)化黑科技:整機(jī)CPU利用率最高提升至90%

十、動(dòng)態(tài)調(diào)整 Pod 資源限制

參考:超大規(guī)模商用 K8s 場(chǎng)景下,阿里巴巴如何動(dòng)態(tài)解決容器資源的按需分配問(wèn)題?

在大規(guī)模集群場(chǎng)景,服務(wù)可能會(huì)因高峰期資源不足導(dǎo)致響應(yīng)慢等問(wèn)題,對(duì)于某些應(yīng)用時(shí)間內(nèi) HPA 或者 VPA 都不是件容易的事情。先說(shuō) HPA,我們或許可以秒級(jí)拉起了 Pod,創(chuàng)建新的容器,然而拉起的容器是否真的可用呢。從創(chuàng)建到可用,中間要經(jīng)過(guò)調(diào)度、分配ip、拉取鏡像、同步白名單等,可能需要比較久的時(shí)間,對(duì)于大促和搶購(gòu)秒殺 這種訪問(wèn)量“洪峰”可能僅維持幾分鐘或者十幾分鐘的實(shí)際場(chǎng)景,如果我們等到 HPA 的副本全部可用,可能市場(chǎng)活動(dòng)早已經(jīng)結(jié)束了。至于社區(qū)目前的 VPA 場(chǎng)景,刪掉舊 Pod,創(chuàng)建新 Pod,這樣的邏輯更難接受。

目前阿里的 policy engine 支持動(dòng)態(tài)調(diào)整 pod 的資源限制,底層使用類似 cadvisor 的一個(gè)數(shù)據(jù)采集組件,直接采集 cgroup 數(shù)據(jù),然后對(duì)容器做畫像,當(dāng)容器資源不足時(shí)會(huì)瞬時(shí)快速修改容器 cgroup 文件目錄下的的參數(shù),如果是 cpu 型的,直接調(diào)整低優(yōu)先級(jí)容器的 cgroup 下 cpu quota 的值,首先抑制低優(yōu)先級(jí)的容器對(duì)于 cpu 的爭(zhēng)搶,然后再適當(dāng)上調(diào)高優(yōu)先級(jí)容器的相關(guān)資源值。

十一、其他優(yōu)化方法

1、禁用 kubectl--all 操作,避免誤操作導(dǎo)致某一資源全部被刪除

十二、總結(jié)

以上是筆者對(duì) kubernetes 性能優(yōu)化方法的一些思考及總結(jié),部分方法參考社區(qū)的文檔。kubernetes 擁有龐大而快速發(fā)展的生態(tài)系統(tǒng),以上提及的優(yōu)化方法僅是冰山一角,性能優(yōu)化無(wú)終點(diǎn),在生產(chǎn)環(huán)境中能發(fā)揮價(jià)值才是最有用的。

參考:
eBay應(yīng)用程序集群管理器TESS.IO在大規(guī)模集群下的性能優(yōu)化

Meet a Kubernetes Descheduler

網(wǎng)易云基于Kubernetes的深度定制化實(shí)踐

開(kāi)放下載《阿里巴巴云原生實(shí)踐 15 講》揭秘九年云原生規(guī)?;涞?/a>

使用 K8S 幾年后,這些技術(shù)專家有話要說(shuō)

Kubernetes API 分析

Kubernetes 調(diào)度優(yōu)化--重平衡策略方案整理

探秘金融級(jí)云原生發(fā)布工作負(fù)載 CafeDeployment

騰訊成本優(yōu)化黑科技:整機(jī)CPU利用率最高提升至90%

華為云在 K8S 大規(guī)模場(chǎng)景下的 Service 性能優(yōu)化實(shí)踐

優(yōu)化Kubernetes集群負(fù)載的技術(shù)方案探討

記一次kubernetes集群異常: kubelet連接apiserver超時(shí)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容