本文翻譯自 learnk8s 的 Architecting Kubernetes clusters — choosing the best autoscaling strategy,<u>有增刪部分內(nèi)容</u>。

TL;DR: 在默認(rèn)設(shè)置下,擴(kuò)展 Kubernetes 集群中的 pod 和節(jié)點(diǎn)可能需要幾分鐘時(shí)間。了解如何調(diào)整集群節(jié)點(diǎn)的大小、配置水平和集群自動(dòng)縮放器以及過(guò)度配置集群以加快擴(kuò)展速度。
自動(dòng)擴(kuò)展器
在 Kubernetes 中,常說(shuō)的“自用擴(kuò)展”有:
不同類型的自動(dòng)縮放器,使用的場(chǎng)景不一樣。
HPA
HPA 定期檢查內(nèi)存和 CPU 等指標(biāo),自動(dòng)調(diào)整 Deployment 中的副本數(shù),比如流量變化:


VPA
有些時(shí)候無(wú)法通過(guò)增加 Pod 數(shù)來(lái)擴(kuò)容,比如數(shù)據(jù)庫(kù)。這時(shí)候可以通過(guò) VPA 增加 Pod 的大小,比如調(diào)整 Pod 的 CPU 和內(nèi)存:


CA
當(dāng)集群資源不足時(shí),CA 會(huì)自動(dòng)配置新的計(jì)算資源并添加到集群中:


自動(dòng)縮放 Pod 出錯(cuò)時(shí)
比如一個(gè)應(yīng)用需要 1.5 GB 內(nèi)存和 0.25 個(gè) vCPU。一個(gè) 8GB 和 2 個(gè) vCPU 的節(jié)點(diǎn),可以容納 4 個(gè)這樣的 Pod,完美!

做如下配置:
- HPA:每增加 10 個(gè)并發(fā),增加一個(gè)副本。即 40 個(gè)并發(fā)的時(shí)候,自動(dòng)擴(kuò)展到 4 個(gè)副本。(這里使用自定義指標(biāo),比如來(lái)自 Ingress Controller 的 QPS)
- CA:在資源不足的時(shí)候,增加計(jì)算節(jié)點(diǎn)。
當(dāng)并發(fā)達(dá)到 30 的時(shí)候,系統(tǒng)是下面這樣。完美!HPA 工作正常,CA 沒(méi)工作。

當(dāng)增加到 40 個(gè)并發(fā)的時(shí)候,系統(tǒng)是下面的情況:
- HPA 增加了一個(gè) Pod
- Pod 掛起
- CA 增加了一個(gè)節(jié)點(diǎn)


為什么 Pod 沒(méi)有部署成功?
節(jié)點(diǎn)上的操作系統(tǒng)進(jìn)程和 kubelet 也會(huì)消耗一部分資源,8G 和 2 vCPU 并不是全都可以提供給 Pod 用的。并且還有一個(gè)驅(qū)逐閾值:在節(jié)點(diǎn)系統(tǒng)剩余資源達(dá)到閾值時(shí),會(huì)驅(qū)逐 Pod,避免 OOM 的發(fā)生。

當(dāng)然上面的這些都是可配置的。
那為什么在創(chuàng)建該 Pod 之前,CA 沒(méi)有增加新的節(jié)點(diǎn)呢?
CA 如何工作?
CA 在觸發(fā)自動(dòng)縮放時(shí),不會(huì)查看可用的內(nèi)存或 CPU。
CA 是面向事件工作的,并每 10 秒檢查一次是否存在不可調(diào)度(Pending)的 Pod。
當(dāng)調(diào)度器無(wú)法找到可以容納 Pod 的節(jié)點(diǎn)時(shí),這個(gè) Pod 是不可調(diào)度的。
此時(shí),CA 開(kāi)始創(chuàng)建新節(jié)點(diǎn):CA 掃描集群并檢查是否有不可調(diào)度的 Pod。
當(dāng)集群有多種節(jié)點(diǎn)池,CA 會(huì)通過(guò)選擇下面的一種策略:
-
random:默認(rèn)的擴(kuò)展器,隨機(jī)選擇一種節(jié)點(diǎn)池 -
most-pods:能夠調(diào)度最多 Pod 的節(jié)點(diǎn)池 -
least-waste:選擇擴(kuò)展后,資源空閑最少的節(jié)點(diǎn)池 -
price:選擇成本最低的節(jié)點(diǎn)池 -
priority:選擇用戶分配的具有最高優(yōu)先級(jí)的節(jié)點(diǎn)池
確定類型后,CA 會(huì)調(diào)用相關(guān) API 來(lái)創(chuàng)建資源。(云廠商會(huì)實(shí)現(xiàn) API,比如 AWS 添加 EC2;Azure 添加 Virtual Machine;阿里云增加 ECS;GCP 增加 Compute Engine)
計(jì)算資源就緒后,就會(huì)進(jìn)行節(jié)點(diǎn)的初始化。
注意,這里需要一定的耗時(shí),通常比較慢。
探索 Pod 自動(dòng)縮放的前置時(shí)間
四個(gè)因素:
- HPA 的響應(yīng)耗時(shí)
- CA 的響應(yīng)耗時(shí)
- 節(jié)點(diǎn)的初始化耗時(shí)
- Pod 的創(chuàng)建時(shí)間
默認(rèn)情況下,kubelet 每 10 秒抓取一次 Pod 的 CPU 和內(nèi)存占用情況。
每分鐘,Metrics Server 會(huì)將聚合的指標(biāo)開(kāi)放給 Kubernetes API 的其他組件使用。

