1. 项目概述:当云原生网络遇上传统路由
在云原生和Kubernetes的世界里,Cilium已经凭借其基于eBPF的高性能、可观测性和强大的安全策略,成为了容器网络接口(CNI)领域的一个明星选择。它处理集群内部东西向流量游刃有余。但当我们把视角拉到集群外部,问题就来了:集群外的客户端或传统数据中心里的服务器,怎么知道该把数据包发给哪个Kubernetes节点上的哪个Pod呢?这时候,传统的边界网关协议(BGP)就登场了。
把Cilium和BGP配在一起工作,本质上是在搭建一座桥梁,连接起云原生微服务架构和传统数据中心网络基础设施。Cilium负责管理集群内部精细、动态的Pod网络,而BGP则负责向外部网络“广播”这些Pod的IP地址路由,让整个网络世界都能找到它们。我最近在帮一个从传统虚拟化向Kubernetes迁移的团队做架构设计,他们的运维团队对BGP非常熟悉,但面对Cilium的配置有些无从下手。这个组合正好能让他们用熟悉的方式管理外部路由,同时享受Cilium带来的现代网络能力。如果你也在面临混合云、金属裸机集群(Bare Metal)或者需要与现有路由器深度集成的场景,那么搞懂Cilium+BGP的配置,就是一项非常实用的技能。
2. 整体架构与组件选型解析
在动手配置之前,我们得先理清楚几个关键组件是如何协同工作的,这能帮你避开很多后续的坑。
2.1 Cilium的BGP实现:MetalLB vs. Cilium BGP Control Plane
Cilium本身并不直接运行BGP守护进程。它需要通过一个“控制器”来学习集群内的服务或Pod IP,并将其通过BGP宣告出去。目前主流有两个选择:
方案一:集成MetalLB作为BGP扬声器这是目前最成熟、社区应用最广的方案。MetalLB是一个负载均衡器实现,它有两种模式:Layer2模式和BGP模式。我们这里用的就是它的BGP模式。
- 工作原理:Cilium负责分配Pod IP和管理网络策略。MetalLB部署在集群内,它的BGP Speaker组件会与集群外部的物理路由器(或BGP对等体)建立BGP会话。Cilium通过Kubernetes的Service API(特别是LoadBalancer类型的Service)或者自定义资源,将需要对外暴露的IP地址池(Pool)告知MetalLB,再由MetalLB通过BGP将这些IP的路由宣告出去。
- 优势:功能稳定,社区活跃,文档丰富。除了宣告Pod CIDR(整个Pod网段),还能精细控制LoadBalancer Service IP的宣告,实现外部负载均衡。
- 适用场景:需要将Kubernetes Service(LoadBalancer类型)暴露给外部的场景,或者在BGP对等体上需要做更复杂策略的场景。
方案二:使用Cilium BGP Control Plane(实验性功能)从Cilium 1.14版本开始,引入了原生的BGP Control Plane功能,目前仍处于Alpha或Beta阶段。
- 工作原理:Cilium Agent本身集成了一个BGP控制器,可以直接配置BGP对等体。它主要通过CiliumNode自定义资源来感知节点和Pod CIDR的变化,并自动宣告这些路由。
- 优势:架构更简洁,无需部署额外组件,与Cilium的集成度更深。
- 劣势:功能较新,可能不够稳定,高级BGP策略支持相对MetalLB较弱。
- 适用场景:追求架构简洁,主要需求是宣告Pod CIDR让外部可达,且愿意尝试新功能的集群。
注意:对于生产环境,除非你有强烈的简化架构需求且能接受潜在风险,否则我强烈建议从MetalLB方案开始。它久经考验,接下来的配置也将围绕此方案展开。
2.2 外部BGP对等体:你的路由器
这是网络架构中的另一个关键角色。它可以是:
- 物理路由器/交换机:如Cisco、Juniper、Arista等厂商的设备,这是最常见的场景。
- 软件路由器:如FRRouting、Bird、GoBGP等,运行在虚拟机或容器中。
- 云厂商的托管网络:一些云厂商的VPC环境支持建立BGP会话。
你的对等体需要支持BGP协议,并且你有权限配置它(通常需要网络团队协作)。你需要从对等体那边获取的信息包括:对等体IP地址、AS号(自治系统号)、以及可选的认证密码(MD5)。
2.3 网络拓扑与IP地址规划
清晰的规划是成功的一半。你需要明确以下几点:
- Pod CIDR:Cilium分配给Pod的网段,例如
10.0.0.0/16。每个Kubernetes节点会从这个大网段中分得一个小子网(如10.0.1.0/24)。 - External IP Pool:MetalLB用来分配给LoadBalancer Service的IP地址池。这个池子必须是你的外部网络可以路由到的IP段,并且不能与Pod CIDR或Node IP段重叠。例如,你可以规划一个
192.168.100.0/24的段。 - BGP对等体IP:你的路由器接口IP,Kubernetes节点需要能访问到这个IP。
- BGP AS号:
- 私有AS号(64512-65534):常用于企业内部。你可以为整个Kubernetes集群分配一个私有AS号(如
64500)。 - 公共AS号:如果需要与互联网交换路由,则需要向区域互联网注册机构申请。
- 你的路由器也会有它自己的AS号。
- 私有AS号(64512-65534):常用于企业内部。你可以为整个Kubernetes集群分配一个私有AS号(如
在我的项目里,我们规划如下:
- Pod CIDR:
10.244.0.0/16 - MetalLB External IP Pool:
172.16.100.0/24 - 路由器IP:
192.168.1.254, AS号:65001 - Kubernetes集群AS号:
64500
3. 基于MetalLB的详细配置实操
这里我们假设你已经有一个运行中的Kubernetes集群,并且已经通过Helm或Yaml方式安装了Cilium作为CNI,集群内部Pod网络通信正常。
3.1 部署与配置MetalLB
首先,通过Helm部署MetalLB,这是最方便的方式。
# 添加MetalLB的Helm仓库 helm repo add metallb https://metallb.github.io/metallb helm repo update # 在metallb-system命名空间安装MetalLB helm install metallb metallb/metallb -n metallb-system --create-namespace安装完成后,MetalLB的控制器(controller)和每个节点上的扬声器(speaker)Pod会启动,但此时它们还处于空闲状态,因为没有配置BGP对等体和IP地址池。
接下来,创建关键的配置:IPAddressPool和BGPPeer。我们将它们放在一个ConfigMap里(MetalLB也支持通过CRD配置,但ConfigMap更通用)。
# metallb-config.yaml apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | peers: - peer-address: 192.168.1.254 # 你的路由器IP peer-asn: 65001 # 路由器的AS号 my-asn: 64500 # 集群的AS号 # password: "your-bgp-password" # 如果BGP会话需要MD5认证,取消注释并设置 address-pools: - name: default-pool protocol: bgp addresses: - 172.16.100.0/24 # MetalLB可分配的External IP池 # 自动将池中IP分配给LoadBalancer类型的Service auto-assign: true应用这个配置:
kubectl apply -f metallb-config.yaml应用后,观察MetalLB的Speaker Pod日志,应该能看到类似“BGP session established”的日志,表示与路由器的BGP会话已经建立。
3.2 配置Cilium以通告Pod CIDR
默认情况下,MetalLB只会通告你配置的address-pools(即LoadBalancer IP)。为了让外部网络能直接访问任意Pod(而不仅仅是Service),我们需要让MetalLB也通告整个Pod的CIDR网段。
这里需要一个技巧:创建一个特殊的LoadBalancer Service,但其External IP选择Pod CIDR范围内的一个子网。更优雅的方式是利用MetalLB的BGPAdvertisement和L2AdvertisementCRD(如果你安装的MetalLB版本支持),但通过ConfigMap配置时,我们可以创建一个“虚拟”的IP池。
修改上面的metallb-config.yaml,在address-pools部分增加一个池子,这个池子覆盖你的整个Pod CIDR:
# metallb-config.yaml (更新版) apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | peers: - peer-address: 192.168.1.254 peer-asn: 65001 my-asn: 64500 address-pools: - name: lb-pool # 用于LoadBalancer Service的池 protocol: bgp addresses: - 172.16.100.0/24 auto-assign: true - name: pod-cidr-pool # 用于通告Pod CIDR的池 protocol: bgp addresses: - 10.244.0.0/16 # 你的Pod CIDR auto-assign: false # 重要!这个池不自动分配IP给Service再次应用配置。这样,MetalLB就会向对等体宣告两条路由:172.16.100.0/24和10.244.0.0/16。
3.3 外部路由器配置示例(以Cisco IOS为例)
光在Kubernetes这边配置还不够,你的路由器也需要接受并正确处理这些BGP路由。以下是一个Cisco IOS设备的简化配置示例:
router bgp 65001 ! 定义BGP邻居,即Kubernetes节点的IP。如果有多节点,每个节点IP都需要配置。 ! 通常我们会将MetalLB Speaker部署为DaemonSet,每个节点都会与路由器建联。 neighbor 192.168.1.10 remote-as 64500 # 节点1的IP neighbor 192.168.1.10 password YOUR_PASSWORD # 如果配置了MD5认证 neighbor 192.168.1.11 remote-as 64500 # 节点2的IP neighbor 192.168.1.11 password YOUR_PASSWORD ! 允许接收来自Kubernetes集群的路由 address-family ipv4 neighbor 192.168.1.10 activate neighbor 192.168.1.10 soft-reconfiguration inbound # 方便调试 neighbor 192.168.1.11 activate neighbor 192.168.1.11 soft-reconfiguration inbound exit-address-family ! ! 将接收到的BGP路由重分发到你的内部网关协议(如OSPF、EIGRP)中,让全网都能学到。 router ospf 1 redistribute bgp 65001 subnets配置完成后,在路由器上使用show ip bgp summary和show ip route bgp命令,应该能看到与Kubernetes节点的BGP邻居关系为“Established”,并且路由表中出现了来自AS 64500的10.244.0.0/16和172.16.100.0/24的路由。
4. 验证与故障排查实录
配置完成后,必须进行系统性的验证。
4.1 基础连通性验证
检查MetalLB组件状态:
kubectl get pods -n metallb-system kubectl logs -n metallb-system -l app=metallb -c speaker在Speaker日志中搜索“Established session”,确认BGP会话已建立。
创建测试Service:
# test-service.yaml apiVersion: v1 kind: Service metadata: name: my-nginx spec: selector: app: nginx ports: - port: 80 type: LoadBalancer --- apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:alpine应用后,查看Service,EXTERNAL-IP栏位应该从MetalLB的
lb-pool(172.16.100.0/24) 中获得一个IP。kubectl apply -f test-service.yaml kubectl get svc my-nginx -w从集群外部访问: 在你的办公电脑或数据中心另一台服务器上(与路由器网络相通),尝试访问上一步获得的External IP。
curl http://172.16.100.x应该能看到Nginx的欢迎页面。
直接访问Pod IP: 获取一个Pod的IP地址(
kubectl get pods -o wide),然后从外部网络尝试ping或curl这个Pod IP(例如10.244.1.5)。这是验证Pod CIDR路由是否成功宣告的关键一步。ping 10.244.1.5
4.2 常见问题与排查技巧
在实际操作中,我踩过不少坑,这里把典型问题和排查思路列出来:
问题1:BGP会话无法建立(状态不是Established)
- 排查思路:
- 网络连通性:在Kubernetes节点上
ping和telnet <路由器IP> 179(BGP端口),确保TCP 179端口可达。防火墙是头号嫌疑犯。 - AS号配置错误:仔细核对MetalLB配置中的
my-asn和peer-asn,与路由器上的配置是否互为对方的AS号。这是最常见的配置错误。 - 认证失败:如果配置了MD5密码,确保双方完全一致,包括大小写和特殊字符。
- 查看详细日志:检查MetalLB Speaker Pod日志,通常会有明确的错误信息,如“connection refused”、“notification sent”等。
- 网络连通性:在Kubernetes节点上
问题2:BGP会话已建立,但学不到路由
- 排查思路:
- 检查IP地址池:确认
address-pools中配置的网段是否正确,并且没有重叠。 - 路由器配置:在路由器上使用
show ip bgp neighbors <邻居IP> advertised-routes,查看Kubernetes节点是否发送了路由。如果没有,问题在MetalLB侧。如果有,再查路由器是否activate了该邻居,以及路由策略是否过滤了这些路由。 - MetalLB Speaker选择:MetalLB的Speaker默认可能只在部分节点运行(基于节点标签)。确保所有需要通告路由的节点上都运行了Speaker Pod。
- 检查IP地址池:确认
问题3:外部可以访问LoadBalancer IP,但无法直接访问Pod IP
- 排查思路:
- 确认Pod CIDR宣告:这是最可能的原因。按照3.2节的方法,确保你配置了用于通告Pod CIDR的地址池,并且
auto-assign: false。 - 检查Cilium网络策略:Cilium的NetworkPolicy可能默认拒绝所有入站(Ingress)流量。你需要创建策略允许来自外部(如
192.168.1.0/24)的流量访问你的Pod。这是一个安全特性,不是故障。 - 节点防火墙:检查Kubernetes节点主机本身的防火墙(如iptables, firewalld),是否放行了前往Pod CIDR的流量。
- 确认Pod CIDR宣告:这是最可能的原因。按照3.2节的方法,确保你配置了用于通告Pod CIDR的地址池,并且
问题4:流量负载不均默认的BGP路由选择可能将所有外部流量引向同一个Kubernetes节点(通常是第一个宣告路由的节点),导致该节点压力过大。
- 解决方案:
- 使用ECMP(等价多路径):这是最理想的方案。在你的路由器上配置,使其将前往同一目标网段(如
10.244.0.0/16)的流量,均匀分发给所有宣告了该路由的Kubernetes节点。这需要路由器支持,并在BGP配置中启用。 - MetalLB的BGP属性:可以尝试在MetalLB的BGPAdvertisement中配置
local-preference或communities属性,来影响路由器的选路,但这需要更深入的BGP知识和路由器侧的配合。
- 使用ECMP(等价多路径):这是最理想的方案。在你的路由器上配置,使其将前往同一目标网段(如
5. 生产环境进阶考量与优化
当基本功能跑通后,为了生产环境的稳定和高可用,还需要考虑以下几点:
5.1 高可用与冗余设计
- 多节点Speaker:MetalLB Speaker以DaemonSet部署,确保每个工作节点都运行一个实例。这样即使某个节点故障,其他节点上的Speaker会接管BGP会话(需要路由器支持BFD快速故障检测或配置较短的保持时间Hold Timer)。
- 多路由器对等体:在MetalLB配置中,可以定义多个
peers,指向不同的物理路由器,实现网络层面的冗余。 - Pod CIDR的精确通告:我们之前宣告的是整个
/16的Pod CIDR。更优的做法是让每个节点只宣告它自己所拥有的/24子网。这可以通过开启Cilium的bgp-control-plane功能(即使不用它做主控制面),或者使用MetalLB的BGPPeerCRD配合节点选择器来实现,可以减少路由表震荡范围。
5.2 安全加固
- BGP会话认证:务必使用MD5密码保护BGP会话,防止路由劫持。
- 路由过滤:在路由器侧,应严格过滤从Kubernetes集群学来的路由,只接受你明确规划的IP地址池(如
172.16.100.0/24和10.244.0.0/16),拒绝其他任何路由,这是一个重要的安全边界。 - Cilium网络策略:利用Cilium强大的网络策略,为Pod定义精细的入站和出站规则,遵循最小权限原则。不要因为通了网络就放开所有权限。
5.3 监控与运维
- 监控MetalLB:监控Speaker和Controller Pod的健康状态、重启次数。Prometheus可以抓取MetalLB的指标,如BGP会话状态、宣告路由数量等。
- 监控BGP会话:通过网络设备本身的监控或通过SNMP、Telemetry等手段,监控BGP会话的建立/断开历史、路由数量变化,设置告警。
- 清晰的文档:记录下所有的AS号、IP地址池、路由器配置片段、以及故障排查流程。当网络出现问题时,清晰的文档能节省大量时间。
配置Cilium与BGP协同工作,是一次典型的云原生与传统基础设施的握手。它没有太多“黑科技”,更多的是对双方协议和配置细节的精准把握。最大的体会就是,一定要边配置边验证,从底层网络连通性,到BGP会话状态,再到路由表,最后到应用层访问,层层递进地排查。一旦打通,你会发现你的Kubernetes集群不再是信息孤岛,而是真正融入了企业整体网络架构中,这为后续的混合云、多集群、乃至服务网格的扩展,都打下了坚实的基础。如果在配置过程中,路由器侧的同事不太理解Kubernetes的需求,不妨把他们请过来,一起看着日志和路由表来调试,这种跨团队的协作往往能更快地解决问题。