GitHub Actions 实战:生产级 CI/CD 流水线搭建全过程

GitHub Actions CI/CD 流水线

GitHub Actions 实战:生产级 CI/CD 流水线搭建全过程

很多团队想上 CI/CD,但被 GitLab CI、Jenkins、GitHub Actions 这么多选择搞晕了。这篇直接说清楚我的判断:*如果你的代码在 GitHub 上,用 GitHub Actions 是最优解*——不需要额外部署 runner,YAML 语法简洁,社区 action 生态成熟。

下面是我给真实项目搭建 CI/CD 流水线的完整记录,包括踩过的坑和解决方案。

基础结构:三段式流水线

一个生产级别的 GitHub Actions 流水线通常包含三个阶段:

.github/workflows/ci.yml

name: CI/CD Pipeline

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

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4

- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm'

- name: Install dependencies run: npm ci

- name: Run tests run: npm test

build: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4

- name: Build Docker image run: | docker build -t myapp:${{ github.sha }} .

- name: Push to Registry run: | echo ${{ secrets.REGISTRY_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin docker push ghcr.io/myorg/myapp:${{ github.sha }}

deploy: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' environment: production steps: - name: Deploy to server run: | echo "Deploying ${{ github.sha }} to production..."

这个结构把*测试 → 构建 → 部署*拆成独立 job,通过 `needs` 声明依赖关系。

坑1:secrets 作用域搞错,部署时密码找不到

第一次部署失败,runner 日志直接报:

Error: Input required and not supplied: password

排查半天发现是 `secrets.REGISTRY_TOKEN` 根本没配置。GitHub Actions 的 secrets 有三个作用域:

作用域

配置位置

可见性 --------

----------

-------- Repository secrets

Settings → Secrets → Actions

当前仓库所有 workflow Environment secrets

Settings → Environments → 生产环境

仅限关联该环境的 job Organization secrets

Organization → Settings → Secrets

所有成员仓库可用

*我的教训:生产部署用的镜像仓库密码,必须配在 Environment secrets 里*,而不是普通 Repository secrets。这样做还有一个好处:可以通过 environment protection rules 添加审批环节。

deploy:
  needs: build
  runs-on: ubuntu-latest
  if: github.ref == 'refs/heads/main'
  environment:
    name: production
    url: https://myapp.com
  steps:
    - name: Deploy
      run: kubectl rollout restart deployment/myapp

加上 `environment: production` 之后,GitHub 会在部署前自动检查环境保护规则是否满足。

坑2:npm 缓存无效,每次 CI 都重新下载依赖

项目依赖越来越多,`npm ci` 每次都要等 2-3 分钟。GitHub Actions 的 `setup-node` 内置缓存支持,但需要手动指定:

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'  # 关键:自动缓存 ~/.npm

加了 `cache: 'npm'` 之后,第二次运行从缓存恢复,依赖安装时间从 3 分钟降到 20 秒。

但这里有个坑:*依赖的 `package-lock.json` 变化时缓存会自动失效*,这是预期行为。但有时候 lock 没变但缓存莫名失效,可以手动加缓存 key:

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'

- name: Cache node_modules uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-npm-

坑3:并发控制——多个 PR 同时提交,部署打架

团队大了之后,经常出现 main 分支刚部署完,另一个 hotfix 分支又触发部署,资源抢占导致两边的服务都不稳定。

解决方法是限制并发数:

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    concurrency:
      group: production-deploy
      cancel-in-progress: false

`concurrency.group` 相同的所有 workflow 会在同一组内串行执行。`cancel-in-progress: false` 意味着如果已经在部署,新的部署会排队等待而不是取消正在进行的。

如果想更激进一点——新提交来了直接取消旧部署:

concurrency:
  group: production-deploy
  cancel-in-progress: true

但这个要慎用,如果你的部署涉及数据库迁移,取消进行中的部署可能导致环境处于不一致状态。

生产级细节:多环境部署

成熟的项目通常有三个环境:开发、预发布、生产。GitHub Actions 可以用 matrix 策略处理:

jobs:
  deploy:
    strategy:
      matrix:
        environment: [staging, production]
        include:
          - environment: staging
            branch: develop
          - environment: production
            branch: main
    runs-on: ubuntu-latest
    if: github.ref == format('refs/heads/{0}', matrix.branch)
    environment: ${{ matrix.environment }}
    steps:
      - name: Deploy to ${{ matrix.environment }}
        run: |
          echo "Deploying to ${{ matrix.environment }}"
          ./deploy.sh ${{ matrix.environment }}

每个环境的 secrets 完全隔离,审批规则也可以独立配置。生产环境可以设置为:

- 必须有管理员角色才能审批
- 部署前必须通过安全扫描
- 部署窗口限制(只在工作时间允许)

监控与通知:流水线跑完谁在看?

流水线部署成功了,但如果服务挂了没人知道,那就等于没做。GitHub Actions 可以对接多种通知渠道:

- name: Notify on failure
  if: failure()
  uses: slackapi/slack-github-action@v1.26.0
  with:
    payload: |
      {
        "text": "❌ CI/CD Pipeline Failed",
        "blocks": [{
          "type": "section",
          "text": {
            "type": "mrkdwn",
            "text": "${{ github.workflow }} failed on `${{ github.ref }}`\n"
          }
        }]
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
    SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

`if: failure()` 确保只在流水线失败时发通知,避免平时噪音太多。

总结:GitHub Actions 生产级最佳实践

1. *三段式结构*:test → build → deploy,职责分离,问题好定位
2. *secrets 配在 Environment 里*:生产环境密码必须走环境级别的 secrets,别用仓库级别
3. *善用缓存*:`cache: 'npm'` 能把依赖安装从 3 分钟降到 20 秒
4. *并发控制*:用 `concurrency.group` 避免多部署打架
5. *环境保护规则*:生产部署必须有审批环节,别让人肉部署钻空子
6. *失败通知*:接 Slack/飞书,流水线挂了第一时间知道

这套流水线跑稳之后,团队从提交代码到生产生效的时间从人工操作的 30 分钟降到自动化 5 分钟,而且每次部署都有完整的日志记录,出问题可以直接回溯。

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享