Docker与JVM的内存关系(基于K8S-Charts设置)

Docker与JVM的内存关系(基于K8S-Charts设置)

  • 背景
  • SpringBoot及监控插件版本
  • JVM与Docker总结
  • 出问题Dockerfile文件及java版本(Java-1.8.0_111)
  • 进行正确设置的Dockerfile文件及java版本(Java-1.8.0_212)
    • 1. 不进行参数的设置Dockerfile文件:
    • 2. 设置 `-XX:MaxRAMPercentage`=25.0的Dockerfile文件
    • 3. 设置 `-XX:MaxRAMPercentage`=75.0的Dockerfile文件
  • 实验结果
    • Java-1.8.0_111
    • Java-1.8.0_212

背景

最近生产环境k8s-pod出现oom的情况,导致服务重启,最近对这个情况进行详细监控并验证。废话不多说,直接上干货和验证数据。

SpringBoot及监控插件版本

SpringBoot版本:

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

监控插件基于metrics和prometheus
版本如下:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
            <version>1.5.1</version>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-core</artifactId>
            <version>1.5.1</version>
        </dependency>
        <!--prometheus监控,client-java-->
        <dependency>
            <groupId>io.prometheus</groupId>
            <artifactId>simpleclient_hotspot</artifactId>
            <version>0.9.0</version>
        </dependency>

        <dependency>
            <groupId>io.prometheus</groupId>
            <artifactId>simpleclient</artifactId>
            <version>0.9.0</version>
        </dependency>
        <dependency>
            <groupId>io.prometheus</groupId>
            <artifactId>simpleclient_spring_boot</artifactId>
            <version>0.9.0</version>
            <!--内含springboot1.5.4 spring-boot-starter-actuator-->
        </dependency>

简单配置yml即可使用:

management:
  endpoints:
    web:
      exposure:
        include: metrics,prometheus
    jmx:
      exposure:
        include: metrics,prometheus

JVM与Docker总结

根据度娘的一些博客,怎么说,不得不吐槽部分博客的质量。大家可以自行百度相关资料。
最后总结如下:

  • 老版本的Java 跟Docker无法很好地一起工作。问题是在你的机器上,JVM的可用内存和CPU数量并不是Docker允许你使用的可用内存和CPU数量。
    比如,如果你限制了你的Docker容器只能使用100MB内存,但是呢,旧版本的Java并不能识别这个限制。Java看不到这个限制。JVM会要求更多内存,而且远超这个限制。如果使用太多内存,Docker将采取行动并杀死容器内的进程!JAVA进程被干掉了,很明显,这并不是我们想要的。

  • Docker是通过linux-cgroup来实现最大内存设置的。而在新版本的Java 中对容器化做了相应的优化处理,JVM通过感知linux-cgroup,从而获取Docker内的资源限制。

  • 新版本中Java 可以通过以下参数进行设置:
    -XX:+UseContainerSupport 开启是否感知容器(Docker)内存,在新版本的Java 中这一参数默认为True-开启。
    -XX:MaxRAMPercentage进行设置JVM的最大堆内存比例,新版JVM中这一值默认为25.0。
    例如:当Docker的最大内存为1000Mi时,JVM的最大**堆内存*为250Mi。

  • 普遍的说法是从Java 10开始引入-XX:+UseContainerSupport(默认情况下启用),并把其更新到了最新版本的Java 8中。本人没有对Java 10进行验证(鉴于生产环境还没有使用Java 10)。
    以下对2个Java 8版本进行了监控数据分析与验证:Java-1.8.0_111Java-1.8.0_212

出问题Dockerfile文件及java版本(Java-1.8.0_111)

出问题Dockerfile文件:

FROM java:8
VOLUME /tmp
COPY target/lsh-mcp-iam-auth-service-v100r001c01.jar /app.jar
ENV APP_HOME /
WORKDIR $APP_HOME
EXPOSE  9090
ENTRYPOINT ["sh", "-c"]
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

进入pod内,输出java版本为:

[root@master-1 auth]# kubectl exec -it test-auth-lsh-mcp-iam-auth-service-7fc68cc676-tqd9g  bash
root@test-auth-lsh-mcp-iam-auth-service-7fc68cc676-tqd9g:/# java -version
openjdk version "1.8.0_111"
OpenJDK Runtime Environment (build 1.8.0_111-8u111-b14-2~bpo8+1-b14)
OpenJDK 64-Bit Server VM (build 25.111-b14, mixed mode)

