Kubernetes OOM 和 CPU Throttling 問(wèn)題
介紹
使用 Kubernetes 時(shí),內(nèi)存不足(OOM)錯(cuò)誤和 CPU 限制(Throttling)是云應(yīng)用程序中資源處理的主要難題。為什么呢?
云應(yīng)用程序中的 CPU 和內(nèi)存要求變得越來(lái)越重要,因?yàn)樗鼈兣c您的云成本直接相關(guān)。
通過(guò)?limits
?和?requests
,您可以配置 pod 應(yīng)如何分配內(nèi)存和 CPU 資源,以防止資源匱乏并調(diào)整云成本。
如果節(jié)點(diǎn)沒(méi)有足夠的資源,Pod 可能會(huì)因搶占或節(jié)點(diǎn)壓力而被驅(qū)逐。
當(dāng)進(jìn)程運(yùn)行內(nèi)存不足 (OOM) 時(shí),它會(huì)因?yàn)闆](méi)有所需的資源而被 Kill。
如果 CPU 消耗高于實(shí)際?
limits
,進(jìn)程將開(kāi)始受到限制。
OK,如何監(jiān)控 Pod 快要 OOM 了,或者 CPU 快要被限制了呢?
Kubernetes OOM
Pod 中的每個(gè)容器都需要內(nèi)存才能運(yùn)行。
Kubernetes limits 是在 Pod 定義或 Deployment 定義中為每個(gè)容器設(shè)置的。
所有現(xiàn)代 Unix 系統(tǒng)都有一種方法可以殺死進(jìn)程,以此回收內(nèi)存(沒(méi)用空閑內(nèi)存的時(shí)候,只能殺進(jìn)程了)。這個(gè)錯(cuò)誤將被標(biāo)記為 137 錯(cuò)誤碼或 OOMKilled。
退出代碼 137 意味著該進(jìn)程使用的內(nèi)存超過(guò)允許的數(shù)量,必須被 OS 終止。
這是 Linux 中的一項(xiàng)功能,內(nèi)核為系統(tǒng)中運(yùn)行的進(jìn)程設(shè)置 oom_score 值。此外,它還允許設(shè)置一個(gè)名為 oom_score_adj 的值,Kubernetes 使用該值來(lái)實(shí)現(xiàn)服務(wù)質(zhì)量。它還具有 OOM Killer,它將檢查進(jìn)程并終止那些使用超過(guò)過(guò)多內(nèi)存(比如申請(qǐng)了超過(guò) limits 限制的數(shù)量的內(nèi)存)的進(jìn)程。
請(qǐng)注意,在 Kubernetes 中,進(jìn)程可能會(huì)達(dá)到以下任何限制:
在容器上設(shè)置的 Kubernetes 限制。
在 namespace 上設(shè)置的 Kubernetes ResourceQuota。
節(jié)點(diǎn)的實(shí)際內(nèi)存大小。

內(nèi)存過(guò)量分配(overcommitment)
限制(limits)可以高于請(qǐng)求(requests),因此所有限制的總和可以高于節(jié)點(diǎn)容量。這稱為過(guò)量分配,而且很常見(jiàn)。實(shí)際上,如果所有容器使用的內(nèi)存多于 request 的內(nèi)存,則可能會(huì)耗盡節(jié)點(diǎn)中的內(nèi)存。這通常會(huì)導(dǎo)致一些 pod 死亡,以釋放一些內(nèi)存。
監(jiān)控 Kubernetes OOM
在 Prometheus 生態(tài)中,使用 node-exporter 時(shí),有一個(gè)名為 node_vmstat_oom_kill 的指標(biāo)。跟蹤 OOM 終止何時(shí)發(fā)生非常重要,但您可能希望在此類事件發(fā)生之前搶占先機(jī)并了解其情況。
我們更希望的是,檢查進(jìn)程與 Kubernetes limits 的接近程度:
Kubernetes CPU throttling
CPU 限制(throttling)是一種當(dāng)進(jìn)程即將達(dá)到某些資源限制時(shí)減慢速度的行為。與內(nèi)存情況類似,這些限制可能是:
在容器上設(shè)置的 Kubernetes limits。
在命名空間上設(shè)置的 Kubernetes ResourceQuota。
節(jié)點(diǎn)的實(shí)際算力大小。
想想下面的類比。我們有一條高速公路,交通流量如下:
CPU 就好比一條路
車輛代表 Process,每輛車都有不同的尺寸
多個(gè)通道代表有多個(gè) CPU 核心
request 將是一條專用道路,例如自行車道
這里的 throttling 被表示為交通擁堵:最終,所有進(jìn)程都會(huì)運(yùn)行,但一切都會(huì)變慢。
Kubernetes 中的 CPU 處理邏輯
CPU 在 Kubernetes 中通過(guò)?shares
?進(jìn)行處理。每個(gè) CPU 核心被分為 1024 個(gè) shares,然后使用 Linux 內(nèi)核的 cgroups(control groups)功能在運(yùn)行的所有進(jìn)程之間進(jìn)行劃分。

