容器环境 JVM 内存动态配置
在微服务架构中, JAVA 框架占用了绝大部分的市场,比如 Spring Cloud
、 Dubbo
等,其中在使用容器化部署的时候经常碰到关于JVM的内存分配的大小的配置,以下来讲述自己所用到过的配置方式。
固定配置
此方式,顾名思义,就是将JVM参数进行固定化,比如在将JAR打包成容器镜像时
FROM openjdk:8-jdk-alpine
LABEL maintainer="Shuhui<[email protected]>"
RUN set -xe \
&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update \
&& apk add --no-cache ca-certificates ttf-dejavu fontconfig tzdata tini \
&& cp -rf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
COPY app.jar /
VOLUME ["/apps/logs"]
ENTRYPOINT ["/sbin/tini", "--", "java", "-jar", "-Xms1g", "-Xmx2g", "/scheduler.jar"]
可以看到,在容器运行时将JVM参数固定死了,也就是后续需要改动的话需要重新操作镜像。Em。。。 这种牵一发动全身的方式不可取。不推荐使用
环境变量
环境变量的方式可理解为固定配置的升级版本,就是将JVM参数包装成一个专用的环境变量,比如" JAVA_OPTS
":
FROM openjdk:8-jdk-alpine
LABEL maintainer="Shuhui<[email protected]>"
RUN set -xe \
&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update \
&& apk add --no-cache ca-certificates ttf-dejavu fontconfig tzdata tini \
&& cp -rf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
ENV JAVA_OPTS "-server -Xms1024m -Xmx2048m"
COPY app.jar /
VOLUME ["/apps/logs"]
ENTRYPOINT exec /sbin/tini -- java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /app.jar
JVM参数直接引用一个叫 JAVA_OPTS
的环境变量,后续需要改动时直接改动这个参数即可,比如,使用 configmap
的方式:
$ cat xx-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: xx-config
data:
JAVA_OPTS: '-Xms1024m -Xmx2048m'
编排文件中引入
containers:
- image: #IMAGE_NAME#:#IMAGE_TAG#
name: xx
imagePullPolicy: IfNotPresent
env:
- name: JAVA_OPTS
valueFrom:
configMapKeyRef:
name: xx-config
key: JAVA_OPTS
当然,若你跟我一样,不喜欢套来套去--嫌弃使用 configmap
繁琐,直接传参亦可:
env:
- name: JAVA_OPTS
value: "-server -Xms2048m -Xmx4096m -Djava.security.egd=file:/dev/./urandom -Duser.timezone=GMT+08"
动态分配
在 jdk1.8.191的版本新增了以下 JVM参数,可以按容器所分配的内存资源大小去动态分配,我们无须去固定指定为个值的大小,这也是目前比较合理的一种方式,同时也是推荐的一种方式。。
参数 | 说明 |
---|---|
-XX:InitialRAMPercentage=N |
将初始堆大小设置为总内存的百分比 |
-XX:MinRAMPercentage=N |
将最小堆大小设置为总内存的百分比 |
-XX:MaxRAMPercentage=N |
将最大堆大小设置为总内存的百分比 |
提示: 如果已经配置了
-Xms
, 那么-XX:InitialRAMPercentage
参数将忽略,如果已经配置了Xmx
,那么-XX:MaxRAMPercentage
参数将忽略.
如果应用程序在容器中运行,并且指定了 -XX:+UseContainerSupport
,则容器的默认堆大小(默认4分1的内存容量)。
示例: 通用的启动脚本中指定80%( -XX:MaxRAMPercentage=80.0 -XX:InitialRAMPercentage=80.0 -XX:MinRAMPercentage=80.0
)。那么以pod为1G的内存为例,服务就相当于设置了 -Xmx819m -Xms819m
。
-
Dockerfile
范例如下:
FROM openjdk:8-jdk-alpine
LABEL maintainer="Qiu Shuhui<[email protected]>"
ARG user=shuhui
ARG group=shuhui
ARG uid=1000
ARG gid=1000
ARG APP_HOME=/app
ENV APP_HOME $APP_HOME
ENV JAVA_OPTS "-server -Djava.security.egd=file:/dev/./urandom"
RUN set -xe \
&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update upgrade \
&& apk add --no-cache procps ca-certificates ttf-dejavu fontconfig curl tzdata tini bash \
&& cp -rf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& rm -rf /var/cache/apk/*
RUN set -xe \
&& mkdir -p $APP_HOME \
&& chown ${uid}:${gid} $APP_HOME \
&& addgroup -g ${gid} ${group} \
&& adduser -h "$APP_HOME" -u ${uid} -G ${group} -s /bin/bash -D ${user}
COPY --chown=$user xx.jar $APP_HOME/app.jar
USER $uid
WORKDIR $APP_HOME
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["java -XX:InitialRAMPercentage=80.0 -XX:MinRAMPercentage=80.0 -XX:MaxRAMPercentage=80.0 -jar /app.jar"]
此后,访问容器无需再设置 Xmx
为固定值,随着CGroup的资源去动态分配。
参考引用
- [1] https://blog.softwaremill.com/docker-support-in-new-java-8-finally-fd595df0ca54
- [2] https://www.ibm.com/docs/en/sdk-java-technology/8?topic=options-xx-usecontainersupport
- [3] https://stackoverflow.com/questions/54292282/clarification-of-meaning-new-jvm-memory-parameters-initialrampercentage-and-minr/54297753#54297753