P3 Apollo
发布策略:签名、漂移忽略与渠道约束
发布策略就是部署的"红绿灯":auto-rollback 决定失败怎么兜底、国密签名链决定制品能不能上、ignore_paths 决定什么才算漂移、渠道决定哪个版本能推进到哪。策略是声明式的、可审计的,不再靠口头约定。看完这 3 个故事,你就能给一次发布配上"签名 + 忽略规则 + 渠道约束"的完整护栏。
平台运维 / SRE
安全合规
发布策略
国密签名
漂移忽略
渠道约束
共 3 个故事
能 / 不能速览
✅ 这个主题能做
- 部署策略 CRUD:auto_rollback / max_concurrent / readiness_timeout_sec / reconcile_backoff
- bundle 国密签名链:逐文件 SM3 清单 + digest + signer 白名单 + SM2 验签
- signer 白名单管理:新增 / 启用 / 禁用,禁用后验签返回 VERIFY_SIGNER_NOT_TRUSTED
- 期望状态声明 policy 块配 ignore_paths,排除运行时字段不产生漂移
- 渠道可配置 promotion_policy / approval_policy(本版存储策略,执行点在后续)
⛔ 这个主题做不了
- promotion_policy / approval_policy 仅存储不执行:通道级审批 gate 与指标门控渐进发布未落地(期望状态级审批 G15 已实现)
- OPA 策略即代码未实现,本版用内置声明字段表达策略
- require_guomi_signature 在期望状态 policy 声明,demo seed 默认 false,需自行开启
- 系统级信任锚依赖 demo-signer 白名单,多签名者 / PKI / 透明日志留扩展
适用角色
本主题面向两个角色:
- 平台运维 / SRE(陈工):维护部署策略、配 ignore_paths、发起部署,是策略的执行者。
- 安全合规(刘经理):制定"无签名不上线""白名单签名者才可信"的规则,检查验签结果与审计留痕。
应用开发(阿强)在自建 bundle 与期望状态时配合签名与策略字段;平台管理员负责授权。
能力速览(能做什么)
部署策略(policy-as-code)
POST /policies 声明策略规则:auto_rollback(默认 true)、max_concurrent(默认 2)、readiness_timeout_sec(默认 60)、reconcile_backoff。
bundle 签名链
构建时生成 manifest + 文件清单(SM3)+ 不可变 digest,SM2 私钥签名;验签依次查逐文件 SM3 → digest → 白名单 → SM2。
signer 信任锚
POST /signers 登记公钥指纹白名单;验签通过 ≠ 可信,签名者不在白名单即拒绝(VERIFY_SIGNER_NOT_TRUSTED)。
漂移忽略规则
期望状态 policy.ignore_paths 用 JSON Pointer 路径(精确 / 单段 * / 深 **),命中路径不产生漂移、不触发收敛。
渠道策略载体
ReleaseChannel 带 promotion_policy / approval_policy 字段,为渐进发布与审批 gate 预留(本版存储策略不执行)。
调整指南(怎么调整)
- 改失败兜底:策略 auto_rollback=false 时不自动回滚,失败只告警;readiness_timeout_sec 控制就绪判定的耐心。
- 改签名要求:期望状态声明 policy.require_guomi_signature=true 后,无签名 / 白名单外签名的 bundle 不允许应用。
- 改信任范围:POST /signers 加白名单、POST /signers/:id/disable 立即下线一个签名者,误禁用会立刻挡住对应 bundle 验签。
- 改忽略规则:ignore_paths 只排除"运行态噪声"(PID / restart_count / 运行时生成文件),别把真实配置漂移也遮掉。
- 改渠道策略:渠道 promotion_policy / approval_policy 先存结构,正式审批 gate 上线前用文档约定,避免"以为有审批其实没有"。
做得好的场景
策略把"安全底线 + 运维纪律"写进系统,特别适合以下场景:
- 供应链安全:制品必须过"SM3 清单 + SM2 验签 + 白名单信任锚",杜绝"任意有效签名"和文件被篡改。
- 发布纪律:auto-rollback 默认开启,失败自动退上一稳定版,把"发布事故"从运维事故变成一次可审计的自动回滚。
- 干净漂移视图:ignore_paths 过滤运行时噪声,漂移事件只报"真正该修"的配置 / 版本差异,告警不炸屏。
- 合规留痕:策略变更、验签结果、签名者启停全部走审计,安全团队随时可查"谁在什么时候改了什么策略"。
限制与不足
以下是明确的边界,使用前先知道:
- 策略执行点有限:auto-rollback 由编排 Advance 消费,require_guomi_signature 的"拒绝应用"依赖 Spoke 校验链;通道级审批 gate / 指标门控渐进发布(promotion_policy / approval_policy)只存储不执行(期望状态级审批 G15 已实现,可先启用)。
- OPA 未引入:策略用内置声明字段表达,复杂策略(如"禁止 latest 标签 + 强制资源上限")需后续接入。
- 信任锚单一:demo 只有 demo-signer 一个白名单签名者;多签名者轮换、PKI / 透明日志留扩展。
- 签名默认关闭:demo seed 的 require_guomi_signature=false,验签动作在 /bundles/:id/verify 手动触发,真正"强制上线前验签"需在策略与 Spoke 侧开启。
- ignore_paths 太宽有风险:如配 ** 会连真实配置漂移一起忽略,需权衡。
场景故事
故事 1
部署策略 default + 国密签名要求:无签名的 bundle 上不了线
场景:签名与信任锚
角色:安全合规 + 平台运维
耗时:约 6 分钟
- 背景
- 刘经理(安全合规)要求:生产 bundle 必须过国密签名链,签名者必须在白名单里,否则不允许上线。陈工现场演示:上传文件构建 bundle(POST /bundles),构建时自动生成 SM2 密钥对并把 demo-signer 注册进白名单;然后 POST /bundles/:id/verify 跑完整校验链,再看"白名单外签名"被拦的效果。
- 传统做法对比
- 以前制品下载下来就直接用,没人验哈希、没人验签名;出了供应链事故才回头排查,等查出来已经运行了几个发布周期。
- 角色
- 刘经理(安全合规,提出"无签名不上线"规则并检查验签结果);陈工(平台运维 / SRE,执行构建 / 验签 / 白名单操作)。
- 操作步骤
-
- POST /bundles 上传文件(multipart:app、version、bundle_version、signer,文件键为包内路径)
- 响应返回 bundle 记录(含不可变 digest 与签名指纹)
- POST /bundles/:id/verify 触发完整校验链
- POST /signers/:id/disable 禁用一个签名者,再 verify 一次看拒绝效果
- 系统响应
- verify 返回逐项校验结果:
{
"files_ok": true, "digest_ok": true,
"signature_ok": true, "trusted": true,
"signer_name": "demo-signer"
}
禁用签名者后再次 verify,返回 403 与错误码 VERIFY_SIGNER_NOT_TRUSTED(签名者指纹不在白名单,拒绝应用)。
- 结果洞察
- "验签通过 ≠ 可信":SM2 验签只证明签名有效,白名单才决定"这是不是我们认的签名者"。demo-signer 首次构建时由服务器自动把占位指纹升级为真实 SM2 密钥指纹,白名单自洽;禁用签名者等于立刻吊销其制品的应用资格,审计留痕。
- 调整建议
- 把 require_guomi_signature=true 写进期望状态 policy 块,配合 Spoke 校验链实现"上线前强制验签";签名密钥生命周期(轮换 / 吊销)要有流程,别让 demo-signer 一直当唯一信任锚。
- 动手试一试
- 登录:admin / admin1。页面路径:制品与渠道 /apollo/bundles。输入内容:POST /bundles 上传一个文件(app=demo-app、version=1.1.0、bundle_version=2)→ POST /bundles/:id/verify。预期结果:verify 返回 trusted=true;禁用 demo-signer 后再 verify 返回 403 VERIFY_SIGNER_NOT_TRUSTED。
- 限制提示
- demo seed 的 require_guomi_signature 默认为 false,强制验签需在策略与期望状态声明中显式开启;验签动作在 /bundles/:id/verify 手动触发,Spoke 自动校验链的完整落地依赖配置 ProcManager。
故事 2
ignore_paths 配置:把运行时噪声从漂移里过滤掉
场景:漂移忽略规则
角色:应用开发 + 平台运维
耗时:约 4 分钟
- 背景
- 阿强发现 web 组件每次重启,运行时字段(PID、restart_count)都会变,漂移检测把它们当成"配置被改"报了一堆 replace 告警,天天炸屏。他和陈工把这类"运行态噪声"写进期望状态的 ignore_paths,漂移视图立刻清净,而真实的版本差异仍然照报。
- 传统做法对比
- 以前要么忽略所有差异(等于关了漂移检测),要么天天被误报打扰;没人愿意去解析一堆 JSON diff 找"哪些是真的要改的"。
- 角色
- 阿强(应用开发,提出运行时字段被误报);陈工(平台运维 / SRE,配置 ignore_paths 并验证漂移视图)。
- 操作步骤
-
- 在期望状态声明的 policy 块配置 ignore_paths,如 /components/*/runtime/*
- 登记或更新期望状态(仅 draft 可改,active 需先重建 / 弃用)
- POST /drift/check,body 带 actual_state 模拟"仅运行时字段不同"
- 对比加 ignore_paths 前后的 diff 结果
- 系统响应
- 漂移 diff 是 JSON Patch 路径级结构,命中忽略规则后相关路径消失:
// 未忽略时
[{"op":"replace","path":"/components/0/runtime/pid","expected":1234,"actual":5678},
{"op":"replace","path":"/components/0/runtime/restart_count","expected":0,"actual":3}]
// 配了 ignore_paths 后
[{"has_drift": false}]
真正的配置 / 版本差异(如 /components/0/version)仍会正常报告 version_drift。
- 结果洞察
- ignore_paths 支持精确路径、单段通配 *、深通配 **,命中路径不产生漂移、不触发收敛——避免"自愈与运行态打架"。漂移事件里 config_drift / version_drift / file_drift / process_drift 按 diff 路径前缀归类,只剩真正该修的差异。
- 调整建议
- 只忽略"运行态噪声"字段(PID / restart_count / 运行时生成文件),别用 ** 一把梭;配置类字段(如 db_host)务必保留检测;seed 默认已带 /components/*/runtime/*,新环境照抄即可。
- 动手试一试
- 登录:admin / admin1。页面路径:部署与漂移 → 漂移事件。输入内容:声明 policy.ignore_paths=[/components/*/runtime/*],POST /drift/check 传只含 runtime 字段的 actual_state。预期结果:runtime 路径不产生 diff,has_drift=false;改 version 字段仍报 version_drift。
- 限制提示
- ignore_paths 配置在期望状态声明里,active 状态不可直接改声明(仅 draft 可改),需走更新 / 重建流程;忽略规则配太宽会连真实漂移一起遮掉。
故事 3
渠道策略:stable 只放行"签名 + 版本单调"的制品推进
场景:渠道约束
角色:安全合规 + 平台运维
耗时:约 5 分钟
- 背景
- 刘经理定的规矩:stable 通道只能指"签名可信且版本单调递增"的期望状态,谁都不能把低版本或无签名的东西悄悄指进来。陈工演示 stable 通道的指向操作:set-desired-state 要求目标必须是 active 的期望状态,而版本单调由登记机制保证,回滚走 rollback 生成高版本新行。
- 传统做法对比
- 以前"发布通道"就是一个口头的环境名单,谁都能指,版本回退没有审计;出了事只能靠会议记录复盘"当时到底哪个版本在生产"。
- 角色
- 刘经理(安全合规,定"签名 + 单调版本"规则);陈工(平台运维 / SRE,执行通道指向与验证)。
- 操作步骤
-
- GET /channels 查看 stable 通道及其 current_desired_state_id
- 登记新期望状态并激活(POST /desired-states + activate)
- POST /channels/:id/set-desired-state 指向新版本(目标须 active)
- 若想退旧版本,改用 POST /desired-states/:id/rollback 生成高版本新行再指
- 系统响应
- 通道指向成功返回:
{"id":1,"desired_state_id":9}
若目标是 draft(未激活)或不同应用的期望状态,返回 400 / 409(CHANNEL_INVALID 或状态转移错误);GET /channels 可见 stable 的 current_desired_state_id 已更新。
- 结果洞察
- 渠道 = 发布目标的总开关:stable 指哪,Agent 拉取目标就是哪(pullTargetForAgent 优先通道指向)。"只放行签名 + 版本单调"由两层机制共同保证:登记时 bundle_version 自动取 per-app max+1(低版本无法构造),验签链 + 白名单挡住无签名制品;回滚也走 max+1 新行,版本永远向前、全程留痕。
- 调整建议
- 为不同环境建不同通道(dev / test / prod)配合多环境推进;promotion_policy / approval_policy 先存入通道结构,审批 gate 上线前用团队文档约定;定期抽查通道指向与审计记录是否一致。
- 动手试一试
- 登录:admin / admin1。页面路径:制品与渠道 /apollo/bundles → 发布通道。输入内容:激活一个新期望状态后 POST /channels/:id/set-desired-state(desired_state_id=新 id)。预期结果:返回 {id, desired_state_id},GET /channels 的 stable.current_desired_state_id 更新。
- 限制提示
- promotion_policy / approval_policy 仅存储不执行,通道级审批 gate 与指标门控渐进发布未落地(期望状态级审批 G15 已实现:POST /desired-states/:id/approve,未过审不被 Spoke 应用);set-desired-state 要求目标 active;通道为软删,删除不影响已指向的部署记录。
常见问题
部署策略和期望状态里的 policy 块有什么区别?
部署策略(POST /policies)是"平台侧规则":auto_rollback、max_concurrent、readiness_timeout_sec、reconcile_backoff,部署时按 policy_name 解析;期望状态声明的 policy 块是"这份声明自带的规则":require_guomi_signature、ignore_paths、trusted_signers、rollout。前者管怎么部署,后者管这份内容怎么被信任与比对。
"验签通过 ≠ 可信"是什么意思?
SM2 验签只证明"签名是用对应私钥做的",但签名者是不是我们认的人,要靠 trusted_signers 白名单判定(按 SM3(公钥) 指纹匹配)。签名有效但签名者不在白名单,会返回 VERIFY_SIGNER_NOT_TRUSTED 拒绝应用——这正是为了杜绝"任意有效签名"(Sigstore 结论)的信任锚设计。
ignore_paths 能忽略哪些路径?
任意 JSON Pointer 路径:精确路径(/components/0/healthcheck)、单段通配(/components/*/digest)、深通配(/components/**/readiness),还支持命中祖先前缀时忽略整棵子树。典型用法是排除 PID、restart_count、运行时生成文件这类"每次都变但不该管"的字段。
渠道的审批策略现在能用吗?
ReleaseChannel 上带 promotion_policy / approval_policy 字段可以存储与校验 JSON 合法性,但"通道审批 gate 必须过才能 promote"的执行逻辑未落地。期望状态级审批(approval_status + POST /desired-states/:id/approve)已实现,未过审期望状态不会被 Spoke 应用——可作为通道审批的替代。当前阶段建议用文档约定通道审批流程。
策略改了立刻生效吗?
部署策略按名解析、改动即时生效(下次部署 / 推进时读取);期望状态 policy 块的改动需在 draft 状态下修改后重新激活;ignore_paths 对漂移检测即时生效(检测时读取声明)。所有策略 / 签名者 / 通道变更都会落审计日志。
主题小结
一句话:发布策略把"安全底线 + 运维纪律"写进系统:auto-rollback 兜底失败、SM3/SM2 签名链 + 白名单挡无签名制品、ignore_paths 过滤漂移噪声、渠道只放行签名且版本单调的制品。记住边界:审批 gate / 指标门控渐进发布(promotion_policy / approval_policy)本版只存储不执行,OPA 策略即代码未引入,require_guomi_signature 默认关闭需显式开启。