應用運行在k8s平台上,有時候會發現POD自動重啟造成業務影響,通過kubectl describe pod可以看到POD重啟的原因,如果是OOM killed,則是因為應用使用內存超過了limit,被OOM killed了。
其實,應用被OOM killed應該分為兩種情況:
1. POD OOM killed;
2. 宿主機內存不足,跑在宿主機上的進程被OOM killed;
這篇文章只討論第一種情況。
其實我們在定義POD的時候,可以通過resource requests及limits值,定義POD的資源需求。
根據requests和limits值,POD的QoS分為三種類型:
Guaranteed - 如果POD中所有容器的所有resource的limit等於request且不為0,則這個POD的QoS class就是Guaranteed;
Best-Effort - 如果POD中所有容器的所有resource的request和limit都沒有賦值,則這個POD的QoS class就是Best-Effort;
Burstable - 以上兩種情況之外就是Burstable了,通常也是畢竟普遍的配置;
這三種QoS分別會有什么待遇呢?因為cpu不是一種hard limit,cpu到達上限之后會被限制,但不會引起進程被殺;這里我們主要看Memory,可以簡單這樣理解:
Guaranteed - 優先級最高,它能夠確保不達到容器設置的Limit一定不會被殺。當然,如果連宿主機都OOM的話,宿主機也是會根據評分選擇性殺進程;
Best-Effort - 優先級最低,如果系統內存耗盡,該類型的POD中的進程最先被殺;
Burstable - 在系統存在內存瓶頸時,一旦內存超過他們的request值並且沒有Best-Effort類型的容器存在,這些容器就先被殺掉,當然,內存到達limit時也會被殺;
不管是哪種QoS的POD,如果內存使用量持續上漲,都是有可能被OOM killed的。
對於跑在POD里面的應用來說,就是應用重啟了,因為POD被殺之后k8s會自動重新拉起一個新的。
如果應用被莫名重啟並且應用無任何報錯,可以到監控頁面查看kube_pod_memory_working_set,確認POD內存是否已超過limit值。
但有的用戶有時會有疑問,為什么ps看到進程使用的rss內存跟POD監控看到的內存使用量不一致呢?
我們先看一下POD的內存占用包括了什么?
POD的內存控制其實是利用了Linux內核的cgroup實現的,POD的內存其實也就是cgroup的內存。這個cgroup下的進程通常包括:
1. k8s基礎容器pause進程,但這個很小,通常只占幾百kb內存;
2. 如果應用是通過shell腳本啟動的,都會有一個shell進程,但這個也不會占太多內存;
3. 應用進程(比如java, python等),主要還是這個進程占用的內存比較大;
另外有一點需要注意的是,POD是否OOM是以cgroup的memory.usage_in_bytes為判斷依據的,它跟ps進程看到的rss有以下差別:
memory.usage_in_bytes = total_cache + total_rss
以上數據可以在宿主機/sys/fs/cgroup/memory/kubepods/[burstable] | [besteffort]/pod<pod id>/目錄下的memory.usage_in_bytes和memory.stat中查到。
可以簡單理解為POD內存占用包括進程使用的rss和cache,如果進程使用的cache太大(如打開了大文件,比如日志)也會引起OOM killed。
假如應用有輸出日志並且日志文件是按時間進行切割的,那么在POD的內存監控圖中,會看到當日志文件切割時,POD的內存占用會有一個很陡峭的下降。