进行正确设置的Dockerfile文件及java版本(Java-1.8.0_212)

1. 不进行参数的设置Dockerfile文件:

FROM openjdk:8-jre-alpine
VOLUME /tmp
COPY target/lsh-mcp-iam-auth-service-v100r001c01.jar /app.jar
ENV APP_HOME /
WORKDIR $APP_HOME
RUN apk add --no-cache tzdata bash  ttf-dejavu fontconfig \
	&& fc-cache --force \
EXPOSE  9090
ENTRYPOINT ["sh", "-c"]
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

2. 设置 -XX:MaxRAMPercentage=25.0的Dockerfile文件

ps:25.0代表比例,及25%。

FROM openjdk:8-jre-alpine
VOLUME /tmp
COPY ./lsh-mcp-iam-auth-service-v100r001c01.jar /app.jar
ENV APP_HOME /
WORKDIR $APP_HOME
RUN apk add --no-cache tzdata bash  ttf-dejavu fontconfig \
        && fc-cache --force \
EXPOSE  9090
ENTRYPOINT ["sh", "-c"]
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-XX:+UseContainerSupport","-XX:MaxRAMPercentage=25.0","-jar","/app.jar"]

3. 设置 -XX:MaxRAMPercentage=75.0的Dockerfile文件

FROM openjdk:8-jre-alpine
VOLUME /tmp
COPY ./lsh-mcp-iam-auth-service-v100r001c01.jar /app.jar
ENV APP_HOME /
WORKDIR $APP_HOME
RUN apk add --no-cache tzdata bash  ttf-dejavu fontconfig \
        && fc-cache --force \
EXPOSE  9090
ENTRYPOINT ["sh", "-c"]
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-XX:+UseContainerSupport","-XX:MaxRAMPercentage=75.0","-jar","/app.jar"]

进入pod内,以上三种输出java版本均为:

[root@master-1 ~]# kubectl exec -it lsh-mcp-iam-auth-service-7669ff48d6-5cwtk bash
bash-4.4# java -version
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (IcedTea 3.12.0) (Alpine 8.212.04-r0)
OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)
bash-4.4#

实验结果

Java-1.8.0_111

k8s的资源配置:

resources:
  requests:
    memory: 512Mi
    cpu: 200m
  limits:
    memory: 2000Mi
    cpu: 800m

无论设置limits-memory为2000Mi,1000Mi,600Mi等,此版本JVM不能根据容器(Docker)资源的调整而进行调整,及JVM堆内存读取的始终是物理机(Node节点)的最大内存的1/4(不加设置,JVM默认会读取宿主机最大内存的1/4作为JVM最大堆内存,可以自己度娘)。
查询到此物理机的内存为:19917.5MB

实验结果数据:
Docker与JVM的内存关系(基于K8S-Charts设置)_第1张图片
JVM堆内存=16.5+3320+1626=4962.5MB
物理机 19917.5 * (1/4)=4979.3
2者差不多相等

结论:

  1. Java-1.8.0_111读取的是宿主机资源,Docker-limits无法对其进行限制
  2. -XX:+UseContainerSupport, -XX:MaxRAMPercentage=25.0加入此参数进行实验也没有起到任何效果,及此Java版本参数不生效

Java-1.8.0_212

实验结果数据:
Docker与JVM的内存关系(基于K8S-Charts设置)_第2张图片
Docker与JVM的内存关系(基于K8S-Charts设置)_第3张图片

结论:

  1. Java-1.8.0_212支持 -XX:+UseContainerSupport, -XX:MaxRAMPercentage,且默认情况下为开启状态,-XX:MaxRAMPercentage默认值为25.0
  2. 建议优化参数将-XX:MaxRAMPercentage设置为-XX:MaxRAMPercentage=75.0
    解释:及使用容器最大内存的75%作为JVM的堆内存,尽最大可能利用Docker内存,并留有一定的空间。当然可以根据容器内的其他进程多少,此数值还可以进一步减小或升高。

你可能感兴趣的:(java,docker,k8s)