容器中的java程序无法感知docker的内存资源限制
[root@xm-phy-kvm-113-2 ~]# docker run -m 100MB openjdk:8u131 java -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 17.43G
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
我设置了 100MB 的容器内存,但JVM 设置了17.3G 的最大堆?它很可能会导致内核在某个时候杀死JVM程序
[root@xm-phy-kvm-113-2 ~]# docker run -m 100MB openjdk:8u131 java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 44.50M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
java服务无感知docker资源限制导致的问题
在K8s中Deployments配置资源限制和预留的时候,比如设置最大内存为100M。但是宿主机的总内存为80G,这时候Pod启动后会超出内存限制,被Deployments杀掉。但为了维护设置的副本数量又创建新的,如此反复。
原因是默认情况JVM默认最大堆空间为系统总内存的1/4,在容器中没有感知到集群为Pod设置的资源限制,而是按宿主机的内存算的,所以最终超过限制内存。
解决方案
解决思路有两种:
- 按资源限制算好,在 Java 启动参数中设置堆内存大小,保证不超限制
- 让JVM感知到容器资源限制,按这个值来分配内存
Java SE 8u131向后 和 JDK 9 都有参数支持:如下
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
但是如果资源限制默认分配1/4有点浪费,因为一般一个Pod就跑这一个主要进程,所以可以改下这个默认值,如下:
# 2 代表1/2
-XX:MaxRAMFraction=2
这样配置并不代表就是资源限制的1/2,因为除了堆内存还有其他堆外内存需要空间,所以实际值要远小于1/2。网上有人在测试环境设置1,代表100%,但是我试过之后还是超过资源限制了,所以选了2,实际需要根据情况决定。
Java 8u131及以上版本开始支持了Docker的cpu和memory限制。
cpu limit(默认支持)
即如果没有显式指定-XX:ParalllelGCThreads 或者 -XX:CICompilerCount, 那么JVM使用docker的cpu限制。如果docker有指定cpu limit,jvm参数也有指定-XX:ParalllelGCThreads 或者 -XX:CICompilerCount,那么以指定的参数为准。
memory limit
在java8u131+及java9,需要加上-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap才能使得Xmx感知docker的memory limit。
参数/版本 | -XX:+UseCGroupMemoryLimitForHeap | -XX:ActiveProcessorCount | -XX:+UseContainerSupport |
---|---|---|---|
java9 | experimental,默认false | 无 | 无 |
java10 | experimental,默认false | -1 | 无 |
java11 | 移除 | -1 | product,默认true |
参考链接:
链接:
https://segmentfault.com/a/1190000014142950
Java SE support for Docker CPU and memory limits