Cilium BGP 控制平面测试框架实战:基于 hive/script 与 GoBGP 的场景化测试指南
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
导读
本文围绕 Cilium 仓库中 pkg/bgp/test/testdata/README.md 所描述的内容展开,系统讲解 Cilium Agent 中 BGP 控制平面(BGP Control Plane,简称 BGP CP)的脚本化测试框架:如何用*.txtar场景文件驱动真实 GoBGP 实例与 Cilium 对等互联,如何通过gobgp/*与bgp/*两类脚本命令管理测试对端并断言 Cilium 的 BGP 状态,以及特权测试的运行、编写与调试方法。读完本文,你将掌握在 Cilium 源码仓库内运行和扩展 BGP 控制平面集成测试的完整技能,并能结合源码理解这些命令背后的实现原理。
背景:为什么需要 BGP CP 的场景化测试
Cilium 将 BGP 路由能力内置于 Agent(以及独立的 BGP Control Plane)中,通过 Kubernetes CRD(如CiliumBGPNodeConfig、CiliumBGPPeerConfig、CiliumBGPAdvertisement)声明式地控制 BGP 实例、对端与通告策略。由于 BGP 涉及真实网络连接、状态机与路由表,单测难以覆盖端到端行为,因此仓库在pkg/bgp/test/下建立了一套脚本驱动的集成测试:
- 测试场景存放在
pkg/bgp/test/testdata/目录下,每个场景是一个*.txtar文件,其中既包含测试脚本(命令序列),也通过-- 文件名 --分隔内嵌了测试所需的各类资源文件(GoBGP TOML 配置、Kubernetes YAML 清单、期望输出文件等)。仓库中现有 commands.txtar、peering-auth.txtar、peering-changes.txtar、peering-ipv6.txtar、peering-multi-instance.txtar、pod-cidr.txtar、pod-ip-pool.txtar,以及一系列svc-*.txtar(服务通告、聚合、共享、流量策略等)与multi-peer-advertisements.txtar等共 20 个场景文件。 - 场景由 Kubernetes 资源驱动,主要是节点级的
CiliumBGPNodeConfig(携带节点专属的测试 BGP 配置),以及其他CiliumBGP*资源;唯一的例外是CiliumBGPClusterConfig,它只由 Cilium operator 处理,因此不在这套 Agent 侧测试的驱动范围内。 - 期望状态通过对与 Cilium 建立对等关系的测试 GoBGP 实例进行校验获得,即既验证 Cilium 通告给对端的内容,也验证 Cilium 从对端学到并接受的路由。
- 测试框架基于 Cilium 的 hive/script 脚本框架(
github.com/cilium/hive/script),并扩展了两类自定义命令:用于管理 GoBGP 测试实例的gobgp/*,以及用于观察 Cilium BGP 状态的bgp/*。命令注册逻辑见 pkg/bgp/test/commands/gobgp.go 与 pkg/bgp/commands/cell.go。
管理 GoBGP 测试实例:gobgp/* 命令
要搭建一个与 Cilium 对等的"外部路由器",使用gobgp/*命令。它们直接以内嵌库的方式驱动 GoBGP(github.com/osrg/gobgp/v4),无需启动独立进程。全部命令及参数如下:
gobgp/add-server name config-file Add a new GoBGP server instance from a native GoBGP configuration file gobgp/delete-server name Delete an existing GoBGP server instance gobgp/reload-server name config-file Reload the configuration of a running GoBGP server instance gobgp/peers [-os] [--out=string] [--server-asn=uint32] List peers on the GoBGP server gobgp/routes [-os] [--out=string] [--server-asn=uint32] [afi] [safi] List routes on the GoBGP server gobgp/wait-state [-st] [--server-asn=uint32] [--timeout=duration] peer state Wait until the GoBGP peer is in the specified stateadd-server 与 reload-server 的配置语义
gobgp/add-server与gobgp/reload-server的config-file参数是一个相对路径,相对于测试的工作目录解析(通常指向 txtar 内嵌的-- name.toml --段)。配置文件使用 GoBGP 原生gobgpd的 schema:本目录测试统一使用 TOML(与上游 GoBGP 文档一致),但 YAML/JSON 同样受支持,且会根据文件扩展名自动识别。
两者的语义分别对应gobgpd的两种配置行为:
gobgp/add-server:与gobgpd启动时加载配置等价,通过 GoBGP 自身的InitialConfig应用整个文件(包括全局参数,如 ASN、router-id、监听地址与端口)。gobgp/reload-server:与gobgpd收到SIGHUP时的热加载等价,通过 GoBGP 自身的UpdateConfig将新文件与上次成功应用的配置做 diff,然后增、删、改 peer / policy 使其收敛。全局参数无法通过 reload 修改(GoBGP 不允许在运行中的服务器上重新绑定这些参数),需要变更全局参数时应先gobgp/delete-server再gobgp/add-server。
在 pkg/bgp/test/commands/gobgp.go 的实现中,GoBGPAddServerCmd读取配置后启动server.NewBgpServer并调用gobgpconfig.InitialConfig,将服务器与"最后应用的配置"记录在GoBGPCmdContext里;GoBGPReloadServerCmd(L186-L231)则调用gobgpconfig.UpdateConfig完成 diff 应用,与上游 reload 行为一致——单项失败(如某个 peer 配置非法)只会记录日志而不会让命令报错,因此 reload 后应通过gobgp/peers、gobgp/routes等命令确认变更确实生效。
状态同步:wait-state
由于 BGP 会话建立是异步的,gobgp/wait-state用于等待指定对端进入目标状态,避免断言过早执行。它支持的状态包括:UNKNOWN、IDLE、CONNECT、ACTIVE、OPENSENT、OPENCONFIRM、ESTABLISHED。常用参数:
-s, --server-asn:GoBGP 服务器实例名,只有单实例时可省略;-t, --timeout:最长等待时间(默认 30 秒,实现常量waitStateTimeout位于 gobgp.go)。
实现上它先通过WatchEvent注册 peer 事件回调,再用ListPeer检查对端是否已处于目标状态,若未到达则阻塞等待事件或超时。
实际使用的配置样例
以下是一个典型的 GoBGP 测试服务器配置(来自 commands.txtar 中的server0.toml),它监听在fd00:10:99:7::1:1790,被动等待 Cilium 以fd00:10:99:7::5主动建连,并启用 IPv4/IPv6 双栈地址族与 graceful-restart:
[global.config] as = 65000 router-id = "10.99.7.1" local-address-list = ["fd00:10:99:7::1"] port = 1790 [[neighbors]] [neighbors.config] neighbor-address = "fd00:10:99:7::5" peer-as = 65000 [neighbors.transport.config] passive-mode = true [neighbors.graceful-restart.config] enabled = true [[neighbors.afi-safis]] [neighbors.afi-safis.config] afi-safi-name = "ipv4-unicast" [[neighbors.afi-safis]] [neighbors.afi-safis.config] afi-safi-name = "ipv6-unicast"除了上述 README 列出的命令外,源码还注册了gobgp/advertise-route <prefix>(见 GoBGPScriptCmds),用于让测试 GoBGP 实例主动通告指定前缀,从而构造"从对端学到路由"的场景;commands.txtar 中即用它对四个服务器通告了10.0.0.0/24、10.0.1.0/24、10.0.2.0/24与对应 IPv6 前缀。
关键约束:测试并行与唯一 peering IP
每个测试必须使用唯一的 peering IP,因为所有测试是并行执行的。测试通过 shebang 中的test-peering-ips参数把 IP 传给测试基础设施,例如:
#! --test-peering-ips=10.0.1.122,10.0.1.123若测试准备阶段检测到重复的 peering IP,setup 会以"跨测试重复 IP"的错误信息直接失败。从 script_test.go 的实现可以看到,setup 会把 shebang 解析出的每个 IP 以netlink.AddrAdd绑定到测试链路cilium-bgp-test(一个netlink.Dummy设备)上,若地址已存在则判定为被其他测试占用并报错。这也是为什么该测试必须特权运行(见下文"运行测试")。
观察 / 断言 Cilium BGP 控制平面状态:bgp/* 命令
bgp/*命令用于从 Cilium BGP 控制平面自身视角读取状态,是断言的另一侧。完整命令如下:
bgp/peers [-fo] [--format=string] [--no-uptime] [--out=string] List BGP peers on Cilium bgp/route-policies [-io] [--instance=string] [--out=string] List BGP route policies on Cilium bgp/routes [--no-age] <table type> <afi> <safi> List BGP routes on Cilium这些命令通过agent.BGPRouterManager查询底层 GoBGP 服务器状态(命令注册见 pkg/bgp/commands/cell.go,实现分别在 peer.go、routes.go、route_polices.go):
bgp/peers:列出 Cilium 侧每个 BGP 实例(Instance)下各 peer(Peer)的会话状态、地址族、收/收接受/通告路由数。--no-uptime隐藏 Uptime 列(便于稳定比对),--format支持切换输出格式(如详细模式,可见对端能力、定时器、eBGP multihop TTL、Graceful Restart、TCP 密码等),--out把结果写入文件而非 stdout。bgp/route-policies:列出 Cilium BGP 控制平面内的路由策略(import/export 方向、匹配的 peer、地址族、前缀长度范围、RIB Action 与路径属性动作)。--instance指定实例。bgp/routes:列出 RIB 中的路由,参数为<table type> <afi> <safi>。table type 为loc(loc-rib)、in(adj-rib-in)或out(adj-rib-out);--no-age隐藏 Age 列,-a/--with-attrs附带路径属性(routes.go 中的 flags 与解析逻辑)。
在 commands.txtar 中可以看到这套断言模式的完整用法:先用bgp/peers --no-uptime -o bgp-peers.actual导出实际输出,再用* cmp bgp-peers.expected bgp-peers.actual与内嵌的期望文件逐字节比对;bgp/routes同样分别对 loc-rib 的 ipv4/ipv6、adj-rib-out、adj-rib-in 导出并比对。由于输出中包含随机的 FQDN/软件版本信息,测试还会先用sed将name:.*、software-version:.*替换为<redacted>再比对,保证断言稳定(见 commands.txtar#L37-L38)。
运行测试
这套测试必须以特权运行,因为测试过程中会新增/删除一个测试网络接口(cilium-bgp-testdummy 设备)并绑定 peering 地址。在pkg/bgp/test目录下,用 sudo 执行全部脚本测试的命令为:
PRIVILEGED_TESTS=true go test -exec "sudo -E" . -test.run TestPrivilegedScript入口是 script_test.go 中的TestPrivilegedScript,它调用testutils.PrivilegedTest(t)校验特权环境,然后通过scripttest.Test装载testdata/*.txtar下的全部场景。可用-test.v查看含日志的详细输出:
PRIVILEGED_TESTS=true go test -exec "sudo -E" . -test.run TestPrivilegedScript -test.v测试启动时除了创建 dummy 链路,还会构造一个 Cilium hive 实例(启用bgp.Cell、提供 StateDB 路由/设备/节点地址表、Fake K8s 客户端、LoadBalancer writer 与 reflectors 等),并把 BGP 配置覆盖为启用状态、指定 secrets 命名空间为kube-system(见 script_test.go#L90-L221)。场景内通过k8s/add命令注入CiliumBGPNodeConfig、CiliumBGPPeerConfig、CiliumBGPAdvertisement等资源,随后 Cilium 会据此创建 BGP 实例并与测试 GoBGP 建连。
编写新测试:自动生成期望输出
新增场景时,可以先只写好"动作"部分(启动 hive、配置 GoBGP、注入 K8s 资源、等待建连),把断言所需的期望数据交给框架自动填充:运行测试时加上-scripttest.update参数,所有用cmp断言的目标文件(即*.expected)会被自动更新为当前实际输出,人工核对无误后即可固化进 txtar:
PRIVILEGED_TESTS=true go test -exec "sudo -E" . -test.run TestPrivilegedScript -scripttest.update这大大降低了编写期望文件的成本——尤其适合bgp/peers、bgp/routes这类表格化输出。
调试测试
调试单个场景时,可在测试文件任意位置插入break命令,运行到该处会进入交互式提示符,此时可以自由执行bgp/*、gobgp/*以及其他 hive/script 命令观察现场状态:
hive start gobgp/add-server server0 server0.toml k8s/add cilium-node.yaml bgp-node-config.yaml bgp-peer-config.yaml bgp-advertisement.yaml break # 在此进入交互式调试,可执行 bgp/peers、gobgp/peers 等如果 60 秒不够用,可以调大 script_test.go 中testTimeout常量(默认60 * time.Second)以延长调试窗口。整个测试上下文由context.WithTimeout控制(script_test.go#L223),调试期间超时会中断会话。
场景案例:认证对等(peering-auth)
peering-auth.txtar 展示了如何测试带 TCP MD5 认证的 IPv6 对等:
- shebang 使用
--probe-tcp-md5先探测内核是否支持TCP_MD5SIGsocket 选项(探测逻辑见 probe.go,若内核未启用CONFIG_TCP_MD5SIG则跳过测试),再传入两个唯一 peering IP; - GoBGP 侧配置
auth-password = "Cilium123"; CiliumBGPPeerConfig通过authSecretRef: bgp-auth-secret引用一个kube-system命名空间下的Secret,其data.password为Q2lsaXVtMTIz(即Cilium123的 Base64);- 建连后分别断言
gobgp/peers输出对端为ESTABLISHED,并校验 Cilium 通告的 IPv4(10.244.0.0/24)与 IPv6(fd00:11:22::/64)路由及其路径属性。
该场景同时体现了前文三个关键点:唯一 peering IP、gobgp/wait-state等待建连、cmp比对期望输出。
场景案例:多实例与多对端(commands)
commands.txtar 是覆盖面最全的综合场景:配置了 4 个 GoBGP 服务器、2 个 Cilium BGP 实例(instance0/instance1,ASN 分别为 65000/65001),每个实例对 2 个 peer 建连并通告 PodCIDR。CiliumBGPNodeConfig中每个实例通过localASN、routerID、peers[].peerASN/peerAddress/localAddress定义,CiliumBGPPeerConfig统一设置对端端口 1790、connectRetryTimeSeconds: 1与 ipv4/ipv6 双地址族,CiliumBGPAdvertisement以advertisementType: PodCIDR并打上advertise: bgp标签,通过matchLabels与 peer config 关联。随后断言 4 条 peer 全部established,loc-rib 中两个实例都持有本地 PodCIDR,adj-rib-out 向各 peer 通告 PodCIDR,adj-rib-in 收到各 GoBGP 通告的前缀;最后用gobgp/reload-server server0 server0-no-peer.toml演示删除对端(reload 后gobgp/peers应为空,与no-peer.expected比对)。
场景案例:Service 通告与 Envoy 代理模拟(svc-*)
svc-*.txtar系列场景覆盖 LoadBalancer/ClusterIP 等 Service 的 BGP 通告行为(svc-adverts.txtar、svc-modifications.txtar、svc-sharing.txtar、svc-aggregation.txtar、svc-traffic-policy.txtar、svc-no-endpoints.txtar 等)。其中 svc-gateway-proxy-redirect.txtar 依赖一个额外的脚本命令svc/set-proxy-redirect:它通过在 Service 上设置ProxyRedirects(默认代理端口 10000,可在参数中指定)来模拟本地 Envoy 代理已接管该 Service 流量,从而测试 Gateway API / Ingress Service 的 BGP 通告(实现见 pkg/bgp/test/commands/svc.go)。这些命令同样由 script_test.go 注册进脚本引擎。
总结
Cilium 的 BGP 控制平面测试框架用"txtar 场景 + hive/script 命令 + 真实 GoBGP 实例"的组合,把 BGP 建连、路由通告、策略生成、认证、热加载等复杂行为变成了可读、可复现、可并行的声明式测试。理解gobgp/*(管理对端)与bgp/*(观察自身)两组命令的对称关系,是阅读和扩展pkg/bgp/test/testdata/下 20 个场景的钥匙。无论是排查 BGP 相关回归、为新增 CRD 字段补充覆盖,还是研究 Cilium BGP 控制平面的内部状态,这套框架都是最直接的入口。进一步深入可阅读 script_test.go(测试装配)、pkg/bgp/test/commands/gobgp.go(GoBGP 命令实现)与 pkg/bgp/commands/routes.go(Cilium 侧 RIB 查询实现)。
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考