环境保护与部署门禁:环境规则、审批与 CD 流程

深度解析 GitHub Actions 的 Environments 机制与部署门禁体系,涵盖 environment protection rules、required reviewers 人工审批、deployment branches 分支限制、并发部署控制、生产环境等待/冷却时间窗,以及如何通过 Deployments API 与 deployment_status 事件构建可审计的 CD 流程。

「能不能部署」与「什么时候部署」是 CD 流程中最关键的两个问题。GitHub Actions 的 Environments 机制把部署目标建模成带保护规则的命名环境:production 可以要求指定审批人、限定部署分支、设置冷却等待期,并通过 concurrency 与 Deployments API 提供并发控制与审计追踪。本文从环境建模到生产门禁,完整覆盖企业级 CD 的工程实践。


一、Environments 概念与配置

1.1 环境是什么

Environment 是仓库级的一个命名部署目标,可以在 Settings → Environments 中创建。它有三个核心作用:

作用说明
权限隔离为不同环境定义独立的 Secrets 与保护规则
审批门禁部署到受保护环境前必须满足规则
可视化追踪Environments 页面展示每次部署的时间线与状态

1.2 在 workflow 中声明环境

name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://staging.example.com
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh staging

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://app.example.com
    steps:
      - run: ./deploy.sh production

当 job 声明 environment 后,GitHub 会在该环境页面上自动记录一次 deployment,并将 job 中的 Secrets 解析为该环境专属的 Secrets(若同名,环境级覆盖仓库级)。

1.3 环境级 Secrets 与变量

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: echo "使用环境变量 ${{ vars.REGION }}"
      - run: ./deploy.sh ${{ secrets.DEPLOY_KEY }}

一句话:把「生产库密码」放在 production 环境的 Secrets 中,把「测试库密码」放在 staging 环境的 Secrets 中——环境级隔离让误用密钥的风险从根上消除。


二、Environment Protection Rules

2.1 保护规则总览

保护规则在 Settings → Environments → <环境名> → Protection rules 中配置,作用于所有引用该环境的 job:

规则配置项效果
Required reviewers指定用户/团队/至少人数部署 job 等待人工审批
Wait timer等待秒数(30-3600)冷却期,防止误操作
Deployment branches分支选择器仅允许特定分支部署
(预留)自定义保护Enterprise 可扩展自定义部署策略

2.2 规则行为示例

# 引用 production 环境的 job 会:
# 1. 检查当前分支是否在 deployment branches 白名单内
# 2. 命中 wait timer 时先等待指定秒数
# 3. 有 required reviewers 时挂起等待审批
# 全部通过后才真正执行 steps
jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: echo "开始生产部署"

审批交互:受保护环境的 job 在「等待审批」状态下,会在仓库的 Checks 区域展示等待卡片,审批人可以点 Approve 或 Reject;Reject 会取消该 job。


三、Required Reviewers 审批

3.1 配置要求

Required reviewers 是最常用的门禁。配置后,部署 job 会等待指定审批人确认:

# 在 Settings → Environments → production → Protection rules 配置:
# Required reviewers: 至少 1 名审批人,可指定 @team

3.2 审批在流程中的位置

PR 合入 → push main → Deploy workflow 启动
    → staging 部署(无审批,自动)
    → production 部署触发
    → ⏳ 等待审批(Check 卡片显示 Pending)
    → 审批人 Approve ✅ → 部署执行
    → 审批人 Reject ❌ → 部署取消,job 失败

3.3 审批与矩阵/多个生产 job

多个引用同一受保护环境的 job 会分别等待审批。若希望一次审批驱动多个 job,应让它们共享一个前置的「审批 gate」job:

jobs:
  approval-gate:
    runs-on: ubuntu-latest
    environment: production     # 只有这个 job 需要审批
    steps:
      - run: echo "审批通过,放行下游"

  deploy-aws:
    needs: approval-gate
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: ./deploy-aws.sh

  deploy-k8s:
    needs: approval-gate
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: ./deploy-k8s.sh

一句话:审批是按 job 计的——让「审批 gate」成为唯一挂起点,下游 job 通过 needs 依赖它,即可实现一次审批、多路并行部署。


四、Deployment Branches 分支限制

4.1 为什么需要分支限制

没有分支限制时,任意分支 push 触发的工作流都能部署到 production——这可能让功能分支的未验证代码直接上线。Deployment branches 规则用分支选择器收紧入口。

4.2 分支选择器语法

# Settings → Environments → production → Deployment branches
# 支持三类选择器:
# 1. 所有分支(默认)
# 2. 受保护分支(Protected branches)
# 3. 自定义分支选择器
#
# 自定义选择器示例:
# main                      # 仅 main
# releases/*                # 所有 release 前缀分支
# main, releases/v*         # 组合,支持 glob

4.3 与触发器的协同

on:
  workflow_dispatch:              # 手动触发:不限定分支
    inputs:
      environment:
        type: choice
        options: [staging, production]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: ${{ github.event.inputs.environment }}
    steps:
      - run: echo "部署到 ${{ github.event.inputs.environment }}"

即便 workflow_dispatch 允许手动选择 production,job 执行时仍会受 Deployment branches 规则约束——手动触发不等于绕过保护。


五、并发部署控制

5.1 用 concurrency 防止部署竞态

多个部署同时进行时,可能出现「旧版本覆盖新版本」的竞态。concurrency 保证同一 group 内只有一个 job 运行:

