03 - 多阶段构建(Multi-stage Build)
为什么需要多阶段构建?
问题:构建应用需要编译工具和依赖,但运行时不需要。 如果把编译工具也打包进最终镜像,会导致镜像臃肿且不安全。
多阶段构建:在一个 Dockerfile 中使用多个 FROM,只把最终产物复制到精简的运行镜像中。
┌──────────────────┐ COPY --from ┌──────────────────┐
│ Build Stage │ ──────────────────► │ Runtime Stage │
│ │ │ │
│ 编译工具 500MB │ 只复制二进制 │ Alpine 5MB │
│ 源代码 │ 或产物文件 │ + 应用二进制 │
│ 依赖库 │ │ │
│ 中间产物 │ │ 最终镜像: ~15MB │
└──────────────────┘ └──────────────────┘实战示例
Go 应用(效果最明显)
dockerfile
# === 构建阶段 ===
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server .
# === 运行阶段 ===
FROM alpine:3.19
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]镜像大小对比:
- 不使用多阶段:~800MB(包含整个 Go 工具链)
- 使用多阶段:~15MB(只有 Alpine + 编译后的二进制)
Python 应用
dockerfile
# === 依赖构建阶段 ===
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# === 运行阶段 ===
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 5000
CMD ["python", "app.py"]前端应用(React/Vue)
dockerfile
# === 构建阶段 ===
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# === 运行阶段 ===
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]关键语法
dockerfile
# 命名阶段
FROM image AS stage-name
# 从指定阶段复制文件
COPY --from=stage-name /path/in/builder /path/in/runtime
# 也可以从外部镜像复制
COPY --from=nginx:alpine /etc/nginx/nginx.conf /etc/nginx/nginx.conf最佳实践
- 构建阶段用完整镜像,运行阶段用最小镜像(alpine/distroless/scratch)
- 分离依赖下载和代码编译,充分利用缓存
- 安全考虑:最终镜像不包含源码和构建工具
- 极致精简:Go/Rust 等静态编译语言可以用
FROM scratch(空镜像)
