多阶段构建 #
什么是多阶段构建? #
多阶段构建允许在一个Dockerfile中使用多个FROM语句,每个FROM开始一个新的构建阶段,可以选择性地从之前的阶段复制文件。
传统构建问题 #
text
┌─────────────────────────────────────────────────────┐
│ 传统构建问题 │
├─────────────────────────────────────────────────────┤
│ │
│ 问题: │
│ 1. 构建工具留在最终镜像中 │
│ 2. 镜像体积过大 │
│ 3. 安全风险增加 │
│ 4. 需要多个Dockerfile │
│ │
│ 示例: │
│ FROM golang:1.20 │
│ WORKDIR /app │
│ COPY . . │
│ RUN go build -o myapp │
│ CMD ["./myapp"] │
│ │
│ 结果: 镜像包含Go编译器,体积约1GB │
│ │
└─────────────────────────────────────────────────────┘
多阶段构建优势 #
text
┌─────────────────────────────────────────────────────┐
│ 多阶段构建优势 │
├─────────────────────────────────────────────────────┤
│ │
│ 1. 镜像体积小 - 只包含运行时必需文件 │
│ 2. 安全性高 - 不包含构建工具 │
│ 3. 单文件 - 一个Dockerfile完成所有构建 │
│ 4. 可维护性好 - 构建逻辑清晰 │
│ │
└─────────────────────────────────────────────────────┘
基本语法 #
简单示例 #
dockerfile
# 构建阶段
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# 运行阶段
FROM alpine:3.18
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]
语法说明 #
dockerfile
# FROM ... AS <name> 命名构建阶段
FROM node:18 AS builder
# COPY --from=<stage> 从指定阶段复制文件
COPY --from=builder /app/dist /app/dist
# FROM --platform 指定平台
FROM --platform=linux/amd64 node:18 AS builder
实战示例 #
Go应用 #
dockerfile
# 构建阶段
FROM golang:1.20-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY go.mod go.sum ./
RUN go mod download
# 复制源代码
COPY . .
# 构建
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# 运行阶段
FROM alpine:3.18
# 安装CA证书
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/
# 从构建阶段复制二进制文件
COPY --from=builder /app/main .
# 暴露端口
EXPOSE 8080
# 运行
CMD ["./main"]
Node.js应用 #
dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
RUN npm ci
# 复制源代码
COPY . .
# 构建
RUN npm run build
# 运行阶段
FROM node:18-alpine
WORKDIR /app
# 复制依赖
COPY package*.json ./
RUN npm ci --only=production
# 从构建阶段复制构建产物
COPY --from=builder /app/dist ./dist
# 暴露端口
EXPOSE 3000
# 运行
CMD ["node", "dist/index.js"]
前端应用 #
dockerfile
# 构建阶段
FROM node:18-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
# 复制nginx配置
COPY nginx.conf /etc/nginx/nginx.conf
# 暴露端口
EXPOSE 80
# 运行
CMD ["nginx", "-g", "daemon off;"]
Python应用 #
dockerfile
# 构建阶段
FROM python:3.11 AS builder
WORKDIR /app
# 安装依赖
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# 运行阶段
FROM python:3.11-slim
WORKDIR /app
# 从构建阶段复制依赖
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
# 复制应用代码
COPY . .
# 运行
CMD ["python", "app.py"]
高级技巧 #
多阶段选择 #
dockerfile
# 开发阶段
FROM node:18 AS development
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "run", "dev"]
# 构建阶段
FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build
# 生产阶段
FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]
# 测试阶段
FROM node:18 AS test
WORKDIR /app
COPY . .
RUN npm ci
RUN npm test
bash
# 构建特定阶段
docker build --target development -t myapp:dev .
docker build --target production -t myapp:prod .
从外部镜像复制 #
dockerfile
# 从其他镜像复制文件
FROM alpine:3.18
COPY --from=nginx:alpine /etc/nginx/nginx.conf /etc/nginx/nginx.conf
使用多阶段优化缓存 #
dockerfile
# 依赖阶段
FROM node:18 AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
# 构建阶段
FROM node:18 AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# 运行阶段
FROM node:18-alpine
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/index.js"]
使用BuildKit #
dockerfile
# syntax=docker/dockerfile:1
FROM alpine AS base
RUN apk add --no-cache curl
FROM base AS builder
COPY src/ /src/
RUN build-script.sh
FROM base AS production
COPY --from=builder /app /app
CMD ["./app"]
bash
# 启用BuildKit
DOCKER_BUILDKIT=1 docker build -t myapp .
镜像大小对比 #
text
┌─────────────────────────────────────────────────────┐
│ 镜像大小对比 │
├─────────────────────────────────────────────────────┤
│ │
│ Go应用: │
│ 单阶段构建: ~1.2GB │
│ 多阶段构建: ~10MB │
│ │
│ Node.js应用: │
│ 单阶段构建: ~1GB │
│ 多阶段构建: ~150MB │
│ │
│ 前端应用: │
│ 单阶段构建: ~1GB │
│ 多阶段构建: ~25MB │
│ │
└─────────────────────────────────────────────────────┘
最佳实践 #
1. 命名构建阶段 #
dockerfile
# 使用有意义的名称
FROM node:18 AS builder
FROM nginx:alpine AS production
2. 最小化运行阶段 #
dockerfile
# 使用最小基础镜像
FROM alpine:3.18
# 或
FROM scratch
3. 只复制必需文件 #
dockerfile
# 只复制需要的文件
COPY --from=builder /app/dist ./dist
# 不要复制整个目录
# COPY --from=builder /app .
4. 利用缓存 #
dockerfile
# 依赖阶段单独处理
FROM node:18 AS deps
COPY package*.json ./
RUN npm ci
# 构建阶段
FROM node:18 AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
小结 #
本节学习了Docker多阶段构建:
- 多阶段构建的概念和优势
- 基本语法和使用方法
- 各种语言的实战示例
- 高级技巧和最佳实践
下一步 #
接下来,让我们学习 Docker Swarm,了解Docker的集群编排功能。