构建缓存与产物管理
1. 概念
CI 构建最耗时的两步:装依赖(npm install)和编译(build)。缓存这两步的中间产物能让 CI 从 10 分钟降到 2 分钟。
2. npm 缓存
2.1 GitHub Actions
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # 自动缓存 ~/.npm
底层等价于:
- uses: actions/cache@v4
with:
path: ~/.npm
key: $}} runner.os }}-npm-$}} hashFiles('**/package-lock.json') }}
restore-keys: $}} runner.os }}-npm-
2.2 pnpm 缓存
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
2.3 GitLab CI
cache:
key:
files: [package-lock.json]
paths:
- .npm/
install:
script:
- npm ci --cache .npm --prefer-offline
3. Docker 层缓存
3.1 GitHub Actions + Buildx
- uses: docker/build-push-action@v6
with:
cache-from: type=gha
cache-to: type=gha,mode=max
type=gha:用 GitHub Actions 内置 cache(10GB 上限)。
3.2 Registry 缓存
cache-from: type=registry,ref=ghcr.io/myorg/myapp:cache
cache-to: type=registry,ref=ghcr.io/myorg/myapp:cache,mode=max
跨 runner 共享。
3.3 Dockerfile 缓存优化
# package.json 单独 COPY → npm ci 层可缓存
COPY package*.json ./
RUN npm ci
# 代码变了只重新 COPY + build,不重装依赖
COPY . .
RUN npm run build
3.4 BuildKit cache mount
RUN --mount=type=cache,target=/root/.npm \
npm ci
不进镜像但本地复用。
4. Monorepo 缓存(Turborepo / Nx)
4.1 Turborepo Remote Cache
// turbo.json
{
"remoteCache": { "enabled": true }
}
npx turbo login
npx turbo link
npx turbo run build # 命中远程缓存秒出
4.2 Nx
npx nx build frontend --skip-nx-cache=false
Nx Cloud 提供分布式缓存 + 任务分发。
4.3 CI 本地缓存
- uses: actions/cache@v4
with:
path: .turbo
key: turbo-$}} github.sha }}
restore-keys: turbo-
5. 产物管理
5.1 Artifact 上传
- uses: actions/upload-artifact@v4
with:
name: dist-$}} github.sha }}
path: dist/
retention-days: 7
5.2 跨 job 传递
jobs:
build:
steps:
- run: npm run build
- uses: actions/upload-artifact@v4
with: { name: dist, path: dist/ }
deploy:
needs: build
steps:
- uses: actions/download-artifact@v4
with: { name: dist, path: dist/ }
- run: deploy ./dist
5.3 产物归档到对象存储
# 版本化归档
aws s3 cp dist/ s3://builds/frontend/v1.2.3/ --recursive
ossutil sync dist/ oss://builds/frontend/v1.2.3/
保留 N 个版本,回滚时直接部署旧产物。
6. 构建速度优化
| 手段 | 效果 |
|---|---|
| npm ci(代替 install) | 快 30-50% |
| 缓存 node_modules / .npm | 省 1-3 分钟 |
| Docker 层缓存 | 省镜像构建 2-5 分钟 |
| Turborepo / Nx 缓存 | Monorepo 省 60-90% |
| 并行 job | 总时间 = 最慢而非总和 |
| swc / esbuild 替代 babel/tsc | 编译快 10-50 倍 |
| 只构建变更包(affected) | Monorepo 省大量 |
7. 版本与标记
# 语义化版本
npm version patch/minor/major
# CI 自动标记
git tag "v$(node -p 'require("./package.json").version')"
git push --tags
产物命名包含 git sha:frontend-abc1234.tar.gz
8. 常见反模式
- 不缓存 npm:每次 CI 装 3 分钟
- 缓存 node_modules 目录:跨平台不安全,应该缓存 .npm(cache store)
- artifact 不设过期:磁盘占满
- 不区分 build / deploy:代码没变也重新 build
- Monorepo 全量 build:应该只 build affected
- 产物不版本化:无法回滚到特定版本
- 缓存 key 不含 lockfile hash:依赖变了还用老缓存