多阶段构建不是压缩镜像,而是从源头剔除编译期依赖,让最终镜像不含JDK、Maven、源码等运行时不需要的组件,使Spring Boot镜像从1.2GB降至约200MB,同时缩小攻击面、加快冷启动。

多阶段构建不是“压缩”镜像,而是从源头剔除编译期依赖——让最终镜像里根本不存在 JDK、Maven、源码、测试包、调试工具这些运行时完全不需要的东西。瘦身效果立竿见影,Spring Boot 镜像常从 1.2GB 直降到 200MB 左右,同时攻击面收窄、冷启动更快。
拆开构建与运行:两个阶段各司其职
第一阶段专注“生产”,用完整工具链完成编译打包;第二阶段专注“交付”,只加载可执行产物。
- 构建阶段(builder):选用
maven:3.9-openjdk-17或gradle:8-jdk17这类含 SDK 和构建器的镜像,执行mvn package或gradle build,生成target/*.jar - 运行阶段(runtime):切换至极简 JRE 镜像,如
eclipse-temurin:17-jre-alpine或gcr.io/distroless/java17,仅COPY --from=builder拷贝 jar 包,不带任何构建残留 - 避免使用
openjdk:17-jdk-slim作为最终基础镜像——它仍含 javac、jconsole 等 400MB+ 冗余组件,本质还是 JDK 全量版
选对运行时镜像:精简 ≠ 不可用
轻量镜像若缺失关键依赖,会导致 NoClassDefFoundError 或 DNS 解析失败,需兼顾体积与兼容性。
- 优先选
eclipse-temurin:17-jre-alpine或amazoncorretto:17-jre-alpine:体积约 120–150MB,预装 ca-certificates,支持 Jakarta EE 9+(适配 Spring Boot 3.x) - distroless 镜像(如
gcr.io/distroless/java17)更小(~15MB),但无 shell、无/bin/sh,无法 exec 进入调试,适合强安全要求场景 - 避免直接用
scratch:Java 应用依赖 glibc 和 TLS 证书,硬切会报No such file or directory,除非你自行注入 ca-certificates 并静态链接 JVM
不止 COPY,还要主动清理中间产物
光靠阶段分离不够,builder 阶段若不清理缓存和临时文件,可能把 target/test-*.jar、src/test、.m2 缓存一并带入最终镜像。
立即学习“Java免费学习笔记(深入)”;
- 在 builder 阶段末尾加清理命令:
RUN mvn clean package -DskipTests && rm -rf ~/.m2/repository - COPY 时精确指定路径:
COPY --from=builder /app/target/app.jar /app.jar,而非/app/target/整个目录 - 启用
.dockerignore:排除.git、README.md、secrets.json、**/test/**,防止误拷源码或密钥
进一步削减运行时内存与体积
镜像变小只是起点,JVM 启动时的元空间、类扫描、符号解析等开销,也随依赖精简而下降。
- 对 Spring Boot 应用,在
application.properties中设spring.aot.enabled=true,配合 Maven 插件提前生成原生代码,减少 JIT 和反射元数据加载 - 构建时添加 JVM 参数优化:
CMD ["java", "-XX:MaxMetaspaceSize=96m", "-XX:+UseG1GC", "-jar", "app.jar"] - Java 21+ 可启用
--enable-preview --XX:+EnableDynamicCDS,利用共享归档加速类加载,降低冷启动内存峰值


















