WEBKT

裸金属 Kubernetes 基于 eBPF 的高性能 CNI 架构设计与调优实践

26 0 0 0

在裸金属(Bare-metal)环境下部署 Kubernetes 时,网络性能往往决定了整个集群的吞吐上限和延迟下限。传统的 CNI(如 Flannel、Calico)默认依赖 Linux 虚拟网桥、iptables 或 IPVS。这些机制基于 Linux 内核协议栈的通用路由表和 conntrack(连接跟踪)模块,在面对 100GbE 双百兆网卡、高并发短连接业务时,CPU 中断和协议栈上下文切换会迅速成为性能瓶颈。

为了将裸金属物理网卡的性能发挥到极致,基于 eBPF(Extended Berkeley Packet Filter)的 CNI(以 Cilium 为典型代表)通过绕过内核协议栈(Kernel Bypass)、Socket 重定向等技术,实现了接近物理线速的网络数据平面。

本文将深入探讨如何在裸金属 K8s 环境下,设计并落地一套基于 eBPF 的高性能 CNI 网络架构。

传统 CNI 在裸金属环境下的性能痛点

传统的 Calico(IPVS 模式)或 Flannel(VxLAN 模式)在裸金属节点上面临以下三个核心瓶颈:

  1. conntrack 锁竞争与表满丢包:Linux conntrack 模块使用全局锁。在高并发新建连接场景下,多个 CPU 核心争抢全局锁会导致严重的系统态 CPU 消耗。一旦并发连接数超过 nf_conntrack_max,系统会直接丢弃数据包。
  2. iptables 规则链线性查表延迟:当 Service 数量达到数万级别时,iptables 规则链会变得极长。由于 iptables 只能进行线性 O(N) 匹配,每次报文转发都需要遍历规则,导致延迟指数级上升。
  3. 内核栈拷贝开销:在 Pod 到本地 Pod,或者 Pod 到 Host 的通信中,报文需要经过多次 VETH 对(Virtual Ethernet)的虚拟 MAC 寻址、ARP 广播和内存拷贝,无法实现真正的本地零拷贝。

基于 eBPF 的高性能网络架构设计

本架构设计基于 Cilium CNI,采用 Underlay 直连路由(No-Overlay) 方案,并启用 eBPF Host RoutingXDP(eXpress Data Path)

1. 物理网络与路由拓扑(Underlay BGP)

在裸金属环境,不要使用 VxLAN 或 Geneve 等 Overlay 隧道技术。隧道封装会带来 50 字节左右的头部开销,降低 MTU 效率,并极大消耗 CPU 进行封包/解包操作。

我们采用 BGP 直连路由方案

       +--------------------+
       |   Spine Switch     |
       +----------+---------+
                  | BGP
       +----------+---------+
       |   Leaf Switch      |
       +----+-----------+---+
            |           | BGP (ECMP)
+-----------+---+   +---+-----------+
| Bare-metal A  |   | Bare-metal B  |
| Pod CIDR:     |   | Pod CIDR:     |
| 10.244.1.0/24 |   | 10.244.2.0/24 |
+---------------+   +---------------+
  • 交换机协同:裸金属服务器通过双 100G 网卡(LACP 绑定或主动备份模式)接入 Leaf 交换机。
  • BGP 宣告:每个 K8s 节点运行一个 BGP 客户端(Cilium 内置的 BGP Control Plane),向 Leaf 交换机宣告本地 Pod CIDR。
  • ECMP 等价多路径:交换机侧开启 ECMP,实现跨节点流量的负载均衡与快速收敛。

2. eBPF 极速转发面设计

通过 eBPF 替换传统的 kube-proxy 和 VETH 转发路径,其核心机制包含以下三点:

+-------------------------------------------------------------+
|                         User Space                          |
|    Pod A (10.244.1.10)              Pod B (10.244.1.20)     |
+--------|------------------------------------|---------------+
         | [Socket FD]                        | [Socket FD]
