一、背景
对于Java8的一些较早的版本(8u131以前?),如果部署在docker容器中,由于JVM不能感知到docker容器的资源限制,会导致docker编排文件中设置的资源限制对JVM无效。轻则多个docker容器抢占资源导致性能下降,重则docker容器不稳定频频重启。
在Java8 8u131+和JDK9以后,JVM增加了感知容器资源的参数,解决了这个问题。
本文以JDK11为例,说明如何设置java应用的容器感知参数,以及如何在docker编排文件中设置容器资源限制。
二、环境
- java: AdoptOpenJDK 11.0.8+10
- docker ce: 19.03.4
- docker-compose: 1.24.0
三、Java应用相关设置
制作一个基于openjdk11的java应用镜像。
3.1 Java基础镜像
本文基于AdoptOpenJDK的openjdk11镜像 adoptopenjdk/openjdk11:jdk-11.0.8_10-alpine
制作了中国时区,支持中文的openjdk11镜像。
Dockerfile如下:
# 从dockerhub拉取基础镜像
FROM adoptopenjdk/openjdk11:jdk-11.0.8_10-alpine
# 设置时区与语言环境变量
ENV TIME_ZONE=Asia/Shanghai \
LANG=en_US.UTF-8 \
LANGUAGE=en_US.UTF-8 \
LC_ALL=en_US.UTF-8
# 执行以下命令设置时区
RUN apk add --no-cache tzdata \
&& echo "${TIME_ZONE}" > /etc/timezone \
&& ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime
在Dockerfile所在目录执行docker镜像编译命令:docker build -t <自定义镜像名>:<tag> .
, 如:
docker build -t czhao/base-openjdk11:jdk-11.0.8_001 .
3.2 Java应用
事先准备一个openjdk11开发的java应用,编译为jar,基于前面的Java基础镜像,编译java应用镜像。
Dockerfile如下:
FROM czhao/base-openjdk11:jdk-11.0.8_001
# JDK11支持: -XX:+UseContainerSupport 使JVM能够感知容器资源, -XX:InitialRAMPercentage 初期容器内存占比, -XX:MaxRAMPercentage 最大容器内存占比
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:InitialRAMPercentage=50 -XX:MaxRAMPercentage=80"
# 复制上下文目录下的target/*.jar 到容器里
ADD target/*.jar app.jar
# 指定容器启动程序及参数 <ENTRYPOINT> "<CMD>"
ENTRYPOINT java ${JAVA_OPTS} -jar /app.jar
-XX:+UseContainerSupport
开启容器感知。-XX:InitialRAMPercentage
与-XX:MaxRAMPercentage
用于设置JVM初始的容器内存占比和最大容器内存占比。- 上述命令仅在java10以后有效,其他支持感知的版本的参数配置各不相同。
- 镜像编译命令不再赘述。
四、docker编排与启动
编排java应用的docker compose文件,启动,并检查资源占用。
4.1 docker编排文件
这里使用docker compose v3的编排规则,编排文件如下:
service.yaml:
version: '3'
...
services:
<java应用名>:
container_name: <java应用名>
image: <java应用镜像名>:<java应用镜像标签>
...
deploy:
resources:
limits:
cpus: '1.00'
memory: 1024M
reservations:
# cpus: '0.1'
memory: 512M
- deploy本来是docker swarm的
docker stack deploy
命令才支持的属性,但可以在docker-compose up
命令中通过添加--compatibility
参数使之生效。但deploy.resources.reservations.cpus
仍然无法生效。- limits是资源上限,reservations是保留资源。
- cpus设置的数字是占用宿主机cpu的比例,
1.00
即100%
,代表完全占用了宿主机CPU的一个process。注意,宿主机如果是多核机器的话,100%
只是占用了一个process而已。例如一个8 core 16 process
的宿主机,cpus理论上可以设置到16.00
。但这里建议一个容器的cpu占用不要超过1.00
。
4.2 启动docker容器并检查资源占用
使用以下命令启动:
docker-compose --compatibility -f service.yaml up -d
然后使用以下命令,查看docker容器的资源占用情况:
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
docker资源占用如下:
NAME CPU % MEM USAGE / LIMIT
<java应用名> 0.66% 418.5MiB / 1GiB
注意观察就可以发现,该容器占用CPU最高在100%左右,而内存则不会超过1G的限制。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。