业务故事站
P3 Apollo

DAG 分批部署:依赖与就绪门控

部署编排器的核心是"按依赖分批、就绪才放行":db 没就绪 web 就不启动,readiness 探针失败就超时、超时就判失败、失败就自动回滚并联动告警事件单。看完这 4 个故事,你会看懂部署状态为什么这样流转。

运维工程师 SRE DAG 拓扑 就绪门控 自动回滚 告警联动 共 4 个故事

能 / 不能速览

✅ 这个主题能做
  • 按 depends_on 构建依赖 DAG,Kahn 分层逐批放行组件(首批并行、依赖就绪才解锁下一批)
  • 组件就绪门控:批次内全部 ready 才推进,就绪以声明 readiness 探针为准(由 monitoring 执行并落库)
  • 手动推进:POST /deployments/:id/advance 上报组件状态,批次由人控制放行节奏
  • 就绪超时判失败(readiness_timeout_sec 默认 60s),失败自动回滚(auto_rollback 默认开启)
  • 部署失败自动写告警事件 + 事件单(rule_id=0 系统级);同一 Agent 部署锁串行化
⛔ 这个主题做不了
  • 演示 Poller 不真实部署进程,就绪需手动 advance 模拟(声明探针由 monitoring 执行,失败即不就绪)
  • 自动回滚只回滚到"上一稳定成功版本",不能任意挑版本(任意版本回滚走 rollback API)
  • 对 auto-rollback 产生的版本不再自动回滚(防循环 F3,仅人工处置)
  • 指标门控 / 渐进发布(metric_gate)属规划,本版策略仅落地 auto_rollback / 超时 / 并发

适用角色

  • 运维工程师 / SRE:部署编排核心使用者,发起部署、手动推进、处理失败与自动回滚告警。
  • 应用开发工程师:在期望状态声明里写 depends_on 与探针,决定依赖顺序与就绪判据。
  • 安全合规工程师:关注自动回滚是否掩盖根因、回滚是否留审计、部署失败告警事件是否闭环。

能力速览(能做什么)

Kahn 分层 DAG

按 depends_on 计算拓扑层级:无依赖组件 level=0 首批放行,同层可并行,依赖就绪才解锁下一层。

就绪门控

批次内全部 ready 才放行下一批;readiness 探针(http/tcp/process/file)由 monitoring 执行并落健康检查记录。

手动推进

POST /deployments/:id/advance 上报组件状态,返回 next_batch 指明本批放行的组件,支持人工确认窗口。

就绪超时

组件进入 deploying 后超过 readiness_timeout_sec(默认 60s)未就绪即判 failed,进入失败处理。

失败自动回滚

auto_rollback=true(默认)时,组件失败自动生成回滚草稿(rollback_of 指向上一稳定版),部署标 failed。

失败告警联动

部署失败/自动回滚时直写 alert 事件 + 事件单(G12),离线部署超时(F7)自动收敛悬挂记录;手动 sync(F2)可解除退避。

调整指南(怎么调整)

  • 改依赖顺序:调整组件声明的 depends_on;加 requires(component / min_version / health_ok)做版本级就绪约束。
  • 改推进节奏:想人工把关就分次 advance;想快就把批次组件都上报 ready,让门控一次放行到底。
  • 改超时:慢启动组件调大 policy 的 readiness_timeout_sec;探针配准(interval_s / failure_threshold)能减少误判。
  • 改自动回滚:策略 auto_rollback 默认 true;确需关闭可在 policy 里置 false(慎重,会失去兜底)。
  • 改失败处置:失败部署可用 POST /deployments/:id/sync 清退避置回 pending 让 Agent 重调和一轮(F2)。

做得好的场景

DAG 分批部署最擅长"把有依赖关系的应用安全地推上线":
  • 依赖顺序不再出错:db 先就绪、web 后启动,启动竞态由拓扑保证,而不是脚本顺序。
  • 部署可观察:组件状态 pending / deploying / ready / failed 一览,批次推进看得到。
  • 失败自动兜底:探针失败 → 超时 → 自动回滚上一稳定版,并把故障窗口从小时级压到分钟级。
  • 告警闭环:部署失败自动写事件单(rule_id=0 系统级),运维无需再手工补录故障工单。

