业务故事站
P3 Apollo

发布通道:版本推进与多环境

发布通道是"指哪打哪"的开关:stable 通道指向哪个期望状态,Agent 就拉哪个版本;切换通道的指向,就是一次受控的版本推进。dev → test → prod 用不同通道,配合"版本单调递增",让多环境发布有序、可审计、可回滚。看完这 3 个故事,你就能搭出自己的多环境发布流水。

平台运维 / SRE 应用开发 发布通道 版本推进 多环境 期望状态 共 3 个故事

能 / 不能速览

✅ 这个主题能做
  • 发布通道 CRUD(软删),demo seed 自带 stable 通道指向 demo-app
  • POST /channels/:id/set-desired-state 把通道指向新的 active 期望状态
  • Agent 拉取目标优先通道指向,切换指向即推进发布
  • 多通道对应多环境,各自独立指向,版本单调约束保证推进有序
  • 回滚走 rollback 生成高版本新行,再指回通道,全流程留痕
⛔ 这个主题做不了
  • 指标门控渐进发布(metric_gate / 分批 promote)属 P2,通道只存储 promotion_policy
  • 审批 gate(require_approval)属 P2,通道 approval_policy 仅存储不执行
  • set-desired-state 要求目标必须是 active 的期望状态,draft 指向会报错
  • demo 只 seed 了 stable 一个通道,dev / test / prod 通道需自行创建

适用角色

本主题面向两个角色:

  • 平台运维 / SRE(陈工):创建 / 维护通道、切换通道指向、编排多环境发布,是发布的调度者。
  • 应用开发(阿强):在 dev / test 环境自助推进自己的版本,验证通过后请运维把 prod 通道指过去。

安全合规(刘经理)抽查通道指向与审计记录;平台管理员负责授权。

能力速览(能做什么)

通道管理

POST /channels 创建发布通道,GET /channels 查看全部通道与各自 current_desired_state_id;软删不破坏历史部署记录。

发布目标切换

POST /channels/:id/set-desired-state 把通道指向新的 active 期望状态——切换指向即推进发布,一次调用完成。

Agent 拉取联动

pullTargetForAgent 优先取 Agent 最近部署对应的 active 期望状态,其次取 stable 通道指向;指向变化后 Agent 下轮即见新 digest。

多环境推进

dev / test / prod 各建通道,各自指向不同版本;版本单调(per-app max+1)保证推进只能向前、回退必须留痕。

渐进发布载体

通道带 promotion_policy / approval_policy 字段,为灰度比例与审批 gate 预留(本版存储策略,执行在 P2)。

调整指南(怎么调整)

  • 改发布目标:激活新期望状态后 POST /channels/:id/set-desired-state 指过去;想回退先 POST rollback 生成高版本新行再指。
  • 改多环境布局:dev / test / prod 各建通道,期望状态里用不同探针端口 / 配置体现环境差异,通道指向互相独立。
  • 改 Agent 拉取顺序:pullTargetForAgent 优先"Agent 最近部署的 active 期望状态",其次 stable 通道指向;想让通道彻底说了算,保持 Agent 无历史部署记录即可。
  • 改通道描述 / 策略:PUT /channels/:id 更新描述与 promotion_policy / approval_policy(名称不可变)。
  • 改推进节奏:多环境推进时先 test 通道验证,再 prod 通道;监控部署记录确认 synced 后再切下一环境。

做得好的场景

通道把"发布目标"从脚本里的硬编码变成可审计的开关,特别适合以下场景:
  • 统一发布控制:一次通道指向切换,所有指向该通道的 Agent 下轮自动拉新版本,不用逐台机器改。
  • 环境有序推进:dev → test → prod 用不同通道,版本单调约束保证"先验证后上线",不会跳环境乱指。
  • 可审计回退:回滚生成高版本新行走同一通道,谁能指、指到哪、什么时候指的全在审计日志里。
  • 演示 / 培训场景:seed 自带 stable 通道指向 demo-app,打开就能演示"指哪打哪"的发布流程。

限制与不足

以下是明确的边界,使用前先知道:
  • 渐进发布未执行:metric_gate(按指标阈值分批 promote)属 P2,当前通道指向是全量切换,没有灰度比例。
  • 审批 gate 未执行:approval_policy / require_approval 只存储,PROD 通道强制审批的逻辑未落地(P2)。
  • 目标必须 active:set-desired-state 指向 draft / deprecated 期望状态会报 400,先激活再指。
  • 单通道 seed:demo 只创建了 stable 通道,多环境通道需手动创建;环境与 Agent 已由 envmgr 绑定(environments / env_agents 表),但"通道 ↔ 环境"的完整绑定仍未打通(通道拉取不按环境过滤)。
  • 版本单调是 per-app 约束:多应用在同一通道推进时,各自按 app 单调,不能指望跨 app 的全局顺序。

场景故事

