「能不能部署」与「什么时候部署」是 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 branches | job 启动前 |
| 人工审批 | Required reviewers | job 启动前 |
| 冷却时间 | Wait timer | job 启动前 |
| 自动健康检查 | 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 发布自动化 — 发布流程与审批/回滚策略
- GitHub Actions OIDC 云认证 — 部署云资源时的免密认证
- GitHub Actions 部署 Vercel — 环境保护在托管平台的实践
- GitHub Actions 安全与 Secret 管理 — Secrets 环境级隔离
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。