人人都会AI编程

19.3 CI/CD 自动化部署流程

更新时间:2026-07-10

把代码从开发者的本地机器推送到生产环境,如果每一步都靠人工操作,不仅繁琐易错,还会拖慢迭代速度。CI/CD(持续集成与持续部署)就是来解决这个问题的:通过自动化流水线,让代码变更在经过一系列质量检查后,安全、快速地到达服务器上。 对于 Node.js 项目,这套流程的搭建并不复杂,而且收益巨大。

19.3.1 理解 CI/CD 的核心环节

在 Node.js 项目中,一个典型的 CI/CD 流水线通常包含以下步骤:

  1. 代码推送触发:开发者将代码推送到 Git 仓库的某个分支(如 maindevelop)。
  2. 持续集成(CI)
  • 安装项目依赖
  • 运行代码风格检查(ESLint + Prettier)
  • 执行单元测试和集成测试
  • 构建 TypeScript 编译或打包(如果需要)
  • 生成测试覆盖率报告
  1. 制品构建:将应用打包为可部署的形式,可能是压缩包、Docker 镜像,或直接将源代码推送到服务器。
  2. 持续部署(CD):将构建好的制品发布到目标环境(测试环境、预发环境、生产环境),并执行启动或更新操作。

每一道关卡都是自动化执行的,只要有一个步骤失败,流水线就会中止并通知开发者,这样可以尽早发现问题,避免坏代码蔓延

19.3.2 选择 CI/CD 工具

市面上主流的 CI/CD 工具很多,对于 Node.js 项目来说,选择相对简单:

  • GitHub Actions:与 GitHub 仓库深度集成,配置即代码(YAML 文件),免费额度对中小项目足够,是目前最流行的选择。
  • GitLab CI/CD:如果团队使用 GitLab,其内置的 CI/CD 功能同样强大,配置文件为 .gitlab-ci.yml
  • Jenkins:功能最灵活但需要自行维护服务器,更适合大型企业或有定制需求。
  • 其他托管服务:Travis CI、CircleCI、Bitbucket Pipelines 等,也都对 Node.js 有良好支持。

本节以 GitHub Actions 为例展开说明,因为它的使用门槛最低,而且社区提供了大量现成的 Action,可以像搭积木一样组装流水线。

19.3.3 编写第一个 Node.js CI 流水线

在项目根目录创建 .github/workflows/ci.yml,内容如下:

name: Node.js CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [ 16, 18, 20 ]  # 在多个 Node 版本上测试

    steps:
      - name: 检出代码
        uses: actions/checkout@v3
      
      - name: 设置 Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'     # 自动缓存 node_modules
      
      - name: 安装依赖
        run: npm ci       # 使用锁文件安装,更快且确定
      
      - name: 代码风格检查
        run: npm run lint
      
      - name: 运行单元测试
        run: npm test
      
      - name: 上传测试覆盖率
        if: matrix.node-version == '18'
        uses: coverallsapp/github-action@v2
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}

这个流水线会做几件事:

  • pushmaindevelop 分支,或者创建针对 main 的 PR 时触发。
  • 在三个 Node.js 版本(16、18、20)上并行运行。
  • 每个版本都执行一遍 npm ci(严格按 lock 文件安装)、lint 检查、单元测试。
  • 仅在 Node 18 版本上,将测试覆盖率报告上传到 Coveralls,方便跟踪代码质量变化。

这里有几个实用细节需要注意:

  • 使用 npm ci 而不是 npm install,是为了保证依赖安装的稳定一致,并且速度更快。
  • actions/setup-nodecache: 'npm' 会自动缓存 node_modules,下次运行时会大幅提速。
  • 多版本测试可以有效避免因 Node.js 版本升级引起的兼容性问题。

19.3.4 构建 Docker 镜像并推送

对于容器化部署(如 Kubernetes、Docker Swarm),CI 流水线还需要构建 Docker 镜像并推送到镜像仓库。以下示例在测试通过后,构建并推送镜像到 Docker Hub:

  build-and-push:
    needs: test          # 必须等测试通过
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'  # 仅 main 分支执行
    
    steps:
      - uses: actions/checkout@v3
      
      - name: 登录 Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: 构建并推送镜像
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: |
            myapp:latest
            myapp:${{ github.sha }}   # 用提交 SHA 标记版本

