容器中的JVM资源该如何被安全的限制?

栏目: IT技术 · 发布时间: 5年前

内容简介:容器中的JVM资源该如何被安全的限制?

容器中的JVM资源该如何被安全的限制?


前言

容器中的JVM资源该如何被安全的限制?

Java与 Docker 的结合,虽然更好的解决了application的封装问题。但也存在着不兼容,比如 Java 并不能自动的发现Docker设置的内存限制,CPU限制。

这将导致JVM不能稳定服务业务!容器会杀死你JVM进程,而健康检查又将拉起你的JVM进程,进而导致你监控你的Pod一天重启次数甚至能达到几百次。

我们希望当Java进程运行在容器中时,Java能够自动识别到容器限制,获取到正确的内存和CPU信息,而不用每次都需要在Kubernetes的yaml描述文件中显示的配置完容器,还需要配置JVM参数。

使用JVM MaxRAM参数或者解锁实验特性的JVM参数,升级JDK到10+,我们可以解决这个问题(也许吧~.~)。

首先Docker容器本质是是宿主机上的一个进程,它与宿主机共享一个/proc目录,也就是说我们在容器内看到的/proc/meminfo,/proc/cpuinfo与直接在宿主机上看到的一致,如下。

Host:

  1. cat /proc/meminfo

  2. MemTotal:      197869260KB

  3. MemFree:         3698100KB

  4. MemAvailable:   62230260KB


容器:

  1. docker run -it --rm alpine cat /proc/meminfo

  2. MemTotal:      197869260KB

  3. MemFree:         3677800KB

  4. MemAvailable:   62210088KB


那么Java是如何获取到Host的内存信息的呢?没错就是通过/proc/meminfo来获取到的。

默认情况下,JVM的Max Heap Size是系统内存的1/4,假如我们系统是8G,那么JVM将的默认Heap≈2G。

Docker通过CGroups完成的是对内存的限制,而/proc目录是已只读形式挂载到容器中的,由于默认情况下Java压根就看不见CGroups的限制的内存大小,而默认使用/proc/meminfo中的信息作为内存信息进行启动,这种不兼容情况会导致,如果容器分配的内存小于JVM的内存,JVM进程会被理解杀死。

内存限制不兼容

容器中的JVM资源该如何被安全的限制?

我们首先来看一组测试,这里我们采用一台内存为188G的物理机。

  1. #free -g

  2.          total used free shared buff/cache available

    Mem:      188   122   1      0     64           64


以下的测试中,我们将包含OpenJDK的hotspot虚拟机,IBM的OpenJ9虚拟机。

以下测试中,我们把正确识别到限制的JDK,称之为安全(即不会超出容器限制不会被kill),反之称之为危险。

测试用例1(OpenJDK)

这一组测试我们使用最新的OpenJDK8-12,给容器限制内存为4G,看JDK默认参数下的最大堆为多少?看看我们默认参数下多少版本的JDK是安全的命令如下,如果你也想试试看,可以用一下命令。

  1. docker run -m 4GB --rm  openjdk:8-jre-slim java  -XshowSettings:vm  -version

  2. docker run -m 4GB --rm  openjdk:9-jre-slim java  -XshowSettings:vm  -version

  3. docker run -m 4GB --rm  openjdk:10-jre-slim java -XshowSettings:vm  -version

  4. docker run -m 4GB --rm  openjdk:11-jre-slim java -XshowSettings:vm  -version

  5. docker run -m 4GB --rm  openjdk:12 java -XshowSettings:vm  -version


OpenJDK8(并没有识别容器限制,26.67G) 危险。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:8-jre-slim java  -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 26.67G

  4.    Ergonomics Machine Class: server

  5.    Using VM: OpenJDK 64-Bit Server VM

  6. openjdk version "1.8.0_181"

  7. OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-2~deb9u1-b13)

  8. OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)


OpenJDK8 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap (正确的识别容器限制,910.50M)安全。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:8-jre-slim java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 910.50M

  4.    Ergonomics Machine Class: server

  5.    Using VM: OpenJDK 64-Bit Server VM

  6. openjdk version "1.8.0_181"

  7. OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-2~deb9u1-b13)

  8. OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)


OpenJDK 9(并没有识别容器限制,26.67G)危险。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:9-jre-slim java  -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 29.97G

  4.    Using VM: OpenJDK 64-Bit Server VM

  5. openjdk version "9.0.4"

  6. OpenJDK Runtime Environment (build 9.0.4+12-Debian-4)

  7. OpenJDK 64-Bit Server VM (build 9.0.4+12-Debian-4, mixed mode)


OpenJDK 9 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap(正确的识别容器限制,1G)安全。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:9-jre-slim java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 1.00G

  4.    Using VM: OpenJDK 64-Bit Server VM

  5. openjdk version "9.0.4"

  6. OpenJDK Runtime Environment (build 9.0.4+12-Debian-4)

  7. OpenJDK 64-Bit Server VM (build 9.0.4+12-Debian-4, mixed mode)


