最近生产环境k8s-pod出现oom的情况,导致服务重启,最近对这个情况进行详细监控并验证。废话不多说,直接上干货和验证数据。
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
根据度娘的一些博客,怎么说,不得不吐槽部分博客的质量。大家可以自行百度相关资料。
最后总结如下:
老版本的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_111
及 Java-1.8.0_212
出问题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)
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"]
-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"]
-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#
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
实验结果数据:
JVM堆内存=16.5+3320+1626=4962.5MB
物理机 19917.5 * (1/4)=4979.3
2者差不多相等
结论:
-XX:+UseContainerSupport
, -XX:MaxRAMPercentage=25.0
加入此参数进行实验也没有起到任何效果,及此Java版本参数不生效结论:
-XX:+UseContainerSupport
, -XX:MaxRAMPercentage
,且默认情况下为开启状态,-XX:MaxRAMPercentage
默认值为25.0-XX:MaxRAMPercentage
设置为-XX:MaxRAMPercentage=75.0