构建缓存详解
构建缓存是 Docker 构建系统的核心特性,能够显著加快构建速度。本文将深入介绍 Docker 构建缓存的工作原理、优化策略和高级用法。
目录
缓存概述
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 Layer2.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 build3.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.04BuildKit 缓存
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 密钥 |
ssh | SSH 代理 | 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 package4.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-cache5.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:latest6.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 无缓存 |
| 缓存大小 | < 10GB | du -sh |
| 缓存恢复时间 | < 30s | CI/CD 日志 |
下一步
- 学习 Docker Build 概述
- 了解 BuildKit 详解
- 掌握 构建优化技巧
- 深入 Dockerfile 完整参考