OpenJDK 10(正确的识别容器限制,1G)安全。

  1. [root@xiaoke-test ~]# docker run -m 32GB --rm  openjdk:10-jre-slim java -XshowSettings:vm -XX:MaxRAMFraction=1  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 1.00G

  4.    Using VM: OpenJDK 64-Bit Server VM

  5. openjdk version "10.0.2" 2018-07-17

  6. OpenJDK Runtime Environment (build 10.0.2+13-Debian-2)

  7. OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-2, mixed mode)


OpenJDK 11(正确的识别容器限制,1G)安全。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:11-jre-slim java -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 1.00G

  4.    Using VM: OpenJDK 64-Bit Server VM

  5. openjdk version "11.0.1" 2018-10-16

  6. OpenJDK Runtime Environment (build 11.0.1+13-Debian-3)

  7. OpenJDK 64-Bit Server VM (build 11.0.1+13-Debian-3, mixed mode, sharing)


OpenJDK 12(正确的识别容器限制,1G)安全。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  openjdk:12 java -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 1.00G

  4.    Using VM: OpenJDK 64-Bit Server VM

  5. openjdk version "12-ea" 2019-03-19

  6. OpenJDK Runtime Environment (build 12-ea+23)

  7. OpenJDK 64-Bit Server VM (build 12-ea+23, mixed mode, sharing)


测试用例2(IBM OpenJ9)

  1. docker run -m 4GB --rm  adoptopenjdk/openjdk8-openj9:alpine-slim  java -XshowSettings:vm  -version

  2. docker run -m 4GB --rm  adoptopenjdk/openjdk9-openj9:alpine-slim  java -XshowSettings:vm  -version

  3. docker run -m 4GB --rm  adoptopenjdk/openjdk10-openj9:alpine-slim  java -XshowSettings:vm  -version

  4. docker run -m 4GB --rm  adoptopenjdk/openjdk11-openj9:alpine-slim  java -XshowSettings:vm  -version


OpenJDK8-OpenJ9(正确的识别容器限制,3G)安全。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  adoptopenjdk/openjdk8-openj9:alpine-slim  java -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 3.00G

  4.    Ergonomics Machine Class: server

  5.    Using VM: Eclipse OpenJ9 VM

  6. openjdk version "1.8.0_192"

  7. OpenJDK Runtime Environment (build 1.8.0_192-b12_openj9)

  8. Eclipse OpenJ9 VM (build openj9-0.11.0, JRE 1.8.0 Linux amd64-64-Bit Compressed References 20181107_95 (JIT enabled, AOT enabled)

  9. OpenJ9   - 090ff9dcd

  10. OMR      - ea548a66

  11. JCL      - b5a3affe73 based on jdk8u192-b12)


OpenJDK9-OpenJ9 (正确的识别容器限制,3G)安全。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  adoptopenjdk/openjdk9-openj9:alpine-slim  java -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 3.00G

  4.    Using VM: Eclipse OpenJ9 VM

  5. openjdk version "9.0.4-adoptopenjdk"

  6. OpenJDK Runtime Environment (build 9.0.4-adoptopenjdk+12)

  7. Eclipse OpenJ9 VM (build openj9-0.9.0, JRE 9 Linux amd64-64-Bit Compressed References 20180814_248 (JIT enabled, AOT enabled)

  8. OpenJ9   - 24e53631

  9. OMR      - fad6bf6e

  10. JCL      - feec4d2ae based on jdk-9.0.4+12)


OpenJDK10-OpenJ9 (正确的识别容器限制,3G)安全。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  adoptopenjdk/openjdk10-openj9:alpine-slim  java -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 3.00G

  4.    Using VM: Eclipse OpenJ9 VM

  5. openjdk version "10.0.2-adoptopenjdk" 2018-07-17

  6. OpenJDK Runtime Environment (build 10.0.2-adoptopenjdk+13)

  7. Eclipse OpenJ9 VM (build openj9-0.9.0, JRE 10 Linux amd64-64-Bit Compressed References 20180813_102 (JIT enabled, AOT enabled)

  8. OpenJ9   - 24e53631

  9. OMR      - fad6bf6e

  10. JCL      - 7db90eda56 based on jdk-10.0.2+13)


OpenJDK11-OpenJ9(正确的识别容器限制,3G)安全。

  1. [root@xiaoke-test ~]# docker run -m 4GB --rm  adoptopenjdk/openjdk11-openj9:alpine-slim  java -XshowSettings:vm  -version

  2. VM settings:

  3.    Max. Heap Size (Estimated): 3.00G

  4.    Using VM: Eclipse OpenJ9 VM

  5. openjdk version "11.0.1" 2018-10-16

  6. OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.1+13)

  7. Eclipse OpenJ9 VM AdoptOpenJDK (build openj9-0.11.0, JRE 11 Linux amd64-64-Bit Compressed References 20181020_70 (JIT enabled, AOT enabled)

  8. OpenJ9   - 090ff9dc

  9. OMR      - ea548a66

  10. JCL      - f62696f378 based on jdk-11.0.1+13)


