kubelet 配置資源預(yù)留的姿勢(shì)
Kubernetes 的節(jié)點(diǎn)可以按照節(jié)點(diǎn)的資源容量進(jìn)行調(diào)度,默認(rèn)情況下 Pod 能夠使用節(jié)點(diǎn)全部可用容量。這樣就會(huì)造成一個(gè)問(wèn)題,因?yàn)楣?jié)點(diǎn)自己通常運(yùn)行了不少驅(qū)動(dòng) OS 和 Kubernetes 的系統(tǒng)守護(hù)進(jìn)程。除非為這些系統(tǒng)守護(hù)進(jìn)程留出資源,否則它們將與 Pod 爭(zhēng)奪資源并導(dǎo)致節(jié)點(diǎn)資源短缺問(wèn)題。
當(dāng)我們?cè)诰€上使用 Kubernetes 集群的時(shí)候,如果沒(méi)有對(duì)節(jié)點(diǎn)配置正確的資源預(yù)留,我們可以考慮一個(gè)場(chǎng)景,由于某個(gè)應(yīng)用無(wú)限制的使用節(jié)點(diǎn)的 CPU 資源,導(dǎo)致節(jié)點(diǎn)上 CPU 使用持續(xù)100%運(yùn)行,而且壓榨到了 kubelet 組件的 CPU 使用,這樣就會(huì)導(dǎo)致 kubelet 和 apiserver 的心跳出問(wèn)題,節(jié)點(diǎn)就會(huì)出現(xiàn) Not Ready 狀況了。默認(rèn)情況下節(jié)點(diǎn) Not Ready 過(guò)后,5分鐘后會(huì)驅(qū)逐應(yīng)用到其他節(jié)點(diǎn),當(dāng)這個(gè)應(yīng)用跑到其他節(jié)點(diǎn)上的時(shí)候同樣100%的使用 CPU,是不是也會(huì)把這個(gè)節(jié)點(diǎn)搞掛掉,同樣的情況繼續(xù)下去,也就導(dǎo)致了整個(gè)集群的雪崩,集群內(nèi)的節(jié)點(diǎn)一個(gè)一個(gè)的 Not Ready 了,后果是非常嚴(yán)重的,或多或少的人遇到過(guò) Kubernetes 集群雪崩的情況,這個(gè)問(wèn)題也是面試的時(shí)候鏡像詢問(wèn)的問(wèn)題。
要解決這個(gè)問(wèn)題就需要為 Kubernetes 集群配置資源預(yù)留,kubelet 暴露了一個(gè)名為 Node Allocatable 的特性,有助于為系統(tǒng)守護(hù)進(jìn)程預(yù)留計(jì)算資源,Kubernetes 也是推薦集群管理員按照每個(gè)節(jié)點(diǎn)上的工作負(fù)載來(lái)配置 Node Allocatable。
本文的操作環(huán)境為 Kubernetes v1.22.1 版本,使用 Containerd 的容器運(yùn)行時(shí),Containerd 和 Kubelet 采用的 cgroup 驅(qū)動(dòng)為 systemd。
Node Allocatable
Kubernetes 節(jié)點(diǎn)上的 Allocatable 被定義為 Pod 可用計(jì)算資源量,調(diào)度器不會(huì)超額申請(qǐng) Allocatable,目前支持 CPU, memory 和 ephemeral-storage 這幾個(gè)參數(shù)。
我們可以通過(guò) kubectl describe node 命令查看節(jié)點(diǎn)可分配資源的數(shù)據(jù):
- ➜ ~ kubectl describe node node2
- ......
- Capacity:
- cpu: 4
- ephemeral-storage: 36678148Ki
- hugepages-1Gi: 0
- hugepages-2Mi: 0
- memory: 7990056Ki
- pods: 110
- Allocatable:
- cpu: 4
- ephemeral-storage: 33802581141
- hugepages-1Gi: 0
- hugepages-2Mi: 0
- memory: 7887656Ki
- pods: 110
- ......
可以看到其中有 Capacity 與 Allocatable 兩項(xiàng)內(nèi)容,其中的 Allocatable 就是節(jié)點(diǎn)可被分配的資源,我們這里沒(méi)有配置資源預(yù)留,所以默認(rèn)情況下 Capacity 與 Allocatable 的值基本上是一致的。下圖顯示了可分配資源和資源預(yù)留之間的關(guān)系:
Node Allocatable
- Kubelet Node Allocatable 用來(lái)為 Kube 組件和 System 進(jìn)程預(yù)留資源,從而保證當(dāng)節(jié)點(diǎn)出現(xiàn)滿負(fù)荷時(shí)也能保證 Kube 和 System 進(jìn)程有足夠的資源。
- 目前支持 cpu, memory, ephemeral-storage 三種資源預(yù)留。
- Node Capacity 是節(jié)點(diǎn)的所有硬件資源,kube-reserved 是給 kube 組件預(yù)留的資源,system-reserved 是給系統(tǒng)進(jìn)程預(yù)留的資源,eviction-threshold 是 kubelet 驅(qū)逐的閾值設(shè)定,allocatable 才是真正調(diào)度器調(diào)度 Pod 時(shí)的參考值(保證節(jié)點(diǎn)上所有 Pods 的 request 資源不超過(guò) Allocatable)。
節(jié)點(diǎn)可分配資源的計(jì)算方式為:
- Node Allocatable Resource = Node Capacity - Kube-reserved - system-reserved - eviction-threshold
調(diào)度到某個(gè)節(jié)點(diǎn)上的 Pod 的 requests 總和不能超過(guò)該節(jié)點(diǎn)的 allocatable。
配置資源預(yù)留
比如我們現(xiàn)在需要為系統(tǒng)預(yù)留一定的資源,我們可以使用如下的幾個(gè) kubelet 參數(shù)來(lái)進(jìn)行配置:
- --enforce-node-allocatable=pods
- --kube-reserved=memory=...
- --system-reserved=memory=...
- --eviction-hard=...
這里我們暫時(shí)不設(shè)置對(duì)應(yīng)的 cgroup,比如我們這里先只對(duì) node2 節(jié)點(diǎn)添加資源預(yù)留,我們可以直接修改 /var/lib/kubelet/config.yaml 文件來(lái)動(dòng)態(tài)配置 kubelet,添加如下所示的資源預(yù)留配置:
- apiVersion: kubelet.config.k8s.io/v1beta1
- ......
- enforceNodeAllocatable:
- - pods
- kubeReserved: # 配置 kube 資源預(yù)留
- cpu: 500m
- memory: 1Gi
- ephemeral-storage: 1Gi
- systemReserved: # 配置系統(tǒng)資源預(yù)留
- memory: 1Gi
- evictionHard: # 配置硬驅(qū)逐閾值
- memory.available: "300Mi"
- nodefs.available: "10%"
修改完成后,重啟 kubelet,啟動(dòng)完成后重新對(duì)比 Capacity 及 Allocatable 的值:
- ➜ ~ kubectl describe node node2
- ......
- Capacity:
- cpu: 4
- ephemeral-storage: 36678148Ki
- hugepages-1Gi: 0
- hugepages-2Mi: 0
- memory: 7990056Ki
- pods: 110
- Allocatable:
- cpu: 3500m
- ephemeral-storage: 32728839317
- hugepages-1Gi: 0
- hugepages-2Mi: 0
- memory: 5585704Ki
- pods: 110
仔細(xì)對(duì)比可以發(fā)現(xiàn)其中的 Allocatable的值恰好是 Capacity 減去上面我們配置的預(yù)留資源的值:
- allocatale = capacity - kube_reserved - system_reserved - eviction_hard
- 5585704Ki = 7990056Ki - 1*1024*1024Ki - 1*1024*1024Ki - 300*1024Ki
再通過(guò)查看 kubepods.slice(systemd 驅(qū)動(dòng)是以 .slice 結(jié)尾)cgroup 中對(duì)節(jié)點(diǎn)上所有 Pod 內(nèi)存的限制,該值決定了 Node 上所有的 Pod 能使用的資源上限:
- ➜ ~ cat /sys/fs/cgroup/memory/kubepods.slice/memory.limit_in_bytes
- 6034333696
得到的 Pod 資源使用上限為:
- 6034333696Bytes = 5892904Ki = Allocatable(5585704Ki) + eviction_hard(300*1024Ki)
也可以通過(guò)計(jì)算驗(yàn)證我們的配置是正確的:
- kubepods.slice/memory.limit_in_bytes = capacity - kube_reserved - system_reserved
Eviction 與 OOM
1、eviction 是指 kubelet 對(duì)該節(jié)點(diǎn)上的 Pod 進(jìn)行驅(qū)逐,OOM 是指 cgroup 對(duì)進(jìn)程進(jìn)行 kill
2、kubelet 對(duì) Pod 進(jìn)行驅(qū)逐時(shí),是根據(jù) --eviction-hard 參數(shù),比如該參數(shù)如果設(shè)置了 memory.available<20%,那么當(dāng)主機(jī)的內(nèi)存使用率達(dá)到80%時(shí),kubelet 便會(huì)對(duì)Pod進(jìn)行驅(qū)逐。但是,--eviction-hard=memory.available<20% 不會(huì)對(duì) /sys/fs/cgroup/memory/kubepods.slice/memory.limit_in_bytes 的值產(chǎn)生影響,因?yàn)? kubepods.slice/memory.limit_in_bytes = capacity - kube-reserved - system-reserved,換句話說(shuō),Pod 的內(nèi)存使用量總和是可以超過(guò)80%的,且不會(huì)被 OOM-kill,只會(huì)被 eviction。
3、kubernetes 對(duì) Pod 的驅(qū)逐機(jī)制如下(其實(shí)就是 QoS 章節(jié)的定義):
- 首先驅(qū)逐沒(méi)有設(shè)置資源限制的 Pod
- 然后驅(qū)逐資源上限和資源下限不一樣的 Pod
- 最后驅(qū)逐資源上限等資源下限的Pod
可分配約束
前面我們?cè)谂渲觅Y源預(yù)留的時(shí)候其中有一個(gè) enforceNodeAllocatable 配置項(xiàng)(--enforce-node-allocatable),該配置項(xiàng)的幫助信息為:
- --enforce-node-allocatable strings A comma separated list of levels of node allocatable enforcement to be enforced by kubelet. Acceptable options are 'none', 'pods', 'system-reserved', and 'kube-reserved'. If the latter two options are specified, '--system-reserved-cgroup' and '--kube-reserved-cgroup' must also be set, respectively. If 'none' is specified, no additional options should be set. See https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/ for more details. (default [pods]) (DEPRECATED: This parameter should be set via the config file specified by the Kubelet's --config flag. See https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/ for more information.)
kubelet 默認(rèn)對(duì) Pod 執(zhí)行 Allocatable 可分配約束,如果所有 Pod 的總用量超過(guò)了 Allocatable,那么驅(qū)逐 Pod 的措施將被執(zhí)行,我們可以可通過(guò)設(shè)置 kubelet --enforce-node-allocatable 標(biāo)志值為 pods 控制這個(gè)措施。
此外我們還可以通過(guò)該標(biāo)志來(lái)同時(shí)指定 kube-reserved 和 system-reserved 值,可以讓 kubelet 強(qiáng)制實(shí)施 kube-reserved 和 system-reserved 約束,不過(guò)需要注意,如果配置了 kube-reserved 或者 system-reserved 約束,那么需要對(duì)應(yīng)設(shè)置 --kube-reserved-cgroup 或者 --system-reserved-cgroup 參數(shù)。
如果設(shè)置了對(duì)應(yīng)的 --system-reserved-cgroup 和 --kube-reserved-cgroup 參數(shù),Pod 能實(shí)際使用的資源上限是不會(huì)改變,但系統(tǒng)進(jìn)程與 kube 進(jìn)程也會(huì)受到資源上限的限制,如果系統(tǒng)進(jìn)程超過(guò)了預(yù)留資源,那么系統(tǒng)進(jìn)程會(huì)被 cgroup 殺掉。但是如果不設(shè)這兩個(gè)參數(shù),那么系統(tǒng)進(jìn)程就可以使用超過(guò)預(yù)留的資源上限。
所以如果要為系統(tǒng)預(yù)留和 kube 預(yù)留配置 cgroup,則需要非常小心,如果執(zhí)行了 kube-reserved 約束,那么 kubelet 就不能出現(xiàn)突發(fā)負(fù)載用光所有可用資源,不然就會(huì)被殺掉。system-reserved 可以用于為諸如 sshd、udev 等系統(tǒng)守護(hù)進(jìn)程爭(zhēng)取資源預(yù)留,但是如果執(zhí)行 system-reserved 約束,那么可能因?yàn)槟承┰驅(qū)е鹿?jié)點(diǎn)上的關(guān)鍵系統(tǒng)服務(wù) CPU 資源短缺或因?yàn)閮?nèi)存不足而被終止,所以如果不是自己非常清楚如何配置,最好別配置 cgroup 約束,如果需要自行配置,可以參考第一期的資源預(yù)留文檔進(jìn)行相關(guān)操作。
因此,我們強(qiáng)烈建議用戶使用 enforce-node-allocatable 默認(rèn)配置的 pods 即可,并為系統(tǒng)和 kube 進(jìn)程預(yù)留出適當(dāng)?shù)馁Y源,以保持整體節(jié)點(diǎn)的可靠性,不需要進(jìn)行 cgroup 約束,除非操作人員對(duì)系統(tǒng)非常了解。