
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 有三个作用域:
配置位置
----------
Settings → Secrets → Actions
Settings → Environments → 生产环境
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 分钟,而且每次部署都有完整的日志记录,出问题可以直接回溯。
更多交流点击入群





