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-alpinedockerignore
# .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 部署到自己服务器》