name: Deploy
on:
  push:
    branches: [main]

concurrency:
  group: production-deploy          # 同组串行化
  cancel-in-progress: false         # 不要取消在跑的任务,排队等待

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: ./deploy.sh

5.2 不同粒度对比

粒度group 取值行为
全局串行production所有分支部署互斥
按分支deploy-${{ github.ref }}同分支串行,不同分支并行
按环境env-${{ github.environment }}staging/production 各自独立
取消旧任务cancel-in-progress: true新部署顶掉旧部署(适合快速迭代)

5.3 手动部署按钮的并发

on:
  workflow_dispatch:
    inputs:
      environment:
        required: true
        type: environment

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    concurrency:
      group: env-${{ inputs.environment }}
      cancel-in-progress: false
    steps:
      - run: ./deploy.sh ${{ inputs.environment }}

一句话:生产环境的 concurrency 应永远 cancel-in-progress: false——宁可排队等待,也不能让新部署打断正在进行的上线。


六、生产环境门禁:等待、审批与时间窗

6.1 三层门禁组合

企业级生产门禁通常叠加多层保护:

jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - run: ./health-check.sh         # 1. 前置健康检查
      - run: ./deploy.sh               # 2. 执行部署
      - run: ./smoke-test.sh           # 3. 部署后冒烟
门禁配置方式时机
分支白名单Deployment branchesjob 启动前
人工审批Required reviewersjob 启动前
冷却时间Wait timerjob 启动前
自动健康检查job 内 step部署前/后

6.2 Wait Timer 的时间窗用法

# Wait timer 常配合审批使用,例如:
# Wait timer: 300 秒
# 含义:审批通过后仍需等待 5 分钟才真正执行
# 用途:给部署窗口留出「反悔期」,配合 concurrency 排队

6.3 在 UI 上模拟完整生产流程

Settings → Environments → production
├── Protection rules
│   ├── Required reviewers: @releases/approvers(至少 1 人)
│   ├── Wait timer: 300 seconds
│   └── Deployment branches: main
├── Environment secrets: PROD_API_KEY, PROD_DB_PASSWORD
└── Environment variables: REGION=ap-southeast-1

七、与 Deployments API 集成

7.1 deployment_status 事件

当引用环境部署的状态变化时,会触发 deployment_status 事件,可用于联动通知、更新看板、触发下游验证:

name: Deployment Status Notifier
on:
  deployment_status:

jobs:
  notify:
    runs-on: ubuntu-latest
    if: ${{ github.event.deployment_status.state == 'success' }}
    steps:
      - run: echo "部署成功,URL=${{ github.event.deployment_status.environment_url }}"
      - run: ./post-to-slack.sh

7.2 用 API 创建部署并设置状态

在自定义 CD 编排中,可以用 github-script 创建 deployment 并回写状态:

const { owner, repo } = context.repo;
const ref = context.sha;

// 创建 deployment
const deployment = await github.rest.repos.createDeployment({
  owner, repo,
  ref,
  environment: 'production',
  production_environment: true,
  required_contexts: [],
});

// 部署完成后回写状态
await github.rest.repos.createDeploymentStatus({
  owner, repo,
  deployment_id: deployment.data.id,
  state: 'success',
  description: '部署完成',
  environment_url: 'https://app.example.com',
});

7.3 回滚:部署状态与分支回退联动

jobs:
  rollback:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: ./rollback-to-last-tag.sh
状态含义典型后续动作
pending部署排队/进行中等待、更新看板
success部署成功通知、启用流量
failure部署失败告警、触发回滚
inactive已下线/被替换清理资源

一句话:Deployments API 是 CD 的「事实来源」——把部署状态回写到 GitHub,就能统一驱动通知、看板与审计,而不是各系统各记一份。


八、最佳实践与常见陷阱

8.1 最佳实践清单

  • 每个环境有独立 Secrets,禁止跨环境复用同一密钥
  • 生产环境必配 Required reviewers + Wait timer
  • Deployment branches 只放行 main 或受保护分支
  • concurrency 对生产环境 cancel-in-progress: false
  • 用 environment.url 指向真实的线上地址
  • 审批 gate 只挂在一个 job,下游 needs 串联
  • 部署状态通过 API 回写,供审计与通知消费

8.2 常见陷阱

陷阱症状对策
每个生产 job 都挂审批审批次数爆炸独立 approval-gate job
cancel-in-progress: true 于生产上线被顶断生产环境改为排队
Secrets 放在仓库级测试环境可读生产密钥迁到环境级
忘记 environment无部署追踪job 显式声明 environment
手动触发绕过分支限制功能分支部署生产信任 Deployment branches 规则约束

总结

GitHub Actions 的环境体系把「部署安全」从约定提升为机制。

门禁维度实现手段解决的问题
谁能部署Required reviewers授权不足/过度授权
何时能部署Wait timer + 时间窗误操作、无冷却
从哪里部署Deployment branches未经验证分支上线
并发安全concurrency group竞态覆盖、滚动冲突
可审计性Deployments API + 环境页面部署无据可查

一句话:把 production 建造成带保护规则的环境,把 staging 建成快速通道——审批、冷却、分支白名单与并发控制共同构成企业 CD 的完整门禁体系。


延伸阅读:

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「github-actions」更多文章

  1. 移动端 CI/CD:Flutter/iOS/Android 构建与签名
  2. 工作流安全加固与供应链防御
  3. 基础设施即代码自动化:Terraform/CloudFormation 与 Atlantis