CI/CD 流水线搭建
手动部署是技术债务的温床——"只有我知道怎么部署"、"上次部署忘了跑测试"、"生产环境的环境变量和测试不一样"。CI/CD(持续集成 / 持续部署)流水线把构建、测试、部署自动化,让每次代码变更都经过一致的质量门禁,最终自动或半自动地到达生产环境。本章以 GitHub Actions 为主,构建一条完整的 Nuxt4 CI/CD 流水线。
1. CI/CD 的核心概念
1.1 持续集成 vs 持续部署
| 概念 | 含义 | 触发时机 |
|---|---|---|
| CI(持续集成) | 每次提交自动运行 lint + 类型检查 + 测试 | Push / PR |
| CD(持续交付) | CI 通过后自动构建部署包,等待手动审批发布 | Merge to main |
| CD(持续部署) | CI 通过后自动部署到生产环境,无需手动干预 | Merge to main |
大多数团队选择持续交付——自动化到"部署包准备就绪",发布到生产由人工确认。完全的持续部署需要非常成熟的测试覆盖和监控体系。
1.2 流水线设计原则
- 快速反馈:lint 和类型检查最先执行(最快),测试其次,构建最后
- 并行执行:独立的检查项并行跑,缩短总时长
- 失败即停:任何步骤失败都阻止后续步骤,避免浪费资源
- 幂等性:同一份代码多次构建结果相同
2. GitHub Actions 工作流
2.1 基础流水线
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: npx nuxi typecheck
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: npx vitest run --coverage
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage
path: coverage/
build:
needs: [lint, typecheck, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: .output/关键设计:
- lint / typecheck / test 并行执行——互不依赖,总时长取最慢的一个
- build 依赖前三个 job——
needs: [lint, typecheck, test],任何一个失败都不会构建 - pnpm cache——
actions/setup-node自动缓存 pnpm store,第二次运行安装依赖只需几秒 - artifact 上传——构建产物和覆盖率报告保存为 artifact,后续 job 或手动下载使用
2.2 优化:减少重复安装
上面的配置中,每个 job 都要安装依赖。可以用 reusable workflow 或 cache 减少重复:
# 共享的 setup 步骤
setup: &setup
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile或者使用 actions/cache 缓存 node_modules:
- uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}3. 环境变量安全管理
3.1 环境变量的分类
| 类型 | 示例 | 存储位置 |
|---|---|---|
| 公开配置 | API 基础 URL、功能开关 | nuxt.config.ts / .env |
| 构建时密钥 | Sentry DSN、Analytics ID | GitHub Secrets → 构建时注入 |
| 运行时密钥 | 数据库密码、JWT Secret | 部署平台的环境变量 |
3.2 GitHub Secrets
敏感信息存储在 GitHub Repository Secrets 中,CI 中通过 ${{ secrets.XXX }} 访问:
- run: pnpm build
env:
NUXT_PUBLIC_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
NUXT_SESSION_SECRET: ${{ secrets.SESSION_SECRET }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}安全原则:
- Secrets 在日志中自动遮蔽(显示为
***) - Fork 的 PR 无法访问 Secrets(防止恶意 PR 窃取)
- 定期轮转密钥
3.3 多环境配置
# GitHub Environments 配置
jobs:
deploy-staging:
environment: staging
env:
DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
deploy-production:
environment: production
env:
DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}GitHub Environments 支持:
- 不同环境有独立的 Secrets
- Production 环境可以配置审批人——部署前需要指定人员批准
- 部署保护规则——等待时间、分支限制
4. 部署流水线
4.1 Vercel 自动部署
Vercel 与 GitHub 深度集成,Push 到 main 自动部署:
# 不需要额外的 GitHub Actions 配置
# Vercel 的 GitHub App 自动检测 push 并部署
# PR 自动创建 Preview Deployment如果需要在 CI 检查通过后才部署,可以在 Vercel 设置中关闭自动部署,改用 Vercel CLI:
deploy:
needs: [lint, typecheck, test, build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx vercel --prod --token=${{ secrets.VERCEL_TOKEN }}4.2 Docker 部署流水线
deploy:
needs: [lint, typecheck, test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
docker pull ghcr.io/${{ github.repository }}:${{ github.sha }}
docker compose up -d --no-deps app4.3 Cloudflare Pages 部署
deploy:
needs: [lint, typecheck, test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm build
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: pages deploy .output/public --project-name=my-app5. 灰度发布
5.1 什么是灰度发布
灰度发布(Canary Release)是将新版本先发布给一小部分用户,观察效果后再逐步扩大范围:
v1.0(当前版本)= 90% 流量
v1.1(新版本) = 10% 流量
↓ 观察指标正常
v1.0 = 50% | v1.1 = 50%
↓ 确认无问题
v1.1 = 100%
5.2 实现方式
Cloudflare:通过 Traffic Splitting 实现流量分配。
Vercel:支持 Skew Protection,可以在新旧版本之间平滑过渡。
自建 Nginx:
upstream canary {
server 127.0.0.1:3000 weight=9; # 旧版本 90%
server 127.0.0.1:3001 weight=1; # 新版本 10%
}5.3 灰度监控
灰度期间必须监控关键指标,任何异常立即回滚:
- 错误率:新版本的 5xx 错误率是否高于旧版本
- 响应时间:P95 延迟是否增加
- 业务指标:转化率、页面停留时间是否下降
6. 多环境管理
6.1 环境分层
| 环境 | 用途 | 数据 | 部署触发 |
|---|---|---|---|
| local | 开发调试 | 本地/Mock | 手动 |
| preview | PR 预览 | 测试数据 | PR 创建 |
| staging | 预发布验证 | 生产数据副本 | Merge to develop |
| production | 正式环境 | 真实数据 | Merge to main + 审批 |
6.2 分支策略
feature/xxx → PR → develop(自动部署到 staging)
↓
main(手动审批部署到 production)
简化方案(适合小团队):
feature/xxx → PR → main(自动部署到 production)
小团队可以跳过 staging 环境,依赖 Vercel 的 Preview Deployments 作为预发布验证。
7. 流水线性能优化
7.1 缓存策略
| 缓存对象 | 缓存键 | 失效时机 |
|---|---|---|
| pnpm store | pnpm-lock.yaml 哈希 | 依赖变化 |
| Nuxt 构建缓存 | .nuxt/ 目录 | 配置或依赖变化 |
| Docker 层 | Dockerfile 指令 | 指令或 COPY 内容变化 |
| Playwright 浏览器 | Playwright 版本 | 版本更新 |
7.2 时间预算
合理的 CI 时间预算:
| 步骤 | 目标时长 |
|---|---|
| 安装依赖(缓存命中) | < 30s |
| ESLint | < 60s |
| TypeScript 检查 | < 60s |
| 单元测试 | < 120s |
| 构建 | < 120s |
| 总计 | < 5 分钟 |
超过 10 分钟的 CI 流水线会显著影响开发效率——开发者会等待 CI 结果才能合并 PR。
本章小结
- CI 三层检查并行执行:lint + typecheck + test → build,总时长控制在 5 分钟内
- 环境变量:GitHub Secrets 存储敏感信息,Environments 隔离多环境配置,审批人保护生产部署
- 部署集成:Vercel 零配置自动部署,Docker 推送镜像 + SSH 部署,Cloudflare Wrangler CLI
- 灰度发布:10% → 50% → 100% 渐进式发布,监控错误率和延迟
- 多环境:local → preview → staging → production,分支策略匹配部署流程
- 性能优化:pnpm cache / Docker 层缓存 / 并行 job,总时长 < 5 分钟