1. 合理安排指令顺序(利用缓存)

把变化频率低的指令放前面,变化频率高的放后面。一旦某层缓存失效,后续所有层都会重新构建。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ❌ 错误:每次改代码都要重新装依赖
FROM node:18-alpine
WORKDIR /app
COPY . .                          # 代码经常变
RUN npm ci --production           # 依赖很少变
CMD ["node", "server.js"]

# ✅ 正确:依赖变化时才重新安装
FROM node:18-alpine
WORKDIR /app
COPY package.json package-lock.json ./  # 依赖定义(很少变)
RUN npm ci --production                  # 安装依赖(很少变)
COPY . .                                 # 代码(经常变)
CMD ["node", "server.js"]
1
2
# 检查缓存命中情况
docker build -t myapp . 2>&1 | grep -E "(CACHED|Using cache)"

2. 使用 .dockerignore 减少上下文

docker build 会把整个目录发送到 Docker daemon,包括 .git、node_modules 等不需要的文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# .dockerignore
.git
.github
.vscode
.idea
node_modules
dist
build
*.md
*.log
.env
.env.local
.DS_Store
coverage
.nyc_output
1
2
3
4
# 检查上下文大小
docker build --no-cache -t test . 2>&1 | grep "Sending build context"
# 优化前:Sending build context to Docker daemon  1.2GB
# 优化后:Sending build context to Docker daemon  50MB

3. 使用轻量级基础镜像

基础镜像 大小 说明
ubuntu:22.04 ~77MB 完整 Ubuntu
python:3.11 ~1GB 包含完整 Python
python:3.11-slim ~130MB 精简版
python:3.11-alpine ~50MB Alpine 版
1
2
3
4
5
6
7
8
# ❌ 太大
FROM python:3.11          # 1GB

# ✅ 推荐
FROM python:3.11-slim     # 130MB

# ✅ 最小(注意 musl 兼容性问题)
FROM python:3.11-alpine   # 50MB

Alpine 使用 musl 而非 glibc,某些软件可能不兼容。遇到问题选 slim 版本。

4. 合并 RUN 指令减少层数

每个 RUN 创建一个新层,层越多构建越慢、镜像越大。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ❌ 5 个层
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y wget
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*

# ✅ 1 个层
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        curl \
        wget && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
1
2
# 检查镜像层数
docker history myapp:latest

5. 使用多阶段构建

在一个阶段构建,在另一个阶段运行,只复制需要的文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 阶段 1:构建
FROM golang:1.21-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 .

# 阶段 2:运行(只有 ~15MB)
FROM alpine:3.18
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/app .
EXPOSE 8080
CMD ["./app"]
方案 镜像大小 构建时间
单阶段 ~1.2GB 5 分钟
多阶段 ~15MB 2 分钟

6. 使用 BuildKit 加速构建

BuildKit 支持并行构建和高级缓存。

1
2
3
4
5
6
7
# 启用 BuildKit
export DOCKER_BUILDKIT=1

# 或在 /etc/docker/daemon.json 中永久配置
# {
#   "features": { "buildkit": true }
# }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 使用缓存挂载(BuildKit 特性)
# syntax=docker/dockerfile:1
FROM node:18-alpine
WORKDIR /app
COPY package.json package-lock.json ./
# 缓存挂载避免每次重新下载依赖
RUN --mount=type=cache,target=/root/.npm \
    npm ci --production
COPY . .
CMD ["node", "server.js"]
1
2
# 检查 BuildKit 是否启用
docker build --progress=plain -t myapp . 2>&1 | head -5

7. 使用缓存导入导出

在 CI/CD 中,缓存会在每次构建后丢失。使用缓存导入导出可以跨构建保留缓存。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 导出到本地
docker build \
  --cache-from type=local,src=/tmp/cache \
  --cache-to type=local,dest=/tmp/cache \
  -t myapp .

# 使用注册表作为缓存
docker build \
  --cache-from type=registry,ref=myregistry/myapp:cache \
  --cache-to type=registry,ref=myregistry/myapp:cache,mode=max \
  -t myapp .
1
2
3
4
5
6
7
8
9
# GitHub Actions 示例
- name: Build and push
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: myapp:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

8. 并行安装依赖

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Node.js - npm ci 比 npm install 快
RUN npm ci --production

# Python - 禁用缓存
RUN pip install --no-cache-dir -r requirements.txt

# Go - 并行下载模块
RUN go mod download

# Rust - 使用 cargo-chef
COPY --from=chef /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json

9. 清理构建缓存和临时文件

在同一个 RUN 指令中清理,避免文件保留在层中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# ✅ 正确:同一层中清理
RUN apt-get update && \
    apt-get install -y --no-install-recommends build-essential && \
    make && make install && \
    apt-get purge -y --auto-remove build-essential && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# ❌ 错误:清理在另一层,文件仍然存在
RUN apt-get update && apt-get install -y build-essential
RUN make && make install
RUN apt-get clean  # 太晚了!前面的层已经包含了文件

各语言清理命令:

1
2
3
4
5
6
7
8
# Node.js
RUN npm ci --production && npm cache clean --force

# Python
RUN pip install --no-cache-dir -r requirements.txt

# Go
RUN go build -o app . && go clean -cache

10. 使用特定版本标签

1
2
3
4
5
6
7
8
# ❌ latest 可能变化,缓存不可预测
FROM node:latest

# ✅ 特定版本
FROM node:18.17.0-alpine3.18

# ✅ 更安全:使用 SHA256 摘要
FROM node:18.17.0-alpine3.18@sha256:abc123...

镜像层级详解

1
2
# 查看镜像层历史
docker history <image-name>

创建新层的指令:FROM、RUN、COPY、ADD

不创建新层的指令(只是元数据):CMD、ENTRYPOINT、ENV、EXPOSE、WORKDIR、USER、LABEL、ARG、VOLUME

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 这会创建 4 个层
FROM node:18-alpine        # 层 1
RUN apk add --no-cache curl # 层 2
COPY package.json .         # 层 3
RUN npm install             # 层 4

# 这些指令不增加层
WORKDIR /app               # 无层
ENV NODE_ENV=production    # 无层
EXPOSE 3000               # 无层
CMD ["node", "server.js"]  # 无层

优化效果对比

1
2
3
4
5
6
7
8
9
# ❌ 优化前(8 分钟,1.2GB)
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y python3 python3-pip
RUN apt-get install -y build-essential
WORKDIR /app
COPY . .
RUN pip3 install -r requirements.txt
CMD ["python3", "app.py"]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ✅ 优化后(1.5 分钟,150MB)
# syntax=docker/dockerfile:1
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install --no-cache-dir -r requirements.txt

FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]
指标 优化前 优化后 提升
构建时间 8 分钟 1.5 分钟 81% ⬇️
镜像大小 1.2GB 150MB 87% ⬇️
上下文大小 500MB 50MB 90% ⬇️
缓存命中率 10% 90% 80% ⬆️

监控和调试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 显示详细构建时间
time docker build -t myapp .

# BuildKit 详细进度
DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp .

# 分析镜像层
docker history myapp:latest

# 使用 dive 工具深入分析
dive myapp:latest