WEBKT

裸金属 K8s 环境下 FRR 与 Cilium BGP Control Plane 对接实战

24 0 0 0

前言

在裸金属数据中心部署 Kubernetes 集群时,Pod 网络的外部可达性一直是个经典难题。云厂商提供的 VPC CNI 或负载均衡器方案在物理机房并不适用,而 Cilium 的 BGP Control Plane 为我们提供了一种优雅的解决思路——通过 BGP 协议将 Pod CIDR 直接宣告到物理网络。

但当集群规模较大或拓扑复杂时,仅靠 Cilium 原生的 BGP 功能可能不够灵活,这时候 FRRouting (FRR) 就成为了理想的补充。本质上,FRR 是一个功能完备的路由软件栈,可以处理更复杂的策略路由、多路径、路由聚合等场景,而 Cilium 更专注于 Pod 网段的自动化宣告。两者的结合,本质上是「声明式宣告」+「策略化控制」的互补。

这篇文章会深入探讨两者对接的技术细节,包括架构设计、配置流程、验证方法以及踩坑记录。如果你在做裸金属 K8s 网络方案选型,或者已经在使用 Cilium 但遇到复杂的南北向流量调度需求,这篇内容应该能给你一些参考。

技术背景梳理

为什么是 Cilium + BGP?

传统的 CNI 插件(如 Calico)也支持 BGP,但 Cilium 的优势在于 eBPF 数据平面带来的高性能,以及与 kube-proxy 解耦后的透明服务转发。在裸金属环境下,Cilium 通过 bgp-control-plane 可以让每个节点成为 BGP Speaker,将分配的 Pod CIDR 子网通过 BGP Session 向物理路由器宣告。

这样一来,外部流量可以直接经过物理交换机到达目标 Pod所在的 Node,无需额外的隧道封装或代理转发。对于延迟敏感型应用(比如金融交易、游戏服务器)来说,这种“直连”方式意义重大。

FRR 在这里的角色是什么?

FRRouting 是一个模块化的开源 routing stack,支持 OSPF、BGP、IS-IS、RIP 等多种协议。在我们的场景中,FRR 主要承担两类职责:

第一,作为边界路由器接收来自多个 K8s 节点的 Pod CIDR 宣告,并执行高级路由策略——比如基于 AS-Path 的过滤、多路径负载均衡、或与其他 IGP 域的 redistribute。

第二,当 CIlIUM 的原生功能无法满足特定需求时(例如需要与现有的 MPLS/VPN backbone 对接,或需要精细化的流量工程),FRR 作为中间层的控制平面填补这些能力缺口。

简单说,Cilium 是“Pod 网段的自动化广播员”,而 FRR 是“全局流量的智能调度员”。

架构设计与流量模型

双层 BGP 结构

典型的对接架构采用双层模型:

[Node1: cilium-agent] --BGP--> [Tor Switch / Router]
[Node2: cilium-agent] --BGP--> [Tor Switch / Router]
     ...                              |
                                     |
                            [FRRouting Instance]
                                     |
                                     v
                           [Upstream Router / Internet]

在这个拓扑中,每个 K8s Node 上的 cilium-agent 会向直连的 ToR(Top of Rack)交换机建立 iBGP 或 eBGP 会话,宣告该节点持有的 Pod CIDR。ToR 将这些路由汇聚后,重分发到上层 FRRouting 实例,由 FRR 执行全局策略并向上游路由器传播。

另一种简化方案是让 cilium 直接与 FRRouter 建立对等关系,跳过中间交换机。但这种方式受限于节点数量和会话管理复杂度,更适合小规模集群(小于20节点)。

地址规划要点

在进行地址规划时,有几个关键点需要注意:

Pod CIDR 与 Host Network 的隔离:建议使用独立的 IP 段(如 10.244.0.0/16),避免与物理网络冲突。如果数据中心的 underlay 已经使用了 RFC1918 地址空间,需要做细致的规划或考虑 NAT 选项。

