优质博文:IT-BLOG-CN
cicode xxx k8s docker events oom occured
告警原因
特别需要注意的是:docker events oom
的处理方法不同于java.lang.OutOfMemoryError
。
当我们在PAAS/Captain
上申请容器实例时,会设置内存Limit
。比如容器Flavor
为2C4G
,系统为该容器设置的内存limit
就是4G
。当容器内部进程使用的内存达到4G
时,宿主机Kernel
的OOM-Killer
会开始工作,杀死当前容器进程。由于K8S
具备自愈能力,被杀的进程会被重新拉起,因此用户侧看到的现象可能和应用重启的行为类似。下面来详细说下docker evets oom
产生的原因。
以Java
应用为例:内存占用 = Java Heap(Xmx)
+ Native Memory
Native Memory
中主要包括:
【1】JNI
调用,也就是Native Stack
,例如jdbc
,gzip
;
【2】线程占用内存Xss
;
【3】DirectByteBuffer
常说的Java
堆外内存;
【4】管理java heap
的状态数据(用于GC
);
触发容器OOM
的原因就可以从上面几个维度去分析,配合对应的监控指标(JVM Heap
,线程数,DirectByteBuffer
),以4G
容器实例为例,正常情况Xmx3G
,Native 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
触发的时间。
第二步查看Mem Rss Used
图表
以上图为例,容器Mem limit
是32G
,当容器内部进程使用达到32G
时、触发了宿主机的OOM-Killer
,java
进程被杀;当java
进程重新自动启动时,内存回落到27G
左右。
再拉长7天的时间看到,从4月15日开始,java
进程内存使用有持续的增长趋势,在4月19日20:50左右终于达到了32G
的limit
、触发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 \
堆外内存预留为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
,所以Xmx
为0.8×2G =1638M
-Xms = 1638M
-Xmn = 983M
如果觉得默认JVM
参数不满足具体需求的话可以通过extraenv
来覆盖默认参数,详细参考如下:针对tomcat
的应用,PD可以通过这两个文件进行定制化扩展:
名称 | 说明 |
---|---|
extraenv.sh | tomcat启动脚本的扩展,即启动时执行,可以指定jvm启动参数 |
server.xml | 1、即tomcat 的server.xml 2、pd 可以使用自己的server.xml , 配置Connector ,指定线程池大小等;3、不能指定端口,端口使用占位符,由发布系统确定端口; |
过往处理案例
比较常见的案例如下:
【1】在vm
迁移docker
的过程中,或者更改docker flavor
的过程中没有及时调整自己设置的extraenv
。例如,原先的vm
是4C12G
的配置,迁移到docker
后配置为2C6G
但是extraenv
中-Xmx -Xms -Xmn
配置没更改过来。
【2】代码的bug
导致堆外内存打爆。例如,堆内内存使用不多,但是由于代码bug
导致线程池缓慢增加,内存不够,也无法GC
回收,最后触发OOM
。