- 少于 100 個(gè)節(jié)點(diǎn),且每個(gè)節(jié)點(diǎn)最多 30 個(gè) Pod,時(shí)間不超過(guò) 30s。平均延遲大約 5s。
- 100 到 1000個(gè)節(jié)點(diǎn),不超過(guò) 60s。平均延遲大約 15s。

節(jié)點(diǎn)的配置時(shí)間,取決于云服務(wù)商。通常在 3~5 分鐘。

容器運(yùn)行時(shí)創(chuàng)建 Pod:?jiǎn)?dòng)容器的幾毫秒和下載鏡像的幾秒鐘。如果不做鏡像緩存,幾秒到 1 分鐘不等,取決于層的大小和梳理。

對(duì)于小規(guī)模的集群,最壞的情況是 6 分 30 秒。對(duì)于 100 個(gè)以上節(jié)點(diǎn)規(guī)模的集群,可能高達(dá) 7 分鐘。
HPA delay: 1m30s +
CA delay: 0m30s +
Cloud provider: 4m +
Container runtime: 0m30s +
=========================
Total 6m30s
突發(fā)情況,比如流量激增,你是否愿意等這 7 分鐘?
這 7 分鐘,如何優(yōu)化壓縮?
- HPA 的刷新時(shí)間,默認(rèn) 15 秒,通過(guò)
--horizontal-pod-autoscaler-sync-period標(biāo)志控制。 - Metrics Server 的指標(biāo)抓取時(shí)間,默認(rèn) 60 秒,通過(guò)
metric-resolution控制。 - CA 的掃描間隔,默認(rèn) 10 秒,通過(guò)
scan-interval控制。 - 節(jié)點(diǎn)上緩存鏡像,比如 kube-fledged 等工具。
即使調(diào)小了上述設(shè)置,依然會(huì)受云服務(wù)商的時(shí)間限制。
那么,如何解決?
兩種嘗試:
- 盡量避免被動(dòng)創(chuàng)建新節(jié)點(diǎn)
- 主動(dòng)創(chuàng)建新節(jié)點(diǎn)
為 Kubernetes 選擇最佳規(guī)格的節(jié)點(diǎn)
這會(huì)對(duì)擴(kuò)展策略產(chǎn)生巨大影響。
這樣的場(chǎng)景
應(yīng)用程序需要 1GB 內(nèi)存和 0.1 vCPU;有一個(gè) 4GB 內(nèi)存和 1 個(gè) vCPU 的節(jié)點(diǎn)。
排除操作系統(tǒng)、kubelet 和閾值保留空間后,有 2.5GB 內(nèi)存和 0.7 個(gè) vCPU 可用。

最多只能容納 2 個(gè) Pod,擴(kuò)展副本時(shí)最長(zhǎng)耗時(shí) 7 分鐘(HPA、CA、云服務(wù)商的資源配置耗時(shí))
假如節(jié)點(diǎn)的規(guī)格是 64GB 內(nèi)存和 16 個(gè) vCPU,可用的資源為 58.32GB 和 15.8 個(gè) vCPU。
這個(gè)節(jié)點(diǎn)可以托管 58 個(gè) Pod。只有擴(kuò)容第 59 個(gè)副本時(shí),才需要?jiǎng)?chuàng)建新的節(jié)點(diǎn)。