限制与不足

以下是明确的边界,使用前先知道:
  • 演示不真实部署:演示 Poller 默认不自动启动,readiness 探针由 monitoring 执行但就绪需手动 advance 上报驱动。
  • 自动回滚目标固定:只回滚"上一稳定成功版本",不能任意挑选历史版本(那走 rollback API)。
  • 防循环约束:auto-rollback 产生的版本不再触发自动回滚,需人工处置,避免无限抖动(F3)。
  • 指标门控未落地:metric_gate 渐进发布无真实指标来源,属规划;degraded / 指数退避是设计目标,当前简化。

场景故事

故事 1 db 先于 web 就绪——依赖拓扑决定启动顺序
背景
陈工部署 demo-app。声明里 web 组件 depends_on=[db],意味着 web 必须等 db 就绪后才能启动。他发起部署后紧盯组件状态,想亲眼确认平台是不是真的让 db 先动。
传统做法对比
手工部署时数据库和 web 的启动顺序完全靠脚本和责任心:顺序写错或 db 还没就绪就拉起 web,web 疯狂重连,光排查"数据库连不上"就花 2 小时;平台按 DAG 拓扑自动分层,db 永远先于 web。
角色
陈工(平台运维,发起部署并核对启动顺序)。
操作步骤
  1. 发起部署 demo-app 到 spoke-01
  2. 打开组件状态(GET /deployments/:id/components)
  3. 观察 db=deploying(首批放行)、web=pending(等待 db)
  4. 上报 db=ready 后,观察 web 进入 deploying
系统响应
首次查看返回 db=deploying、web=pending;推进后 advance 返回:
POST /api/v1/deployments/1/advance
{ "deployment_id": 1, "status": "deploying",
  "next_batch": ["web"], "ready_count": 1, "total_count": 2 }
结果洞察
依赖分层(Kahn)由平台自动计算:无依赖组件首批并行启动,依赖就绪才解锁下一批。就绪门控保证"db 没就绪,web 不启动",启动竞态从根上消除。
调整建议
组件就绪以声明的 readiness 探针为准(demo 用 tcp 探针),探针由 monitoring 执行并落健康检查记录;可按需改成 http / process / file;想加版本约束用 requires.min_version。
动手试一试
登录:admin / admin1,端口 18082。操作:发起部署 demo-app 到 spoke-01 → 看组件状态 db=deploying、web=pending → 手动推进上报 db=ready,观察 next_batch 返回 ["web"]、web 进入 deploying。
限制提示
就绪超时默认 60s(policy.readiness_timeout_sec);同批组件并行放行;声明探针未通过时组件保持 deploying 不就绪(部署不推进),最终由就绪超时兜底判失败;演示 Poller 不自动启动时组件就绪需手动 advance 上报模拟。
故事 2 deploying → ready:组件推进过程与就绪门控
背景
阿强第一次观察组件状态流转:pending → deploying → ready。db 先就绪、web 后就绪,整个过程像流水线一样按批次前进。他想搞清楚"就绪"到底以什么为准。
传统做法对比
以前只能"ssh 进去 ps 看进程"判断服务起来没有,web 进程在但端口没监听也算"起来了",上线后才发现连不上;现在以就绪探针(tcp 连得上、readiness 探针通过)为准,没就绪就挡住不放行。
角色
阿强(应用开发工程师,观察状态流转与就绪判据)。
操作步骤
  1. 发起部署后打开组件状态页
  2. 每次 advance 上报一批组件状态(先 db=ready)
  3. 观察状态迁移:pending → deploying → ready
  4. 全部 ready 后部署标记 synced
系统响应
组件状态字段 status ∈ pending / deploying / ready / failed;全部就绪时 advance 返回:
{ "deployment_id": 1, "status": "synced",
  "ready_count": 2, "total_count": 2 }