故事 1 stable 通道指向 demo-app,激活即成为默认发布目标
背景
阿强刚接手 demo-app 的发布。他打开"制品与渠道"页发现 seed 已经建好 stable 通道并指向 demo-app(v1.0.0)。他想验证:是不是只要把某个期望状态激活并指给 stable,Agent 就会自动拉它——也就是"激活即成为默认发布目标"。
传统做法对比
以前发布目标写死在某台机器 / 某个部署脚本里,改一次要改 N 处;环境一多,没人说得清"现在生产到底是谁在管"。
角色
阿强(应用开发,验证自己的期望状态能被通道承载);陈工(平台运维 / SRE,维护通道与拉取规则)。
操作步骤
  1. GET /channels 查看 stable 通道及 current_desired_state_id
  2. 登记一个新的期望状态(POST /desired-states,YAML 声明)
  3. 激活草稿:POST /desired-states/:id/activate
  4. POST /channels/:id/set-desired-state 指向它
  5. 观察 Agent 下一轮拉取是否以它为发布目标
系统响应
GET /channels 返回通道列表:
[{"name":"stable",
  "description":"演示发布通道(指向 demo-app)",
  "current_desired_state_id":1}]
Agent 的 GET /api/v1/agent/pull 返回的 desired_state 即 stable 当前指向的期望状态(bundle_digest、has_update 一并返回)。
结果洞察
通道是"指哪打哪"的开关:拉取目标选择优先"Agent 最近部署对应的 active 期望状态",其次 stable 通道指向。因此把期望状态激活并指给 stable,就是让它成为所有新 Agent 的默认发布目标;指向一变,Agent 下轮拉取即见新 digest。
调整建议
给通道起可读的 description(如"演示发布通道(指向 demo-app)"),方便运维在通道列表里一眼定位;切换前先确认目标已激活,避免 400 报错。
动手试一试
登录:admin / admin1。页面路径:制品与渠道 /apollo/bundles → 发布通道。输入内容:GET /channels 看 stable 指向 → 登记并激活一个新期望状态 → set-desired-state 指向。预期结果:current_desired_state_id 更新,Agent 拉取返回新期望状态。
限制提示
set-desired-state 要求目标 active,指向 draft / deprecated 会报 400;通道为软删,删除后其历史部署记录保留;promotion_policy / approval_policy 本版只存储不执行。
故事 2 v1.3.0 验收通过,把 stable 通道从 v1.2.0 切到 v1.3.0
背景
周四 16:00,v1.3.0 在测试环境验收通过。陈工要把生产 stable 通道从 v1.2.0 切换到 v1.3.0。他在"期望状态"里登记 v1.3.0(bundle_version 自动取 per-app max+1=3)、激活,然后把 stable 通道指过去——一次调用,全部指向 stable 的 Agent 下轮自动拉 v1.3.0。
传统做法对比
以前逐个环境手工部署,错一个环境就版本不一致;发完还要人肉确认"每台机器是不是都是新版本",核对一遍一两个小时。
角色
陈工(平台运维 / SRE,执行通道切换);阿强(应用开发,确认 v1.3.0 期望状态声明正确)。
操作步骤
  1. POST /desired-states 登记 v1.3.0(YAML 声明,app=demo-app、version=1.3.0)
  2. POST /desired-states/:id/activate 激活
  3. POST /channels/:stableid/set-desired-state,body 填 desired_state_id=v1.3.0
  4. 到"部署总览 /apollo"看新部署记录推进到 synced
系统响应
切换接口返回:
{"id":1,"desired_state_id":6}
GET /desired-states?app=demo-app 可见 bundle_version 序列递增(1 → 2 → 3);Agent pull 返回的 desired_state 换成 v1.3.0、bundle_digest 为新内容,部署记录新增一条指向 v1.3.0 的 deployment(kind=deploy)。
结果洞察
切换通道指向 = 推进发布:不用碰任何一台目标机,Agent 自己拉新版本;版本单调约束保证只能往前走——想回退必须走 rollback 生成高版本新行再指回来,全程留痕可审计。
调整建议
切换前在 test 通道先跑一遍 v1.3.0 验证;切完在"部署与漂移"页确认所有指向 Agent 都 synced 再宣布发布完成;把通道切换动作记进变更日历,便于安全团队审计。
动手试一试
登录:admin / admin1。页面路径:期望状态列表 → 发布通道。输入内容:登记 v1.3.0 → 激活 → set-desired-state 指向。预期结果:bundle_version 递增为 3,通道 current_desired_state_id 指向 v1.3.0,部署总览出现新部署记录。
限制提示
目标必须是 active;指标门控(按指标阈值分批 promote)与审批 gate 属 P2,当前切换是全量、即时生效;若 Agent 有历史部署记录,拉取目标会优先取"最近部署对应的 active 期望状态"。
故事 3 dev → test → prod 多通道推进,版本单调保证有序
背景
阿强要把 demo-app 从 v1.1.0 一路推到 v1.4.0。他建了 dev / test / prod 三个通道,约定:dev 通道随便指、test 通道验收后指、prod 通道只能由陈工指。v1.4.0 期望状态在三个环境用不同的探针端口(8080 / 18080 / 443)体现环境差异,靠通道指向完成逐级推进。
传统做法对比
以前 dev / test / prod 配置靠人肉复制粘贴,环境一多 diff 靠眼睛;发布顺序没人管,有人直接在 prod 指了个 dev 版本,第二天线上崩了才知道。
角色
阿强(应用开发,dev / test 自助推进);陈工(平台运维 / SRE,负责 prod 通道指向与发布顺序);刘经理(安全合规,抽查审计)。
操作步骤
  1. 创建三个通道:POST /channels(name=dev / test / prod,各自 description)
  2. 为每个环境登记期望状态 v1.4.0(探针端口按环境不同)并激活
  3. POST /channels/:devid/set-desired-state 指 dev 版,验证通过
  4. 再指 test 版,验收通过后由陈工把 prod 通道指向 prod 版
  5. 在部署总览确认各环境部署记录全部 synced
