记录一次K8s pod被杀的排查过程
問題描述
今天下午運維反饋說我們這一個pod一天重啟了8次,需要排查下原因。一看Kiban日志,jvm沒有拋出過任何錯誤,服務就直接重啟了。顯然是進程被直接殺了,初步判斷是pod達到內存上限被K8s oomkill了。
因為我們xmx和xsx設置的都是3G,而pod的內存上限設置的是6G,所以出現這種情況還挺詭異的。
排查過程
初步定位
先找運維拉了一下pod的描述,關鍵信息在這里
Containers:
container-prod--:
Container ID: --
Image: --
Image ID: docker-pullable://--
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Fri, 05 Jan 2024 11:40:01 +0800
Last State: Terminated
Reason: Error
Exit Code: 137
Started: Fri, 05 Jan 2024 11:27:38 +0800
Finished: Fri, 05 Jan 2024 11:39:58 +0800
Ready: True
Restart Count: 8
Limits:
cpu: 8
memory: 6Gi
Requests:
cpu: 100m
memory: 512Mi
- 可以看到Last State:Terminated,Exit Code: 137。這個錯誤碼表示的是pod進程被SIGKILL給殺掉了。一般情況下是因為pod達到內存上限被k8s殺了。
因此得出結論是生產環境暫時先擴大下pod的內存限制,讓服務穩住。然后再排查為啥pod里會有這么多的堆外內存占用。
進一步分析
但是運維反饋說無法再擴大pod的內存限制,因為宿主機的內存已經占到了99%了。
然后結合pod的內存監控,發現pod被殺前的內存占用只到4G左右,沒有達到上限的6G,pod就被kill掉了。
于是問題就來了,為啥pod沒有達到內存上限就被kill了呢。
帶著疑問,我開始在google里尋找答案,也發現了一些端倪:
- 如果是pod內存達到上限被kill,pod的描述里會寫Exit Code: 137,但是Reason不是Error,而是OOMKilled
- 宿主機內存已經吃滿,會觸發k8s的保護機制,開始evict一些pod來釋放資源
- 但是為什么整個集群里,只有這個pod被反復evict,其他服務沒有影響?
謎題解開
最終還是google給出了答案:
Why my pod gets OOMKill (exit code 137) without reaching threshold of requested memory
鏈接里的作者遇到了和我一樣的情況,pod還沒吃到內存上限就被殺了,而且也是:
Last State: Terminated
Reason: Error
Exit Code: 137
作者最終定位的原因是因為k8s的QoS機制,在宿主機資源耗盡的時候,會按照QoS機制的優先級,去殺掉pod來釋放資源。
什么是k8s的QoS?
QoS,指的是Quality of Service,也就是k8s用來標記各個pod對于資源使用情況的質量,QoS會直接影響當節點資源耗盡的時候k8s對pod進行evict的決策。官方的描述在這里.
k8s會以pod的描述文件里的資源限制,對pod進行分級:
| QoS | 條件 |
|---|---|
| Guaranteed | 1. pod里所有的容器都必須設置cpu和內存的request和limit,2. pod里所有容器設置的cpu和內存的request和容器設置的limit必須相等(容器自身相等,不同容器可以不等) |
| Burstable | 1. pod并不滿足Guaranteed的條件,2. 至少有一個容器設置了cpu或者內存的request或者limit |
| BestEffort | pod里的所有容器,都沒有設置任何資源的request和limit |
當節點資源耗盡的時候,k8s會按照BestEffort->Burstable->Guaranteed這樣的優先級去選擇殺死pod去釋放資源。
從上面運維給我們的pod描述可以看到,這個pod的資源限制是這樣的:
Limits:
cpu: 8
memory: 6Gi
Requests:
cpu: 100m
memory: 512Mi
顯然符合的是Burstable的標準,所以宿主機內存耗盡的情況下,如果其他服務都是Guaranteed,那自然會一直殺死這個pod來釋放資源,哪怕pod本身并沒有達到6G的內存上限。
QoS相同的情況下,按照什么優先級去Evict?
但是和運維溝通了一下,我們集群內所有pod的配置,limit和request都是不一樣的,也就是說,大家都是Burstable。所以為什么其他pod沒有被evict,只有這個pod被反復evict呢?
QoS相同的情況,肯定還是會有evict的優先級的,只是需要我們再去尋找下官方文檔。
關于Node資源耗盡時候的Evict機制,官方文檔有很詳細的描述。
其中最關鍵的一段是這個:
If the kubelet can't reclaim memory before a node experiences OOM, the
oom_killercalculates anoom_scorebased on the percentage of memory it's using on the node, and then adds theoom_score_adjto get an effectiveoom_scorefor each container. It then kills the container with the highest score.This means that containers in low QoS pods that consume a large amount of memory relative to their scheduling requests are killed first.
簡單來說就是pod evict的標準來自oom_score,每個pod都會被計算出來一個oom_score,而oom_score的計算方式是:pod使用的內存占總內存的比例加上pod的oom_score_adj值。
oom_score_adj的值是k8s基于QoS計算出來的一個偏移值,計算方法:
| QoS | oom_score_adj |
|---|---|
| Guaranteed | -997 |
| BestEffort | 1000 |
| Burstable | min(max(2, 1000 - (1000 × memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
從這個表格可以看出:
- 首先是BestEffort->Burstable->Guaranteed這樣的一個整體的優先級
- 然后都是Burstable的時候,pod實際占用內存/pod的request內存比例最高的,會被優先Evict
總結
至此已經可以基本上定位出Pod被反復重啟的原因了:
- k8s節點宿主機內存占用滿了,觸發了Node-pressure Eviction
- 按照Node-pressure Eviction的優先級,k8s選擇了oom_score最高的pod去evict
- 由于所有pod都是Burstable,并且設置的request memery都是一樣的512M,因此內存占用最多的pod計算出來的oom_score就是最高的
- 所有pod中,這個服務的內存占用一直都是最高的,所以每次計算出來,最后都是殺死這個pod
那么如何解決呢?
- 宿主機內存擴容,不然殺死pod這樣的事情無法避免,無非就是殺哪個的問題
- 對于關鍵服務的pod,要把request和limit設置為完全一致,讓pod的QoS置為Guaranteed,盡可能降低pod被殺的幾率
總結
以上是生活随笔為你收集整理的记录一次K8s pod被杀的排查过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wasm+pygbag让你在网页上也能运
- 下一篇: MinIO FTP 断点续传