跳转至

阶段十六:控制面后端冗余与断网只读控制台

阶段状态:已完成并归档

计划状态:已完成

当前工作包:无;P16-5 已完成并归档

🧾 计划状态

计划状态:已完成;阶段状态:已完成并归档。 P16-0 已冻结故障事实、只读备用边界和 RPO/RTO;P16-0A 完成 RR、MM、DD、d02、dog-public 的可选 Exit Node 与重启持久性验收;P16-0B 完成 MM 核心域名双栈入口修复;P16-1 至 P16-5 已完成 102 只读 Auth、Console/API 备用、Edge 本地回退、101 单观察者安全路径、RR 整机重启和故障恢复演练。生产端仍由 RR 保持唯一写权威,102 只提供只读降级控制面。完整验收证据见 P16 完成归档。

📌 结论摘要

P16 的目标是让 console.yohan.fun 在 RR 主机或 RR 后端故障时,仍能通过已建成的 MM、d02、DD、dog-public Edge 进入一个明确标注为只读降级的控制面,至少完成登录验证、查看网络态势、节点状态、告警和最近一次权威快照。方案选择 102 作为控制面备用节点,异步复制 Auth SQLite 与同一会话密钥,运行 Console、Auth 和只读 API Gateway,并由 Edge Nginx 在 RR 回源失败时本地回退到 102;不在多个节点同时开放写事务,不复制 Runtime、Nomad 或第二调度器。依据是 P15 完成归档、部署门禁与高可用边界、Auth SQLite 结构、API Gateway 拓扑只读投影和 RR/MM 整机故障现场;影响是 P16 提供 RPO 不超过 5 分钟、只读能力可用的降级控制台,而不是伪造双活写入。

🧱 技术栈与架构选型

候选方案 可维护性 部署复杂度 数据风险 结论
继续增加 Edge,继续单 RR 后端 高 低 高,RR 故障仍全断 不采用,已经被 P15 现场否定
102 只读备用控制面 中高,复用现有镜像和 SQLite 中,新增复制与 Edge 回退 低,禁止备用写事务 采用
Auth/Runtime 双活写入 低,需要冲突解决和 fencing 高 高,Cookie、密码和 Runtime 数据可能分叉 本阶段不采用

只读备用控制面只承载 Console、Auth、API Gateway 的只读投影、源码快照与拓扑快照。Runtime、Nomad Server、Cloudreve、AI 任务执行和 OmniRoute 写操作仍以主控制面为准;备用模式不得创建任务、修改密码、撤销 Key、修改 ACL 或写入 Runtime 数据。

🎯 目标、成果与验收契约

工作包 目标 验收依据
P16-0 故障事实与恢复契约冻结 已完成:确认 Edge、DERP、Peer Relay 不复制 RR 后端;冻结 102 只读备用方案和 RPO/RTO
P16-0A 全网 Exit Node 能力与网络态势标记 已完成:RR、MM、DD、d02、dog-public 的 ExitNodeOption=true、ExitNode=false、双栈路由获批;五台 tailscaled 重启持久;网络态势独立显示“可作为 Exit Node”和“正在通过 Exit Node 出网”,全部节点默认直连
P16-0B MM 核心域名双栈入口一致性修复 已完成:Home 选择 MM Exit Node 后优先走 IPv6,console 命中错误 vhost;十个 MM 核心域名已补齐 IPv6 监听、证书和公网回读,Home 已恢复默认不使用 Exit Node
P16-1 控制面备用运行基线与会话连续性 已完成:Auth SQLite 一致性快照、同一 AUTH_SESSION_SECRET、102 Auth 容器、密码/API Key 写拒绝和 RR 会话跨节点验证通过
P16-2 Console/API Gateway 只读备用 已完成:Console 静态镜像、API Gateway 拓扑投影、快照 freshness 和不写 Runtime/Nomad 通过
P16-3 Edge 本地回退 已完成:活动 Edge 对 Console/Auth/API 使用 RR 主路径和 102 备用路径,连接失败、500/502/503/504 受控回退
P16-4 观察者独立与 DNS 安全 已完成:101 单观察者安全路径、证据冲突 fail closed、非法候选和凭据缺失回归通过
P16-5 故障演练与生产发布 已完成:RR 整机重启、损坏快照负向回归、关键容器恢复和最终发布门禁完成

控制面备用模式必须显式返回 deployment_mode=standby_readonly、source=rr-last-known-good、snapshot_age_seconds 和 read_only=true。认证数据库快照必须使用 SQLite 一致性机制生成,不能直接复制正在写入的 WAL 数据库;复制、安装和运行凭据均保持 root:root 0600。

