25 KiB
23 domainCheck 终局运维架构设计:海外单脑控制面
一、先回答最核心的问题:ops job 是什么
ops job 不是一条 shell 命令,也不是“点一下按钮马上执行”的临时动作。
ops job 的本质是:
一条可审计、可编排、可回放、可中断、可回滚的运维意图记录。
它表达的是:
- 谁发起了什么目标
- 目标作用到哪些节点
- 按什么策略执行
- 分成哪些步骤
- 每一步执行结果是什么
- 有没有失败
- 失败后是否回滚
- 最终系统是否达成目标状态
也就是说:
- shell 是“执行手段”
ops job是“运维对象”
最终系统里,不应该由前端、Codex、定时器直接执行 shell。
它们都只能做一件事:
创建
ops job
然后由专门的执行器去消费这个 ops job。
二、为什么最终不要“直接执行 shell”
因为“直接执行 shell”有 7 个天然缺陷:
1. 无法审计
你知道执行了什么命令,但不知道:
- 谁触发的
- 为什么触发
- 是否按预期执行完
- 执行前后的系统状态
2. 无法标准化
今天你执行:
systemctl restart domaincheck-worker
明天可能要补:
- 重启前健康检查
- 失败后重试
- 失败后回滚
- 成功后采样验证
如果直接跑 shell,这些逻辑会散落在脚本、前端、人工脑子里。
3. 无法并发调度
当节点从 2 台增加到 10 台,直接 shell 无法优雅表达:
- 先更新 controller
- 再灰度更新一台 worker
- 观察 10 分钟
- 再滚动更新剩余节点
4. 无法做回滚
shell 通常只描述“怎么做”,不描述“失败怎么办”。
5. 无法限流和保护
比如:
- 同一时间最多更新 1 台 controller
- 同一地域最多重启 2 台 worker
- 正在跑检测的节点禁止强更
这些都不是 shell 擅长的层面。
6. 无法与 Codex 协作
Codex 可以判断,但最终必须落到稳定、标准、可追踪的动作模型,否则还是人工时代。
7. 无法沉淀成平台
你要的不是“几个脚本”,而是“海外一台机器接管全部大陆机器”的正式运维平台。
平台核心对象必须是 ops job,不是 shell。
但这里还有一个非常重要的配套原则:
- 不直接执行 shell
- 不等于只会给出“你再去跑这几条命令”的人工建议
真正终局形态里,还需要一层:
组合恢复入口
比如“远端日志回传恢复”这类高频动作,就不应该让人工自己拼:
- 开关键回传
- 看 overview 有没有生效
- 再决定去看哪台节点日志
而应该被固化成正式单入口,例如:
bash domain-api/deploy/multi-region/drive_ops_center.sh log-sync-recover
它表达的不是一条 shell,而是一条标准运维意图:
- 执行恢复动作
- 读取复核结果
- 输出下一跳命令
也就是说,终局平台既不是“裸 shell”,也不是“只有抽象 job 没有现场入口”,而是:
- 底层执行对象是
ops job - 上层驾驶入口是组合意图命令
这样海外单脑控制面、后台按钮、Codex 驾驶员,才能既保持标准化,又保持现场处理效率。
同理,“运行中 API 还是旧代码 / 旧 schema” 这类问题,也不应该再退回到人工只记一条:
systemctl restart domaincheck-api
正式平台里它应该被提升为组合恢复入口,例如:
bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover http://127.0.0.1:8100
如果当前不是只做单点刷新,而是要把:
- 运行时刷新
- 总检复核
- 上线门禁
- 下一跳动作判断
一起串成一条标准联调车道,那么入口应该继续提升为:
bash domain-api/deploy/multi-region/drive_ops_center.sh go-live-recover http://127.0.0.1:8100
它表达的依然不是“直接 shell”,而是:
- 先识别 runtime drift
- 再执行统一恢复
- 再回读 go-live / stack diagnosis contract
- 最后输出主动作与复核动作
这样页面、CLI、Codex 驾驶员就不会再次退回到:
- 人工猜该不该重启
- 人工猜重启完先看哪个接口
- 人工自己拼下一跳命令
而是继续保持“上层是组合意图入口、底层才是执行手段”的正式平台结构。
同样,“节点接管缺口恢复”也不应该拆成散落在脑子里的三四步:
- 先看哪台节点有缺口
- 再看 handover 详情
- 再生成 bootstrap plan
- 再手工整理下一步命令
它同样应该被固化成正式组合入口,例如:
bash domain-api/deploy/multi-region/drive_ops_center.sh agent-gap-recover
它表达的依然不是“直接 SSH 干活”,而是一条标准运维意图:
- 找到首个接管缺口节点
- 读取该节点 handover 状态
- 生成该节点接入方案
- 输出该节点当前 onboarding 阶段
- 输出后续检查与执行建议
为了不让值班同事继续在脑子里手动拼:
node-handovernode-bootstrap-planonboarding.acceptance
还应该有一个节点级入口:
bash domain-api/deploy/multi-region/drive_ops_center.sh node-onboarding http://127.0.0.1:8100 mainland-worker-01
它负责把单节点当前到底处于:
- 待补资料
- 待生成 bootstrap
- 待执行 bootstrap
- 可进入
onboarding.acceptance
统一压成一个节点级结果。
并且当节点已经进入 acceptance_ready 时,不应该再让值班同事自己拼 playbook 参数,而是继续直接给出:
bash domain-api/deploy/multi-region/drive_ops_center.sh node-acceptance-plan http://127.0.0.1:8100 mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh node-acceptance-run http://127.0.0.1:8100 mainland-worker-01 ops-center
也就是:
- 先看这次验收将创建哪些标准化任务
- 再把验收统一签发进 ops job / playbook run 体系
同理,bootstrap 也不应该只停在“生成脚本块”,还应该有:
bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-preview http://127.0.0.1:8100 mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-run http://127.0.0.1:8100 mainland-worker-01 ops-center
这样:
- 需要人工落地脚本时,用
node-bootstrap-plan - 需要纳入标准工单追踪时,用
node-bootstrap-run - 需要 ready 之后做验收时,用
node-acceptance-run
再往上还应该有一个真正的节点级自动恢复入口:
bash domain-api/deploy/multi-region/drive_ops_center.sh node-recover http://127.0.0.1:8100 mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh node-recover http://127.0.0.1:8100 mainland-worker-01 confirm ops-center
它的职责不是提供新的能力,而是替值班同事做状态分流:
- 当前还在接入阶段,就转向
node-bootstrap-run - 当前已经进入验收窗口,就转向
node-acceptance-run - 默认先预览决策,
confirm后再真正执行
这样平台里真正被沉淀下来的就不是“几条应急命令”,而是:
log-sync-recoveragent-gap-recovernode-onboardingnode-recover- 后续还会继续有
release-rollout-recover delivery-queue-recover
也就是说,终局平台里的高频运维动作,最终都应该沉淀为:
- 组合恢复入口
- 正式 preview / execute contract
- 一次动作只执行一次
特别是 node-recover 这类入口,后续必须固定成:
- preview 走
onboarding/recovery/preview - confirm 走
onboarding/recovery/execute
如果后端 execute 已经成功接单,CLI / 页面 / Codex 不允许再补跑一次底层:
node-bootstrap-runnode-acceptance-run
因为这已经不是“展示问题”,而是会直接造成重复创建 ops job / playbook run 的正式执行错误。
这里再补一个页面落地约束,避免海外单脑控制面首屏继续堆四五套重复摘要:
- Ops Center 首屏必须先有一块
单脑驾驶舱首屏
- 它不是新的数据源
- 而是把现有
go-live-summarystack-diagnosisdriver-feedcodex-brief.automation_coverage统一压成一屏结论
- 而是把现有
首屏至少应固定展示:
- 上线收口状态
- 发布闸门状态
- 自动化收口状态
- 主处理车道
- 默认下一步
- 后端接管覆盖
- 观察层完整度
- 现场日志覆盖度
并且页面分层必须明确:
- 首屏负责
- 判断
- 决策
- 默认下一步
- 一键进入处理面板
- 下方详情区负责
- 解释原因
- 展开问题清单
- 展示推荐动作与命令
- 提供 contract 下钻
也就是说,下方区块不应该再和首屏重复堆一遍相同按钮,而应明确退到“详情 / 复核 / 下钻”角色。
这样值班时第一眼先回答四个问题:
- 现在能不能上线
- 现在能不能正式发版
- 现在是不是已经进入自动化可接管状态
- 现在默认下一步到底是什么
然后才继续下钻到:
- 问题清单
- 推荐动作
- job / playbook / rollout 细节
也就是说,页面首屏本身就应该体现“海外单脑”的产品形态,而不是只把多个接口原样堆到一起。
标准组合恢复入口
而不是重新退化回人工 shell 时代。
这里还要固定一个已经进入实现层的动作口径:
- 如果后端已经把首个缺口节点收敛成
bootstrap_run- 页面、CLI、Codex 都优先显示“跑接入收口”
- 如果后端已经把首个缺口节点收敛成
run_acceptance- 页面、CLI、Codex 都优先显示“跑接管验收”
fix_managed_nodes- 仅保留为兜底入口
- 不能再覆盖已经明确收敛的单节点恢复动作
三、最高级方案的总目标
最终形态不是“海外后台 + 若干工具脚本”,而是:
一套以海外控制面为唯一运维大脑、以
ops job为核心对象、以节点 agent 为标准执行器、以 Codex 为智能驾驶员的正式运维平台。
一句话理解:
- 海外控制面负责“决策、编排、发布、审计、汇聚”
- 大陆节点负责“执行、回传、承载业务”
- Codex 负责“分析、建议、触发”
四、终局架构分层
建议直接按 8 层设计。
1. Control Plane 控制面
部署在海外主机。
职责:
- Web UI
- API
- 运维中心
- 节点目录
- 发布中心
- 日志中心
- 审计中心
它是唯一入口。
2. Intent Layer 意图层
所有动作先变成意图:
- 新增节点
- 安装节点
- 更新节点
- 重启服务
- 拉取日志
- 采集诊断
- 启动检测
- 暂停检测
- 回滚版本
这些意图统一落成 ops job。
3. Workflow Engine 工作流层
把一个运维动作拆成步骤和状态机。
例如 deploy.release:
- 预检
- 下载发布包
- 校验 checksum
- 解压到 releases
- 切换 current
- 重启服务
- 健康检查
- 标记成功
- 失败则回滚
这层不关心 shell 细节,只关心工作流编排。
4. Scheduler 调度层
决定:
- 哪个 job 先执行
- 哪些节点能执行
- 是否需要分批
- 是否需要串行
- 是否超过并发上限
- 是否落在维护窗口
5. Executor 执行器层
真正执行动作的不是前端,也不是 Codex,而是执行器。
执行器分 3 类:
node-agent executorrelease executordiagnostic executor
兜底再保留:
ssh rescue executor
6. Node Agent 节点代理层
每台大陆节点都跑一个常驻 domaincheck-node-agent。
职责:
- 主动注册
- 主动心跳
- 拉取待执行 job
- 本地执行命令
- 采集 stdout/stderr
- 推送结构化结果
- 推送日志和诊断包
7. Artifact & Release 发布层
发布必须是不可变版本制。
不能把正式运维建立在:
- 节点直接
git pull - 节点工作区可能脏
- root/www 用户混跑
正式模型应该是:
- 海外控制面构建 release 包
- release 包有版本号和 checksum
- 节点只下载、校验、切换
- 回滚也只是在 release 之间切换
8. Observability 观测与审计层
所有动作必须产生:
- 事件
- 日志
- 结构化结果
- 诊断包
- 审计记录
这层既给前端看,也给 Codex 看。
五、ops job 的正式对象模型
1. 一个 ops job 至少包含这些字段
job_codejob_typerequested_bysourceprioritytarget_selectorexecution_policydesired_statepayloadstatusplanresultcreated_atstarted_atfinished_at
2. ops job 的 5 个核心部分
A. Intent
用户真正想做什么。
例如:
service.restartdeploy.releasenode.bootstrap
B. Selector
作用到谁。
例如:
- 指定节点
- 指定 region=mainland
- 指定 role=worker
- 指定
hybrid=true
C. Policy
怎么执行。
例如:
- 串行
- 每批 1 台
- 失败即停止
- 健康检查失败自动回滚
D. Plan
实际拆出来的步骤。
例如:
- 预检
- 执行
- 验证
- 回滚
E. Result
最终结果和证据。
例如:
- 成功 / 失败 / 部分成功
- stdout / stderr 摘要
- 健康检查结果
- 诊断包链接
六、ops job 的状态机
建议不要只用简单的 queued/running/success/failed。
正式版建议:
draftapprovedqueuedplanningscheduleddispatchingrunningverifyingpartially_succeededsucceededrollback_runningrolled_backfailedcancelledtimed_out
这样后续你看后台时,能明确知道卡在哪一层。
七、ops job 不是单步骤,而是 DAG 工作流
最高级方案里,一个 job 不应只是一条线性步骤,而应支持 DAG。
例如“更新大陆两台 worker”:
- 先跑公共预检
- 再更新
mainland-worker-01 - 成功后更新
mainland-controller-01的 worker 部分 - 两边都成功后再做集群验证
有些步骤可以并行,有些必须串行。
所以正式设计应支持:
ops_job_stepsdepends_on_step_idretry_policytimeout_secondsrollback_step_id
八、节点不再是“机器”,而是“受控资源”
最高级方案里,要把节点建模成受控资源,而不是一堆 IP。
每个节点建议有:
node_coderegionrolecapabilitieslabelsdeploy_channelagent_versionreachablemaintenance_modeeffective_workerhybrid_workercurrent_releasedesired_release
这样调度器才能按资源能力派发任务。
九、Node Agent 应该怎样设计
这是整套方案最关键的一环。
1. 连接模式
首选:
- Agent 主动连海外控制面
- 长轮询或 WebSocket
不推荐以 SSH 为主链路。
2. Agent 的职责边界
Agent 不负责“决策”,只负责:
- 执行
- 回传
- 保持本机状态一致
3. Agent 的本地能力
Agent 至少应内置这些执行器:
systemd executorrelease executorshell executorlog collectordiagnostics collectorconfig renderer
4. Agent 的安全限制
Agent 必须执行白名单动作。
不能允许控制面下发任意 shell 就直接执行。
正确方式是:
- 控制面发
action + payload - agent 解析成受控执行
例如:
service.restart->systemctl restart xxxlogs.collect-> 收集指定日志源
shell executor 只能是受限兜底能力,且必须带审批和审计。
十、发布系统必须独立出来
终局方案里,发布不应该再依赖 git 工作区。
建议正式设计为:
1. Release Object
每个发布包都应有:
release_idversioncommit_shabuild_timeartifact_urlchecksummigration_versionrollback_to
2. 节点上的目录规范
/opt/domaincheck/releases/<version>/
/opt/domaincheck/current -> /opt/domaincheck/releases/<version>/
/opt/domaincheck/shared/
3. 更新流程
- 下载发布包
- 校验 checksum
- 解压到新目录
- 渲染配置
- 停服务
- 切
current - 启服务
- 做健康检查
- 失败回滚
这才是正式运维。
十一、日志体系必须分层
如果日志不分层,后期还是会被日志量拖死。
1. Event 事件流
默认长期保留,体量小。
包含:
- 服务启动/停止
- 任务开始/结束
- 检测阶段切换
- 代理异常
- DB/Redis 异常
- 第三方依赖异常
2. Job Log 任务日志
和 ops job 绑定。
用于看某次动作:
- 执行了什么
- 返回了什么
3. Runtime Log 运行日志
业务服务自身日志。
默认不全量常驻上传,只做:
- 游标读取
- 按需拉取
- 临时全量回传
4. Diagnostics Bundle 诊断包
故障时一键采集:
- env 摘要
- systemd 状态
- 关键日志
- cluster snapshot
- sync summary
- release info
十二、Codex 的最高级接管方式
你说的“海外 Codex 作为智能驾驶员”,最高级不是让 Codex 直接 SSH。
最高级方式是:
1. Codex 是 Planner + Operator
Codex 拿到的是:
- 控制面所有 API
- 节点状态
- ops jobs
- 结构化日志
- 发布记录
然后它只做两类事:
- 分析
- 创建标准
ops job
2. Codex 不直接绕过控制面
Codex 不应该:
- 直接登录每台机器
- 直接手搓 root 命令
- 直接破坏审计链
它应该永远走控制面 API。
3. Codex 的价值
它的价值不是“替代平台”,而是:
- 自动读状态
- 自动识别问题模式
- 自动建议动作
- 必要时自动创建低风险 job
- 把复杂联调变成结构化流程
十三、没有 Codex 时也必须完整可用
这点非常关键。
最终平台不能依赖 Codex 才能工作。
没有 Codex 时,后台也必须能:
- 纳管节点
- 发布更新
- 一键重启
- 一键巡检
- 一键采日志
- 一键采诊断包
- 查看执行结果
Codex 只是锦上添花,不是唯一入口。
十四、最高级方案里的安全体系
正式版必须加入:
1. RBAC
区分:
- 观察员
- 运维员
- 发布员
- 超级管理员
2. 审批流
高风险动作必须审批:
- 批量更新
- controller 重启
- 清理数据
- 回滚生产版本
3. Policy Engine
可以内置规则:
- 正在跑检测的 worker 禁止强更
- 大陆 controller 每次只能更新 1 台
busy节点默认拒绝重启
4. Secrets 管理
不把所有 SSH 密钥和密码散落到脚本里。
正式版建议独立 secrets 管理,例如:
- Vault
- 或最少做控制面统一加密存储
十五、这套方案为什么是“后续最少返工”的
因为它不是按“当前临时联调”设计,而是按“节点继续增长、功能继续增加、Codex 持续接管”设计。
它天然支持:
- 节点从 3 台扩到 30 台
- 从手工联调升级到一键部署
- 从后台按钮升级到 Codex 自动驾驶
- 从局部脚本升级到平台化运维
也就是说:
这是终局骨架,不是过渡补丁。
十六、我给你的最终建议
如果按最高级、最优秀、后续最少反复修改的标准来做,路线应该明确成:
- 海外主机做唯一控制面
- 一切动作先建模成
ops job - 节点统一由
node-agent执行 - 发布统一走 release 包,不走 git pull
- 日志统一做事件流 + 任务日志 + 诊断包分层
- Codex 只通过控制面 API 接管,不绕过控制面
- SSH 只保留 bootstrap 与救援,不做日常主链路
这套方案里,ops job 就是整个系统的“运维订单”和“执行凭证”。
它不是附属品,而是整套平台的中心对象。
十七、终局不是“一份大文档”,而是 7 份冻结 contract
真正能让这套体系后续少返工的,不只是理念对,而是:
概念层有总架构,执行层有正式 contract。
最终至少要冻结 7 份正式 contract:
1. ops_job_contract
建议以后直接以 ops_job_contract.md 为正式母本。
约束:
ops job如何创建- 如何审批
- 如何取消
- 如何派发
- 如何承诺 job / step / event 结构
它定义的是:
一切正式运维动作的执行订单模型
2. ops_agent_protocol
约束:
- Node Agent 如何注册
- 如何心跳
- 如何拉任务
- 如何回传事件
- 如何回传交付队列快照
它定义的是:
节点怎样成为“可托管执行器”
3. ops_driver_contract
约束:
overview.driver_recommendationsdriver-feedcodex-briefdriver-actions.preview/resolve/executeactivity-stream
它定义的是:
海外控制面、CLI、Codex 驾驶员如何共享同一套判断与动作入口
4. ops_playbook_contract
约束:
- playbook catalog
- preview
- execute
- playbook run
- playbook events
它定义的是:
多步标准作业怎样从“按钮集合”升级成正式编排对象
5. release_hub_contract
约束:
- Release
- Rollout
- Launchpad
- Default Rollout Gate
- Default Rollout Execution
它定义的是:
发布到底能不能发、该怎么发、先灰哪一台、失败怎么停
6. ops overview / inspection / delivery queue 收口 contract
这块现在虽然散落在 overview、inspection、activity-stream、delivery-queue 里,但终局一定要收成稳定协议。
建议以后直接以 ops_observability_contract.md 为正式母本。
至少要固定:
- 执行现场分组:
dispatch_active_nodesrecent_only_nodesstandby_nodesload_syncing_nodes
- 巡检收口:
- 最近
health.snapshot - 最近
logs.collect - 最近
diagnostics.collect
- 最近
- 回执队列治理:
- 节点级摘要
head_only头部记录flush / replay / discard
它定义的是:
海外控制面如何真正看懂现场,而不是只看到几条状态字串
7. ops_stack_diagnosis_contract
建议以后直接以 ops_stack_diagnosis_contract.md 为正式母本。
至少要固定:
stack_statusissuesnext_steprecommended_actionsquick_commandsfocus_ref
它定义的是:
海外单脑控制面的第一现场总检入口,页面、CLI、Codex 都必须先从这里起步
所以总架构文档的职责不是把所有字段都写死,而是明确:
- 哪 7 份 contract 才是后续实现的正式母本
- 页面、CLI、Codex 都只能复用这些 contract
- 不允许再额外衍生第 8 套逻辑
并且这 7 份 contract 以后不该只存在于文档目录里,还要进入统一 registry:
GET /api/v1/ops/contractsGET /api/v1/ops/contracts/{contract_key}
这样海外控制面、CLI、Codex 驾驶员才能按同一份 contract graph 导航,而不是继续靠人脑记“哪份协议在哪个 md 里”。
这一步非常关键,因为一旦没有 contract 冻结,你后面就会再次回到:
- 页面自己猜
- CLI 自己拼
- Codex prompt 自己解释
- 节点 agent 再走一套临时语义
那“单脑控制面”就又会退化成“多脑拼接系统”。
十八、单脑控制面的日常闭环应该固定成 6 段
你后面不应该再按“这次故障怎么处理”临时想流程,而应该把日常路径固定成下面 6 段。
1. 接管
- 签发 token
- 生成 bootstrap plan
- 节点注册
- 节点心跳进入托管目录
这里的目标不是“节点能连上”,而是:
节点成为正式受控资源
2. 验收
- 执行
onboarding.acceptance - 必要时追加
inspection.standard - 确认:
- service topology 正确
- agent 在线
- delivery queue 正常
- 巡检结果可回看
这里的目标不是“接进来就算完”,而是:
接入后立刻进入正式运维
3. 观察
- 看 execution scene
- 看 inspection overview
- 看 activity stream
- 看 delivery queue
如果现场异常,优先动作应固定成:
- 开关键日志回传
- 跑标准巡检
- 处理死信 / retrying
而不是重新 SSH 上去抓一遍日志。
4. 发布
- 看 Release Launchpad
- 看 Default Rollout Gate
- 先 worker canary
- 再批次推进
- 异常即停
- 必要时回滚
这一步必须始终坚持:
- 节点不
git pull - 发布只认 release artifact
- rollout 只认正式 gate
5. 故障
故障时优先路径应固定成:
driver recommendationactivity-streamplaybook run detaildelivery queueinspection / diagnostics
也就是说,先走:
结构化现场收口
再决定是否需要更重动作。
6. 救援
只有下面场景,才进入 SSH Rescue:
- Node Agent 没注册成功
- Node Agent 已失联
- release 切换中断,当前目录损坏
- systemd / Python 运行环境坏到 agent 无法自救
而且即使进入救援,也应该做到:
- 仍生成正式
ops job - 仍记录执行人、目标节点、动作来源
- 仍把结果写回控制面审计链
这就是“单脑控制面”的关键边界:
- 日常动作走标准 contract
- 标准动作走 agent / playbook / rollout
- 救援动作才进入 SSH
这样以后新增机器、换地域、换发布流程,都只是替换某一层执行器,不需要重写整个体系。