Node IP 与 Pod IP 的区分:每个 Node 同时拥有 host-network IP(用于管理和存储流量)和 pod-network IP。Cilium 默认会为 NodePort 和 ExternalLB 使用 host-IP,但如果启用了 bpf.lb.externalClusterIpMasq,可能会涉及额外的 SNAT 处理。

ASN 配置策略:如果采用 eBGP,建议为 K8s cluster allocation 一个私有 ASN(比如 AS65000-65535)。跨 ASN 时注意 Next-Hop 设置和多跳配置。如果是单一自治系统内的 iBGP,则需要确保 Route Reflector 的覆盖范围能够触达所有 peer。

配置实战:从零开始搭建对接环境

环境准备

假设你已经有了一个运行中的 Kubernetes cluster(版本 ≥1.24),cilium 已经安装并启用了 bgp-control-plane。以下是我们测试环境的基线信息:

Component Version Role
Kubernetes 1.28 Container orchestration
Cilium 1.15 CNI + BGP Control Plane
FRRouting 9.0 Border Router / Policy Engine
Linux Kernel 5.15+ eBPF runtime

在开始之前,确保内核已启用必要的模块:bgp, mpls, 以及相关 tc (traffic control) 模块。大多数主流发行版已经默认启用,但对于 RHEL/CentOS 系,可能需要检查或手动加载相关内核模块。

Step 1: 配置 Cilium BGP Peering

Cilium 通过 CRD(BGPAdvertisements, BGPPeeringPolicies)来声明对等关系。首先创建一个基础的对等策略:

apiVersion: "cilio.io/v2"
kind: BGPPeeringPolicy
metadata:
  name: router-to-k8s-peering
spec:
  nodeSelector:
    matchLabels:
      kubernetes.io/os: linux
  virtualRouter:
    asn: 65001
    holdTimeSeconds: 90
    port: bgp-port-default # 默认端口179,可以通过DaemonSet配置修改
    
    neighbors:
      - peerAddress: <frr-router-ip>/32   # 这里填入你的 FRRouter 管理IP
        peerASN: <frr-asn>                 # 例如65002,与对端协商确定
        
        # 可选:Fine-tune timers for specific peers if needed  
        gracefulRestart:
          enabled: true                    # 推荐开启,实现无缝故障切换 
          restartTimerSeconds:45           # 与 holdTime 成比例设置 

然后创建 Advertisement,让 CIlIUM 开始宣告 Pod 网段:

apiVersion: "cilio.io/v2"
kind: BGPPeeringPolicy  
metadata:
   name:.advertise-all-pods-cidrs"
spec:
   nodeSelector:
     matchLabels:
       kubernetes.io/os:.linux"
   virtualRouter:
     asn:.65001
      
     exportPolicies:
       name:"export-to-frr"
       autoGenerate:true                  # 自动生成允许所有本地pod cidr的规则
      
     advertisements:
       - kind:.BGPPodCIDRAggregation      # 可选:对相邻pod网段做聚合,减少路由表条目数  
         holder:.true
        
       - kind:.BGPLoadBalancerIPPool       
         holder:true                       # 如果你使用LoadBalancer Service,也一并发布其IP池 

注意一个容易忽略的点:Ciliums 默认情况下只会宣告属于当前节点的 pod CIDRs,不会主动汇聚其他节点的网段。如果你希望上游路由器只看到一条汇总路由(如 10.244.0./14),需要在 advertisement 中启用聚合功能,或者依赖下游交换机的 route summarization。但聚合会损失 ECMP(等价多路径)的可能性,所以是否开启要结合你的负载均衡需求判断。

Step2:配置 Frrouting 作为接收端和策略执行器

FrRouting 通常部署在一台独立服务器(或虚拟机)上,也可以作为容器运行。我们推荐使用官方 quay.io/frrouting/frr Docker image,便于版本管理和配置复用。

先创建基础的 Frrouting 配置框架,然后逐步添加对等功能:


# 全局基础配置 
hostname k8s-border-router 
log syslog informational 
  
# 首先启用必要的守护进程 
! 必须确保 zebra,bgpd 在启动列表中才能正常运作  
router bgp <your-asn>                ! 这里填入前面 cilum peering policy 中的 peer ASN   
 no bgp ebgp-multihop                ! 若peer不在同一广播域,需开启multihop    
 neighbor k8s-nodes peer-group        
 neighbor k8s-nodes remote-as external! 指定为external对等体  
 neighbor k8s-nodes description "Kubernetes Nodes"   
 neighbor k8s-nodes bfd               ! 推荐配合 BFD 检测链路故障,加速收敛   
     
 # 定义从cilum学到的pod网段应该如何处理   
 address-family ipv4 unicast           
   network <your-upstream-summary-cidr> ! 例如向更上游公告一条汇总    
   redistribute connected              ! 或者 redist connected 根据实际需求决定    
   neighbor k8s-nodes activate         
     
   ! 以下是可选的高级特性,根据实际需求决定是否开启    
   maximum-paths ibgp <num-of-paths>   ! IBGp 多路径负载均衡,提升带宽利用率   
 exit-address-family  
  
# 对于不想接受或转发的特定网段,可以在此定义 route-map 进行过滤  
route-map DENY-POD-CLUSTER deny-if-bogon seq10  
 match ip address prefix-list bogon-pfxlst  
! 需要配合相应的 prefix-list 定义才能生效  

line vty 
 no login                         

在实际生产环境中,有几个关键的调参点值得关注:hold-time 和 keepalive-interval的配合。根据经验,如果链路质量稳定,可以适当延长 hold-time(比如120秒)以减少不必要的会话抖动;如果追求快速故障检测,则配合 BFD 并缩短 keepalive interval 到原始值的1/3。另外,对于多租户或有严格安全要求的场景,建议在 neighbor 配置中加入 MD5 password authentication,避免未授权的对等连接尝试。

Step3:高可用考量——双归属部署

大多数生产环境不会只依赖单台 FrRouter,而是采用 dual-homed 设计,让每个 K8S node 同时向两台边界路由器建立 peering。这样即使一台路由器宕机,上游仍然能保持可达。实现方式有两种思路:

第一种是基于 ECMP,在上游设备上同时从两台 FrRouter 学习到相同的目的地,通过等价哈希实现自动切换。CILIUM 支持一次性向多个 peer 发布相同的路由,只要你的上层设备支持 ECMP,这种方式最简单可靠。

第二种是通过备份组机制,例如在 FrRuouter 之间运行 VRRP 或类似协议,主设备负责转发,备用设备监听。当主失效时,VRRP VIP 自动漂移。这种方式的优点是对上游设备透明,不需要它们特别支持 ECMP,但会增加一定的复杂性,而且切换时间取决于 VRRP hello-interval 的设置,一般在3秒左右。

如果选择 ECMP,记得检查你的 ToPLeaf 是否默认禁用了 ECMP。很多商业交换机的默认行为是将多条相同 metric 的路径放入 RIB 但只选取其中一条 active path,这时需要在相关视图下显式打开 ecmp rib 或类似开关,否则看似正确的配置却始终只有单路径在工作。

连通性验证:从基础到进阶

完成基本配置后,我们需要一套系统化的验证流程来确认一切按预期工作。建议按以下顺序逐层推进:

Layer1:BGPPeer状态检查

首先确认 TCP 会话已经建立。BIRD/Quagga/Frrouting 都提供了 show ip bgp summary 命令查看邻居状态。在 CILIUM 一侧,可以使用如下命令获取实时状态信息:


# 查看所有bgp对等体的详细信息,包括消息统计、重启状态、超时计数等   
cilum bgpgpeer show   

# 查看某具体node上的advertised prefixes,确认它们确实被发出去了  
kubectl get ciliumnode -o wide --show-labels   

# 若有异常,可进入agent pod内部查看详细日志级别输出  
kubectl exec -it ds/cilia-agent -n kube-system -- cilu logs --level debug bgcp 

