P3 Apollo
漂移检测:期望 vs 实际
环境会"漂":有人手改配置、有人偷偷降版本、pid 天天变。Apollo 用 JSON Patch 路径级 diff 把期望状态与实际状态逐一对比,还能用 ignore_paths 忽略动态字段、用 reconcile 收敛回去,甚至按 drift_policy=auto_fix 自动调和。看完这 4 个故事,漂移治理不再靠人工巡检。
运维工程师
安全合规
漂移
JSON Patch
ignore_paths
auto_fix
共 4 个故事
能 / 不能速览
✅ 这个主题能做
- 期望状态 vs 实际状态做字段级结构化 diff(JSON Patch:op / path / expected / actual)
- 漂移事件落库(drift_events)+ 关联部署标记 drifting,进审计
- ignore_paths 忽略规则:精确路径、单段通配 *、深通配 **,命中即不产生漂移
- reconcile 收敛判定:仍有漂移 → reconciling=true;无漂移 → deployment synced
- drift_policy=auto_fix 时漂移自动调和:部署置 pending,Agent 下轮拉取重应用(防循环)
- 漂移事件按 desired_state_id / agent_name 过滤查询;部署列表对 drifting 行可手动 Sync(F2)
⛔ 这个主题做不了
- reconcile / auto_fix 的"实际应用"依赖 Spoke 拉取重调和(演示 Poller 不自动应用进程)
- auto_fix 有防循环上限:单条部署最多 3 轮、防抖 5 分钟,超限只记录不调和
- 漂移检测当前由 Hub 汇总比对,Spoke 本地比对是设计目标
- 无自动通知:事件只落库 + 审计,不发告警(通知属告警模块能力)
适用角色
- 运维工程师 / SRE:触发漂移检测、查看 diff、配置 ignore_paths、执行 reconcile 收敛——核心使用者。
- 安全合规工程师:关注"谁在什么时候改了配置",漂移事件是配置变更审计的补充证据。
- 应用开发工程师:在期望状态声明里配置 ignore_paths,避免运行态字段误报。
能力速览(能做什么)
结构化 diff
JSON Patch(RFC 6902)风格:add(期望有而实际缺)/ remove(实际有而期望无)/ replace(值不同),带 expected / actual。
漂移事件落库
POST /drift/check 检出漂移即写 drift_events,并把关联部署标记 drifting,事件进审计。
ignore_paths 忽略
精确路径 / 单段通配 * / 深通配 **,命中路径不产生漂移、不触发收敛,避免与运行态打架。
reconcile 收敛
POST /drift/:id/reconcile 判定:仍有漂移 → reconciling=true(指示 Spoke 重新拉取);无漂移 → synced。
auto_fix 自动调和
drift_policy=auto_fix 时漂移自动置部署 pending,Agent 下轮 poll 重应用;防循环(轮数上限 + 防抖窗口)。
手动 Sync(F2)
drifting 部署可调 POST /deployments/:id/sync 清退避置回 pending 并打指令戳,强制 Agent 重调和一轮。
调整指南(怎么调整)
- 改忽略规则:pid / restart_count / 临时日志目录这类"运行态产物"放进 policy.ignore_paths;精确路径用 /components/0/healthcheck,一批组件用 /components/*/runtime/*,深通配用 /components/**/readiness。
- 改检测对象:actual_state 建议从 /desired-states/:id/declaration 导出结构后修改,保证字段路径与期望一致。
- 改收敛方式:修完配置先 drift/check 复核再 reconcile;反复收敛不成功要查是不是还有其他字段被改。
- 改自动调和策略:同步目标的 drift_policy=auto_fix 允许自动调和;改为 alert 则只记录漂移不自动调和。
- 改巡检节奏:把 drift/check 挂到定时任务形成巡检节奏;事件按 desired_state_id / agent_name 过滤定位归属。
做得好的场景
漂移检测最擅长"发现环境悄悄变了、并把环境拉回去":
- 配置被手改能发现:端口、版本、路径被手工改动,diff 精确到字段,几秒定位。
- 噪音可治理:ignore_paths 把 pid / 重启次数这类动态字段滤掉,告警不再狼来了。
- 收敛可闭环:reconcile 判定收敛结果,auto_fix 自动调和,部署状态在 drifting / synced / pending 之间受控流转。
- 审计留痕:每次检测 / 调和都有审计事件,配置变更"谁在何时改的"可回溯。
限制与不足
以下是明确的边界,使用前先知道:
- 只判定不应用:reconcile / auto_fix 只判定与记录,实际把组件改回期望状态需 Spoke Agent 重应用;演示 Poller 不自动应用进程。
- auto_fix 防循环上限:单条部署最多 3 轮自动调和、两次间隔 ≥5 分钟;无关联同步目标(drift_policy 非 auto_fix)时仅记录。
- Hub 汇总比对:漂移检测当前由 Hub 汇总 actual_state 比对;Spoke 本地比对是设计目标,避免中心化流量。
- 无自动通知:事件只落库 + 审计,不自动发告警通知(通知属告警模块能力)。
场景故事
故事 1
有人手改了服务器配置,漂移事件产生
场景:发现漂移
角色:运维工程师 + 应用开发
耗时:约 5 分钟
- 背景
- 陈工上午发完 demo-app 部署后,有人(多半是临时救火的同事)直接登录服务器把 web 端口从 8080 改成了 9090。陈工用漂移检测一查,平台把"期望状态 vs 实际状态"一对比,立刻发现了差异。
- 传统做法对比
- 以前服务器配置被手改,只能靠"下次出事才发现"或定期登录人工对比,一次巡检 6 台机器 40 分钟,改完还没人记得;现在漂移检测秒级给出差异清单,部署状态直接标成 drifting。
- 角色
- 陈工(平台运维,发现漂移)+ 阿强(应用开发,被怀疑的对象,帮忙确认改动)。
- 操作步骤
-
- 打开"部署与漂移"页(/apollo/deployments)
- 对 demo-app 执行漂移检测:构造 actual_state(web 探针 URL 改为 127.0.0.1:9090)
- 提交,查看返回
- 到漂移事件列表看落库记录
- 系统响应
- 检测返回结构与事件记录:
POST /api/v1/drift/check
{ "code": 0, "data": { "has_drift": true,
"diff": [ { "op": "replace",
"path": "/components/0/probes/readiness/url",
"expected": "127.0.0.1:8080",
"actual": "127.0.0.1:9090" } ] } }
关联部署被标记 drifting,审计记录 DRIFT_DETECTED。
- 结果洞察
- 漂移 = 期望状态与实际运行状态的偏差,用 JSON Patch 路径级 diff 表达:哪个字段、期望什么、实际是什么,一目了然。漂移事件落库并可进审计,是"配置悄悄变了"的第一手证据。
- 调整建议
- 定期 / 定时触发 drift/check 形成巡检节奏;事件按 desired_state_id、agent_name 过滤定位归属,再决定是修复还是更新期望状态。
- 动手试一试
- 登录:admin / admin1,端口 18082。操作:在部署页对 demo-app 跑一次漂移检测,把 web 探针 URL 端口字段改成 9090 提交。预期结果:has_drift=true,diff 里出现 op=replace、path 指向 web 探针 URL 那条差异。
- 限制提示
- actual_state 需按期望声明结构提交(建议从 /desired-states/:id/declaration 导出后改);漂移检测当前由 Hub 汇总比对,Spoke 本地比对是设计目标;无漂移时不落事件(has_drift=false 直接返回)。
故事 2
漂移 diff:一目了然的 JSON Patch 差异
场景:查看 diff
角色:应用开发
耗时:约 3 分钟
- 背景
- 阿强打开漂移事件列表,看到一条条结构化 diff:字段路径、期望值、实际值全都在,不用再靠"两个配置逐行对"。他要把这些 diff 讲给陈工听,让他快速定位改动。
- 传统做法对比
- 以前比对配置靠 diff 两个整文件,几百行一起滚,核心差异被淹没在无关变更里;现在 diff 精确到字段路径(/components/0/...),谁变了、从什么变成什么,几秒看清。
- 角色
- 阿强(应用开发工程师,解读 diff 并协助定位改动来源)。
- 操作步骤
-
- 打开漂移事件列表(GET /drift/events)
- 点击一条事件,查看 diff JSON
- 对照期望声明定位到对应组件 / 字段
- 系统响应
- 事件列表返回结构示例:
GET /api/v1/drift/events
{ "code": 0, "data": [ { "id": 1,
"desired_state_id": 1, "agent_name": "spoke-01",
"has_drift": true,
"diff": [ { "op": "add", "path": "/components/0/args/log_level",
"expected": "debug" },
{ "op": "replace", "path": "/components/1/version",
"expected": "1.0.0", "actual": "0.9.0" } ],
"created_at": "2026-08-09T10:20:00Z" } ] }
- 结果洞察
- diff 三种 op:add(期望有而实际缺)、remove(实际有而期望无)、replace(值不同)。数组按下标比对;数值归一化为 float64 比较,避免 int / float 误判。多字段漂移会产出多条 diff。
- 调整建议
- 结合字段路径判断类型:版本 / checksum 类可归类 version_drift,配置字段归类 config_drift;事件按 created_at 排序,先看最新的改动。
- 动手试一试
- 操作:跑一次漂移检测后刷新事件列表,看新事件里的 diff 数组;把端口改回后再检测,确认不再新增同路径事件。预期结果:diff 中的 path 精确指向被改字段。
- 限制提示
- diff 路径对数组用索引(/components/0/...);嵌套结构逐层递归,深层改动会产出多条 diff;demo 只落库记录,不自动发通知(通知属告警模块能力)。
故事 3
ignore_paths 忽略动态字段,别让 pid 天天"报警"
场景:噪音治理
角色:运维工程师
耗时:约 4 分钟
- 背景
- 部署之后陈工发现老是出现"漂移"事件,查 diff 全是 runtime 下的 pid、restart_count、日志目录这类动态字段——它们每次重启都会变,根本不该算漂移。他在期望状态策略里配置 ignore_paths,把这些路径通配忽略掉。
- 传统做法对比
- 没有忽略规则时,动态字段(进程 pid、重启次数)每次都触发漂移,运维被噪音淹没,真问题反而被忽略(狼来了效应);现在路径级忽略规则命中即不产生漂移、不触发收敛。
- 角色
- 陈工(平台运维,配置忽略规则治理噪音)。
- 操作步骤
-
- 打开期望状态声明,在 policy.ignore_paths 配置忽略路径
- 例如 - /components/*/runtime/*(demo 已内置)
- 重新执行漂移检测
- 观察 runtime 相关 diff 消失
- 系统响应
- demo 声明内置 ignore_paths 配置:
policy:
require_guomi_signature: false
ignore_paths:
- /components/*/runtime/*
配置后 drift/check 对 pid / restart_count 等不再产出 diff,has_drift 只反映真实差异。
- 结果洞察
- 忽略规则三种写法:精确路径(/components/0/healthcheck)、单段通配 *(/components/*/digest)、深通配 **(/components/**/readiness)。命中路径不产生漂移、不触发收敛,避免自愈与运行态打架。
- 调整建议
- pid / restart_count / 临时日志目录这类"运行态产物"统统放 ignore_paths;对真正关心版本 / 配置的字段保持敏感,别过度忽略掩盖问题。
- 动手试一试
- 操作:构造一个带 runtime.pid 差异的 actual_state,先看它产生 diff;把 /components/*/runtime/* 加入 ignore_paths 后重检,diff 消失。预期结果:动态字段不再算漂移。
- 限制提示
- 忽略规则命中"祖先前缀"时会忽略整棵子树;过度忽略可能掩盖真实故障,建议只忽略确属动态的字段;ignore_paths 在期望状态 policy 内配置。
故事 4
reconcile 收敛与 auto_fix 自动调和
场景:收敛闭环
角色:运维工程师
耗时:约 4 分钟
- 背景
- 确认 web 端口确实被误改后,陈工把端口改回 8080 并重新提交实际状态,执行调和(reconcile)。平台判定已无差异,部署状态从 drifting 回到 synced;而对于同步目标 drift_policy=auto_fix 的漂移,平台会按策略自动把部署置 pending 触发重调和。
- 传统做法对比
- 以前发现配置被改,要手工改回去、再人工核对、再更新各种台账,流程全靠记忆;现在"检测 → 修 → 调和 → 收敛"一条链路,收敛结果自动判定,配置了 auto_fix 的漂移还能自动触发 Agent 重应用,审计留痕。
- 角色
- 陈工(平台运维,执行调和并确认收敛)+ 阿强(应用开发,确认 auto_fix 策略归属)。
- 操作步骤
-
- 打开漂移事件,点击"调和"(POST /drift/:id/reconcile)
- 携带修正后的 actual_state(端口改回 8080)
- 观察返回 has_drift=false
- 部署状态回到 synced;检查同步目标 drift_policy 是否 auto_fix
- 系统响应
- 调和成功返回:
POST /api/v1/drift/1/reconcile
{ "code": 0, "data": { "has_drift": false,
"reconciling": false } }
auto_fix 触发时返回:{ "has_drift": true,
"auto_fix_triggered": true,
"auto_fix_reason": "drift_policy=auto_fix 允许自动调和:部署已置 pending,Agent 下轮 poll 将重新应用期望状态" }
关联部署由 drifting 更新为 synced(或置 pending 待重调和),审计记录 DRIFT_RECONCILE。
- 结果洞察
- 收敛判定语义:仍有漂移 → reconciling=true(指示 Spoke 重新拉取并应用);无漂移 → deployment synced。auto_fix(阶段 E)把"自动收敛"落地:漂移检测到且同步目标 drift_policy=auto_fix、未超防循环上限时,部署置 pending 让 Agent 下轮 poll 重新应用期望状态,并记录轮数与最近触发时间用于防循环。
- 调整建议
- 修完先验一下(drift/check)再调和,避免提交的 actual_state 还有别的差异;想放开自动调和把同步目标 drift_policy 设 auto_fix,想保守就保持 alert(只记录不调和);反复收敛不成功要查是不是又有其他字段被改。
- 动手试一试
- 操作:对事件做调和——先提交"未修正"的 actual_state,预期返回 reconciling=true;再提交"端口已改回"的 actual_state,预期 has_drift=false、部署回 synced。进阶:把同步目标 drift_policy 改成 auto_fix 再制造漂移,观察 auto_fix_triggered=true。
- 限制提示
- reconcile / auto_fix 当前只"判定与记录",真正把组件改回期望状态需 Spoke Agent 重应用(演示 Poller 不自动应用);auto_fix 有防循环上限(单条部署最多 3 轮、间隔 ≥5 分钟),超限只记录;无关联同步目标时 auto_fix 不生效。
常见问题
漂移和"部署失败"是一回事吗?
不是。部署失败是"上线过程"出问题(探针超时、组件 failed);漂移是"上线之后"环境偏离了期望状态(配置被改、版本被降)。失败看部署状态,漂移看 drift_events。
diff 里的 path 怎么读?
path 是 JSON Pointer 风格,/ 分隔层级,数组用索引:/components/0/probes/readiness/url 表示"第 0 个组件的 readiness 探针 URL"。配合 expected(期望值)和 actual(实际值)就能定位具体改动。
为什么 pid 这种字段老是误报?
pid、restart_count、临时日志目录是每次重启都会变的运行态字段,本来就不该参与"是否漂移"的判定。用 ignore_paths(如 /components/*/runtime/*)把它们忽略掉即可,命中路径不产生漂移。
调和之后部署就变绿了吗?
调和判定"无漂移"时部署回到 synced;但 reconcile 只判定与记录,真正把组件改回期望状态要 Spoke Agent 应用。演示环境里 Poller 不自动应用,收敛路径以判定结果为准。
漂移检测会自动修复吗?
配置了 drift_policy=auto_fix 的同步目标会:漂移检测到 → 部署置 pending → Agent 下轮 poll 重新应用(有轮数上限 3 与防抖 5min 防循环)。未配置(alert 策略)则只记录漂移,需要人工 reconcile 收敛。
主题小结
一句话:漂移治理 = 结构化 diff 看清偏差 + ignore_paths 滤掉噪音 + reconcile 判定收敛 + auto_fix 自动调和。核心认知:漂移是"环境偏离期望",diff 精确到字段。边界:只判定不应用(应用靠 Spoke 重拉取)、Hub 汇总比对、auto_fix 有防循环上限、无自动通知。