Docker 镜像最佳实践 5 条最佳建议 1.仅安装产线需要依赖与软件 镜像尽可能最小原则
仅复制 jar/war
使用自定义 JRE(Java Runtime Environment)
2.使用多阶段构建 1 2 3 4 5 6 7 8 9 10 11 12 FROM maven:3.6.3-jdk-11-slim AS build RUN mkdir /project COPY . /project WORKDIR /project RUN mvn clean package -DskipTests FROM adoptopenjdk/openjdk11:jre-11.0.9.1_1-alpine RUN mkdir /app COPY --from=build /project/target/java-application.jar /app/java-application.jar WORKDIR /app CMD "java" "-jar" "java-application.jar"
3.不要使用 root 运行应用 为了应用的安全,不要使用 root 账户运行应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # Add app user ARG APPLICATION_USER=appuser RUN adduser --no-create-home -u 1000 -D $APPLICATION_USER # Configure working directory RUN mkdir /app && \ chown -R $APPLICATION_USER /app USER 1000 COPY --chown=1000:1000 ./app.jar /app/app.jar WORKDIR /app EXPOSE 8080 ENTRYPOINT [ "exec","/jre/bin/java", "-jar", "/app/app.jar" ]
4.尽可能处理终止信号量 推荐使用exec
命令
1 ENTRYPOINT [ "exec","/jre/bin/java", "-jar", "/app/app.jar" ]
推荐处理应用终止回调方法
1 Runtime.getRuntime().addShutdownHook(yourShutdownThread);
5.使用.dockerigore,使用新版 jdk 使用.dockerignore
排除不需要的文件
使用Java 8 update 191+
和jdk10+
版本,其他版本无法感知 docker 内存和 cpu 限制。
最小镜像实践 在云环境下,精简镜像有极大的资源利用率的提升,节省磁盘和网络带宽,有着极大的收益。
jdk1.8 以下推荐使用alpine
镜像
1 docker pull openjdk:8u212-jre-alpine3.9
jdk11+,由于不再提供 jre 运行时,推进使用alpine
+jlink 剪裁出最佳运行时。
自定义 jre 首先使用jdeps
查看依赖的 module
1 2 $ jdeps --print-module-deps --ignore-missing-deps --recursive --multi-release 17 --module-path="./app/BOOT-INF/lib/*" --class-path ./app/BOOT-INF/lib/* app.jar java.base,java.compiler,java.desktop,java.instrument,java.management,java.naming,java.prefs,java.scripting
对于 spring 应用推荐增加--ignore-missing-deps
,可以忽略 spring 的一些 module 缺失,一般情况下我们的 jar 会包含 spring 所有依赖,对于这种 fat.jar 我们可以先解压该包,然后增加--module-path="./app/BOOT-INF/lib/*" --class-path ./app/BOOT-INF/lib/*
参数进行分析。
最后使用jlink
输出自定义 jre
1 2 3 4 5 6 7 $ jlink \ --add-modules java.base,java.compiler,java.desktop,java.instrument,java.management,java.naming,java.prefs,java.scripting \ --strip-debug \ --no-man-pages \ --no-header-files \ --compress=2 \ --output ./javaruntime
最佳镜像 以下为最佳实践案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 # base image to build a JRE FROM amazoncorretto:17.0.3-alpine as corretto-jdk # required for strip-debug to work RUN apk add --no-cache binutils # Build small JRE image RUN $JAVA_HOME/bin/jlink \ --verbose \ --add-modules java.base,java.compiler,java.desktop,java.instrument,java.management,java.naming,java.prefs,java.scripting \ --strip-debug \ --no-man-pages \ --no-header-files \ --compress=2 \ --output /customjre # main app image FROM alpine:latest ENV JAVA_HOME=/jre ENV PATH="${JAVA_HOME}/bin:${PATH}" # copy JRE from the base image COPY --from=corretto-jdk /customjre $JAVA_HOME # Add app user ARG APPLICATION_USER=appuser RUN adduser --no-create-home -u 1000 -D $APPLICATION_USER # Configure working directory RUN mkdir /app && \ chown -R $APPLICATION_USER /app USER 1000 COPY --chown=1000:1000 ./app.jar /app/app.jar WORKDIR /app EXPOSE 8080 ENTRYPOINT [ "exec","/jre/bin/java", "-jar", "/app/app.jar" ]
Native Image 使用 native image 可以极大的减少镜像大小,启动时间,占用内存,因此如果项目可以接入 native image,建议优先接入。
具体文档可参考:NativeImage
说明 如果你对这个系列感兴趣,可以看我在写的一本电子书 :《Better Javaer》
参考
How to reduce JVM docker image size
10 best practices to build a Java container with Docker
使用 jlink 的 Java 运行时