我配了三年 GitHub Actions,今天才把 Environment Protection Rules 这套组合拳的账全算清楚了——生产部署不只是配个 Secrets,还有四层防护是你可能从来没配到位的

我配了三年 GitHub Actions,今天才把 Environment Protection Rules 这套组合拳的账全算清楚了——生产部署不只是配个 Secrets,还有四层防护是你可能从来没配到位的。

先说个真实场景:你配了一个 production Environment,往里扔了 DATABASE_URL 和 DEPLOY_KEY,团队 8 个人都能看到这个 Environment 的 Secrets。CI 跑完自动部署到生产,你以为万事大吉。但如果你哪天手滑推了一个 bug 分支,GitHub Actions 不会拦你——它直接跑完了整个 pipeline,等你发现的时候服务器已经躺着了。

这个问题不是配置错误,是 GitHub Actions 的 Environment 机制本身就有一套完整的保护层,只是大多数团队只用了最外层那一层。

第一层:Environment 隔离

在 workflow 里声明 Environment 是第一步,但也是最容易被忽略的一步:

jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

这个 environment: production 不只是打标签,它会触发 Environment 级别的保护规则检查。如果你没配任何保护规则,GitHub 默认允许任何 push 触发部署——等于没隔离。

第二层:Required Reviewers,必须有人审批才能跑

在 GitHub → Settings → Environments → production 里,点 “Required reviewers”,加上你的同事或者特定团队成员。这样每次跑 deploy job 之前,GitHub 会自动挂起,等指定的人 approve 才能继续。审批者会收到邮件通知。

注意这个审批是针对「这一次 run」的,不是针对「这个 workflow 配置」的——也就是说每次部署都得有人点同意,不会出现一次配好永久放行的情况。

第三层:Deployment branch rule,只允许特定分支部署

在 GitHub UI 的 Environment 设置里:

  1. Deployment branch policy → 设为 “Protected branches only”(只允许 main/master 分支)
  2. 打开 “Required reviewers”
  3. 配一个 Wait timer,比如 30 秒

这个组合的意思是:即使有人往 main 分支推了代码,GitHub 也不会立刻开始部署——它会等 30 秒,给团队成员一个「取消」窗口。30 秒内如果没人干预,才会真正开始跑 job。

很多生产事故的根因不是「代码有 bug」,而是「半夜三点自动部署了带 bug 的代码」——Wait timer 把这个自动化按下了一个暂停键。

第四层:Secrets 权限隔离,不是所有 Secret 都应该被所有人看到

Environment-level Secrets 的可见性规则:

  • 只有对该 Environment 有「Manage repository secrets」权限的人才能看到这个 Environment 的 Secrets 内容
  • 普通 Developer 角色的成员,即使能看到 Secrets 的名字,也不知道值是什么

所以如果你发现「团队成员都能看到 Secrets」,大概率是因为你把变量配成了 Repository-level Secrets(仓库级别),而不是 Environment-level Secrets(环境级别)。

正确做法:

# 错:Repository Secrets,所有有仓库写权限的人都能看到
env:
  DATABASE_URL: ${{ secrets.DATABASE_URL }}

# 对:Environment Secrets,只有有 Environment 访问权限的人才能用
jobs:
  deploy:
    environment: production
    steps:
      - run: echo "$DATABASE_URL"
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}

注意 ${{ secrets.DATABASE_URL }} 在 YAML 里引用的是「当前 Environment」的 Secret,不是仓库全局的。

第五层:超时机制,正确理解而不是当炸弹踩

超时不是「优雅退出」,是被 SIGKILL 崩掉——中间状态全留在 runner 上。这个机制本身不是 bug,是你需要正确配置的地方:

jobs:
  deploy:
    timeout-minutes: 30
    environment: production
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        run: |
          trap "echo Received SIGTERM, cleaning up...; cleanup; exit 0" TERM
          deploy.sh

当 GitHub Actions 因为超时而发送 SIGTERM 时,有 trap 的脚本会先执行 cleanup 函数再退出。如果进程在 SIGTERM 阶段就优雅退出,runner 会认为 job 成功完成;如果进程不响应 SIGTERM,runner 才发 SIGKILL 强杀。

实际配置顺序,踩坑经验总结

按照这个顺序配置,能避开大多数坑:

  1. 先建 Environment(Settings → Environments → New environment → production)
  2. 配 Required Reviewers(至少 1 人,建议 2 人互相备份)
  3. 设 Deployment branch policy(只允许 main/master 或受保护的分支)
  4. 加 Wait timer(30 秒起步,夜班部署可以设更长)
  5. 在 Environment 里添加 Secrets(不是仓库级别!是 Environment 级别!)
  6. 在 workflow 里引用 Environmentenvironment: production

每一步都配到位之后,你的生产部署就变成了一道需要多人同意、有时间窗口观察、有限制分支触发的正规流程——不再是「push 就跑,跑完再说」的裸奔状态。

评论区

0 条评论

登录后可评论。

阿监·前端工程化 465 阅读