运行时配置热重载
状态:Current
最后核对:2026-05-16
本文档说明 nexus-backend 和 nexus-agent 当前使用的运行时配置模型。它是 Agent 热重载、配置版本、应用回报和端口冲突处理的设计参考。
摘要
运行时配置通过数据库权威的 RuntimeConfigSnapshot 下发。这个快照是单个节点 运行时目标的聚合根:
PingoraConfig:L4 gateway listener、route、upstream、TLS 材料和 listener policy。L3TunnelConfig:L3 tunnel rule 和 listener policy。NodeConfig:Agent 拥有的节点连接设置,目前是 multiplex 开关和端口。
生产 secure-control 路径每个配置代际发送一个快照。Agent 将整个快照作为一个 目标应用,并通过 ConfigApplyReport 回报结果。
protobuf 里仍保留旧的 split ConfigUpdate payload 用于兼容,但生产发布和重放 路径使用 RuntimeConfigSnapshot。
已修复的问题
旧热重载逻辑存在以下失败模式:
- 配置代际语义不足。部分路径发送固定版本,部分手工 push 路径会制造脱离数据库状态的版本。
- L4、L3 和节点连接设置作为独立局部更新流式下发,stream lag 或局部失败会让 Agent 处于混合运行时目标。
- broadcast receiver lag 时只继续读取,可能跳过最新权威状态。
- Agent apply 可能在后续校验或 L3 阶段失败前已经改变部分 listener。
- L3 tunnel 更新和 gateway L3 QUIC ingress 更新可能分裂。
- NodeConfig 不在重连重放中,multiplex 变更可能丢失。
- 后端没有 Agent 明确的 apply ack/nack。
- 外部端口冲突只记录日志并跳过,导致缺失 listener 的配置看起来像应用成功。
快照契约
RuntimeConfigSnapshot 是权威状态。如果某个配置域没有期望的运行时对象,快照也 必须携带该域的空配置。没有 rule 表示“删除这些运行时对象”,不是“保留旧对象”。
快照必须包含:
config_versionnode_idpingoral3_tunnelsnode_config
后端把快照放入 ConfigUpdate.runtime_snapshot;外层 ConfigUpdate.config_version 与快照版本保持一致,用于兼容。
版本语义
config_version 来自持久化配置状态。它是一个微秒时间戳代际,取目标节点相关 配置更新时间的最大值:
nodes.config_updated_at- 引用该节点 ID 或名称的 tunnel chain。
- 该节点的 listener policy。
- 包含该节点的 L3 network。
- 包含该节点的 peer network 中的 L3 endpoint。
- 包含该节点的 peer network 中的 L3 route。
nodes.updated_at 不作为配置代际来源。heartbeat、hello、metrics、运行时 listener 上报和 ConfigApplyReport 可以更新节点运行态 metadata,但不能制造新的配置代际。
只有明确要发布运行时配置的后端路径会调用 AppState::publish_runtime_config(node_id)。 该方法推进 nodes.config_updated_at,并发送当前数据库权威快照。
发布路径
以下后端路径会发布快照:
- 端口策略 upsert。
- L3 network create/update/deploy/delete。
- L4 chain create/update/deploy/delete。
- 节点 multiplex 设置变更。
POST /api/nodes/{id}/config刷新。
tunnel chain update 会向旧 transit 节点和新 transit 节点的并集发布快照。这样当某个 节点从 chain 中移除时,该节点也会收到新的空快照或缩减快照,从而删除旧 listener。
tunnel chain delete 会先解析受影响的 transit 节点,再删除数据库行,删除后发布快照。
POST /api/nodes/{id}/config 现在是刷新接口。它接受空 body 或 {},发送当前 数据库权威快照。内联 tcp_rules 或 upstreams payload 会被拒绝,因为它们会绕过 持久化配置代际模型。
重放和 Lag 处理
Agent 在 secure-control ConfigRequest 中发送最后已应用的 config_version。 后端为该节点重新构建当前快照,仅当当前版本更新时发送。
如果后端 broadcast receiver lag,不信任中间遗漏消息,而是重新构建并发送当前权威快照。
历史 AgentService.StreamConfig 入口也遵循同样的快照重放形态,但生产 Agent 控制 路径是 SecureAgentService.Control。
Agent 应用流程
Agent 按以下顺序应用快照:
- 拒绝其他
node_id的快照。 - 忽略
config_version <= last_config_version的旧快照。 - 校验快照包含
PingoraConfig和L3TunnelConfig。 - 准备 L3 tunnel 状态,但不修改 L3 运行时。
- 应用合并后的 gateway 配置,包括 L4 和 L3 QUIC ingress。
- 提交准备好的 L3 tunnel 状态。
- 应用
NodeConfigmultiplex 设置。 - 标记版本已应用,并在后续
ConfigRequest中携带该版本。 - 发送
ConfigApplyReport。
Agent 仍保留旧 split update 处理用于兼容,但生产行为是 snapshot 处理。
回滚规则
快照应用不能让 Agent 留在半新状态。
Agent 在应用新快照前保存旧 gateway、L3 和 multiplex 运行时状态。如果 gateway 校验或 listener reconcile 失败,gateway 内部恢复旧状态。如果 gateway 已应用但 L3 commit 失败,则恢复旧 gateway 状态。如果 NodeConfig 在 gateway 和 L3 成功后 失败,则恢复旧 gateway、L3 和 multiplex 状态。
multiplex listener 会先绑定替换端口,再关闭旧 listener。因此端口切换失败不会关闭 当前正在工作的 multiplex listener。
回滚对操作系统资源是 best-effort。如果恢复本身失败,Agent 记录错误,并仍将原始 apply 标记为 rejected。
端口冲突语义
端口检查区分两种情况:
- 地址已经由当前
ForwarderManager持有。这是正常热重载场景。Agent 会尽可能原地 更新现有 listener 的 route/upstream 配置。 - 地址被 manager 外部占用。这是 apply 错误。Agent 拒绝快照,不跳过 listener。
该规则适用于 TCP listener、UDP listener、QUIC listener 和 L3 QUIC ingress listener。
也就是说,请求 :443 的配置不会在 Agent 没有运行 :443 时静默成功。运维人员应在 rejected ConfigApplyReport 中看到包含 already bound outside this manager 的错误。
应用回报
每个非旧快照 apply 尝试后,Agent 都会发送 ConfigApplyReport:
node_idconfig_versionacceptedmessageruntime_listeners
后端将其写入节点 metadata:
agent_config_applynode_port_runtime_listenersnode_port_runtime_reported_at
回报更新不会推进 config_updated_at,因此不会创建新配置代际。
运维检查
确认节点是否应用了期望快照:
- 查看节点 metadata 中的
agent_config_apply.version。 - 确认
agent_config_apply.accepted为true。 - 查看
agent_config_apply.message;成功应用时为applied。 - 检查
/api/nodes/ports/runtime或 metadata 中的node_port_runtime_listeners。 - 如果 listener 缺失,查看 rejected report 和端口冲突错误。
不改变已存配置、只强制刷新:
curl -X POST "$BACKEND/api/nodes/$NODE_ID/config" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}'不要用这个接口发送内联 L4 rule。应先持久化期望的 L4/L3 资源。
兼容性
protobuf 仍包含 split update variant:
ConfigUpdate.pingoraConfigUpdate.l3_tunnelsConfigUpdate.node_config
Agent 仍接受它们,以避免旧控制面工具和测试立即失效。新的生产代码应发送 runtime_snapshot。