# cicode xxx k8s docker events oom occured告警原因

特别需要注意的是:docker events oom的处理方法不同于java.lang.OutOfMemoryError

当我们在PAAS/Captain上申请容器实例时,会设置内存Limit。比如容器Flavor2C4G,系统为该容器设置的内存limit就是4G。当容器内部进程使用的内存达到4G时,宿主机KernelOOM-Killer会开始工作,杀死当前容器进程。由于K8S具备自愈能力,被杀的进程会被重新拉起,因此用户侧看到的现象可能和应用重启的行为类似。下面来详细说下docker evets oom产生的原因。

Java应用为例:内存占用 = Java Heap(Xmx) + Native Memory

Native Memory中主要包括:
【1】JNI调用,也就是Native Stack,例如jdbcgzip
【2】线程占用内存Xss
【3】DirectByteBuffer常说的Java堆外内存;
【4】管理java heap的状态数据(用于GC);

触发容器OOM的原因就可以从上面几个维度去分析,配合对应的监控指标(JVM Heap,线程数,DirectByteBuffer),以4G容器实例为例,正常情况Xmx3GNative Memory正常需要300M
【1】Xmx设置错误,heap大于3.7G时,整体占用超过4G,处理的方法是设置合理-Xmx -Xms -Xmn
【2】线程数持续增加,线程占用内存超过1G,整体超过4G,处理的方式是检查代码;
【3】DirectByteBuffer超过1G,整体占用超过4G,建议查看堆外预留内存是否设置合理,如果不合理可以通过调整JVM配置来解决;
【4】Native Stack内存占用过大超过1G,例如gzip流未close,整体超过4G,处理的方式是检查代码;

# 收到docker events oom告警后

第一步需要查看Hickwall上的容器实例监控页面,确认本次docker oom触发的时间。

Docker

第二步查看Mem Rss Used图表

Docker

以上图为例,容器Mem limit32G,当容器内部进程使用达到32G时、触发了宿主机的OOM-Killer,java进程被杀;当java进程重新自动启动时,内存回落到27G左右。

Docker

再拉长7天的时间看到,从4月15日开始,java进程内存使用有持续的增长趋势,在4月19日20:50左右终于达到了32Glimit、触发OOM事件。

第三步:结合上面描述的原因,分析导致OOM产生的条件。
【1】容器JVM参数设置说明:容器镜像里会根据该容器的flavor大小自动提供默认的JVM参数如下

-Xmx${JVM_AGG_MAX_MEM}m \
-Xms${JVM_AGG_MIN_MEM}m \
-Xmn${JVM_NEW_SIZE}m \
-XX:MetaspaceSize=128m \
-XX:MaxMetaspaceSize=256m \
-XX:SoftRefLRUPolicyMSPerMB=0 \
-XX:MaxGCPauseMillis=200 \
-XX:+UseG1GC \
-XX:-OmitStackTraceInFastThrow \
1
2
3
4
5
6
7
8
9

堆外内存预留为RESERVED_MEM默认分配容器内存的20%并且最大是2g,即:RESERVED_MEM = maxmem * 0.2 <= 2g
Xmx最大堆大小:xmx = maxmem - RESERVED_MEM
Xms初始堆大小:xms = xmx
Xmn年轻代大小:xmn = Xmx * 0.6

举个例子:一个2C2G的容器,内存为2G,所以Xmx0.8×2G =1638M

-Xms = 1638M
-Xmn = 983M 
1
2

如果觉得默认JVM参数不满足具体需求的话可以通过extraenv来覆盖默认参数,详细参考如下:针对tomcat的应用,PD可以通过这两个文件进行定制化扩展:

名称 说明
extraenv.sh tomcat启动脚本的扩展,即启动时执行,可以指定jvm启动参数
server.xml 1、即tomcatserver.xml
2、pd可以使用自己的server.xml, 配置Connector,指定线程池大小等;
3、不能指定端口,端口使用占位符,由发布系统确定端口;

# 过往处理案例

比较常见的案例如下:
【1】在vm迁移docker的过程中,或者更改docker flavor的过程中没有及时调整自己设置的extraenv。例如,原先的vm4C12G的配置,迁移到docker后配置为2C6G但是extraenv-Xmx -Xms -Xmn配置没更改过来。
【2】代码的bug导致堆外内存打爆。例如,堆内内存使用不多,但是由于代码bug导致线程池缓慢增加,内存不够,也无法GC回收,最后触发OOM

#

(adsbygoogle = window.adsbygoogle || []).push({});