+--------v------------------------------------v---------------+
|                        Kernel Space                         |
|   +----------------------------------------------+          |
|   |         eBPF Sockmap (Socket Redirect)       |          |
|   +----------------------------------------------+          |
|                                                             |
|   +----------------------------------------------+          |
|   |         eBPF Host Routing (TC BPF)           |          |
|   +----------------------------------------------+          |
|                         |                                   |
|   +---------------------v------------------------+          |
|   |                  XDP Driver                  |          |
|   +---------------------|------------------------+          |
+-------------------------|-----------------------------------+
                          | [Direct Ring Buffer]
                     +----v----+
                     |   NIC   |
                     +---------+

① BPF Host Routing(绕过 Host 协议栈)

传统的 VETH 设备在发送数据包时,需要让报文先进入 Host 的网络协议栈,经过路由、iptables 过滤后再发送到物理网卡。
启用 BPF Host Routing 后,eBPF 程序直接挂载到 Pod VETH 的 tc(Traffic Control)钩子上,识别出目的 IP 属于外部节点后,直接通过 bpf_redirect 接口将报文推送到物理网卡的发送队列(TX Queue),完全绕过 Host 的整个 IP 路由和 iptables 过滤流程。

② Sockmap 重定向(本地 Pod 零拷贝)

对于部署在同一个物理节点上的 Pod A 和 Pod B 之间的通信,eBPF 利用 sockmap(Socket Map)和 sockhash 拦截 Socket 层的 sendmsg 系统调用。
eBPF 识别出对端 Socket 正好在本地,直接将数据从 Pod A 的 Socket 写缓冲区拷贝到 Pod B 的 Socket 读缓冲区,不经过任何 TCP/IP 封装,也不经过 VETH 设备,时延降低 50% 以上。

③ XDP 边缘负载均衡(极速 North-South 流量)

对于外部通过 NodePort 或 LoadBalancer 访问集群的流量,在网卡驱动层(XDP 阶段)直接执行 eBPF 字节码。
在 CPU 还没有为数据包分配 sk_buff 内存结构体之前,XDP 程序就完成了 DNAT 转换和路由决策,并直接将报文转发出去或送往对应 Pod。这提供了极高的抗 DDoS 攻击能力和极佳的吞吐量。


落地部署:Cilium Helm 高性能配置

在物理机(推荐 Kernel 5.15 或更高版本,以获得完整的 BPF Host Routing 和 IPAM 性能)上,使用以下 Helm 配置安装 Cilium:

# values.yaml
kubeProxyReplacement: "true" # 彻底移除 kube-proxy

# 路由与隧道配置
tunnel: "disabled" # 关闭 VxLAN/Geneve 隧道,启用 Underlay 路由
autoDirectNodeRoutes: "true" # 自动配置节点间直连路由
ipv4NativeRoutingCIDR: "10.244.0.0/16" # 集群 Pod CIDR

# BPF 核心性能调优
bpf:
  masquerade: "true" # 启用基于 BPF 的 IP 伪装,不使用 iptables
  hostRouting: "true" # 开启 BPF 宿主机路由,绕过 host 栈
  tproxy: "true"

# 启用 Socket 重定向加速本地通信
sockops:
  enabled: "true"

# 启用 BGP 控制平面(用于与交换机建立邻居)
bgpControlPlane:
  enabled: "true"

# XDP 加速配置(选择宿主机物理网卡,如 eth0)
loadBalancer:
  mode: "dsr" # 启用 DSR(Direct Server Return)模式,回包不经过 Node 直接返给 Client
  acceleration: "native" # 启用硬件驱动级 XDP 加速
  devices:
    - eth0

# 连接跟踪调优
bpfMapDynamicSizeRatio: 0.0055 # 根据物理内存自动扩展 BPF Map 大小,防止并发连接暴涨时 Map 溢出

物理网卡与内核层配套调优

