Skip to content

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 CheckESLint、Prettier、TypeScript代码规范 + 类型安全10-30s
BuildVite、Webpack、esbuild编译打包,确保构建不报错30s-3min
TestJest、Vitest、Playwright功能正确性验证1-10min
DeployVercel、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、定时等)启动开关
JobWorkflow 中的一个任务单元,运行在独立的 Runner 上流水线上的一个工位
StepJob 中的一个步骤,可以是 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使用社区 Actionuses: actions/checkout@v4
steps[*].with传入 Action 参数with: { node-version: '20' }
steps[*].run执行 shell 命令run: npm run build
steps[*].envStep 级环境变量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 build

concurrency 配置的作用:当同一 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=max

2.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 test
Matrix 展开后的并行执行:

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 install45s3s93%
Cypress 安装60s2s97%
整体流水线3min1min67%

2.6 自定义 Action 开发

GitHub Actions 支持三种类型的自定义 Action:

类型语言适用场景
JavaScript ActionNode.js逻辑复杂,需要调用 GitHub API
Composite ActionYAML组合多个现有步骤,轻量复用
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 build

JavaScript 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上的资源)

这种架构的核心思想:

  1. HTML 文件:部署在源站,Cache-Control: no-cache,每次请求都验证是否有新版本
  2. 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 FlowGitHub FlowTrunk 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-optimization

5.3 Conventional Commits 规范

Conventional Commits 是一种提交信息的书写规范,格式如下:

<type>(<scope>): <subject>

<body>

<footer>

类型(type)定义:

Type说明示例
feat新功能feat(auth): add OAuth2 login
fixBug 修复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
ciCI 配置ci: add node 22 to test matrix
chore杂务chore: update dependencies

结合 commitlinthusky 在 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 & Merge

GitHub 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=high

npm audit 会检查 node_modules 中所有包是否存在已知安全漏洞。--audit-level=high 表示只有高危及以上漏洞才会导致 CI 失败。

Snyk 集成

yaml
- uses: snyk/actions/node@master
  env:
    SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
  with:
    args: --severity-threshold=high

GitHub 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原生支持自动修复
Snyk200 tests/月Snyk DB(更全)GitHub Action自动 PR
Dependabot完全免费GitHub Advisory原生集成自动 PR
Socket.dev开源免费自有 DBGitHub App告警

七、面试高频问题

问题 1:说说你对 CI/CD 的理解,在项目中是怎么实践的?

回答思路

先说概念(CI 持续集成 + CD 持续交付/持续部署),然后结合实际项目讲具体实践:

  1. 代码规范层:ESLint + Prettier + commitlint 通过 husky 在提交前拦截
  2. CI 层:GitHub Actions 配置 PR 检查流水线(lint → typecheck → test → build)
  3. CD 层:main 分支合并后自动部署到 Vercel/自建服务器
  4. 质量门禁:测试覆盖率阈值、Bundle Size 限制
  5. 安全扫描:Dependabot 自动更新依赖 + npm audit

追问:CI 流水线太慢怎么优化?

  • 缓存 node_modules(actions/cache)
  • 并行执行 lint/test/build(去掉不必要的 needs 依赖)
  • 使用 paths 过滤,只在相关代码变更时触发
  • 换用更快的工具(如 Vitest 替代 Jest,esbuild/SWC 替代 Babel)
  • Self-hosted Runner 提升硬件性能

问题 2:蓝绿部署和金丝雀发布有什么区别?前端怎么实现灰度?

回答思路

蓝绿部署是全量切换——两套环境,瞬间切换流量;金丝雀发布是按比例逐步切换。

前端实现灰度的具体方案:

  1. Nginx 层:通过 split_clients 按用户 IP 或 Cookie 分流
  2. CDN 层:通过 CDN 的 A/B Testing 功能(如 CloudFront 的 Origin Groups)
  3. 应用层:通过 Feature Flag SDK(如 LaunchDarkly、Unleash)控制新功能开关
  4. DNS 层:加权 DNS 解析到不同的服务器集群