系统响应
GET /channels 返回三个通道及各自 current_desired_state_id:
[{"name":"dev","current_desired_state_id":9},
 {"name":"test","current_desired_state_id":10},
 {"name":"prod","current_desired_state_id":11}]
GET /desired-states?app=demo-app 的 bundle_version 单调递增(1/2/3/4),每个环境的期望状态声明内容一致、仅探针等环境字段不同。
结果洞察
多环境推进 = 同一内容 + 环境差异 + 通道独立指向:期望状态声明是"模板",环境差异是"参数",通道指向是"开关"。版本单调约束让整个推进只能向前——任何想跳步 / 回退的操作都会在版本号与审计里现形,发布顺序终于不靠自觉靠机制。
调整建议
dev 通道可放开给开发者自助指,prod 通道建议配审批 gate(P2 落地前用文档约束);环境差异尽量收敛在探针 / 配置字段,别让三个环境的声明内容漂移;推进到 prod 前在 test 通道完整验收一轮。
动手试一试
登录:admin / admin1。页面路径:发布通道 → 期望状态列表。输入内容:创建 dev / test / prod 三个通道,各登记并激活 v1.4.0 期望状态(探针端口按环境不同),依次 set-desired-state。预期结果:三通道各自指向、bundle_version 单调递增、部署记录逐环境 synced。
限制提示
demo 只 seed 了 stable 通道,dev / test / prod 需自行创建;环境与 Agent 已绑定(envmgr 的 environments / env_agents 表),但"通道 ↔ 环境"完整绑定未打通,多环境隔离主要靠通道指向约定;通道级审批 gate(prod 强制审批)未落地,但期望状态级审批(G15:POST /desired-states/:id/approve)已实现,可先启用。

常见问题

发布通道和期望状态是什么关系?

期望状态(desired_states)是"某个版本的内容声明"(组件、探针、digest、bundle_version);发布通道(release_channels)是"指向哪个期望状态"的开关,current_desired_state_id 指向当前发布的期望状态。通道是"指哪打哪",期望状态是"打的内容"。

切换通道后 Agent 多久生效?

Agent 默认 60 秒轮询一次 GET /api/v1/agent/pull,切换指向后下一轮拉取即见新 digest(最快秒级、最慢一个轮询周期)。注意:若 Agent 有历史部署记录,拉取目标会优先取"最近部署对应的 active 期望状态",因此要让通道彻底说了算,保持 Agent 无历史部署记录即可。

多环境用多个通道,版本号会乱吗?

不会。bundle_version 按 app 单调递增(per-app UNIQUE),dev / test / prod 各环境登记的期望状态共享同一个递增序列;回退必须走 rollback 生成高版本新行。多环境之间靠通道独立指向区分"谁在跑哪个版本",版本号始终单调,审计一目了然。

通道的灰度 / 审批现在能用吗?

通道结构上有 promotion_policy / approval_policy 字段可以存储与校验 JSON,但"按比例分批 promote"和"通道级审批 gate 拦截"的执行逻辑未落地。注意:期望状态级审批(approval_status + POST /desired-states/:id/approve)已实现——未过审的期望状态不会被 Spoke 应用,可作为通道审批的替代约束。当前切通道是即时全量生效,正式引入前建议先用 dev → test → prod 的多通道顺序来模拟发布门槛。

通道被删了会影响已部署的东西吗?

通道是软删(GORM DeletedAt),删除后其历史部署记录、指向过的期望状态都保留;正在运行的 Agent 已应用的内容不受影响,只是该通道不再参与"发布目标选择"(会回落到其他 active 期望状态)。删除前建议先把通道指向迁走。

主题小结

一句话:发布通道是"指哪打哪"的开关——期望状态定义内容,通道定义目标,切换指向就是受控的版本推进。stable 通道让激活即默认发布,set-desired-state 一次调用完成切换,dev / test / prod 多通道配合版本单调约束让发布有序、回退可审计。记住边界:灰度渐进与审批 gate 属 P2、目标必须 active、demo 只 seed 了 stable 通道。