这里引入了几个关键点:

  • needs: test 确保只有测试通过后才构建镜像,避免将未通过测试的代码打包。
  • 使用 GitHub Secrets(DOCKER_USERNAMEDOCKER_PASSWORD)存储敏感信息,绝不硬编码。
  • 给镜像打上 latest 标签和 Git 提交 SHA,便于追踪与回滚。

19.3.5 部署到测试/生产环境

当构建好的镜像推送到仓库后,后续的部署方式取决于实际运行环境。

场景一:部署到自有服务器(如裸机或云主机)

可以通过 SSH 执行远程命令,或使用 Docker Compose 更新服务。使用 appleboy/ssh-action 可简化 SSH 操作:

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - name: 远程部署
        uses: appleboy/ssh-action@v0
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/myapp
            docker-compose pull
            docker-compose up -d --remove-orphans

这需要事先在服务器上准备好 docker-compose.yml,确保应用可以通过 Compose 拉起并更新。

场景二:部署到 Kubernetes

可以使用 kubectl 或 Helm 更新部署。例如,使用 azure/k8s-deploy 或直接执行命令:

      - name: 部署到 K8s
        run: |
          kubectl set image deployment/myapp myapp=myapp:${{ github.sha }}

同样,Kubeconfig 等敏感凭证需通过 Secrets 安全传递。

场景三:部署到 Serverless 平台(如阿里云函数计算、Vercel)

如果是无服务器平台,通常有专属的 CLI 或 Action。例如部署到 Vercel:

      - name: 部署到 Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}

19.3.6 环境区分与配置管理

生产、预发、测试环境通常需要不同的配置(数据库连接、密钥等)。CI/CD 流水线应当根据分支或标签区分目标环境:

  • develop 分支 → 部署到测试环境
  • release/* 分支或 main 分支 → 部署到预发/生产环境

在流水线中可以通过环境变量注入对应配置,例如在 Node.js 中常见的是使用 dotenv 或配置中心。CI 环境中的敏感配置也通过 GitHub Environments 或 Secrets 来区分:

  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - run: npm run deploy
        env:
          DB_URL: ${{ secrets.STAGING_DB_URL }}

GitHub 的 Environment 功能允许针对每个环境设置不同的保护规则(如审批人、限制分支),这样生产环境的部署可以要求手动确认,确保发布可控。

19.3.7 实践中的关键点与常见问题

1. 尽量保持快速

一个 CI/CD 流水线如果每次都要跑 20 分钟,开发的节奏就会被打乱。优化手段包括:

  • 利用依赖缓存(actions/setup-nodecache、Docker 分层缓存)
  • 并行执行不相关的任务(如 lint 和 test 可以分两个 job)
  • 只运行与变更相关的测试(利用 Monorepo 工具或路径过滤)

2. 回滚策略

自动部署最大的风险是“一键上线,一键炸服”。好的 CD 流程一定包含快速回滚能力:

  • Docker 镜像每次打上唯一标签(如 Git SHA),回滚时只需重新部署旧版本镜像。
  • Kubernetes 可以直接 rollout undo
  • 裸机部署可保留之前版本的部署目录,利用软链接切换。

3. 数据库迁移

如果部署代码伴随数据库结构变更,需要将数据库迁移步骤也纳入流水线。通常是先运行迁移脚本(如 Prisma 的 prisma migrate deploy),再更新应用代码。确保迁移脚本是幂等的、可回滚的,并做好备份。

4. 安全扫描

建议在流水线中加入依赖漏洞扫描:

      - name: npm 安全审计
        run: npm audit --audit-level=high

如果发现高危漏洞,可让流水线失败,禁止部署。也可以集成 Snyk、Dependabot 等更全面的工具。

5. 通知与日志

配置失败或成功后的通知(如 Slack、钉钉、企业微信),让团队及时了解部署状态。同时保留日志与部署记录,方便追溯。

19.3.8 小结:从手动到自动的思维转变

搭建 CI/CD 流水线,本质上是用代码来定义发布流程,把“人肉操作”转化为“自动化脚本”。对于 Node.js 项目来说,整个流程实现起来非常平滑,因为绝大多数工具链(lint、test、build)都是 npm 脚本的一部分,CI 机器上只要有 Node.js 就能跑起来。

一次配置,永久受益。一旦流水线运转起来,团队就可以专注于写代码和业务逻辑,而把检查、构建、部署这种重复性工作交给机器。这才是工程化的核心价值:让可靠的流程成为日常,而不是依赖个别人的小心与记忆。