這樣觸發(fā) CA 的機(jī)會(huì)更少。
選擇大規(guī)格的節(jié)點(diǎn),還有另外一個(gè)好處:資源的利用率會(huì)更高。

節(jié)點(diǎn)上可以容納的 Pod 數(shù)量,決定了效率的峰值。
物極必反!更大的實(shí)例,并不是一個(gè)好的選擇:
- 爆炸半徑(Blast radius):節(jié)點(diǎn)故障時(shí),少節(jié)點(diǎn)的集群和多節(jié)點(diǎn)的集群,前者影響更大。
- 自動(dòng)縮放的成本效益低:增加一個(gè)大容量的節(jié)點(diǎn),其利用率會(huì)比較低(調(diào)度過(guò)去的 Pod 數(shù)少)
即使選擇了正確規(guī)格的節(jié)點(diǎn),配置新的計(jì)算單元時(shí),延遲仍然存在。怎么解決?
能否提前創(chuàng)建節(jié)點(diǎn)?
為集群過(guò)度配置節(jié)點(diǎn)
即為集群增加備用節(jié)點(diǎn),可以:
- 創(chuàng)建一個(gè)節(jié)點(diǎn),并留空 (比如 SchedulingDisabled)
- 一旦空節(jié)點(diǎn)中有了一個(gè) Pod,馬上創(chuàng)建新的空節(jié)點(diǎn)


這種會(huì)產(chǎn)生額外的成本,但是效率會(huì)提升。
CA 并不支持此功能 -- 總是保留一個(gè)空節(jié)點(diǎn)。
但是,可以偽造。創(chuàng)建一個(gè)只占用資源,不使用資源的 Pod 占用整個(gè) Node 節(jié)點(diǎn)。

一旦有了真正的 Pod,驅(qū)逐占位的 Pod。

待后臺(tái)完成新的節(jié)點(diǎn)配置后,將“占位” Pod 再次占用整個(gè)節(jié)點(diǎn)。

這個(gè)“占位”的 Pod 可以通過(guò)永久休眠來(lái)實(shí)現(xiàn)空間的保留。
apiVersion: apps/v1
kind: Deployment
metadata:
name: overprovisioning
spec:
replicas: 1
selector:
matchLabels:
run: overprovisioning
template:
metadata:
labels:
run: overprovisioning
spec:
containers:
- name: pause
image: k8s.gcr.io/pause
resources:
requests:
cpu: '1739m'
memory: '5.9G'
使用優(yōu)先級(jí)和搶占,來(lái)實(shí)現(xiàn)創(chuàng)建真正的 Pod 后驅(qū)逐“占位”的 Pod。
使用 PodPriorityClass 在配置 Pod 優(yōu)先級(jí):
apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
name: overprovisioning
value: -1 #默認(rèn)的是 0,這個(gè)表示比默認(rèn)的低
globalDefault: false
description: 'Priority class used by overprovisioning.'
為“占位” Pod 配置優(yōu)先級(jí):
apiVersion: apps/v1
kind: Deployment
metadata:
name: overprovisioning
spec:
replicas: 1
selector:
matchLabels:
run: overprovisioning
template:
metadata:
labels:
run: overprovisioning
spec:
priorityClassName: overprovisioning #HERE
containers:
- name: reserve-resources
image: k8s.gcr.io/pause
resources:
requests:
cpu: '1739m'
memory: '5.9G'
已經(jīng)做完過(guò)度配置,應(yīng)用程序是否需要優(yōu)化?
為 Pod 選擇正確的內(nèi)存和 CPU 請(qǐng)求
Kubernetes 是根據(jù) Pod 的內(nèi)存和 CPU 請(qǐng)求,為其分配節(jié)點(diǎn)。
如果 Pod 的資源請(qǐng)求配置不正確,可能會(huì)過(guò)晚(或過(guò)早)觸發(fā)自動(dòng)縮放器。
這樣一個(gè)場(chǎng)景:
- 應(yīng)用程序平均負(fù)載下消耗 512MB 內(nèi)存和 0.25 個(gè) vCPU。
- 高峰時(shí),消耗 4GB 內(nèi)存 和 1 個(gè) vCPU。(即資源限制,Limit)

