Skip to content

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

最佳实践

  1. 构建阶段用完整镜像,运行阶段用最小镜像(alpine/distroless/scratch)
  2. 分离依赖下载和代码编译,充分利用缓存
  3. 安全考虑:最终镜像不包含源码和构建工具
  4. 极致精简:Go/Rust 等静态编译语言可以用 FROM scratch(空镜像)

下一步

04 - Docker Compose