部署创建时已自动向监控侧登记带 deployment_id 的健康检查(G18 打点)。
结果洞察
组件级状态与部署级状态分离:组件负责"自己就绪了吗",部署负责"这一批能不能放行"。readiness 判据 = 声明探针通过(demo 为 tcp 连上端口),探针结果由 monitoring 执行并落库,部署打点(deployment_id)让"变更时间线 ↔ 健康检查"可关联查询。
调整建议
想加快 / 放慢节奏,调整 policy 的 readiness_timeout_sec;同一 Agent 并发上限由 max_concurrent 控制;就绪后组件状态与 readiness 详情持久化到 component_states。
动手试一试
操作:部署 demo-app 后分两次推进——第一次上报 db=ready、第二次上报 web=ready。预期结果:第一次后 db=ready、web=deploying;第二次后部署 synced,两个组件都 ready。
限制提示
advance 幂等(重复上报同一状态结果一致);上报未知组件会被忽略;部署已 synced / failed 后再 advance 只返回终态;组件状态由上报驱动,不上报就不流转。
故事 3 中途手动推进批次——人工确认窗口与失败兜底
背景
生产环境发版有时要在中间批次人工确认(比如 db 升级后要先看监控再放行 web)。Apollo 支持把自动推进拆成手动"推进"步骤:批次就绪后由人决定何时放行下一批;某批失败且部署标 failed 时,还可用手动 sync 让 Agent 重调和。
传统做法对比
以前要人工确认就得在脚本里写"sleep + 检查 + 手工执行下一个脚本",全是时间猜测,确认窗口一过就乱套;现在批次就绪后 next_batch 明示下一批是谁,点一下推进才放行,失败部署一键 sync 回 pending 重来。
角色
陈工(平台运维,控制推进节奏)+ 刘经理(安全合规,在 db 批次后把关放行)。
操作步骤
  1. 发起部署后,组件状态页看到首批 db=ready
  2. 不立即推进,先核对 db 日志 / 端口
  3. 确认后调用推进,放行下一批 web
  4. 若某批失败部署标 failed,点"手动 Sync"(POST /deployments/:id/sync)置回 pending
系统响应
每次推进返回 next_batch 字段指明本批放行的组件:
{ "deployment_id": 1, "status": "deploying",
  "next_batch": ["web"], "ready_count": 1, "total_count": 2 }
失败部署手动 sync 返回:
POST /api/v1/deployments/1/sync
{ "code": 0, "data": { "deployment_id": 1, "previous_status": "failed",
  "status": "pending", "sync_requested_at": "2026-08-10T08:30:00Z" } }
结果洞察
手动推进 = 把"自动流水线"变成"人机协同流水线":自动化保证顺序与门控,人工保证在关键节点把关。手动 sync(F2)是"即时收敛"的载体:failed/drifting/degraded 部署清退避置回 pending 并打指令戳,Spoke 下轮拉取即使 digest 未变也强制重调和一轮(reconcile_required 一次性语义)。每次 advance/sync 都有审计事件。
调整建议
关键组件可把推进节奏交给策略(max_concurrent / readiness_timeout_sec);配合漂移检测在放行前先看有没有配置被改;失败部署先查根因再 sync,别盲目重试掩盖问题。
动手试一试
操作:部署后只上报 db=ready,观察 web 停在 pending 不自动启动;休息一会再手动推进放行 web。预期结果:推进前 web 一直 pending,推进后才变为 deploying;对已 failed 的部署调用 sync,观察 status 回 pending 且带 sync_requested_at。
限制提示
同一 Agent 部署锁串行(进行中不能并发第二单,409 DEPLOYMENT_CONFLICT);推进是显式动作,忘推进会导致后续批次一直等待;sync 仅对 failed/drifting/degraded 生效(synced 409、deploying 幂等 200)。
故事 4 探针失败 → 就绪超时 → 自动回滚 → 告警事件单
背景
阿强升级 demo-app 到 v1.1.0,web 的 readiness 探针指向 127.0.0.1:9090(他以为新端口是 9090),但真实端口是 8080。部署时 web 一直就绪不了,60 秒超时后被判定失败,平台触发 auto_rollback 自动回滚到上一稳定版 v1.0.0,并自动开了一张告警事件单。
传统做法对比
以前升级失败:服务起不来 → 业务报障 → 登录排查 → 手动回退包 → 重启,全程 1~2 小时,期间业务受损;现在探针失败自动判定,超时后自动生成回滚草稿并落告警事件/事件单,把影响窗口压到分钟级。
角色
阿强(应用开发,探针配错的"肇事者")+ 陈工(平台运维,处理失败与回滚告警)。
操作步骤
  1. 登记 v1.1.0 期望状态并激活(web 探针端口误写成 9090)
  2. 发起部署,推进上报 db=ready、web 一直未就绪
  3. web 就绪超时(readiness_timeout_sec=60)→ 组件 failed → 部署 failed
  4. 查看自动回滚:策略 auto_rollback=true(默认)生成回滚草稿,并到告警列表看事件单
