多阶段 Docker 构建分两阶段:第一阶段用 Maven 镜像编译打包,第二阶段用精简 JRE 镜像运行 JAR;需显式声明 AS builder、精准匹配 JAR 路径、非 root 用户运行并分离 EXPOSE 与 ENTRYPOINT。

核心思路是:第一阶段只负责编译和打包,不保留任何运行时无关内容;第二阶段只加载编译好的 JAR,用最小化 JRE 运行,彻底剥离 Maven、源码、依赖缓存等冗余。
第一阶段:用 Maven 镜像完成构建
这一阶段专注编译,不关心运行。推荐使用带明确版本的 slim 或 eclipse-temurin 镜像,避免隐式更新导致构建不稳定。
-
基础镜像选型:例如
maven:3.8.6-eclipse-temurin-17或maven:3.9.6-openjdk-17-slim,轻量且兼容性好 -
复制顺序很重要:先复制
pom.xml,再RUN mvn dependency:go-offline,这样能利用 Docker 缓存加速后续构建 -
跳过测试加快构建:加
-DskipTests参数(CI 环境中尤其必要),也可用-B启用批处理模式防卡住 -
工作目录统一:设
WORKDIR /app,后续所有路径(如/app/target/*.jar)都基于此,避免路径错位
第二阶段:用纯 JRE 镜像运行 JAR
目标是只保留 Java 运行时 + 应用包,体积压到最低,同时保持兼容性和安全性。
-
基础镜像建议:优先选
eclipse-temurin:17-jre-alpine(约 80MB)或openjdk:17-jre-slim(约 150MB),避免用-jdk或完整maven镜像 -
精准复制产物:用
COPY --from=builder /app/target/your-app-*.jar app.jar,注意通配符匹配和路径一致性 -
可进一步精简 JRE(进阶):在第二阶段用
jlink构建定制 JRE,仅包含应用实际依赖的模块,体积还能再降 30%~50% -
非 root 用户运行(安全加固):添加
RUN addgroup -g 1001 -f appgroup && adduser -S appuser -u 1001和USER appuser
关键细节与常见踩坑点
多阶段看似简单,但路径、命名、缓存、权限几个地方容易出错,直接影响镜像能否启动。
立即学习“Java免费学习笔记(深入)”;
-
阶段名必须显式声明:第一阶段结尾写
AS builder,第二阶段COPY --from=builder才能正确引用,不能省略或拼错 -
JAR 名称要确定:Maven 默认生成
artifactId-version.jar,若用了spring-boot-maven-plugin,可能生成 fat jar,确保第二阶段复制路径匹配 -
端口暴露和入口命令分离:用
EXPOSE 8080声明端口,ENTRYPOINT ["java", "-jar", "app.jar"]定义启动方式,不要混用CMD和ENTRYPOINT -
Spring Profile 等参数建议写进 ENTRYPOINT:比如
["java", "-Dspring.profiles.active=docker", "-jar", "app.jar"],便于环境差异化控制
一个精简可用的完整示例
以下是一个经验证、体积小、启动快的典型写法:
# 构建阶段 FROM maven:3.8.6-eclipse-temurin-17 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests <h1>运行阶段</h1><p>FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75", "-jar", "app.jar"]


















