Skip to content

Dockerfile 基础见《Docker 核心概念与 Dockerfile 基础》。这篇讲生产级优化:镜像太大(800MB)→ 启动慢、传输慢、漏洞面大。核心三招:多阶段构建、层顺序优化、非 root 运行

一、多阶段构建(最核心的一招)

问题:构建 Java 应用需要 Maven + JDK(镜像大),但运行只需要 JRE + jar。

多阶段构建:Dockerfile 里写多个 FROM(多个阶段),构建阶段产出的东西拷给运行阶段,最终镜像只保留运行阶段:

多阶段构建:只把"能跑的东西"带进最终镜像

dockerfile
# ===== 阶段 1:构建(大而全) =====
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline   # 先拉依赖(层缓存,见下文)
COPY src ./src
RUN mvn package -DskipTests     # 编译打包

# ===== 阶段 2:运行(只留产物) =====
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=builder /app/target/myapp.jar ./myapp.jar
ENTRYPOINT ["java", "-jar", "myapp.jar"]

效果:镜像从 800MB+ → 200-300MB,Maven/源码/编译中间文件全都不在最终镜像里。

关键指令COPY --from=builder /app/target/myapp.jar——从上一个阶段拷文件。

二、层顺序优化:利用构建缓存

Docker 镜像按 Dockerfile 每行一层,层没变就复用缓存。把不常变的放前面,常变的放最后

dockerfile
# 好:依赖层在前面(pom.xml 不变 → 依赖层一直缓存,代码改不重装依赖)
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .          # ← 依赖文件先拷
RUN mvn dependency:go-offline   # ← 依赖下载(缓存住)
COPY src ./src          # ← 代码最后拷(代码改只重建这层)
RUN mvn package

# 坏:先 COPY src 再 RUN 依赖 → 每次改代码都重下载依赖(慢 10 倍)

判断标准docker build 时看到 CACHED 就是命中了缓存。

三、镜像瘦身三板斧

手段做法效果
多阶段构建见上文800MB → 200MB
轻量基础镜像eclipse-temurin:17-jre(Debian slim 版)或 Alpine再减 100MB+
.dockerignore排除 target/ .git/ node_modules/减小构建上下文(传得少、不泄露)
dockerfile
# 更小的基础镜像(Alpine:5MB 级,但有 musl 兼容问题,Java 一般用 slim)
FROM eclipse-temurin:17-jre-alpine
dockerignore
# .dockerignore(和 Dockerfile 同目录)
target/
.git/
.idea/
*.log

四、安全最佳实践

dockerfile
FROM eclipse-temurin:17-jre
WORKDIR /app

# 1. 非 root 运行(默认 root 容器,被攻破=root 权限,危险)
RUN groupadd -r app && useradd -r -g app app
COPY --from=builder /app/target/myapp.jar ./myapp.jar
USER app                       # 切到普通用户

# 2. 只装运行必要的东西(多阶段已经做到了)
# 3. 固定版本 tag(别用 latest:不可复现)
ENTRYPOINT ["java", "-jar", "myapp.jar"]

为什么非 root:容器内 root 权限 = 宿主机 root(如果逃逸)。普通用户跑应用,攻破也拿不到宿主机权限。

五、完整生产示例(Spring Boot)

dockerfile
# ===== 构建阶段 =====
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests

# ===== 运行阶段 =====
FROM eclipse-temurin:17-jre
WORKDIR /app
RUN groupadd -r app && useradd -r -g app app
COPY --from=builder /app/target/myapp.jar ./myapp.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "myapp.jar"]
bash
docker build -t myapp:1.0 .    # 构建
docker images                  # 看体积(应该 200-300MB 而不是 800MB+)

小结

  • 多阶段构建:builder 阶段编译 → 运行阶段只拷 jar(体积减 60%+)
  • 层缓存:不常变的层放前(依赖),常变的放后(代码)
  • 瘦身:多阶段 + slim/alpine 基础镜像 + .dockerignore
  • 安全:非 root(USER app)+ 固定版本 tag
  • 构建完的镜像怎么部署:见《Docker Compose 多容器编排》和《GitHub Actions 部署到自己服务器