問題描述
今天下午運維反饋說我們這一個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 1101 +0800 Last State: Terminated Reason: Error Exit Code: 137 Started: Fri, 05 Jan 2024 1138 +0800 Finished: Fri, 05 Jan 2024 1158 +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, theoom_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被殺的幾率。
審核編輯:劉清
-
JVM
+關注
關注
0文章
157瀏覽量
12209 -
QoS機制
+關注
關注
0文章
2瀏覽量
4961
原文標題:記錄一次K8s pod被殺的排查過程
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論