如果 CPU 可以處理當(dāng)前所有進(jìn)程,則無(wú)需執(zhí)行任何操作。如果進(jìn)程使用超過(guò) 100% 的 CPU,shares 機(jī)制就要起作用了。與任何 Linux 內(nèi)核一樣,Kubernetes 使用 CFS(Completely Fair Scheduler)機(jī)制,因此擁有更多份額的進(jìn)程將獲得更多的 CPU 時(shí)間。
與內(nèi)存不同,Kubernetes 不會(huì)因?yàn)橄蘖鞫鴼⑺?Pod。

You can check CPU stats in /sys/fs/cgroup/cpu/cpu.stat
CPU 過(guò)渡分配
正如我們?cè)趌imits 和 requests 文章中看到的,當(dāng)我們想要限制進(jìn)程的資源消耗時(shí),設(shè)置 limits 或 requests 非常重要。盡管如此,請(qǐng)注意不要將總 requests 設(shè)置為大于實(shí)際 CPU 大小,每個(gè)容器都應(yīng)該有保證的 CPU。
監(jiān)控 Kubernetes CPU throttling
您可以檢查進(jìn)程與 Kubernetes limits 的接近程度:
如果我們想要跟蹤集群中發(fā)生的限制量,cadvisor 提供了 container_cpu_cfs_throttled_periods_total 和 container_cpu_cfs_periods_total 兩個(gè)指標(biāo)。通過(guò)這兩個(gè)指標(biāo),您可以輕松計(jì)算所有 CPU 周期內(nèi)的限制百分比。
最佳實(shí)踐
注意 limits 和 requests
Limits 是在節(jié)點(diǎn)中設(shè)置資源最大上限的一種方法,但需要謹(jǐn)慎對(duì)待,因?yàn)槟赡茏罱K會(huì)受到限制或終止進(jìn)程。
準(zhǔn)備好應(yīng)對(duì)驅(qū)逐
通過(guò)設(shè)置非常低的請(qǐng)求,您可能認(rèn)為這將為您的進(jìn)程授予最少的 CPU 或內(nèi)存。但 kubelet 會(huì)首先驅(qū)逐那些使用率高于請(qǐng)求的 Pod,因此就相當(dāng)于您將這些進(jìn)程標(biāo)記為最先被殺死的!
如果您需要保護(hù)特定 Pod 免遭搶占(當(dāng) kube-scheduler 需要分配新 Pod 時(shí)),請(qǐng)為最重要的進(jìn)程分配 Priority Classes。
Throttling 是一個(gè)無(wú)聲的敵人
設(shè)置不切實(shí)際的 limits 或過(guò)度使用,您可能沒(méi)有意識(shí)到您的進(jìn)程正在受到限制并且性能受到影響。主動(dòng)監(jiān)控 CPU 用量,了解確切的容器和命名空間層面的限制,及時(shí)發(fā)現(xiàn)問(wèn)題非常重要。
附
下面這張圖,比較好的解釋了 Kubernetes 中 CPU 和內(nèi)存的限制問(wèn)題。供參考:

本文翻譯自:https://sysdig.com/blog/troubleshoot-kubernetes-oom/