Skip to content

构建缓存详解

构建缓存是 Docker 构建系统的核心特性,能够显著加快构建速度。本文将深入介绍 Docker 构建缓存的工作原理、优化策略和高级用法。

目录

  1. 缓存概述
  2. 缓存工作原理
  3. 层缓存
  4. BuildKit 缓存
  5. 远程缓存
  6. 缓存策略
  7. 故障排查

缓存概述

1.1 为什么需要缓存

缓存的价值:

无缓存构建:
  FROM node:18-alpine    ←─ 拉取镜像 (30s)
  COPY package*.json .   ←─ 复制文件 (1s)
  RUN npm ci             ←─ 安装依赖 (5min)
  COPY . .               ←─ 复制文件 (5s)
  RUN npm run build      ←─ 构建应用 (2min)
  总时间: ~7.5 分钟

有缓存构建:
  FROM node:18-alpine    ←─ 使用缓存 (0s)
  COPY package*.json .   ←─ 使用缓存 (0s)
  RUN npm ci             ←─ 使用缓存 (0s)
  COPY . .               ←─ 复制文件 (5s)
  RUN npm run build      ←─ 构建应用 (2min)
  总时间: ~2 分钟
  
  节省时间: 73%

1.2 缓存类型

缓存类型说明适用范围
层缓存Dockerfile 指令结果缓存所有构建
挂载缓存BuildKit 缓存挂载BuildKit
外部缓存导出/导入的缓存BuildKit
注册表缓存存储在镜像仓库的缓存BuildKit
Git 缓存远程 Git 缓存BuildKit

缓存工作原理

2.1 层缓存机制

层缓存工作原理:

Dockerfile 指令:
  FROM node:18-alpine@sha256:abc123...
  WORKDIR /app
  COPY package*.json ./
  RUN npm ci
  COPY . .
  RUN npm run build

缓存键计算:
  Layer 1: FROM
    Cache Key = "node:18-alpine@sha256:abc123..."
    
  Layer 2: WORKDIR
    Cache Key = hash("WORKDIR /app") + Parent Layer
    
  Layer 3: COPY
    Cache Key = hash("COPY package*.json ./") + File Hash + Parent Layer
    
  Layer 4: RUN
    Cache Key = hash("RUN npm ci") + Parent Layer
    
  Layer 5: COPY
    Cache Key = hash("COPY . .") + File Hash + Parent Layer
    
  Layer 6: RUN
    Cache Key = hash("RUN npm run build") + Parent Layer

2.2 缓存失效规则

dockerfile
# 缓存失效场景示例

# 场景 1: 指令变化
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci                    # ←─ 修改此指令,当前及后续层失效
COPY . .
RUN npm run build

# 场景 2: 文件变化
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./         # ←─ package.json 变化,当前及后续层失效
RUN npm ci
COPY . .                      # ←─ 任何文件变化,当前及后续层失效
RUN npm run build

# 场景 3: 父层失效
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci                    # ←─ 此层失效
COPY . .                      # ←─ 此层也失效(父层失效)
RUN npm run build             # ←─ 此层也失效

2.3 缓存存储位置

bash
# 查看缓存存储
ls -la /var/lib/docker/buildkit/

# BuildKit 缓存目录
ls -la /var/lib/docker/buildkit/cache/

# 查看缓存大小
du -sh /var/lib/docker/buildkit/cache/

# Buildx 缓存
docker buildx du

# 详细缓存信息
docker buildx du -v

层缓存

3.1 优化指令顺序

dockerfile
# ❌ 不推荐 - 代码变化导致依赖重新安装
FROM node:18-alpine
WORKDIR /app
COPY . .                      # ←─ 任何文件变化都触发
RUN npm ci                    # ←─ 重新安装依赖
RUN npm run build

# ✅ 推荐 - 依赖文件单独复制
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./         # ←─ 只有依赖文件变化才触发
RUN npm ci                    # ←─ 复用缓存
COPY . .                      # ←─ 代码变化只影响此处
RUN npm run build