仅仅配置 CNI 是不够的,裸金属的底层硬件和内核参数必须同步进行调优,否则硬件性能无法完全透传给容器。

1. 物理网卡调优 (ethtool)

在节点启动脚本或 Ansible 部署模板中,对宿主机网卡进行调优:

# 1. 开启网卡多队列并绑定 CPU 核心
ethtool -L eth0 combined 16 # 设置队列数与 CPU 核心数匹配

# 2. 开启硬件卸载(Offloading)功能
ethtool -K eth0 tso on gso on gro on rxhash on txhash on

# 3. 增大环形缓冲区(Ring Buffer),防止大流量时硬件丢包
ethtool -G eth0 rx 4096 tx 4096

# 4. 开启 MTU 9000(巨型帧 Jumbo Frames)
# 注意:交换机端口也必须同步开启 MTU 9000
ip link set dev eth0 mtu 9000

2. 内核参数调优 (/etc/sysctl.conf)

针对高并发 eBPF 场景优化内核网络参数:

# 提高系统全局套接字最大连接数
net.core.somaxconn = 65535

# 增大接收和发送套接字缓冲区大小
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# 禁用旧的桥接流量过滤(避免内核多次处理)
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-arptables = 0

# 提高网络设备接收队列积压的最大值
net.core.netdev_max_backlog = 100000

性能收益与实测对比

在一组双 100G 网卡、Intel Xeon 64 核物理机集群上,使用 iperf3wrk 对传统 IPVS 模式与本套 eBPF 架构进行对比测试,数据表现如下:

指标 传统 Calico (IPVS + VETH) Cilium (eBPF Direct Routing + XDP) 性能提升
Pod-to-Pod 吞吐量 ~42 Gbps ~96 Gbps +128% (接近线速)
Pod-to-Pod 延迟 (RTT) 120 μs 48 μs 降低 60%
并发新建连接数 (CPS) 85,000/s (开始出现丢包) 380,000/s (系统平稳) 提升 4.4 倍
NodePort 转发 CPU 损耗 18% (全核心) 2.5% (仅中断处理核心) CPU 消耗暴降

故障排查与可观测性实战

eBPF 的黑盒特性曾经是网络运维的噩梦。现代 eBPF CNI 已经提供了极强的调试工具。

1. 使用 cilium-bugtoolcilium monitor 抓包

在裸金属节点上,传统的 tcpdump 在开启 XDP 或 Sockmap 的情况下可能抓不到包(因为报文根本没进入传统的协议栈路径)。
使用以下命令在 eBPF 层监控报文:

# 进入 Cilium Agent Pod 执行监控,捕获丢包事件
cilium monitor --type drop

# 查看特定 Pod 的 eBPF 路由决策
cilium monitor --src-pod-id <pod-id>

2. 使用 bpftool 检查内核 BPF Map 装载情况

查看当前宿主机加载了哪些 eBPF 程序:

bpftool net show
# 输出示例:
# xdp:
#   eth0(2) driver id 102
# tc:
#   eth0(2) clsact/ingress bpf_network.o:[from-netdev] id 108
#   eth0(2) clsact/egress bpf_network.o:[to-netdev] id 115

如果发现物理网卡 eth0xdp 处于 driver 模式,说明硬件驱动级 XDP 已经成功加载;如果显示为 generic,说明网卡驱动不支持,降级为了通用内核模式,性能会打折扣。

总结

在物理机和超宽带网络环境下,基于 eBPF 的 CNI 不再只是一个“备选项”,而是要榨干硬件红利的“必选项”。通过 Underlay BGP 路由BPF 宿主机路由Sockmap 本地加速驱动级 XDP 优化,我们可以让 K8s 容器网络拥有与物理网络几乎一致的带宽和时延表现,为高性能数据库、分布式存储和 AI 训练任务提供坚实的基础底座。

云原生硬核玩家 eBPFKubernetesCNI

评论点评