追问:灰度发布时如何监控新版本的健康状况?

  • 错误监控(Sentry 按版本分组)
  • 性能指标(Core Web Vitals 按版本对比)
  • 业务指标(转化率、跳出率按版本分组)
  • 自动化告警 + 自动回滚

问题 3:前端项目如何做到零停机部署?

回答思路

核心策略是 HTML 和静态资源分离 + 文件名 hash

  1. JS/CSS 文件名包含 content hash,部署到 CDN,永久缓存
  2. 先部署新的 JS/CSS 到 CDN(与旧版 HTML 无关联,不影响线上)
  3. 再部署新的 HTML 到源站(Nginx 通过符号链接原子切换)
  4. 旧用户:旧 HTML + 旧 JS(CDN 上的旧文件不删除)
  5. 新用户:新 HTML + 新 JS

追问:如果 CDN 上的旧文件被清理了,还在访问的用户怎么办?

  • CDN 旧文件至少保留 24-48 小时再清理
  • SPA 应用可以通过 chunk 加载失败时自动刷新页面的方式兜底
  • 使用 Service Worker 提前缓存关键资源

问题 4:GitHub Actions 中如何安全地管理密钥?

回答思路

  1. Repository Secrets:在 Settings → Secrets 中存储,在 workflow 中通过 ${{ secrets.KEY }} 引用
  2. Environment Secrets:关联到特定环境(staging/production),可配置审批人
  3. OIDC:使用 OpenID Connect 与云服务商(AWS、GCP、Azure)建立信任关系,无需存储长期凭证
  4. 安全原则: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 应用这类需要频繁发布的项目,因为:

  1. Web 应用不需要维护多个版本(用户总是访问最新版本)
  2. 分支管理简单,降低心智负担
  3. PR 驱动开发与 Code Review 文化契合
  4. 与 CI/CD 无缝配合——merge to main 即触发部署

追问:Trunk Based Development 为什么要求 Feature Flag?

因为所有代码直接提交到 main,未完成的功能也会被部署到生产环境。Feature Flag 可以在代码层面控制功能的可见性,确保未完成的功能对用户不可见。

问题 6:前端 monorepo 项目的 CI/CD 有什么特殊考虑?

回答思路

Monorepo 的核心挑战是变更检测和按需执行

  1. 变更检测:只对有变更的包运行 CI(使用 turborepo--filternx affected
  2. 依赖关系:包 A 变更后,依赖 A 的包 B 也需要重新测试
  3. 缓存:Turborepo 的 Remote Cache 可以跨 CI 共享构建缓存
  4. 并行构建:利用 turbo run build --concurrency 并行构建多个包
  5. 版本发布:使用 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 流水线本身的可靠性?

回答思路

  1. 版本锁定:Actions 使用具体版本号(@v4),避免 @latest@main
  2. 超时设置:为每个 Job 设置 timeout-minutes,防止死锁占用 Runner
  3. 重试机制:对不稳定的步骤(如网络请求)添加重试
  4. 监控告警:CI 失败时通过 Slack/飞书/钉钉 通知
  5. 定期维护:定期更新 Actions 版本,清理废弃的 Workflow
  6. 安全审计:审查第三方 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 }}

八、延伸阅读

推荐资源

相关工具链

分类工具说明
CI/CD 平台GitHub Actions、GitLab CI、CircleCI、Jenkins流水线执行引擎
部署平台Vercel、Netlify、Cloudflare Pages前端专属托管
容器化Docker、Podman标准化运行环境
编排Kubernetes、Docker Compose容器编排与管理
监控Sentry、Datadog、Grafana线上质量监控
Feature FlagLaunchDarkly、Unleash、Flagsmith功能开关管理
依赖管理Renovate、Dependabot自动化依赖更新
MonorepoTurborepo、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 的终极目标就是持续优化这四个指标——提高部署频率、缩短前置时间、降低失败率、加速故障恢复。

用心学习,用代码说话 💻