主题
CI/CD
CI/CD 是现代软件工程的基石,它将代码从开发者的本地环境安全、高效地推送到生产环境。对于前端工程师而言,掌握 CI/CD 不仅是日常工作的必备技能,更是理解整个软件交付流程的关键。
本文将从 核心概念 → GitHub Actions → 部署策略 → 部署平台 → Git 工作流 → 质量保障 → 面试高频题 七个维度全面拆解 CI/CD。
一、CI/CD 核心概念
1.1 CI、CD 的定义与区别
CI(Continuous Integration,持续集成) 指的是开发者频繁地将代码变更合并到主干分支,每次合并都会触发自动化的构建和测试流程,以尽早发现集成问题。
CD 有两层含义:
- Continuous Delivery(持续交付):在 CI 的基础上,确保代码随时可以部署到生产环境,但最终的部署动作需要人工触发。
- Continuous Deployment(持续部署):在持续交付的基础上更进一步,每次通过所有阶段的代码变更都会自动部署到生产环境,完全消除人工干预。
三者的递进关系:
CI(持续集成)
┌─────────────────────────────────────────────┐
│ 开发者推送代码 → 自动构建 → 自动测试 │
│ │
│ 核心目标:尽早发现集成错误 │
└─────────────────────────────────────────────┘
│
▼
CD(持续交付)
┌─────────────────────────────────────────────┐
│ CI 通过 → 自动部署到预发布环境 → 人工审批 │
│ ↓ │
│ 手动部署到生产 │
│ │
│ 核心目标:代码随时可发布 │
└─────────────────────────────────────────────┘
│
▼
CD(持续部署)
┌─────────────────────────────────────────────┐
│ CI 通过 → 自动部署到预发布 → 自动部署到生产 │
│ │
│ 核心目标:全自动化,零人工干预 │
└─────────────────────────────────────────────┘三者的关键区别:
| 维度 | 持续集成(CI) | 持续交付(CD) | 持续部署(CD) |
|---|---|---|---|
| 自动化范围 | 构建 + 测试 | 构建 + 测试 + 预发布部署 | 构建 + 测试 + 生产部署 |
| 人工干预 | 代码合并需人工审查 | 生产部署需人工触发 | 完全自动化 |
| 风险级别 | 低 | 中 | 高(需要完善的测试体系) |
| 适用场景 | 所有团队 | 大多数团队 | 测试覆盖率极高的成熟团队 |
| 发布频率 | - | 按需发布 | 每次提交都发布 |
1.2 CI/CD 流水线的典型阶段
一条完整的前端 CI/CD 流水线通常包含以下阶段:
完整的前端 CI/CD 流水线:
┌──────────────────────────────────────────────────────────────────────┐
│ CI/CD Pipeline │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Code │ │ Build │ │ Test │ │ Deploy │ │
│ │ Check │──▶│ │──▶│ │──▶│ │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ ESLint │ │ tsc编译 │ │ 单元测试 │ │ 预发布 │ │
│ │ Prettier│ │ Vite构建 │ │ E2E测试 │ │ 灰度发布 │ │
│ │ stylelint│ │ 产物分析 │ │ 覆盖率 │ │ 全量发布 │ │
│ │ commitlint│ │ │ │ 快照测试 │ │ │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 失败时任意阶段都会中断流水线,通知开发者修复 │
└──────────────────────────────────────────────────────────────────────┘每个阶段的职责:
| 阶段 | 工具 | 目的 | 耗时 |
|---|---|---|---|
| Code Check | ESLint、Prettier、TypeScript | 代码规范 + 类型安全 | 10-30s |
| Build | Vite、Webpack、esbuild | 编译打包,确保构建不报错 | 30s-3min |
| Test | Jest、Vitest、Playwright | 功能正确性验证 | 1-10min |
| Deploy | Vercel、Docker、Nginx | 将产物交付到目标环境 | 1-5min |
1.3 CI/CD 的核心价值
为什么前端团队需要 CI/CD?
没有 CI/CD 的工作流:
开发者A ──push──▶ main ──▶ 手动 npm run build ──▶ 手动 scp 到服务器
开发者B ──push──▶ main ──▶ 忘了跑测试 ──▶ 线上出 bug
开发者C ──push──▶ main ──▶ 构建失败 ──▶ 其他人无法部署
问题:
1. 容易遗忘步骤(忘了跑 lint / test)
2. 环境不一致(本地能跑,CI 上跑不了)
3. 部署过程不可追溯
4. 出问题无法快速回滚
有 CI/CD 的工作流:
开发者A ──push──▶ 触发 Pipeline ──▶ lint ✓ ──▶ build ✓ ──▶ test ✓ ──▶ deploy ✓
开发者B ──push──▶ 触发 Pipeline ──▶ lint ✓ ──▶ build ✓ ──▶ test ✗ ──▶ 阻断 ──▶ 通知修复
开发者C ──push──▶ 触发 Pipeline ──▶ lint ✗ ──▶ 阻断 ──▶ 通知修复
优势:
1. 自动化执行所有质量检查
2. 统一的构建环境(Runner 容器)
3. 完整的部署记录和审计日志
4. 一键回滚能力二、GitHub Actions
GitHub Actions 是 GitHub 原生提供的 CI/CD 平台,也是目前前端社区使用最广泛的 CI/CD 工具。
2.1 核心概念
GitHub Actions 架构模型:
┌─────────────────────────────────────────────────────┐
│ Workflow(工作流) │
│ 一个 .yml 文件定义一个 Workflow │
│ │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ Job A (build) │ │ Job B (test) │ │
│ │ │ │ │ │
│ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │
│ │ │ Step 1 │ │ │ │ Step 1 │ │ │
│ │ │ checkout │ │ │ │ checkout │ │ │
│ │ └──────────────┘ │ │ └──────────────┘ │ │
│ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │
│ │ │ Step 2 │ │ │ │ Step 2 │ │ │
│ │ │ npm install │ │ │ │ npm test │ │ │
│ │ └──────────────┘ │ │ └──────────────┘ │ │
│ │ ┌──────────────┐ │ │ │ │
│ │ │ Step 3 │ │ │ Runner: ubuntu │ │
│ │ │ npm build │ │ │ │ │
│ │ └──────────────┘ │ └────────────────────┘ │
│ │ │ ▲ │
│ │ Runner: ubuntu │ │ │
│ └────────────────────┘ needs: build │
│ │
│ 触发条件:on: push / pull_request / schedule ... │
└─────────────────────────────────────────────────────┘核心术语解析:
| 概念 | 说明 | 类比 |
|---|---|---|
| Workflow | 一个完整的自动化流程,由 .github/workflows/*.yml 定义 | 一条流水线 |
| Event | 触发 Workflow 的事件(push、PR、定时等) | 启动开关 |
| Job | Workflow 中的一个任务单元,运行在独立的 Runner 上 | 流水线上的一个工位 |
| Step | Job 中的一个步骤,可以是 Action 或 shell 命令 | 工位上的一个操作 |
| Action | 可复用的最小执行单元,社区或自定义 | 封装好的工具 |
| Runner | 执行 Job 的虚拟机环境(GitHub 提供或自托管) | 执行任务的工人 |
2.2 workflow.yml 语法详解
一个完整的 workflow 文件解析:
yaml
name: CI Pipeline
on:
push:
branches: [main, develop]
paths-ignore:
- '**.md'
- 'docs/**'
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * 1'
workflow_dispatch:
inputs:
environment:
description: 'Deploy environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
env:
NODE_VERSION: '20'
PNPM_VERSION: '9'
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm run lint
- run: pnpm run typecheck
test:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm run test -- --coverage
- uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
build:
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm run build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
deploy:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment: production
steps:
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- run: echo "Deploying to production..."语法要点速查:
| 字段 | 作用 | 示例 |
|---|---|---|
on | 定义触发事件 | push, pull_request, schedule, workflow_dispatch |
on.push.branches | 限定触发分支 | [main, develop] |
on.push.paths-ignore | 忽略特定路径变更 | ['**.md', 'docs/**'] |
jobs.<id>.runs-on | 指定运行环境 | ubuntu-latest, macos-latest, windows-latest |
jobs.<id>.needs | 声明 Job 依赖 | needs: [lint, test] |
jobs.<id>.if | 条件执行 | if: github.ref == 'refs/heads/main' |
jobs.<id>.timeout-minutes | 超时控制 | timeout-minutes: 10 |
jobs.<id>.environment | 关联部署环境 | environment: production |
steps[*].uses | 使用社区 Action | uses: actions/checkout@v4 |
steps[*].with | 传入 Action 参数 | with: { node-version: '20' } |
steps[*].run | 执行 shell 命令 | run: npm run build |
steps[*].env | Step 级环境变量 | env: { API_KEY: ${{ secrets.API_KEY }} } |
${{ secrets.* }} | 引用加密密钥 | ${{ secrets.DEPLOY_TOKEN }} |
${{ github.* }} | 引用上下文信息 | ${{ github.sha }}, ${{ github.actor }} |
2.3 常用场景配置
PR 检查(Lint + Test + Build)
yaml
name: PR Check
on:
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm run test -- --ci
- run: npm run buildconcurrency 配置的作用:当同一 PR 有新的推送时,自动取消正在运行的旧流水线,避免资源浪费。
自动部署到 GitHub Pages
yaml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-pages-artifact@v3
with:
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4发布 npm 包
yaml
name: Publish Package
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm run build
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}Docker 镜像构建与推送
yaml
name: Docker Build
on:
push:
tags: ['v*']
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.ref_name }}
ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max2.4 Matrix 策略:多版本测试
Matrix 允许你在多个环境组合下并行运行同一个 Job:
yaml
name: Cross-Version Test
on: [push, pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [18, 20, 22]
exclude:
- os: macos-latest
node-version: 18
include:
- os: ubuntu-latest
node-version: 22
experimental: true
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm testMatrix 展开后的并行执行:
strategy.matrix:
os: [ubuntu, macos, windows]
node: [18, 20, 22]
展开为 3 × 3 = 9 个 Job(排除后 8 个):
┌─────────────────────────────────────────────────┐
│ 并行执行 │
│ │
│ ubuntu + node18 ubuntu + node20 ubuntu+22 │
│ macos + node20 macos + node22 │
│ windows + node18 windows + node20 windows+22 │
│ │
│ fail-fast: false → 一个失败不影响其他 │
└─────────────────────────────────────────────────┘2.5 缓存优化
CI 中最耗时的步骤通常是依赖安装。通过缓存 node_modules 或包管理器缓存目录,可以大幅减少流水线运行时间。
使用 setup-node 内置缓存
yaml
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'这是最简单的方式,setup-node 会自动缓存包管理器的全局缓存目录。
使用 actions/cache 精细控制
yaml
- uses: actions/cache@v4
id: deps-cache
with:
path: |
node_modules
~/.cache/Cypress
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
deps-${{ runner.os }}-
- run: pnpm install --frozen-lockfile
if: steps.deps-cache.outputs.cache-hit != 'true'缓存策略的关键设计:
缓存命中逻辑:
第一次运行:
key: deps-linux-abc123 → 未命中
restore-keys: deps-linux- → 未命中
→ 完整安装 → 保存缓存 deps-linux-abc123
第二次运行(lockfile 未变):
key: deps-linux-abc123 → 命中 ✓
→ 跳过安装 → 节省 30-60s
第三次运行(lockfile 变化):
key: deps-linux-def456 → 未命中
restore-keys: deps-linux- → 部分命中(恢复旧缓存)
→ 增量安装(只安装差异部分)→ 保存新缓存缓存优化效果对比
| 场景 | 无缓存 | 有缓存 | 节省 |
|---|---|---|---|
| pnpm install | 45s | 3s | 93% |
| Cypress 安装 | 60s | 2s | 97% |
| 整体流水线 | 3min | 1min | 67% |
2.6 自定义 Action 开发
GitHub Actions 支持三种类型的自定义 Action:
| 类型 | 语言 | 适用场景 |
|---|---|---|
| JavaScript Action | Node.js | 逻辑复杂,需要调用 GitHub API |
| Composite Action | YAML | 组合多个现有步骤,轻量复用 |
| Docker Action | 任意 | 需要特定环境依赖 |
Composite Action 示例
创建 .github/actions/setup-env/action.yml:
yaml
name: 'Setup Environment'
description: 'Setup Node.js and install dependencies'
inputs:
node-version:
description: 'Node.js version'
required: false
default: '20'
runs:
using: 'composite'
steps:
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
shell: bash在 Workflow 中使用:
yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-env
with:
node-version: '20'
- run: pnpm run buildJavaScript Action 示例
action.yml:
yaml
name: 'Bundle Size Check'
description: 'Check bundle size and comment on PR'
inputs:
max-size:
description: 'Maximum bundle size in KB'
required: true
build-path:
description: 'Path to build output'
required: false
default: 'dist'
outputs:
size:
description: 'Current bundle size'
runs:
using: 'node20'
main: 'index.js'index.js:
js
const core = require('@actions/core')
const github = require('@actions/github')
const fs = require('fs')
const path = require('path')
async function run() {
const maxSize = parseInt(core.getInput('max-size'))
const buildPath = core.getInput('build-path')
const files = fs.readdirSync(buildPath)
let totalSize = 0
for (const file of files) {
const filePath = path.join(buildPath, file)
const stats = fs.statSync(filePath)
if (stats.isFile()) {
totalSize += stats.size
}
}
const sizeKB = Math.round(totalSize / 1024)
core.setOutput('size', sizeKB)
if (sizeKB > maxSize) {
core.setFailed(`Bundle size ${sizeKB}KB exceeds limit ${maxSize}KB`)
} else {
core.info(`Bundle size: ${sizeKB}KB (limit: ${maxSize}KB)`)
}
if (github.context.eventName === 'pull_request') {
const octokit = github.getOctokit(process.env.GITHUB_TOKEN)
await octokit.rest.issues.createComment({
...github.context.repo,
issue_number: github.context.payload.pull_request.number,
body: `📦 Bundle Size: **${sizeKB}KB** / ${maxSize}KB ${sizeKB > maxSize ? '❌' : '✅'}`,
})
}
}
run().catch((error) => core.setFailed(error.message))三、前端部署策略
3.1 静态资源部署:CDN + HTML 分离
前端部署的核心原则:HTML 文件和静态资源(JS/CSS/图片)应该分开部署。
前端资源分离部署架构:
用户请求
│
▼
┌────────────────┐ ┌──────────────────────────────┐
│ DNS 解析 │ │ CDN 边缘节点 │
└───────┬────────┘ │ │
│ │ index.abc123.js │
▼ │ index.def456.css │
┌────────────────┐ │ logo.ghi789.png │
│ Nginx / LB │ │ │
│ │ │ Cache-Control: │
│ index.html │ │ max-age=31536000 │
│ (不缓存/短缓存) │ │ immutable │
│ │ └──────────────────────────────┘
│ Cache-Control:│ ▲
│ no-cache │ │
└────────────────┘ 静态资源请求(带 hash 文件名)
│
▼
返回 HTML(引用CDN上的资源)这种架构的核心思想:
- HTML 文件:部署在源站,
Cache-Control: no-cache,每次请求都验证是否有新版本 - JS/CSS/图片:部署到 CDN,文件名包含内容 hash,
Cache-Control: max-age=31536000, immutable,永久缓存
为什么要这样设计?
场景:发布新版本
旧版本:
index.html → <script src="https://cdn.example.com/app.a1b2c3.js">
新版本:
index.html → <script src="https://cdn.example.com/app.d4e5f6.js">
用户访问:
1. 请求 index.html → 源站返回最新 HTML(no-cache 保证)
2. HTML 中引用新 hash 的 JS → CDN 返回新文件
3. 旧文件 app.a1b2c3.js 仍然在 CDN 上
4. 正在访问的用户不受影响(他们的 HTML 还引用旧 JS)
结果:零停机更新,新旧版本共存3.2 增量部署 vs 全量部署
| 特征 | 增量部署 | 全量部署 |
|---|---|---|
| 发布内容 | 只上传变化的文件 | 上传所有文件 |
| 速度 | 快 | 慢 |
| 风险 | 可能出现资源引用不一致 | 一致性高 |
| 实现复杂度 | 高(需 diff 机制) | 低 |
| 推荐场景 | 超大型项目、CDN 资源更新 | 中小项目、容器化部署 |
增量部署需要注意的关键问题——先部署资源,再部署 HTML:
错误的部署顺序(先部署 HTML):
时间轴:
T1: 部署新 index.html(引用 new.js)
T2: 用户请求 → 拿到新 HTML → 请求 new.js → 404(还没部署)
T3: 部署 new.js
T4: 用户刷新 → 正常
正确的部署顺序(先部署资源):
时间轴:
T1: 部署 new.js 到 CDN(旧 HTML 不引用它,无影响)
T2: 部署新 index.html(引用 new.js)
T3: 用户请求 → 拿到新 HTML → 请求 new.js → 200 ✓3.3 蓝绿部署
蓝绿部署维护两套完全相同的生产环境(蓝色和绿色),在任何时刻只有一套环境对外服务。
蓝绿部署流程:
初始状态:蓝色环境在线
┌───────────┐
用户 ────▶│ 负载均衡 │
└─────┬─────┘
│
┌──────┴──────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ 蓝色环境 │ │ 绿色环境 │
│ v1.0 │ │ (空闲) │
│ ● 在线 │ │ ○ 离线 │
└──────────┘ └──────────┘
部署新版本到绿色环境:
┌───────────┐
用户 ────▶│ 负载均衡 │
└─────┬─────┘
│
┌──────┴──────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ 蓝色环境 │ │ 绿色环境 │
│ v1.0 │ │ v2.0 │
│ ● 在线 │ │ ○ 测试中 │
└──────────┘ └──────────┘
验证通过后切换流量:
┌───────────┐
用户 ────▶│ 负载均衡 │
└─────┬─────┘
│
┌──────┴──────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ 蓝色环境 │ │ 绿色环境 │
│ v1.0 │ │ v2.0 │
│ ○ 待命 │ │ ● 在线 │
└──────────┘ └──────────┘
回滚:将流量切回蓝色环境(秒级)3.4 金丝雀发布(灰度发布)
金丝雀发布的核心思想:先将新版本推送给一小部分用户,观察指标无异常后再逐步扩大范围。
金丝雀发布流程:
阶段 1:5% 流量切到新版本
┌───────────┐
用户 ────▶│ 负载均衡 │
└─────┬─────┘
│
┌─────────┼─────────┐
│ 95% │ │ 5%
▼ │ ▼
┌──────────┐ │ ┌──────────┐
│ v1.0 │ │ │ v2.0 │
│ 稳定版本 │ │ │ 金丝雀 │
└──────────┘ │ └──────────┘
│
监控指标:错误率、性能、用户反馈
阶段 2:指标正常,扩大到 30%
┌─────────┼─────────┐
│ 70% │ │ 30%
▼ │ ▼
┌──────────┐ │ ┌──────────┐
│ v1.0 │ │ │ v2.0 │
└──────────┘ │ └──────────┘
阶段 3:全量发布
┌─────────┼─────────┐
│ 0% │ │ 100%
▼ │ ▼
┌──────────┐ │ ┌──────────┐
│ v1.0 │ │ │ v2.0 │
│ (下线) │ │ │ 新稳定版 │
└──────────┘ │ └──────────┘实现金丝雀的常见方式:
| 方式 | 实现层 | 精细度 | 复杂度 |
|---|---|---|---|
Nginx split_clients | 网关层 | 按比例 | 低 |
| Cookie / Header 标记 | 应用层 | 按用户 | 中 |
| Feature Flag 平台 | SDK 层 | 按规则 | 高 |
| K8s Ingress 权重 | 基础设施层 | 按比例 | 中 |
3.5 滚动更新
滚动更新逐步替换实例,确保在整个更新过程中服务始终可用:
滚动更新过程(4 个实例):
初始状态:
[v1] [v1] [v1] [v1] 全部 v1
Step 1:下线一个 v1,启动一个 v2
[v2] [v1] [v1] [v1] 25% 新版本
Step 2:
[v2] [v2] [v1] [v1] 50% 新版本
Step 3:
[v2] [v2] [v2] [v1] 75% 新版本
Step 4:
[v2] [v2] [v2] [v2] 100% 新版本
特点:
✓ 始终有实例在服务
✓ 不需要双倍资源
✗ 更新过程中新旧版本共存
✗ 回滚较慢(需要反向滚动)3.6 部署策略对比
| 策略 | 停机时间 | 资源成本 | 回滚速度 | 风险 | 适用场景 |
|---|---|---|---|---|---|
| 蓝绿部署 | 零 | 2x | 秒级 | 低 | 核心业务 |
| 金丝雀发布 | 零 | 1.05x-1.3x | 秒级 | 最低 | 用户量大的产品 |
| 滚动更新 | 零 | 1x | 分钟级 | 中 | K8s 默认策略 |
| 全量发布 | 短暂 | 1x | 分钟级 | 高 | 小型/内部项目 |
3.7 回滚机制
前端回滚策略:
方案 1:CDN + HTML 版本回退
┌──────────────────────────────────────────┐
│ OSS / S3 存储所有历史版本 │
│ │
│ /releases/v1.0/index.html │
│ /releases/v1.1/index.html ← 当前版本 │
│ /releases/v1.2/index.html ← 有 bug │
│ │
│ 回滚操作:将 Nginx 指向 v1.1 的 HTML │
│ 耗时:< 10 秒 │
└──────────────────────────────────────────┘
方案 2:容器化回滚
┌──────────────────────────────────────────┐
│ Docker Registry 保留历史镜像 │
│ │
│ app:v1.0 │
│ app:v1.1 ← 回滚目标 │
│ app:v1.2 ← 有 bug │
│ │
│ kubectl rollout undo deployment/app │
│ 耗时:< 30 秒 │
└──────────────────────────────────────────┘
方案 3:Git Revert + 重新走 CI/CD
┌──────────────────────────────────────────┐
│ git revert HEAD │
│ git push origin main │
│ │
│ 触发 CI/CD → 构建 → 测试 → 部署 │
│ 耗时:3-10 分钟 │
│ 安全但较慢 │
└──────────────────────────────────────────┘四、前端部署平台
4.1 Vercel
Vercel 是目前前端社区最流行的部署平台,由 Next.js 的创建者开发。
核心特性:
Vercel 工作流程:
Git Push / PR
│
▼
┌──────────────────┐
│ Vercel Platform │
│ │
│ 1. 检测框架 │ ← 自动识别 Next.js / Vite / CRA 等
│ 2. 安装依赖 │
│ 3. 执行构建 │
│ 4. 优化产物 │
│ 5. 部署到边缘 │
└──────┬───────────┘
│
▼
┌──────────────────────────────────────────┐
│ Vercel Edge Network │
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 东京 │ │ 硅谷 │ │ 法兰克│ │ 新加坡│ │
│ │ │ │ │ │ 福 │ │ │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ │
│ │
│ 全球 CDN + Edge Functions │
└──────────────────────────────────────────┘Preview Deployments:每个 PR 自动生成一个独立的预览 URL,团队成员可以直接访问该 URL 查看变更效果,极大提升了 Code Review 效率。
Edge Functions:在 CDN 边缘节点运行服务端逻辑,延迟极低,适合 A/B Testing、地理位置重定向、认证等场景。
vercel.json 常用配置:
json
{
"buildCommand": "pnpm run build",
"outputDirectory": "dist",
"framework": "vite",
"rewrites": [
{ "source": "/api/:path*", "destination": "https://api.example.com/:path*" },
{ "source": "/(.*)", "destination": "/index.html" }
],
"headers": [
{
"source": "/assets/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
}
],
"redirects": [
{ "source": "/old-page", "destination": "/new-page", "permanent": true }
]
}4.2 Netlify
Netlify 与 Vercel 功能类似,同样是 Jamstack 架构的倡导者。
Vercel vs Netlify 对比:
┌────────────────┬──────────────────┬──────────────────┐
│ 功能 │ Vercel │ Netlify │
├────────────────┼──────────────────┼──────────────────┤
│ 零配置部署 │ ✓ │ ✓ │
│ Preview Deploy │ ✓ │ ✓ │
│ Edge Functions │ Edge Functions │ Edge Functions │
│ Serverless │ Serverless Fn │ Netlify Fn │
│ 表单处理 │ ✗ │ 内置表单处理 │
│ 身份认证 │ ✗ │ Netlify Identity│
│ 分流测试 │ Edge Config │ Split Testing │
│ 框架支持 │ Next.js 最佳 │ 框架中立 │
│ 带宽(免费) │ 100GB/月 │ 100GB/月 │
│ 构建时间(免费) │ 6000min/月 │ 300min/月 │
│ 并发构建(免费) │ 1 │ 1 │
└────────────────┴──────────────────┴──────────────────┘4.3 Cloudflare Pages
Cloudflare Pages 基于 Cloudflare 全球网络,拥有覆盖超过 300 个城市的边缘节点。
核心优势:
- 无限带宽:免费计划不限带宽
- 全球分布:基于 Cloudflare 的边缘网络
- Workers 集成:可以直接使用 Cloudflare Workers 作为边缘函数
- 构建速度快:每月 500 次免费构建
toml
# wrangler.toml
name = "my-app"
compatibility_date = "2024-01-01"
pages_build_output_dir = "dist"
[vars]
API_BASE_URL = "https://api.example.com"4.4 自建方案
Nginx 静态部署
nginx
server {
listen 80;
server_name example.com;
root /var/www/app/current;
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache";
}
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1024;
gzip_comp_level 6;
brotli on;
brotli_types text/plain text/css application/json application/javascript text/xml;
brotli_comp_level 6;
}Docker 容器化部署
Dockerfile:
dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]docker-compose.yml:
yaml
version: '3.8'
services:
web:
build: .
ports:
- "80:80"
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3完整的自建部署 CI/CD 流水线:
yaml
name: Deploy to Server
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- uses: pnpm/action-setup@v4
with:
version: 9
- run: pnpm install --frozen-lockfile
- run: pnpm run build
- uses: burnett01/rsync-deployments@7.0.1
with:
switches: -avzr --delete
path: dist/
remote_path: /var/www/app/releases/${{ github.sha }}/
remote_host: ${{ secrets.DEPLOY_HOST }}
remote_user: ${{ secrets.DEPLOY_USER }}
remote_key: ${{ secrets.DEPLOY_KEY }}
- uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_KEY }}
script: |
ln -sfn /var/www/app/releases/${{ github.sha }} /var/www/app/current
sudo nginx -s reload
cd /var/www/app/releases && ls -t | tail -n +6 | xargs rm -rf这个部署脚本的巧妙之处:使用符号链接(ln -sfn)实现原子切换,current 永远指向最新版本。保留最近 5 个版本,方便快速回滚。
五、Git 工作流
5.1 三种主流工作流对比
Git Flow
Git Flow 分支模型:
main ─────●────────────────●──────────────────●───▶
\ / \ /
\ release / \ release /
\ ┌───●───┐/ \ ┌───●───┐ /
▼│ │▼ ▼│ │▼
develop ───●───●───●───●────●───●───●───●────●───▶
\ / \ /
\ / \ /
feature/ \ / feature/ \ /
login ●──● payment ●──●特点:
- 两条长期分支:
main(生产)和develop(开发) - 短期分支:
feature/*、release/*、hotfix/* - 适合版本发布周期较长的项目
- 分支管理复杂度高
GitHub Flow
GitHub Flow 分支模型:
main ─────●──────●────────●──────●────────●───▶
\ / \ / \
\ / \ / \
feature/ \/ feature/ \/ feature/ \
login ●──● search ●──● cart ●───(PR open)
PR→merge PR→merge特点:
- 只有一条长期分支:
main - 所有变更通过 Feature Branch + Pull Request
- 简单直观,适合持续部署的 Web 项目
- 前端项目最常用的工作流
Trunk Based Development
Trunk Based Development:
main ─────●──●──●──●──●──●──●──●──●──●───▶
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
直接提交到 main,或极短生命周期的分支
短生命周期分支(< 1天):
main ──●──────●──●──────●──●───▶
\ / \ /
●──● ●──●
(几小时) (几小时)特点:
- 所有开发者直接向主干提交代码
- 分支生命周期极短(通常不超过一天)
- 需要 Feature Flag 控制未完成功能
- 适合成熟团队和强自动化测试的项目
三者对比
| 维度 | Git Flow | GitHub Flow | Trunk Based |
|---|---|---|---|
| 长期分支数 | 2(main + develop) | 1(main) | 1(main) |
| 分支生命周期 | 长(days-weeks) | 中(hours-days) | 极短(hours) |
| 复杂度 | 高 | 低 | 中(需要 Feature Flag) |
| 发布频率 | 低(按版本发布) | 中(PR merge 即发布) | 高(每次提交即发布) |
| 适用场景 | 客户端软件、有版本号的产品 | Web 应用、SaaS | 大型团队、高频发布 |
| CI/CD 要求 | 低 | 中 | 高(必须有完善的自动化测试) |
| 代表企业 | 传统软件公司 | GitHub、大多数创业公司 | Google、Meta |
5.2 分支命名规范
推荐的分支命名规范:
feature/ ── 新功能
feature/user-login
feature/JIRA-123-search
bugfix/ ── Bug 修复
bugfix/fix-header-overflow
bugfix/JIRA-456-cart-total
hotfix/ ── 紧急线上修复
hotfix/fix-payment-crash
release/ ── 发布分支
release/v1.2.0
chore/ ── 工程化任务
chore/upgrade-vite-5
chore/ci-optimization5.3 Conventional Commits 规范
Conventional Commits 是一种提交信息的书写规范,格式如下:
<type>(<scope>): <subject>
<body>
<footer>类型(type)定义:
| Type | 说明 | 示例 |
|---|---|---|
feat | 新功能 | feat(auth): add OAuth2 login |
fix | Bug 修复 | fix(cart): correct total calculation |
docs | 文档变更 | docs(readme): update install guide |
style | 代码格式 | style: format with prettier |
refactor | 重构 | refactor(api): simplify request handler |
perf | 性能优化 | perf(list): add virtual scroll |
test | 测试 | test(utils): add unit tests for formatDate |
build | 构建相关 | build: upgrade vite to v5 |
ci | CI 配置 | ci: add node 22 to test matrix |
chore | 杂务 | chore: update dependencies |
结合 commitlint 和 husky 在 CI 前就拦截不规范的提交:
json
{
"scripts": {
"prepare": "husky"
},
"devDependencies": {
"@commitlint/cli": "^19.0.0",
"@commitlint/config-conventional": "^19.0.0",
"husky": "^9.0.0"
}
}commitlint.config.js:
js
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert'],
],
'subject-max-length': [2, 'always', 72],
},
}.husky/commit-msg:
sh
npx --no -- commitlint --edit $1结合 Conventional Commits 可以自动生成 CHANGELOG:
json
{
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
}
}六、质量保障
6.1 代码审查(Code Review)最佳实践
Code Review 是保障代码质量的最后一道人工防线。
高效 Code Review 流程:
开发者提交 PR
│
▼
┌──────────────────────────────────────┐
│ 自动化检查(CI) │
│ │
│ ✓ Lint 通过 │
│ ✓ 类型检查通过 │
│ ✓ 单元测试通过 │
│ ✓ 构建成功 │
│ ✓ 覆盖率 > 80% │
│ ✓ Bundle Size 未超限 │
│ ✓ Preview 部署成功 │
└──────────────────┬───────────────────┘
│
▼
┌──────────────────────────────────────┐
│ 人工审查 │
│ │
│ □ 业务逻辑是否正确 │
│ □ 边界情况是否处理 │
│ □ 代码是否可维护 │
│ □ 性能是否有隐患 │
│ □ 安全性(XSS、敏感信息泄露) │
│ □ Preview 环境功能验证 │
└──────────────────┬───────────────────┘
│
▼
Approve & MergeGitHub PR 自动化配置(.github/CODEOWNERS):
*.ts @frontend-team
*.tsx @frontend-team
*.css @frontend-team @design-team
/api/ @backend-team
/ci/ @devops-team分支保护规则建议:
| 规则 | 建议值 | 说明 |
|---|---|---|
| Require PR reviews | 至少 1 人 | 核心仓库建议 2 人 |
| Require status checks | 必须 | CI 检查必须通过 |
| Require branches up to date | 建议开启 | 确保 PR 基于最新 main |
| Require signed commits | 可选 | 安全要求高的项目 |
| Include administrators | 建议开启 | 管理员也需遵守规则 |
6.2 自动化测试在 CI 中的集成
yaml
name: Test Suite
on: [push, pull_request]
jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- uses: pnpm/action-setup@v4
with:
version: 9
- run: pnpm install --frozen-lockfile
- run: pnpm run test:unit -- --coverage --reporter=junit --outputFile=junit.xml
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: |
junit.xml
coverage/
e2e-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- uses: pnpm/action-setup@v4
with:
version: 9
- run: pnpm install --frozen-lockfile
- run: pnpm exec playwright install --with-deps chromium
- run: pnpm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/6.3 覆盖率门禁
覆盖率门禁是指在 CI 中设定最低测试覆盖率阈值,低于阈值则阻断合并。
vitest.config.ts 中配置覆盖率阈值:
ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'json-summary', 'html'],
thresholds: {
lines: 80,
functions: 80,
branches: 75,
statements: 80,
},
},
},
})在 CI 中集成覆盖率报告到 PR 评论:
yaml
- uses: davelosert/vitest-coverage-report-action@v2
if: always() && github.event_name == 'pull_request'
with:
json-summary-path: coverage/coverage-summary.json
json-final-path: coverage/coverage-final.json覆盖率门禁工作流:
PR 提交
│
▼
运行测试(带 --coverage)
│
▼
生成覆盖率报告
│
├──▶ lines: 85% ≥ 80% ✓
├──▶ functions: 78% < 80% ✗ ──▶ CI 失败,阻断合并
├──▶ branches: 82% ≥ 75% ✓
└──▶ statements: 84% ≥ 80% ✓6.4 依赖安全扫描
前端项目通常有数百甚至数千个间接依赖,其中任何一个存在安全漏洞都可能影响最终产品。
npm audit
yaml
- run: npm audit --audit-level=highnpm audit 会检查 node_modules 中所有包是否存在已知安全漏洞。--audit-level=high 表示只有高危及以上漏洞才会导致 CI 失败。
Snyk 集成
yaml
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=highGitHub Dependabot
创建 .github/dependabot.yml:
yaml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 10
reviewers:
- "frontend-team"
labels:
- "dependencies"
groups:
dev-dependencies:
dependency-type: "development"
update-types:
- "minor"
- "patch"
prod-dependencies:
dependency-type: "production"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"Dependabot 的 groups 功能可以将多个依赖更新合并到一个 PR 中,减少 PR 数量。
安全扫描工具对比:
| 工具 | 免费额度 | 漏洞数据库 | CI 集成 | 修复建议 |
|---|---|---|---|---|
| npm audit | 完全免费 | npm Advisory | 原生支持 | 自动修复 |
| Snyk | 200 tests/月 | Snyk DB(更全) | GitHub Action | 自动 PR |
| Dependabot | 完全免费 | GitHub Advisory | 原生集成 | 自动 PR |
| Socket.dev | 开源免费 | 自有 DB | GitHub App | 告警 |
七、面试高频问题
问题 1:说说你对 CI/CD 的理解,在项目中是怎么实践的?
回答思路:
先说概念(CI 持续集成 + CD 持续交付/持续部署),然后结合实际项目讲具体实践:
- 代码规范层:ESLint + Prettier + commitlint 通过 husky 在提交前拦截
- CI 层:GitHub Actions 配置 PR 检查流水线(lint → typecheck → test → build)
- CD 层:main 分支合并后自动部署到 Vercel/自建服务器
- 质量门禁:测试覆盖率阈值、Bundle Size 限制
- 安全扫描:Dependabot 自动更新依赖 + npm audit
追问:CI 流水线太慢怎么优化?
- 缓存 node_modules(actions/cache)
- 并行执行 lint/test/build(去掉不必要的
needs依赖) - 使用
paths过滤,只在相关代码变更时触发 - 换用更快的工具(如 Vitest 替代 Jest,esbuild/SWC 替代 Babel)
- Self-hosted Runner 提升硬件性能
问题 2:蓝绿部署和金丝雀发布有什么区别?前端怎么实现灰度?
回答思路:
蓝绿部署是全量切换——两套环境,瞬间切换流量;金丝雀发布是按比例逐步切换。
前端实现灰度的具体方案:
- Nginx 层:通过
split_clients按用户 IP 或 Cookie 分流 - CDN 层:通过 CDN 的 A/B Testing 功能(如 CloudFront 的 Origin Groups)
- 应用层:通过 Feature Flag SDK(如 LaunchDarkly、Unleash)控制新功能开关
- DNS 层:加权 DNS 解析到不同的服务器集群
追问:灰度发布时如何监控新版本的健康状况?
- 错误监控(Sentry 按版本分组)
- 性能指标(Core Web Vitals 按版本对比)
- 业务指标(转化率、跳出率按版本分组)
- 自动化告警 + 自动回滚
问题 3:前端项目如何做到零停机部署?
回答思路:
核心策略是 HTML 和静态资源分离 + 文件名 hash:
- JS/CSS 文件名包含 content hash,部署到 CDN,永久缓存
- 先部署新的 JS/CSS 到 CDN(与旧版 HTML 无关联,不影响线上)
- 再部署新的 HTML 到源站(Nginx 通过符号链接原子切换)
- 旧用户:旧 HTML + 旧 JS(CDN 上的旧文件不删除)
- 新用户:新 HTML + 新 JS
追问:如果 CDN 上的旧文件被清理了,还在访问的用户怎么办?
- CDN 旧文件至少保留 24-48 小时再清理
- SPA 应用可以通过 chunk 加载失败时自动刷新页面的方式兜底
- 使用 Service Worker 提前缓存关键资源
问题 4:GitHub Actions 中如何安全地管理密钥?
回答思路:
- Repository Secrets:在 Settings → Secrets 中存储,在 workflow 中通过
${{ secrets.KEY }}引用 - Environment Secrets:关联到特定环境(staging/production),可配置审批人
- OIDC:使用 OpenID Connect 与云服务商(AWS、GCP、Azure)建立信任关系,无需存储长期凭证
- 安全原则:Secrets 不会出现在日志中(自动遮蔽),fork 仓库的 PR 无法访问 Secrets
追问:如何防止 CI 中泄露 Secrets?
- GitHub 自动遮蔽所有 Secrets 值的日志输出
- 禁止在 PR 的 workflow 中使用
pull_request_target+ checkout PR 代码的组合(防止恶意 PR 窃取 Secrets) - 定期轮换 Secrets
- 使用
environment保护规则,生产环境的 Secrets 需要审批
问题 5:Git Flow 和 GitHub Flow 分别适合什么场景?为什么大多数前端项目用 GitHub Flow?
回答思路:
Git Flow 适合有明确版本发布节奏的项目(如桌面应用、移动端 App),因为它有专门的 release 分支来管理版本。
GitHub Flow 适合 Web 应用这类需要频繁发布的项目,因为:
- Web 应用不需要维护多个版本(用户总是访问最新版本)
- 分支管理简单,降低心智负担
- PR 驱动开发与 Code Review 文化契合
- 与 CI/CD 无缝配合——merge to main 即触发部署
追问:Trunk Based Development 为什么要求 Feature Flag?
因为所有代码直接提交到 main,未完成的功能也会被部署到生产环境。Feature Flag 可以在代码层面控制功能的可见性,确保未完成的功能对用户不可见。
问题 6:前端 monorepo 项目的 CI/CD 有什么特殊考虑?
回答思路:
Monorepo 的核心挑战是变更检测和按需执行:
- 变更检测:只对有变更的包运行 CI(使用
turborepo的--filter或nx affected) - 依赖关系:包 A 变更后,依赖 A 的包 B 也需要重新测试
- 缓存:Turborepo 的 Remote Cache 可以跨 CI 共享构建缓存
- 并行构建:利用
turbo run build --concurrency并行构建多个包 - 版本发布:使用 Changesets 管理多包版本发布
yaml
name: Monorepo CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm exec turbo run lint test build --filter=...[HEAD~1]
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}问题 7:如何保证 CI/CD 流水线本身的可靠性?
回答思路:
- 版本锁定:Actions 使用具体版本号(
@v4),避免@latest或@main - 超时设置:为每个 Job 设置
timeout-minutes,防止死锁占用 Runner - 重试机制:对不稳定的步骤(如网络请求)添加重试
- 监控告警:CI 失败时通过 Slack/飞书/钉钉 通知
- 定期维护:定期更新 Actions 版本,清理废弃的 Workflow
- 安全审计:审查第三方 Action 的源码,或 fork 到自己的组织
失败通知示例:
yaml
- uses: slackapi/slack-github-action@v1
if: failure()
with:
payload: |
{
"text": "CI Failed: ${{ github.repository }} - ${{ github.workflow }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "❌ *${{ github.workflow }}* failed on `${{ github.ref_name }}`\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}八、延伸阅读
推荐资源
- GitHub Actions 官方文档 —— 最权威的 Actions 参考
- Vercel 文档 —— 前端部署最佳实践
- Conventional Commits 规范 —— 提交信息标准化
- The Twelve-Factor App —— 云原生应用开发方法论
- Trunk Based Development —— 主干开发模式详解
- Continuous Delivery(Jez Humble) —— 持续交付的经典著作
- DORA Metrics —— 衡量 DevOps 效能的四个关键指标
相关工具链
| 分类 | 工具 | 说明 |
|---|---|---|
| CI/CD 平台 | GitHub Actions、GitLab CI、CircleCI、Jenkins | 流水线执行引擎 |
| 部署平台 | Vercel、Netlify、Cloudflare Pages | 前端专属托管 |
| 容器化 | Docker、Podman | 标准化运行环境 |
| 编排 | Kubernetes、Docker Compose | 容器编排与管理 |
| 监控 | Sentry、Datadog、Grafana | 线上质量监控 |
| Feature Flag | LaunchDarkly、Unleash、Flagsmith | 功能开关管理 |
| 依赖管理 | Renovate、Dependabot | 自动化依赖更新 |
| Monorepo | Turborepo、Nx、Lerna | 多包管理工具 |
| 版本发布 | Changesets、semantic-release | 自动化版本管理 |
DORA 四个关键指标
| 指标 | 说明 | Elite 水平 |
|---|---|---|
| 部署频率(Deployment Frequency) | 代码部署到生产的频率 | 按需部署(每天多次) |
| 变更前置时间(Lead Time for Changes) | 从代码提交到生产部署的时间 | < 1 小时 |
| 变更失败率(Change Failure Rate) | 导致生产故障的变更比例 | < 5% |
| 故障恢复时间(Time to Restore Service) | 从故障发生到恢复的时间 | < 1 小时 |
这四个指标由 Google 的 DORA 团队提出,是衡量团队 DevOps 成熟度的核心标准。CI/CD 的终极目标就是持续优化这四个指标——提高部署频率、缩短前置时间、降低失败率、加速故障恢复。