POD为什么会OOM
应用运行在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的内存占用会有一个很陡峭的下降。