拓扑备用快照继续使用 agentmeshos.network-topology/v1,超过 5 分钟显示 freshness=stale,超过 30 分钟显示 unavailable,不能把旧状态伪装成实时数据。Edge 回退不得改变 DNS A 记录,也不得把只读备用节点的健康结果写成主控制面恢复。

🧩 分布式底座接入声明

  • 接入结论:接入 Network、Compute、Observability 和 docs-governance。
  • 使用层:102 运行备用控制面容器;Edge 只增加本地回退;101 继续维护 DNS 控制器和快照同步。
  • Scheduler:Nomad 仍是唯一任务调度器;备用控制面不能提交、重试或清理 Nomad Job。
  • Compute:RR 保持主控制面权威;102 只运行无写权限的备用服务。
  • Storage:Auth SQLite 只允许从 RR 到 102 单向快照,禁止 102 反向覆盖 RR。
  • Credentials:会话密钥和 API 凭据只在 root-only 0600 文件与容器环境文件中,不写入仓库、日志或前端。
  • Lifecycle:备用服务不引入审批、租约或人为 TTL;快照保留 last-known-good 和失败隔离。
  • Evidence:登录、Cookie、拓扑 freshness、边缘回退、DNS 冲突和整机恢复必须有现场命令输出。

🛠️ 工作包与依赖

P16-0 已完成故障边界复核。RR 的 Console、Auth、API Gateway 和 Runtime 后端是单点;网络入口的四台 Edge 都回源 100.64.0.1:39200,所以 RR 故障时纯网络冗余不能恢复登录。备用节点选择 102,因为它已有 Tailnet、Docker、官方 OmniRoute 和稳定入口,且不与活动 Edge 的 TLS/Nginx 角色耦合。

P16-0A 已完成。RR、MM、DD、d02、dog-public 统一执行 tailscale set --advertise-exit-node=true,同时保持 --exit-node=、accept-dns=false 和 accept-routes=false;Headscale 批准五台节点的 0.0.0.0/0 与 ::/0。脚本只允许这五台节点通过,其他节点尝试 apply 必须失败。101 本地回读五台均为 ExitNodeOption=true、ExitNode=false、Online=true,且 101 自身 ExitNode=false、ExitNodeID 与 ExitNodeIP 均为空。网络态势已经把“可提供出口能力”和“当前正在使用出口”拆成两个独立标记,并在摘要显示候选数与正在使用数;全部节点默认直连,只有用户明确手动选择后才允许加载 Exit Node。该能力不改变当前默认网络、DNS、代理或业务路由。

P16-0A 网络态势修正(2026-10-04):节点身份改为以 Headscale 稳定的 Tailnet IPv4 为锚点(TAILNET_IP_OVERRIDES),机器名/DNS 名只作次要回退,避免用户重命名节点后掉进“待分类节点”;102 与 NAS 已补登记为 102 计算节点、NAS 存储节点,102 圆圈显示别名 102。生成器同时从 Headscale 回读实时 name 与 given_name,分别展示 Tailnet 节点名和 Tailnet DNS 名,不再把客户端缓存名称当成权威名称。Exit Node 使用不再从观察者视角的 peer ExitNode 标记推断(该字段只表示“观察者正在使用的出口”),改由生成器经受控 SSH 逐节点读取 ExitNodeStatus 或 Self.ExitNode,记录是否使用以及目标 Tailnet IPv4,再映射为 exit_node_target_id;控制台展示“正在使用的出口节点”,例如 Home → dog-public。使用状态缓存收紧为 60 秒,与拓扑定时刷新一致,避免用户切换出口后长时间仍误报直连;探测失败且无新鲜观测时保持“未知”,绝不误报为“直连”。筛选分类保持“全部/公网 HTTPS/Tailnet 直连/DERP/Peer Relay/SSH 管理/公网数据面/Exit Node 能力/正在使用 Exit Node”,与拓扑图例一一对应。

P16-0A 归档后校正(2026-10-05):tailnet_name 改为与节点清单导出相同的 given_name → name 优先级,Tailnet 节点名统一显示 Headplane Name 列的值;tailnet_dns_name 与之同值,控制台不再重复展示,设备注册码继续以“OS 主机名”保留。

P16-0B 已完成。Home 在 2026-10-03 短时选择 MM 为 Exit Node 后,console.yohan.fun 同时解析到当前权威 A 与 AAAA;IPv4 返回 302,但 IPv6 由 MM 上 derp-mm.yohan.fun 的默认 vhost 接管并返回 HTTP/2 404 和 CN=derp-mm.yohan.fun。根因不是 Exit Node、RR Console、Auth 或 DNS 故障,而是 MM 宝塔核心域名 vhost 只监听 IPv4,缺少 listen [::]:80、listen [::]:443 ssl 和 listen [::]:443 quic。agentmeshos-mm-edge-ipv6-guard.sh 已升级为 v0.2.0,覆盖 mesh/cloud/auth/console/docs/status/api/omni/nomad/ai,对缺失文件、监听锚点缺失和重复监听 fail closed,修改前逐文件备份,nginx -t 或 reload 失败自动回退。生产应用后十个域名 IPv6 SNI 均返回自身域名证书,console 为 302;Home 设置 ExitNodeID=6 且 IPv4 出口经公网回显验证为 MM 时,强制 IPv6 访问 console.yohan.fun 返回 302 登录跳转,测试结束已恢复 Exit Node=空。