有三種請(qǐng)求的配置選擇:
- 遠(yuǎn)低于平均使用量
- 匹配平均使用量
- 盡量接近限制



第一種的問(wèn)題在于超賣嚴(yán)重,過(guò)度使用節(jié)點(diǎn)。kubelet 負(fù)載高,穩(wěn)定性差。

第三種,會(huì)造成資源的利用率低,浪費(fèi)資源。這種通常被稱為 QoS:Quality of Service class 中的 Guaranteed 級(jí)別,Pod 不會(huì)被終止和驅(qū)逐。

如何在穩(wěn)定性和資源使用率間做權(quán)衡?
這就是 QoS:Quality of Service class 中的 Burstable 級(jí)別,即 Pod 偶爾會(huì)使用更多的內(nèi)存和 CPU。
- 如果節(jié)點(diǎn)中有可用資源,應(yīng)用程序會(huì)在返回基線(baseline)前使用這些資源。
- 如果資源不足,Pod 將競(jìng)爭(zhēng)資源(CPU),kubelet 也有可能嘗試驅(qū)逐 Pod(內(nèi)存)。
在 Guaranteed 和 Burstable 之前如何做選擇?取決于:
- 想盡量減少 Pod 的重新調(diào)度和驅(qū)逐,應(yīng)該是用
Guaranteed。 - 如果想充分利用資源時(shí),使用
Burstable。比如彈性較大的服務(wù),Web 或者 REST 服務(wù)。
如何做出正確的配置?
應(yīng)該分析應(yīng)用程序,并測(cè)算空閑、負(fù)載和峰值時(shí)的內(nèi)存和 CPU 消耗。
甚至可以通過(guò)部署 VPA 來(lái)自動(dòng)調(diào)整。
如何進(jìn)行集群縮容?
每 10 秒,當(dāng)請(qǐng)求(request)利用率低于 50%時(shí),CA 才會(huì)決定刪除節(jié)點(diǎn)。
CA 會(huì)匯總同一個(gè)節(jié)點(diǎn)上的所有 Pod 的 CPU 和內(nèi)存請(qǐng)求。小于節(jié)點(diǎn)容量的一半,就會(huì)考慮對(duì)當(dāng)前節(jié)點(diǎn)進(jìn)行縮減。
需要注意的是,CA 不考慮實(shí)際的 CPU 和內(nèi)存使用或者限制(limit),只看請(qǐng)求(request)。
移除節(jié)點(diǎn)之前,CA 會(huì):
- 檢查 Pod 確??梢哉{(diào)度到其他節(jié)點(diǎn)上。
- 檢查節(jié)點(diǎn),避免節(jié)點(diǎn)被過(guò)早的銷毀,比如兩個(gè)節(jié)點(diǎn)的請(qǐng)求都低于 50%。
檢查都通過(guò)之后,才會(huì)刪除節(jié)點(diǎn)。
為什么不根據(jù)內(nèi)存或 CPU 進(jìn)行自動(dòng)縮放?
基于內(nèi)存和 CPU 的自動(dòng)縮放器,不關(guān)心 pod。
比如配置縮放器在節(jié)點(diǎn)的 CPU 達(dá)到總量的 80%,就自動(dòng)增加節(jié)點(diǎn)。
當(dāng)你創(chuàng)建 3 個(gè)副本的 Deployment,3 個(gè)節(jié)點(diǎn)的 CPU 達(dá)到了 85%。這時(shí)會(huì)創(chuàng)建一個(gè)節(jié)點(diǎn),但你并不需要第 4 個(gè)副本,新的節(jié)點(diǎn)就空閑了。
不建議使用這種類型的自動(dòng)縮放器。
總結(jié)
定義和實(shí)施成功的擴(kuò)展策略,需要掌握以下幾點(diǎn):
- 節(jié)點(diǎn)的可分配資源。
- 微調(diào) Metrics Server、HPA 和 CA 的刷新間隔。
- 設(shè)計(jì)集群和節(jié)點(diǎn)的規(guī)格。
- 緩存容器鏡像到節(jié)點(diǎn)。
- 應(yīng)用程序的基準(zhǔn)測(cè)試和分析。
配合適當(dāng)?shù)谋O(jiān)控工具,可以反復(fù)測(cè)試擴(kuò)展策略并調(diào)整集群的縮放速度和成本。