系统响应
advance 返回失败与回滚草稿 id:
{ "deployment_id": 1, "status": "failed",
  "ready_count": 1, "total_count": 2,
  "auto_rollback": 3 }
期望状态列表出现 demo-app-rollback-3(status=draft、rollback_of 指向 v1.1.0),原行标记 rollbacked;告警列表新增事件单(title="部署失败(已自动回滚)",severity=critical)。
结果洞察
自动回滚 = 把期望状态指回上一稳定成功版本,作为一条可审计的新部署记录(不覆盖历史,B2 模型)。默认策略 auto_rollback=true 是部署安全的兜底;回滚目标仍要过就绪门控才算成功。部署失败自动写告警事件 + 事件单(G12 衔接,rule_id=0 系统级哨兵),运维无需手工补录故障工单。
调整建议
临时关闭自动回滚可改 policy(auto_rollback=false,慎重);慢启动组件调大 readiness_timeout_sec;回滚后记得修探针再发新版本,别让回滚掩盖根因;Agent 失联导致的悬挂部署由离线部署超时(F7)自动收敛为 failed。
动手试一试
操作:登记一个"坏版本"(web 探针端口改成 9090)→ 激活 → 部署 → 推进上报。预期结果:web 就绪超时失败,部署标 failed,自动生成 demo-app-rollback-N 草稿,告警列表出现"部署失败(已自动回滚)"事件单。
限制提示
自动回滚只回滚到"上一稳定成功版本";对 auto-rollback 产生的版本不再自动回滚(防循环 F3,仅人工处置);回滚目标就绪失败会保持当前版本并告警人工介入;演示 Poller 不真实应用,回滚需手动推进模拟;指标门控渐进发布(metric_gate)属规划。

常见问题

就绪探针有哪些类型?

四种形态:http(探测 URL 返回)、tcp(端口连得上)、process(进程存活)、file(就绪文件存在)。demo 用 tcp 探针(web=8080、db=5432)。readiness 判门控、liveness 判重启,职责不同。

为什么我部署后 web 一直是 pending?

因为 web 依赖 db,而就绪门控要求"前序层全部 ready"才放行下一批。db 还没上报 ready(或还没推进),web 就停在 pending 等待。把 db 推进到 ready 后 web 才会进入 deploying。

自动回滚会不会掩盖真正的故障?

会,这是取舍。自动回滚保证业务优先可用,但可能掩盖根因(比如探针配错)。所以:回滚会生成带 rollback_of 的审计记录并写告警事件单,且对 auto-rollback 产生的版本不再自动回滚,迫使人工介入查根因。

同一 Agent 能并发部署吗?

不能。部署锁按 Agent 串行化:已有 pending / deploying 部署时,新发起返回 409 DEPLOYMENT_CONFLICT。要并发就去不同 Agent(不同目标机)。

手动推进和自动推进怎么切换?

推进总是显式动作(advance),区别在于由谁调用:自动场景由 Spoke Agent 上报驱动(部署中自动推进),演示 / 人工把关场景由人手动调用。批次就绪后不放行就保持等待,这是特性不是缺陷。

主题小结

一句话:DAG 分批部署 = 依赖分层放行 + 就绪门控推进 + 超时判失败 + 失败自动回滚 + 告警事件单联动。记住边界:演示靠手动 advance 模拟就绪、自动回滚只回滚上一稳定版、auto-rollback 防循环(F3)、失败可手动 sync 重调和(F2)、离线悬挂由超时收敛(F7)、指标门控属规划。