构建缓存技巧
为什么缓存如此重要
优化 Docker 构建缓存可以大幅减少构建时间,在生产环境中,好的缓存策略能将构建时间从几十分钟缩短到几分钟。
核心原则
1. 指令顺序优化
Dockerfile 中指令的顺序直接影响缓存命中率:
# ❌ 低效:先复制所有代码,再安装依赖
FROM node:18
WORKDIR /app
COPY . .
RUN npm ci # 代码变了就要重新安装
# ✅ 高效:先复制依赖文件,安装依赖,再复制代码
FROM node:18
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci # 只有依赖文件变化才重装
COPY . .
RUN npm run build
2. 合并 RUN 指令
# ❌ 多个 RUN 指令,每层都有缓存
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
# ✅ 合并为一个 RUN 层
RUN apt-get update && \
apt-get install -y curl vim && \
rm -rf /var/lib/apt/lists/*
3. 使用 .dockerignore
# .dockerignore - 减少上下文大小
node_modules
.git
*.log
.env
dist
.cache
.DS_Store
BuildKit 缓存挂载
–mount=type=cache
# syntax=docker/dockerfile:1
FROM golang:1.21
# Go 模块缓存
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
# Go 构建缓存
RUN --mount=type=cache,target=/root/.cache/go-build \
go build -o /app/server
FROM node:18
# npm 缓存
RUN --mount=type=cache,target=/root/.npm \
npm ci
# 如果使用 pnpm
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install
–mount=type=bind(只读缓存)
# 只读挂载构建时的依赖
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=package-lock.json,target=package-lock.json \
npm ci
CI 中的缓存策略
GitHub Actions
- name: Build with cache
uses: docker/build-push-action@v5
with:
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
GitLab CI
build:
script:
- docker buildx build
--cache-from type=registry,ref=$CI_REGISTRY_IMAGE:cache
--cache-to type=registry,ref=$CI_REGISTRY_IMAGE:cache,mode=max
-t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
--push .
自建 CI
# 使用 registry 缓存
docker build \
--cache-from registry.example.com/myapp:cache \
--build-arg BUILDKIT_INLINE_CACHE=1 \
-t myapp:latest .
# 推送缓存
docker push myapp:cache
多阶段构建缓存
# 合理拆分阶段,最大化缓存利用
FROM node:18 AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:18 AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM nginx:alpine AS production
COPY --from=build /app/dist /usr/share/nginx/html
缓存失效监控
# 检查缓存使用情况
DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp . 2>&1 | grep "CACHED"
# 检查镜像层
docker history myapp:latest
# 分析哪层缓存未命中
docker build --no-cache-filter=build -t myapp .
高级技巧
条件性缓存
# 只有 package.json 变化时重建依赖层
COPY package.json yarn.lock ./
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn \
yarn install --frozen-lockfile
# 利用 BuildKit 的 --mount 实现更精细的缓存
RUN --mount=type=cache,target=/app/node_modules \
--mount=type=bind,source=package.json,target=package.json \
npm install
分层缓存失效
# 让 CI 可以控制缓存层
docker build \
--build-arg CACHE_BUST=$(date +%s) \
--build-arg GIT_COMMIT=$CI_COMMIT_SHORT_SHA \
-t myapp .
面试要点
- 缓存的核心是”尽量不重复做已经做过的编译”
- Dockerfile 指令顺序对缓存命中率影响最大
- BuildKit 的
--mount=type=cache是最强大的缓存工具 - CI 中使用 registry 或 GHA 缓存可跨构建共享缓存
- 多阶段构建天然支持缓存分离
面试官常问:一个 Dockerfile 中的缓存是怎么工作的?什么情况下缓存会失效?
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容