先把这四张图按“总览架构 + 资源到 Path + 资源到 Policy Statement + 命名/优先级规则”梳理出来,并补齐关键辅助函数的细节,避免图里有概念跳步。
图 1
- 总览:
K8s/Node 对象提供意图,Controller + BGPRouterManager触发和编排 reconcile,各 Reconciler分别产出Path与DesiredRoutePolicy,最后由GoBGP Router真正建立 BGP 会话并对外通告。 - 关键代码:入口在 cell.go、控制循环在 controller.go、实例管理在 manager.go、GoBGP 后端在 server.go。
flowchart TD
A[K8s / Node Inputs<br/>CiliumBGPNodeConfig<br/>CiliumBGPPeerConfig<br/>CiliumBGPAdvertisement<br/>CiliumNode<br/>CiliumPodIPPool<br/>Secret<br/>Frontend/Service State] --> B[agent.Controller<br/>监听事件并触发 reconcile]
B --> C[BGPRouterManager<br/>按节点配置管理多个 BGPInstance]
C --> D[Config Reconcilers]
D --> D1[DefaultGatewayReconciler]
D --> D2[InterfaceReconciler]
D --> D3[PodCIDRReconciler]
D --> D4[ServiceReconciler]
D --> D5[PodIPPoolReconciler]
D --> D6[RoutePolicyReconciler]
D --> D7[NeighborReconciler]
D2 --> E1[Path 计算]
D3 --> E1
D4 --> E1
D5 --> E1
D2 --> E2[DesiredRoutePolicy 写入 statedb]
D3 --> E2
D4 --> E2
D5 --> E2
E2 --> F[bgp-desired-route-policies<br/>statedb 中间层]
F --> D6
D6 --> G[编译 RoutePolicy<br/>统一 add/update/remove policy]
E1 --> H[Router API]
G --> H
D7 --> H
H --> I[GoBGP in-process Router<br/>AddPath / WithdrawPath<br/>AddRoutePolicy / RemoveRoutePolicy<br/>AddNeighbor / UpdateNeighbor]
I --> J[对外 BGP Peer]
图 2
- 总览:四类“可宣告资源”都会先被转换成
Path/NLRI,这是“要通告哪些前缀”的主链路。 - 这条链路和 policy 链路是并行关系,不是替代关系。
- 关键代码:
ServiceReconciler在 service.go、PodCIDRReconciler在 pod_cidr.go、PodIPPoolReconciler在 pod_ip_pool.go、InterfaceReconciler在 interface.go。
flowchart LR
A1[CiliumNode.Spec.IPAM.PodCIDRs] --> B1[PodCIDRReconciler]
A2[loadbalancer.Frontend 表<br/>Service VIP / Backends] --> B2[ServiceReconciler]
A3[CiliumPodIPPool + 本地节点归属] --> B3[PodIPPoolReconciler]
A4[本机 Device / Addr 状态表] --> B4[InterfaceReconciler]
B1 --> C1[计算 PodCIDR 前缀]
B2 --> C2[计算 Service 前缀<br/>ClusterIP / ExternalIP / LB IP]
B3 --> C3[计算 PodIPPool 前缀]
B4 --> C4[计算接口地址前缀]
C1 --> D[构造 Path/NLRI]
C2 --> D
C3 --> D
C4 --> D
D --> E[Reconcile Paths]
E --> F[GoBGP RIB / Adj-RIB-Out]
F --> G[对外 BGP UPDATE]
图 3
- 总览:
Service / PodCIDR / PodIPPool / Interface现在统一不再“直接改 router policy”,而是生成RoutePolicyStatement写进bgp-desired-route-policies。 - statement 的公共结构是:
Conditions.MatchNeighborsConditions.MatchPrefixesConditions.MatchFamiliesActions.RouteActionActions.AddCommunities / AddLargeCommunities / SetLocalPreference / NextHop
- 关键代码:statement 类型定义在 bgp.go,统一生成入口在 policies.go。
flowchart TD
A[Reconciler 计算出要宣告的 prefixes] --> B[PolicyStatementName<br/>生成 statement 名]
B --> C[CreatePolicyStatements]
C --> D1[MatchNeighbors = 目标 peer 地址]
C --> D2[MatchPrefixes = 本次资源对应前缀]
C --> D3[MatchFamilies = IPv4 或 IPv6]
C --> D4[Actions = accept]
C --> D5[Actions = communities / large communities]
C --> D6[Actions = local preference]
C --> D7[必要时 NextHop]
D1 --> E[DesiredRoutePolicy]
D2 --> E
D3 --> E
D4 --> E
D5 --> E
D6 --> E
D7 --> E
E --> F[字段补齐<br/>Instance / Peer / PolicyType / Priority / Owner / Resource]
F --> G[写入 statedb: bgp-desired-route-policies]
G --> H[RoutePolicyReconciler 统一读取]
H --> I[按 Instance + Peer + PolicyType 聚合为最终 RoutePolicy]
图 4
- 总览:这一张图描述 “命名、优先级、聚合、下发” 的最终落地规则,是这次 PR 的核心。
- 关键点:
- statement 名由
PolicyStatementName(peer, advertType, resourceID)生成; CreatePolicyStatements()因 GoBGP family 限制,会把 v4/v6 拆成独立 statement;RoutePolicyReconciler按Priority再按StatementName排序;- 同一个
peer + export/import会被编译成一个 policy,例如peer1-export; - 最终仍是 GoBGP 执行 policy 下发与 soft reset。
- statement 名由
- 关键代码:命名与 statement 生成在 policies.go,统一聚合在 route_policy.go,Service 的 exact/summary 优先级在 service.go,GoBGP statement name 转换在 conversions.go。
flowchart LR
A[PolicyStatementName<br/>peer-advertType-resourceID] --> B[CreatePolicyStatements]
B --> C1[name-ipv4]
B --> C2[name-ipv6]
C1 --> D[DesiredRoutePolicy<br/>Priority / Owner / Resource]
C2 --> D
D --> E[RoutePolicyReconciler]
E --> F[按 Instance + Peer + PolicyType 分组]
F --> G[按 Priority 升序]
G --> H[同优先级按 StatementName 稳定排序]
H --> I[生成最终 policy 名<br/>peer-export / peer-import]
I --> J[ReconcileRoutePolicies]
J --> K[GoBGP AddRoutePolicy / RemoveRoutePolicy]
K --> L[必要时 Soft Reset Peer]
四张图合起来怎么理解
图 1看“系统层级”:谁驱动谁,最后谁真正和 BGP peer 说话。图 2看“资源到路由”:哪些资源会变成可通告前缀。图 3看“资源到策略”:这些资源不仅生成 path,也生成 export/import 规则。图 4看“PR #46818 的核心增量”:把零散的 policy 生成收敛到 statedb,再统一编译和下发给 GoBGP。
一句话总结
- 当前 Cilium 的 BGP 架构里,
statedb是 控制面内部的结构化中间层,各类资源 reconciler是 意图生产者,RoutePolicyReconciler是 策略汇编器,GoBGP是 最终执行者。
如果你要,我下一条可以把这四张图继续压缩成一张“面试式总图”,或者展开成一份“每个 Reconciler 一页”的对照表。
整体层级(从上到下)
- Kubernetes/Node 输入层(声明式意图)
- BGP 控制面配置与策略来源主要是这些对象/数据流:
CiliumBGPNodeConfig(每节点的 BGP instance/peer 期望配置)CiliumBGPPeerConfig(peer 的通用参数:计时器、auth、transport 等,供引用)CiliumBGPAdvertisement(“哪些东西要宣告”,以及附带 community/聚合等策略参数)Secret(BGP auth password 等,命名空间由 daemon 配置决定)CiliumNode(本节点的 PodCIDR、Node IP 等信息,是 PodCIDR 类宣告的直接数据源)CiliumPodIPPool(Multi-pool IPAM 下可宣告的池子前缀)Service/Endpoints/Backends(在 agent 内会被归并进 loadbalancer 相关状态,供 Service 宣告使用)
- BGP 控制面配置与策略来源主要是这些对象/数据流:
- Cilium BGP Control Plane 入口层(事件驱动的 reconcile 框架)
- Hive 模块把 BGP CP 组装起来,注册 resource watchers / stores / API handler / statedb tables(见 cell.go;PR 版本也在 worktree cell.go)
agent.Controller监听本地CiliumNode与 BGP 配置 store 的事件信号,驱动一次次 reconcile(见 controller.go)
- 实例管理层(BGPRouterManager / BGPInstance)
BGPRouterManager负责按“本节点应该有几个 BGP instance”创建/销毁实例,并按顺序调用一组 reconcilers(见 manager.go)- 每个
BGPInstance都持有一个types.Router后端实例(当前实现是 GoBGP)
- 路由器后端层(GoBGP in-process)
- Cilium 不是部署外部
gobgpd,而是在 agent 进程内启动 GoBGP server(server.NewBgpServer(); go s.Serve(); StartBgp(...)),并通过 Go API 下发 neighbor/path/policy(见 server.go)
- Cilium 不是部署外部
statedb 在这个体系里扮演什么角色(两类用法)
你可以把 statedb 理解成“agent 进程内的结构化状态总线/数据库”,在 BGP CP 里主要有两种用途:
- A. 作为“业务数据源”的表(外部对象被归并后的本地视图)
- 典型例子:Service 宣告并不是直接 watch K8s Service 再逐个算,而是消费
statedb.Table[*loadbalancer.Frontend],并通过 ChangeIterator 做增量(见 service.go) - 类似地,Interface 宣告会依赖设备/地址等本地状态表(在 InterfaceReconciler 中体现为“从设备表拿接口地址再生成 prefix/path/statement”这一模式,见 interface.go)
- 典型例子:Service 宣告并不是直接 watch K8s Service 再逐个算,而是消费
- B. 作为“BGP CP 内部协调接口”的表(reconciler 之间的中间层)
- PR #46818 引入的核心:
bgp-desired-route-policies表,把“各 reconciler 想要的 route policy statements”集中起来,再统一落到 router 上(见 route_policies.go) - 另外还有 BGP reconcile 错误表(用于可观测/调试):
bgp-reconcile-errors(见 errors.go)
- PR #46818 引入的核心:
“需要宣告路由的资源”如何映射到各 reconciler(谁负责算什么)
BGP CP 在“宣告”这件事上,实际分成两条输出链路:
- 路径/前缀(Path)链路:决定“哪些 NLRI 前缀要出现在 BGP RIB 并被通告”
- 路由策略(Route Policy)链路:决定“这些路由对哪些 peer export、附带什么 community/localpref/聚合顺序、以及变更时如何 reset”
下面按资源类型梳理(只列最核心的几类):
-
PodCIDR(来自 CiliumNode.Spec.IPAM.PodCIDRs)
- Reconciler:
PodCIDRReconciler(见 pod_cidr.go) - 输入:本地
CiliumNode上的PodCIDRs(见 pod_cidr.go)+CiliumBGPAdvertisement中对 PodCIDRAdvert 的配置(通过peerAdvert.GetConfiguredAdvertisements) - 输出:
- Path:把 PodCIDR 前缀作为可通告路径
- Policy statement:决定对哪些 peer export、附加 community 等(#46818 后是写入
bgp-desired-route-policies)
- Reconciler:
-
Service VIP(ClusterIP / ExternalIP / LoadBalancerIP 等,来自 loadbalancer.Frontend 表)
- Reconciler:
ServiceReconciler(见 service.go) - 输入:
statedb.Table[*loadbalancer.Frontend]+CiliumBGPAdvertisement中 Service 相关配置 - 输出:
- Path:把需要通告的 VIP 前缀作为路径(是否通告、通告哪类 VIP、是否聚合,取决于 advert 配置和本地 backend 情况等)
- Policy statement:用于精确路由与汇总路由的优先级控制(summary 路由优先级更低:见 servicePolicyStatementPriority)
- Reconciler:
-
CiliumPodIPPool(Multi-pool IPAM 下的池子前缀宣告)
- Reconciler:
PodIPPoolReconciler(见 pod_ip_pool.go) - 输入:
store.BGPCPResourceStore[*CiliumPodIPPool](见 pod_ip_pool.go)+ advertisement 配置 - 输出:同样是 Path +(#46818 后)policy statements 写入 statedb
- Reconciler:
-
Interface(宣告某接口/地址族的接口地址为路由)
- Reconciler:
InterfaceReconciler - 输入:本机设备/地址状态表 + advertisement 配置(典型逻辑:从接口取 IP → 生成 prefix/path,见 getInterfacePrefixes)
- 输出:Path + 写入 desired policy statements(见 interface.go)
- Reconciler:
#46818 后:Route Policy 的“统一编排/落地层”是怎么工作的
这是你关心的 “gobgp + statedb + 宣告资源” 架构中最关键的“中间层”:
- 各资源 reconciler(Service/PodCIDR/PodIPPool/Interface…)
- 不再直接对 router 调
AddRoutePolicy/RemoveRoutePolicy; - 而是把“我希望存在的 policy statement”写进
bgp-desired-route-policies(写入/对账逻辑见 reconcileDesiredRoutePolicyStatements)
- 不再直接对 router 调
- RoutePolicyReconciler(新增)
- 从表里按 instance 读取全部 statements(见 desiredRoutePolicies)
- 按
(peer, policyType)聚合成一个 policy,按Priority排序,再按StatementName稳定排序(见 desiredRoutePolicyFromStatements) - 统一调用
ReconcileRoutePolicies(...)去真正落地到 router,并合并 soft reset 决策(调用点见 route_policy.go)
- 执行顺序保证
RoutePolicyReconcilerPriority=100,NeighborReconcilerPriority=110,确保 policy 先就绪、再处理 neighbor(见 reconcilers.go)
Gobgp 在链路里的位置(为什么说“根本还是 GoBGP 在干活”)
- 最终所有 neighbor/path/policy 的“生效”都要落到
types.Router实现上,当前实现就是 GoBGP(见 BGP module 注入 provider 的注释与实现:主线在 cell.go,以及 GoBGP server 启动 server.go) - policy statement 的 Name 也会被带到 GoBGP 的
Statement.Name,再从 GoBGP 查询时读回(见 conversions.go、conversions.go)