Java与Docker的结合,虽然更好的解决了application的封装问题。但也存在着不兼容,好比Java并不能自动的发现Docker设置的内存限制,CPU限制。html
这将致使JVM不能稳定服务业务!容器会杀死你JVM进程,而健康检查又将拉起你的JVM进程,进而致使你监控你的pod一天重启次数甚至能达到几百次。java
咱们但愿当Java进程运行在容器中时,java可以自动识别到容器限制,获取到正确的内存和CPU信息,而不用每次都须要在kubernetes的yaml描述文件中显示的配置完容器,还须要配置JVM参数。docker
使用JVM MaxRAM参数或者解锁实验特性的JVM参数,升级JDK到10+,咱们能够解决这个问题(也许吧~.~)。缓存
首先Docker容器本质是是宿主机上的一个进程,它与宿主机共享一个/proc目录,也就是说咱们在容器内看到的/proc/meminfo,/proc/cpuinfo
与直接在宿主机上看到的一致,以下。安全
cat /proc/meminfo MemTotal: 197869260 kB MemFree: 3698100 kB MemAvailable: 62230260 kB
docker run -it --rm alpine cat /proc/meminfo MemTotal: 197869260 kB MemFree: 3677800 kB MemAvailable: 62210088 kB
那么Java是如何获取到Host的内存信息的呢?没错就是经过/proc/meminfo来获取到的。app
默认状况下,JVM的Max Heap Size是系统内存的1/4,假如咱们系统是8G,那么JVM将的默认Heap≈2G。eclipse
Docker经过CGroups完成的是对内存的限制,而/proc目录是已只读形式挂载到容器中的,因为默认状况下Java
压根就看不见CGroups的限制的内存大小,而默认使用/proc/meminfo中的信息做为内存信息进行启动,
这种不兼容状况会致使,若是容器分配的内存小于JVM的内存,JVM进程会被理解杀死。jvm
咱们首先来看一组测试,这里咱们采用一台内存为188G的物理机。测试
#free -g total used free shared buff/cache available Mem: 188 122 1 0 64 6
如下的测试中,咱们将包含openjdk的hotspot虚拟机,IBM的openj9虚拟机。ui
如下测试中,咱们把正确识别到限制的jdk,称之为安全(即不会超出容器限制不会被kill),反之称之为危险。
这一组测试咱们使用最新的openjdk8-12,给容器限制内存为4G,看JDK默认参数下的最大堆为多少?看看咱们默认参数下多少版本的JDK是安全的
docker run -m 4GB --rm openjdk:8-jre-slim java -XshowSettings:vm -version docker run -m 4GB --rm openjdk:9-jre-slim java -XshowSettings:vm -version docker run -m 4GB --rm openjdk:10-jre-slim java -XshowSettings:vm -version docker run -m 4GB --rm openjdk:11-jre-slim java -XshowSettings:vm -version docker run -m 4GB --rm openjdk:12 java -XshowSettings:vm -version
[root@xiaoke-test ~]# docker run -m 4GB --rm openjdk:8-jre-slim java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 26.67G Ergonomics Machine Class: server Using VM: OpenJDK 64-Bit Server VM openjdk version "1.8.0_181" OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-2~deb9u1-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
[root@xiaoke-test ~]# docker run -m 4GB --rm openjdk:8-jre-slim java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 910.50M Ergonomics Machine Class: server Using VM: OpenJDK 64-Bit Server VM openjdk version "1.8.0_181" OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-2~deb9u1-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
[root@xiaoke-test ~]# docker run -m 4GB --rm openjdk:9-jre-slim java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 29.97G Using VM: OpenJDK 64-Bit Server VM openjdk version "9.0.4" OpenJDK Runtime Environment (build 9.0.4+12-Debian-4) OpenJDK 64-Bit Server VM (build 9.0.4+12-Debian-4, mixed mode)
[root@xiaoke-test ~]# docker run -m 4GB --rm openjdk:9-jre-slim java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 1.00G Using VM: OpenJDK 64-Bit Server VM openjdk version "9.0.4" OpenJDK Runtime Environment (build 9.0.4+12-Debian-4) OpenJDK 64-Bit Server VM (build 9.0.4+12-Debian-4, mixed mode)
[root@xiaoke-test ~]# docker run -m 32GB --rm openjdk:10-jre-slim java -XshowSettings:vm -XX:MaxRAMFraction=1 -version VM settings: Max. Heap Size (Estimated): 1.00G Using VM: OpenJDK 64-Bit Server VM openjdk version "10.0.2" 2018-07-17 OpenJDK Runtime Environment (build 10.0.2+13-Debian-2) OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-2, mixed mode)
[root@xiaoke-test ~]# docker run -m 4GB --rm openjdk:11-jre-slim java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 1.00G Using VM: OpenJDK 64-Bit Server VM openjdk version "11.0.1" 2018-10-16 OpenJDK Runtime Environment (build 11.0.1+13-Debian-3) OpenJDK 64-Bit Server VM (build 11.0.1+13-Debian-3, mixed mode, sharing)
[root@xiaoke-test ~]# docker run -m 4GB --rm openjdk:12 java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 1.00G Using VM: OpenJDK 64-Bit Server VM openjdk version "12-ea" 2019-03-19 OpenJDK Runtime Environment (build 12-ea+23) OpenJDK 64-Bit Server VM (build 12-ea+23, mixed mode, sharing)
docker run -m 4GB --rm adoptopenjdk/openjdk8-openj9:alpine-slim java -XshowSettings:vm -version docker run -m 4GB --rm adoptopenjdk/openjdk9-openj9:alpine-slim java -XshowSettings:vm -version docker run -m 4GB --rm adoptopenjdk/openjdk10-openj9:alpine-slim java -XshowSettings:vm -version docker run -m 4GB --rm adoptopenjdk/openjdk11-openj9:alpine-slim java -XshowSettings:vm -version
[root@xiaoke-test ~]# docker run -m 4GB --rm adoptopenjdk/openjdk8-openj9:alpine-slim java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 3.00G Ergonomics Machine Class: server Using VM: Eclipse OpenJ9 VM openjdk version "1.8.0_192" OpenJDK Runtime Environment (build 1.8.0_192-b12_openj9) Eclipse OpenJ9 VM (build openj9-0.11.0, JRE 1.8.0 Linux amd64-64-Bit Compressed References 20181107_95 (JIT enabled, AOT enabled) OpenJ9 - 090ff9dcd OMR - ea548a66 JCL - b5a3affe73 based on jdk8u192-b12)
[root@xiaoke-test ~]# docker run -m 4GB --rm adoptopenjdk/openjdk9-openj9:alpine-slim java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 3.00G Using VM: Eclipse OpenJ9 VM openjdk version "9.0.4-adoptopenjdk" OpenJDK Runtime Environment (build 9.0.4-adoptopenjdk+12) Eclipse OpenJ9 VM (build openj9-0.9.0, JRE 9 Linux amd64-64-Bit Compressed References 20180814_248 (JIT enabled, AOT enabled) OpenJ9 - 24e53631 OMR - fad6bf6e JCL - feec4d2ae based on jdk-9.0.4+12)
[root@xiaoke-test ~]# docker run -m 4GB --rm adoptopenjdk/openjdk10-openj9:alpine-slim java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 3.00G Using VM: Eclipse OpenJ9 VM openjdk version "10.0.2-adoptopenjdk" 2018-07-17 OpenJDK Runtime Environment (build 10.0.2-adoptopenjdk+13) Eclipse OpenJ9 VM (build openj9-0.9.0, JRE 10 Linux amd64-64-Bit Compressed References 20180813_102 (JIT enabled, AOT enabled) OpenJ9 - 24e53631 OMR - fad6bf6e JCL - 7db90eda56 based on jdk-10.0.2+13)
[root@xiaoke-test ~]# docker run -m 4GB --rm adoptopenjdk/openjdk11-openj9:alpine-slim java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 3.00G Using VM: Eclipse OpenJ9 VM openjdk version "11.0.1" 2018-10-16 OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.1+13) Eclipse OpenJ9 VM AdoptOpenJDK (build openj9-0.11.0, JRE 11 Linux amd64-64-Bit Compressed References 20181020_70 (JIT enabled, AOT enabled) OpenJ9 - 090ff9dc OMR - ea548a66 JCL - f62696f378 based on jdk-11.0.1+13)
分析以前咱们先了解这么一个状况:
JavaMemory (MaxRAM) = 元数据+线程+代码缓存+OffHeap+Heap...
通常咱们都只配置Heap即便用-Xmx来指定JVM可以使用的最大堆。而JVM默认会使用它获取到的最大内存的1/4做为堆的缘由也是如此。
OpenJdk8-12,都能保证这个安全性的特色(8和9须要特殊参数,-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap)。
2.IbmOpenJ9全部的版本都能识别到容器限制。
自动识别到容器限制后,OpenJdk把最大堆设置为了大概容器内存的1/4,对内存的浪费不可谓不大。
固然能够配合另外一个JVM参数来配置最大堆。-XX:MaxRAMFraction=int。下面是我整理的一个常见内存设置的表格,
从中咱们能够看到彷佛JVM默认的最大堆的取值为MaxRAMFraction=4,随着内存的增长,堆的闲置空间愈来愈大,在16G容器内存时,java堆只有不到4G。
MaxRAMFraction取值 | 堆占比 | 容器内存=1G | 容器内存=2G | 容器内存=4G | 容器内存=8G | 容器内存=16G |
---|---|---|---|---|---|---|
1 | ≈90% | 910.50M | 1.78G | 3.56G | 7.11G | 14.22G |
2 | ≈50% | 455.50M | 910.50M | 1.78G | 3.56G | 7.11G |
3 | ≈33% | 304.00M | 608.00M | 1.19G | 2.37G | 4.74G |
4 | ≈25% | 228.00M | 455.50M | 910.50M | 1.78G | 3.56G |
关于OpenJ9的的详细介绍你能够从这里了解更多。
对于内存利用率OpenJ9的策略是优于OpenJdk的。如下是OpenJ9的策略表格
容器内存<size> | 最大Java堆大小 |
---|---|
小于1 GB | 50%<size> |
1 GB – 2 GB | <size> – 512 MB |
大于2 GB | 大于2 GB |
1.若是你想要的是jvm进程在容器中安全稳定的运行,不被容器kiil,而且你的JDK版本小于10(大于等于JDK10的版本不须要设置,参考前面的测试)
你须要额外设置JVM参数-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap,便可保证你的Java进程不会由于内存问题被容器Kill。
固然这个方式使用起来简单,可靠,缺点也很明显,资源利用率太低(参考前面的表格MaxRAMFraction=4)。
2.若是想在基础上我还想提升一些内存资源利用率,而且容器内存为1 GB – 4 GB,我建议你设置-XX:MaxRAMFraction=2,在大于8G的能够尝试设置-XX:MaxRAMFraction=1(参考上表格)。
https://blog.kelu.org/tech/2018/05/30/running-a-jvm-in-a-container-without-getting-killed.html
做者:青木
原文连接:https://qingmu.io/2018/12/17/How-to-securely-limit-JVM-resources-in-a-container/