外观
内部互联:iBGP / OSPF / internal_topology
本文解释同一 AS 内多节点互联的合成机制、internal_topology 的语义、以及一条容易被破坏、破坏后很隐蔽的一致性不变量。字段参考见 ../reference/desired-state.md;排错入口见 ../guides/monitoring-and-troubleshooting.md。(真实事故案例存于仓库外的运维叙事档案。)
同一个 AS 内多节点之间的互联(iBGP 交换路由 + OSPF 提供 loopback 可达)由 DesiredState.bird.internal_topology 合成,不走 bgp_sessions(那是 eBGP 对外 peering,见 ../guides/peering.md)。
是什么
internal_topology(InternalTopologySpec,见 ../reference/desired-state.md)驱动 ibgp.conf.j2 + ospf.conf.j2:
| 字段 | 作用 |
|---|---|
routers | 参与内部路由域的节点名列表。full_mesh_ibgp=true 时对除自己外每个建一条 iBGP |
hosts | 节点名 → {ownip, ownip6, ibgp_rr_upstreams}。ownip/ownip6 是该节点 loopback(iBGP next-hop / neighbor)。ibgp_rr_upstreams 非空表示本节点是某 RR 的 client |
igp_adjacencies | 本节点自己的物理内部链路 {node, interface, cost, iface_type}。OSPF 邻接只在这些真实接口上形成 |
full_mesh_ibgp / ospf_v2 / ospf_v3 / private_nodes | 拓扑开关 |
iBGP 跑在 loopback 上(next-hop-self),不需两节点物理直连——但需要 OSPF 把对端 loopback 传过来,否则 neighbor 不可达起不来。所以 OSPF 是 iBGP 的前提:
两层的一致性要求不同:第 1 层按真实物理链路逐节点声明;第 3 层的 routers+hosts 必须全员同一份(见下)。
不变量(最重要)
同一个 AS 内,所有节点的
internal_topology.routers+hosts必须是同一份完整集合。
每个节点只从自己这份 internal_topology 生成 iBGP/OSPF,不会自动发现别的节点。各节点不一致就会各建"部分网格",导致某些节点缺路由——而且 eBGP 不受影响、节点 health 正常,缺失很隐蔽。igp_adjacencies 是例外(每节点各自的物理链路本就不同)。
拓扑选择:
- full-mesh iBGP(节点少时推荐):所有节点
routers含全员、full_mesh_ibgp=true、hosts含全员 loopback、ibgp_rr_upstreams全空。 - route reflector:选一节点当 RR,其余在
hosts[self].ibgp_rr_upstreams指向它。要么全 full-mesh、要么所有节点对 RR 关系一致——别留半成品。
违反不变量的完整事故链(iBGP 水平分割 + 半成品 RR 留盲区)记录在仓库外的运维叙事档案。
加内部节点 checklist
- 给新节点和某已有节点之间建内部 WG 隧道(p2p /31 + /127 +
fe80::link-local),双方各加一条igp_adjacencies指向对方接口。 - 把新节点加进所有已有节点的
routers和hosts,并把全员补进新节点自己的routers/hosts。漏任何一个就缺路由。 - 下发后到
/routing/summary确认:新节点最优路由里出现 iBGP 来源、各节点route_count一致。
参考
deploy/unify_internal_topology.py:把全 fleet 的routers/hosts统一成同一份 full-mesh,保留各节点自己的igp_adjacencies。⚠️ 历史版本硬编码旧地址,renumber 后须先更新。见 ../reference/cli-and-scripts.md。
renumber 节点 loopback 时,hosts[*].ownip/ownip6 是必须全 fleet 手动同步的副本(重构北极星见 ../reference/addressing-model.md),同步点清单见 ../guides/addressing-and-renumber.md。