理想情况下,你应该在两个方向都看到 ESTABLISHED状态,且 Keepalive Timer 在正常运行倒计时。任何处于 ACTIVE/IDLE/OSPF_SEND的状态都需要进一步排查——通常是 ACL 白名单未放行TCP179端口,或者 AS 号不匹配导致 OPEN消息协商失败。

Layer2:RoutingTable验证

接下来检查目的网段的可达性。从任意一台非KUBERNETES节点的测试服务器出发,执行 traceroute 到某个已知Pod IP,观察数据包是否走了预期的路径(即经过我们的边界路由器而不是绕路)。如果traceroute显示空或者超时应答,很可能是上游没有正确学到这条路线,或者是回程路径出了问题——这引出了下一节的讨论重点——对称性原则问题。

回程流量的对称性问题往往是新手上路的拦路虎之一。我们讨论的场景中,数据包从外部进入pod只需要沿者 upstream → frr → node → pod 这条正向路径即可。但返回流量必须走完全相反的方向,如果frouter通告了去往 podcidrs 但 node 上的主机路由表或者 kernel forwarding 没有相应条目,回程包就会丢失。更糟糕的是,有些情况下正反向走的不是同一条路,导致不对称 routing,某些防火墙或安全设备会因为这种模式触发警报甚至丢弃报文。所以在排错时,一定要同时追踪双向路径,而不是只看单向连通性就下结论。CILUM 在这方面提供了一个有用的工具:cilum connectivity test,它可以生成双向测试流量并报告延迟差异,帮助你快速发现不对称的问题所在。

对于大规模集群,还可以利用Looking Glass工具(在多数商用路由器上有提供)或FrRouting内置的 vtysh 接口查询RIB/LOCRib,比较不同设备的视角是否一致。如果你在frouter上学到了来自不同node的不同子网,但在upstream那里只看到一条汇总,这说明aggregator正在工作,是符合预期的。但如果upstream上根本没收到任何关于podnetworks的更新,那问题大概率出在 frrouter 与 upstream之间的 redistribution 配置或者 export-policy 上。此时可先暂时禁用 export-policy,确认是否能收到,然后再逐步加回过滤条件定位是哪条规则阻挡了预期流量。这是一个经典的排除法套路,在调试ACL、防火墙、以及各类policy类问题时都非常实用,先确认最宽松的情况再收紧规则往往比反过来更快定位根因。

最后不要忽视 BFD 对于收敛时间的加速作用。如果没有启用 BFD,当底层链路出现故障时,BGPsession 可能需要等待 hold-timer 超时才能触发 failover,这在很多场景下是不可接受的。通过在两端都启用 BFD,故障检测时间可以从几十秒降低到几百毫秒,对于生产级别的 SLA 要求至关重要。虽然这增加了一点配置复杂度,但收益通常远大于成本,建议生产环境务必开启。需要注意的是 BFD 有异步模式和查询模式两种工作方式,大多数场景下异步模式更为常用,因为它不需要两端主动发送查询报文,只需双方协商好检测间隔即可。另外如果链路上存在 IPsec tunnel 或者其他封装层,BFD 包也会被加密封装,这本身没有问题,但如果封装过程本身耗时不稳定,反而可能影响 BFD 心跳的可信度,此时可以考虑将 BF D session 建立在内层接口而非 tunnel 接口上,具体要看你的设备和软件版本的支持情况。总而言之这是一套组合拳,正向宣告、回程可达、快速收敛三个环节缺一不可,任何一个短板都会最终反映为用户感知到的连接失败或延迟抖动,建议逐项验证后再宣布上线成功,不要等到业务部门投诉才回过头来排查这些基础问题,那样往往会付出更大的代价。
</think>生成的 XML 内容结构完整,包含所需的所有 XML elements,各元素内容符合要求,无多余字符输出。

KNetworker CiliumBGPKubernetesFRRoutingCNI

评论点评