外观
健康与自观测
agent 对账结果与 agent 自身运行指标的落库层,回答两个问题:该节点是否已收敛至期望世代,以及 agent 进程自身运行是否正常。
| 表 | 层 | 粒度 | 保留 |
|---|---|---|---|
node_status | L2 | 节点 | 覆盖式 upsert |
node_status_events | L2 | 节点 × 上报事件 | 每 (节点, 种类) 100 条 |
node_metrics_rollup | L3 | 节点 × 5 min 桶 | 60 天 |
node_metrics_last | L2 | 节点 | 覆盖式 upsert |
模型定义在 app/db/models/node_status.py 与 metrics.py。上报节奏约 5 min,摄入策略见写入路径;读侧的聚合结果有一层 L5 缓存(health:*,TTL 10 秒),见 KV 与缓存键索引。
本册的四张表两两成对:node_status 为当下态,node_status_events 为最近数小时的历史;node_metrics_last 为差分基准,node_metrics_rollup 为 60 天的降采样曲线。事件历史窗口仅约 8 小时,更长的观测窗口依赖 rollup——这是自观测存档存在的唯一理由。
node_status
每节点最新运行时健康,随上报 upsert。
| 列 | 类型 | 约束 / 默认 | 说明 |
|---|---|---|---|
node_id | String(64) | PK,FK→nodes.node_id(CASCADE) | 主键即外键 |
desired_generation | Integer | 可空 | 控制面已发布的期望世代 |
observed_generation | Integer | 可空 | agent 实际观测到的世代 |
last_report_status | String(32) | 可空 | 最近对账状态(succeeded / degraded / failed / skipped) |
last_apply_status | String(32) | 可空 | 最近 apply 状态 |
drift_count | Integer | NOT NULL,默认 0 | 漂移计数(仅由 kind=report 事件的 payload.drift 长度给出) |
health | String(16) | NOT NULL,默认 unknown | 派生健康。库里只落四态:ok / degraded / stale / unknown;第五态 down 是读侧按时间阈值叠加的覆盖,不改库 |
last_snapshot / last_report / last_apply | JSON | 可空 | 最近一次完整 payload(界面下钻用) |
bgp_summary | JSON | 可空 | 快照摄入时预提取的 payload.bgp_protocols 原样副本 |
peer_rates | JSON | 可空 | 与上一份快照差分得出的 per-peer 速率(摄入时预提取) |
last_snapshot_at / last_report_at / last_apply_at | DateTime(tz) | 可空 | 对应时刻 |
updated_at | DateTime(tz) | server_default now,带 onupdate | 更新时刻 |
健康五态的判定顺序见 Control Server 内部。
该表仅 10 行却存有三份完整 payload,实测 336 kB,主要体积在 TOAST[1] 中。bgp_summary 与 peer_rates 为预提取列:若不预提取,每次 fleet 聚合都需将 last_snapshot 整份取出并解析。
这两列尚未纳入迁移链,缺列时读侧自动回退到解析完整快照的慢路径。见已知偏差。
node_status_events
append-only 上报历史。写入后按 (节点, 种类) 各保留最近 100 条裁剪(_HISTORY_KEEP,services/node_status.py)。四种 kind × 10 节点即上限 4 000 行(实测 3 740),约覆盖 8 小时。
| 列 | 类型 | 约束 / 默认 | 说明 |
|---|---|---|---|
id | Integer | PK,autoincrement | 主键 |
node_id | String(64) | FK→nodes.node_id(CASCADE),NOT NULL,index | 所属节点 |
kind | String(16) | NOT NULL,index | snapshot / report / apply / reresolve(WG endpoint 重解析) |
generation | Integer | 可空 | 关联世代 |
status | String(32) | 可空 | 状态 |
payload | JSON | NOT NULL | 完整上报 payload |
created_at | DateTime(tz) | server_default now,index | 写入时刻 |
提示: ix_node_status_events_created_at(712 kB)实测从未被使用,见索引清单。
node_metrics_rollup
agent 自观测(self_metrics)的 5 min 降采样存档,是 GET /ui/nodes/metrics 机群指标矩阵的数据层。随快照入库累加;旧版 agent 的快照不带 self_metrics 则 no-op。
通用形态(桶、sum + count、保留期、分区门控)见时序建模通则。
| 列 | 类型 | 约束 / 默认 | 说明 |
|---|---|---|---|
node_id | String(64) | PK(复合),FK→nodes.node_id(CASCADE) | 所属节点 |
bucket_start | BigInteger | PK(复合) | 桶起点(epoch 秒,对齐 5 min) |
cpu_sum / cpu_count | Float / Integer | NOT NULL,默认 0 | CPU 百分比的和与计数,均值 = 和 / 计数 |
rss_sum / rss_count | Float / Integer | NOT NULL,默认 0 | RSS(MB)的和与计数 |
reconcile_failures | Integer | NOT NULL,默认 0 | 桶内 reconcile 失败次数(累计计数与 prev 差分后累加;agent 重启归零时按当前值计) |
sample_count | Integer | NOT NULL,默认 0 | 落桶快照数(>0 即「该桶有上报」) |
updated_at | DateTime(tz) | server_default now,带 onupdate | 更新时刻 |
提示:cpu_count / rss_count 与 sample_count 分列,是因为自观测字段全部可选,一份快照可能仅携带其中一项。共用同一计数会使缺字段的桶算出偏低的均值。
node_metrics_last
每节点最近一次采样时刻与累计失败数,是 reconcile_failures 差分的持久基准,取值仅向前推进。
| 列 | 类型 | 说明 |
|---|---|---|
node_id | String(64) | PK,FK→nodes.node_id(CASCADE) |
captured_at | String(64) | 最近一次采样时刻(ISO 8601 原文) |
total_failures | BigInteger | 累计失败数 |
updated_at | DateTime(tz) | 更新时刻 |
TOAST(The Oversized-Attribute Storage Technique):PostgreSQL 对超过约 2 kB 的大字段的存储机制,将其压缩后移出主表、存入关联的 TOAST 表。因此含大 JSON 列的表,主表行数虽少,
pg_total_relation_size仍可能显著偏大。 ↩︎