P16-1 已完成。RR 侧 Auth SQLite 一致性快照记录了 PRAGMA quick_check、Schema、账号数、Key 数和 SHA-256,并通过受控 SSH 小文件通道复制到 101 与 102。102 的备用 Auth 使用同一 AUTH_SESSION_SECRET,只开放登录验证和只读读取;密码、Key 和管理写操作返回 503 standby_readonly_write_rejected。

P16-2 已完成。102 的 Console、API Gateway 和回源网关绑定私有地址运行;API Gateway 只开放 topology、node inventory、alerts 和 bridge status 等只读投影,未代理 Runtime、Nomad 管理、系统操作或写接口。

P16-3 已完成。Edge 模板把 console/auth/api 的 proxy_pass 改为主备 upstream;主路径仍为 RR,102 作为 backup,只对连接失败、超时和上游 500/502/503/504 回退,其他域名继续 fail closed。

P16-4 已完成。DNS 控制器在 RR 在线时保持双视角;RR 明确不可达时,101 单视角只能在活动 Edge 连续失败且本地管理路径正常时写入,并在写入前发送告警。证据冲突、凭据错误或 API 失败继续 fail closed。

P16-5 已完成。生产已验证 102 备用服务可登录读取、写操作被拒绝、损坏快照不会覆盖 last-known-good,并执行 RR 整机重启、Tailnet/Docker/关键容器恢复和发布门禁复核。

🧪 测试与验收

认证连续性测试必须覆盖 RR 登录后在 102 备用节点读取同一 Cookie、102 密码修改被拒绝、API Key 撤销在写者侧等待下一次快照、会话密钥错误时拒绝启动和快照损坏时不覆盖 last-known-good。拓扑测试必须覆盖 fresh、stale、unavailable、节点/链路字段缺失、敏感字段泄漏和旧快照不伪报实时。

Exit Node 回归必须覆盖五台目标节点的正路径、101 与未知节点的负路径、RR 新旧 Tailnet 名称兼容、控制台能力标记、正在使用标记、摘要计数和“可选但默认不使用”详情展示;现场还要覆盖五台 tailscaled 受控重启后的宣告持久性。

MM 双栈入口回归必须覆盖 console 仅 IPv4、omni 已有部分 QUIC、重复 IPv6 listener、目标站点缺失和重复 apply 幂等五类场景。生产验收必须逐域名以 MM 公网 IPv6 和正确 SNI 回读证书主体、HTTP 状态和 nginx -t 结果;Home 经 MM Exit Node 的复现必须在 finally 中恢复 --exit-node=,不得把 Home 永久留在 MM 出口。

Edge 回退测试必须在 1440x900 和 390x844 验证 Console/Auth/API 三条路径,RR 连接拒绝、502、503、504、超时和恢复;其他核心域名在无备用后端时继续失败关闭。DNS 测试必须覆盖 RR 在线双视角、RR 不可达单视角、两方冲突、101 本地路径失败、写 API 401 和回滚。所有测试通过后,才允许提交和部署生产。

🔁 回退方案

回退分为四层:Edge 模板恢复单 RR upstream;102 停止备用 Console/Auth/API 容器;Auth 和拓扑快照恢复上一版 last-known-good;DNS 控制器恢复只读双视角模式。回退不删除 RR 主库、不覆盖 RR 原始认证数据、不改变 Cloudreve、Nomad 和 Runtime 生产数据。

⚠️ 未决项 / 需要用户决策

事项 当前事实 下一步
102 只读备用 已部署并通过健康、只读会话和写拒绝验收 已完成并归档,后续漂移按新运维事件处理
Exit Node 默认使用 RR、MM、DD、d02、dog-public 只提供候选能力,默认不改变客户端出口 后续客户端需要时显式选择,P16 不自动切换任何节点出口
备用数据范围 仅 Auth、Console、只读 API 和拓扑快照 不纳入 Runtime、Nomad、Cloudreve 或 OmniRoute 写操作;保持只读边界
RPO/RTO 目标 RPO 不超过 5 分钟,目标 RTO 不超过 3 分钟 P16 已按 timer、重启和恢复证据收口;后续异常按运维事件复验
DNS 观察者 RR 不可达时的 101 单观察者安全路径已验证 保持双视角优先、证据冲突 fail closed