3.2 合并相关指令

dockerfile
# ❌ 不推荐 - 产生多个层
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
RUN rm -rf /var/lib/apt/lists/*

# ✅ 推荐 - 单层,原子操作
RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*

3.3 使用特定版本

dockerfile
# ❌ 不推荐 - 版本变化不可控
FROM node:latest
FROM ubuntu:latest

# ✅ 推荐 - 固定版本,可预测缓存
FROM node:18.17.1-alpine3.18
FROM ubuntu:22.04

BuildKit 缓存

4.1 缓存挂载

dockerfile
# syntax=docker/dockerfile:1

# 包管理器缓存
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci

# 多项目共享缓存
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip,sharing=locked \
    pip install -r requirements.txt

缓存挂载类型:

类型说明示例
cache持久缓存目录/root/.npm
tmpfs临时内存文件系统/tmp
secret秘密文件API 密钥
sshSSH 代理Git 私有仓库
bind绑定挂载本地文件

4.2 缓存共享模式

dockerfile
# syntax=docker/dockerfile:1

# 共享模式 (默认) - 多个构建共享缓存
RUN --mount=type=cache,target=/go/pkg/mod,sharing=shared \
    go mod download

# 私有模式 - 每个构建独立缓存
RUN --mount=type=cache,target=/root/.cache,sharing=private \
    pip install -r requirements.txt

# 锁定模式 - 串行访问,保证一致性
RUN --mount=type=cache,target=/root/.m2,sharing=locked \
    mvn package

4.3 缓存 ID

dockerfile
# syntax=docker/dockerfile:1

# 使用特定 ID 的缓存
FROM node:18-alpine
WORKDIR /app

# 项目 A 的缓存
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm,id=project-a \
    npm ci

# 项目 B 的缓存(不同 ID,独立缓存)
COPY other-package*.json ./
RUN --mount=type=cache,target=/root/.npm,id=project-b \
    npm ci

远程缓存

5.1 本地缓存导出/导入

bash
# 导出缓存到本地目录
docker buildx build \
  --cache-to type=local,dest=/tmp/.buildx-cache,mode=max \
  -t myapp:latest .

# 导入缓存
docker buildx build \
  --cache-from type=local,src=/tmp/.buildx-cache \
  -t myapp:latest .

# 压缩缓存
tar czf buildx-cache.tar.gz -C /tmp/.buildx-cache .

# 解压缓存
tar xzf buildx-cache.tar.gz -C /tmp/.buildx-cache

5.2 注册表缓存

bash
# 推送到仓库的缓存
docker buildx build \
  --cache-to type=registry,ref=myregistry/myapp:cache,mode=max \
  -t myregistry/myapp:latest \
  --push .

# 使用仓库缓存
docker buildx build \
  --cache-from type=registry,ref=myregistry/myapp:cache \
  -t myregistry/myapp:latest \
  --push .

# 多缓存源
docker buildx build \
  --cache-from type=registry,ref=myregistry/myapp:cache \
  --cache-from type=registry,ref=myregistry/myapp:cache-previous \
  -t myregistry/myapp:latest \
  --push .

5.3 内联缓存

bash
# 导出内联缓存(存储在镜像中)
docker buildx build \
  --cache-to type=inline \
  -t myregistry/myapp:latest \
  --push .

# 使用内联缓存
docker buildx build \
  --cache-from myregistry/myapp:latest \
  -t myregistry/myapp:latest \
  --push .

5.4 GitHub Actions 缓存

yaml
# .github/workflows/build.yml
name: Build
on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      
      - name: Cache Docker layers
        uses: actions/cache@v3
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-buildx-
      
      - name: Build
        uses: docker/build-push-action@v4
        with:
          context: .
          push: false
          tags: myapp:latest
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
      
      - name: Move cache
        run: |
          rm -rf /tmp/.buildx-cache
          mv /tmp/.buildx-cache-new /tmp/.buildx-cache

缓存策略

6.1 CI/CD 缓存策略

yaml
# GitLab CI 缓存示例
stages:
  - build

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: ""

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker pull $CI_REGISTRY_IMAGE:latest || true
    - docker build
        --cache-from $CI_REGISTRY_IMAGE:latest
        --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        --tag $CI_REGISTRY_IMAGE:latest
        .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest

6.2 多分支缓存策略

bash
#!/bin/bash
# build.sh - 多分支缓存策略

IMAGE_NAME="myregistry/myapp"
BRANCH=$(git rev-parse --abbrev-ref HEAD)
CACHE_TAG="cache-${BRANCH}"
MAIN_CACHE="cache-main"

# 拉取分支缓存和主分支缓存
docker pull $IMAGE_NAME:$CACHE_TAG || true
docker pull $IMAGE_NAME:$MAIN_CACHE || true

# 构建
docker buildx build \
  --cache-from type=registry,ref=$IMAGE_NAME:$CACHE_TAG \
  --cache-from type=registry,ref=$IMAGE_NAME:$MAIN_CACHE \
  --cache-to type=registry,ref=$IMAGE_NAME:$CACHE_TAG,mode=max \
  -t $IMAGE_NAME:latest \
  --push .

6.3 缓存清理策略

bash
# 查看缓存使用
docker buildx du

# 清理未使用缓存
docker buildx prune

# 清理所有缓存
docker buildx prune -f

# 自动清理脚本
#!/bin/bash
# cleanup-cache.sh

# 保留最近 7 天的缓存
find /tmp/.buildx-cache -type f -mtime +7 -delete

# 限制缓存大小
CACHE_SIZE=$(du -sm /tmp/.buildx-cache | cut -f1)
MAX_SIZE=10240  # 10GB

if [ $CACHE_SIZE -gt $MAX_SIZE ]; then
    echo "Cache size $CACHE_SIZE MB exceeds limit $MAX_SIZE MB, cleaning..."
    rm -rf /tmp/.buildx-cache
fi

故障排查

7.1 缓存未命中排查

bash
# 查看详细构建输出
docker buildx build --progress=plain . 2>&1 | tee build.log

# 分析缓存命中
grep -E "(CACHED|\[.*\])" build.log

# 查看层哈希
docker history myapp:latest --no-trunc

# 比较层
docker inspect myapp:latest --format='{{range .RootFS.Layers}}{{.}}\n{{end}}'

7.2 常见问题

bash
# 问题 1: 缓存未生效
# 原因: .dockerignore 排除了缓存文件
# 解决: 检查 .dockerignore
cat .dockerignore

# 问题 2: 缓存过大
# 原因: 未清理临时文件
# 解决: 同层清理
RUN apt-get update && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*

# 问题 3: 多平台缓存冲突
# 解决: 使用不同缓存 ID
docker buildx build \
  --cache-to type=local,dest=/tmp/.buildx-cache-amd64 \
  --platform linux/amd64 \
  .

docker buildx build \
  --cache-to type=local,dest=/tmp/.buildx-cache-arm64 \
  --platform linux/arm64 \
  .

7.3 调试技巧

bash
# 禁用缓存测试
docker build --no-cache .

# 查看构建详情
docker buildx build --debug .

# 导出构建元数据
docker buildx build --metadata-file metadata.json .

# 分析元数据
cat metadata.json | jq

最佳实践

8.1 缓存优化检查清单

  • [ ] 使用 .dockerignore 减少上下文
  • [ ] 优化 Dockerfile 指令顺序
  • [ ] 使用特定版本标签
  • [ ] 合并相关 RUN 指令
  • [ ] 使用 BuildKit 缓存挂载
  • [ ] 配置远程缓存(CI/CD)
  • [ ] 定期清理过期缓存
  • [ ] 监控缓存命中率

8.2 性能目标

指标目标值测量方法
缓存命中率> 80%构建日志分析
构建时间减少> 50%有缓存 vs 无缓存
缓存大小< 10GBdu -sh
缓存恢复时间< 30sCI/CD 日志

下一步

基于 MIT 许可发布