实用指南:了解Docker的多阶段构建(Multi-stage Build)
2025-11-25 16:12 tlnshuju 阅读(24) 评论(0) 收藏 举报概述
你有没有发现,自己构建的 Docker 镜像动不动就几百 MB,甚至上 GB?
而别人分享的镜像,功能一样,却只有几十 MB?
问题很可能出在:你把“构建环境”也打包进了最终镜像。
比如:
- 用 Node.js 构建前端,却把
node_modules和npm工具留在了生产镜像 - 用 Maven 编译 Java,却把整个 JDK 打包进去
- 用 Python 编译依赖,却包含了
pip、编译器等构建工具
这些在运行时根本不需要的文件,白白占用了大量空间,还带来了安全风险。
解决方案就是:多阶段构建(Multi-stage Build)
什么是多阶段构建
多阶段构建允许你在一个 Dockerfile 中定义多个构建阶段,每个阶段可以基于不同的基础镜像。
你可以:
- 在第一阶段:使用“胖镜像”完成编译、打包
- 在第二阶段:使用“瘦镜像”只复制必要的产物(如可执行文件、静态文件)
最终镜像只包含运行所需的内容,不包含构建工具和中间文件。
用多阶段构建优化 Node.js 应用
场景
你有一个 React 前端项目,需要:
- 安装依赖(
npm install) - 打包构建(
npm run build) - 用 Nginx 托管静态文件
普通构建(镜像臃肿)
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm ci && npm run build
FROM nginx:alpine
COPY --from=0 /app/build /usr/share/nginx/html
EXPOSE 80
多阶段构建(镜像精简)
# 阶段 1:构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 阶段 2:运行阶段
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
效果对比
| 方式 | 镜像大小 | 是否包含 Node.js? |
|---|---|---|
| 普通构建 | ~300MB | 是(不需要) |
| 多阶段构建 | ~20MB | 否 |
体积减少 93%
多阶段构建如何工作
第一阶段(
builder):- 使用
node:18-alpine,安装所有依赖并执行npm run build - 生成
build/目录(静态文件)
- 使用
第二阶段(运行):
- 使用极轻的
nginx:alpine(仅 ~16MB) - 只复制
builder阶段生成的build/文件 - 最终镜像不包含 Node.js、npm、源码等
- 使用极轻的
关键语法:COPY --from=builder ... ,表示从名为 builder 的阶段复制文件
更多场景
- Java(Spring Boot)项目
# 阶段 1:编译
FROM maven:3.8-openjdk-17 AS builder
COPY pom.xml .
COPY src ./src
RUN mvn package -DskipTests
# 阶段 2:运行
FROM eclipse-temurin:17-jre-alpine
COPY --from=builder /target/myapp.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
优势:最终镜像使用 JRE 而非 JDK,体积更小
- Golang 应用
# 阶段 1:编译
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o main .
# 阶段 2:运行(使用极小的 distroless 镜像)
FROM gcr.io/distroless/static-debian11
COPY --from=builder /app/main .
CMD ["/main"]
优势:最终镜像不含 shell、包管理器,极致轻量且安全
多阶段构建的好处
| 优势 | 说明 |
|---|---|
| 镜像体积大幅减小 | 只包含运行时所需文件 |
| 构建更高效 | 可复用构建阶段 |
| 安全性更高 | 不包含编译器、shell 等攻击面 |
| 职责分离 | 构建与运行环境解耦 |
最佳实践
- 为阶段命名:使用
AS <name>,便于引用 - 选择合适的运行基础镜像:
- 前端:
nginx:alpine - Java:
eclipse-temurin:17-jre-alpine - Go:
distroless或scratch
- 前端:
- 避免在最终阶段安装不必要的包
- 在 CI/CD 中使用多阶段构建,确保一致性
常见问题
多阶段构建会影响构建速度吗?
不会。虽然阶段多,但 Docker 会缓存每一层,构建速度通常更快。如何只构建某个阶段?
docker build --target builder -t myapp:build .
这在调试时非常有用。
总结
| 传统方式 | 多阶段构建 |
|---|---|
| 一个镜像搞定所有 | 分阶段,各司其职 |
| 镜像臃肿 | 镜像精简 |
| 安全性低 | 安全性高 |
多阶段构建是编写高质量 Dockerfile 的必备技能,尤其适合生产环境
浙公网安备 33010602011771号