分析

分析之前我们先了解这么一个情况:

  1. JavaMemory (MaxRAM) = 元数据+线程+代码缓存+OffHeap+Heap...


一般我们都只配置Heap即使用-Xmx来指定JVM可使用的最大堆。而JVM默认会使用它获取到的最大内存的1/4作为堆的原因也是如此。

安全性(即不会超过容器限制被容器kill)

OpenJDK:

OpenJdk8-12,都能保证这个安全性的特点(8和9需要特殊参数,-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap)。

OpenJ9:

IBM OpenJ9所有的版本都能识别到容器限制。

资源利用率

OpenJDK:

自动识别到容器限制后,OpenJDK把最大堆设置为了大概容器内存的1/4,对内存的浪费不可谓不大。

当然可以配合另一个JVM参数来配置最大堆。-XX:MaxRAMFraction=int。下面是我整理的一个常见内存设置的表格,从中我们可以看到似乎JVM默认的最大堆的取值为MaxRAMFraction=4,随着内存的增加,堆的闲置空间越来越大,在16G容器内存时,Java堆只有不到4G。

  1. MaxRAMFraction取值    堆占比 容器内存=1G 容器内存=2G 容器内存=4G   容器内存=8G   容器内存=16G

  2. 1                     90%  910.50M     1.78G       3.56G      7.11G      14.22G

  3. 2                     50%  455.50M     910.50M     1.78G      3.56G      7.11G

  4. 3                     33%  304.00M     608.00M     1.19G      2.37G      4.74G

  5. 4                     25%  228.00M     455.50M     910.50M    1.78G      3.56G


OpenJ9:

关于OpenJ9的的详细介绍你可以从https://www.eclipse.org/openj9/docs/xxusecontainersupport/了解更多。

对于内存利用率OpenJ9的策略是优于OpenJDK的。以下是OpenJ9的策略表格。

  1. 容器内存<size>    最大Java堆大小

  2. 小于1GB           50%<size>

  3. 1GB-2GB          -512MB

  4. 大于2GB           大于2GB


结论

注意:这里我们说的是容器内存限制,和物理机内存不同。

自动档

如果你想要的是,不显示的指定-Xmx,让Java进程自动的发现容器限制。

如果你想要的是JVM进程在容器中安全稳定的运行,不被容器kiil,并且你的JDK版本小于10(大于等于JDK10的版本不需要设置,参考前面的测试)。

你需要额外设置JVM参数-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap,即可保证你的Java进程不会因为内存问题被容器Kill。

当然这个方式使用起来简单,可靠,缺点也很明显,资源利用率过低(参考前面的表格MaxRAMFraction=4)。

如果想在基础上我还想提高一些内存资源利用率,并且容器内存为1GB - 4GB,我建议你设置-XX:MaxRAMFraction=2,在大于8G的可以尝试设置-XX:MaxRAMFraction=1(参考上表格)。

手动挡

如果你想要的是手动挡的体验,更加进一步的利用内存资源,那么你可能需要回到手动配置时代-Xmx。

手动挡部分,请可以完全忽略上面我的BB。

上面我们说到了自动挡的配置,用起来很简单很舒服,自动发现容器限制,无需担心和思考去配置-Xmx。

比如你有内存1G那么我建议你的-Xmx750M,2G建议配置-Xmx1700M,4G建议配置-Xmx3500-3700M,8G建议设置-Xmx7500-7600M,总之就是至少保留300M以上的内存留给JVM的其他内存。如果堆特别大,可以预留到1G甚至2G。

手动挡用起来就没有那么舒服了,当然资源利用率相对而言就更高了。

原文链接:https://qingmu.io/2018/12/17/How-to-securely-limit-JVM-resources-in-a-container/
Kubernetes实战培训

容器中的JVM资源该如何被安全的限制?

Kubernetes实战培训将于2019年3月8日在深圳开课,3天时间带你系统掌握Kubernetes,学习效果不好可以继续学习本次培训包括:云原生介绍、微服务;Docker基础、Docker工作原理、镜像、网络、存储、数据卷、安全;Kubernetes架构、核心组件、常用对象、网络、存储、认证、服务发现、调度和服务质量保证、日志、监控、告警、Helm、实践案例等。

容器中的JVM资源该如何被安全的限制?

以上所述就是小编给大家介绍的《容器中的JVM资源该如何被安全的限制?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ActionScript 3.0 Cookbook

ActionScript 3.0 Cookbook

Joey Lott、Darron Schall、Keith Peters / Adobe Dev Library / 2006-10-11 / GBP 28.50

Well before Ajax and Microsoft's Windows Presentation Foundation hit the scene, Macromedia offered the first method for building web pages with the responsiveness and functionality of desktop programs......一起来看看 《ActionScript 3.0 Cookbook》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具