导读:本期聚焦于石川澪创作的《集群网络中 eBPF 数据路径与 kube-proxy 有哪些本质区别?》,敬请观看详情。数据包从 Pod 发出到最终到达后端,转发路径上的每一跳都由内核机制决定。kube-proxy 依赖 iptables 或 IPVS 规则修改报文,而 eBPF 数据路径则把决策逻辑下沉到更靠近网卡的内核钩子中。两者在转发时延、吞吐、规则更新开销以及可观测性上存在明显差距。本文先拆解 kube-proxy 各模式的转发流程与代价,再说明 eBPF 如何通过 BPF map 和尾调用绕过 netfilter 和 conntrack 完成服务负载均衡,最后结合集群规模、内核版本与运维成本给出替换建议。理解这些差异有助于在新建或改造集群时选择合适的数据面。

在 Kubernetes 集群里,Service 到 Pod 的流量转发通常由 kube-proxy 负责,但越来越多的发行版开始用 eBPF 实现数据路径,甚至直接替代 kube-proxy。要理解这种变化,必须从内核转发机制和规则同步方式入手。

集群网络中 eBPF 数据路径与 kube-proxy 有哪些本质区别?

kube-proxy 的核心任务是把访问 Service 的流量转换成对具体 Pod 的访问,而 eBPF 数据路径则直接在更靠近网卡的内核上下文中完成同样的工作。两者的差异不仅体现在性能数据上,也直接影响到大规模集群的稳定性和可维护性。

kube-proxy 的规则模型与性能代价

kube-proxy 早期使用 userspace 模式,所有流量都要先进入用户态再由代理进程转发,效率很低。现代版本默认使用 iptables 模式,通过在每个节点上刷写大量 netfilter 规则来实现 Service 的 DNAT 和负载均衡。kube-proxy 监听 API Server 中的 Service 和 Endpoints 变化,然后调用 iptables-restore 将规则同步到内核。当集群中 Service 数量达到数千个时,节点上的规则数量会迅速膨胀,因为每个 Service 可能对应多条 KUBE-SERVICES 链和 KUBE-SVC-xxx 链。

以 iptables 模式为例,一个 ClusterIP 类型的 Service 通常会生成类似下面的规则,用于在多个后端 Pod 之间按概率选择目标。这些规则在内核网络栈的 PREROUTING 和 OUTPUT 阶段被执行。

-A KUBE-SERVICES -d 10.96.0.10/32 -p tcp -m tcp --dport 80 -j KUBE-SVC-XXXX
-A KUBE-SVC-XXXX -m statistic --mode random --probability 0.33333 -j KUBE-SEP-AAAA
-A KUBE-SVC-XXXX -m statistic --mode random --probability 0.50000 -j KUBE-SEP-BBBB
-A KUBE-SVC-XXXX -j KUBE-SEP-CCCC

iptables 的问题不只是规则数量,还在于规则匹配是线性遍历的。每个数据包进入 netfilter 后,要从链的第一条规则开始逐条比较,直到命中。虽然内核有优化,但在大规模场景中,几千条甚至几万条规则会导致明显的尾部延迟。另外,当 Service 或 Endpoints 发生变化时,kube-proxy 需要重新生成并全量刷新规则,这个过程中会持有锁,进一步影响数据面稳定性。

IPVS 模式在一定程度上缓解了线性匹配问题,它使用内核 IPVS 模块维护一张哈希表,通过目的地址和端口直接查找后端,转发性能比 iptables 高。但 IPVS 仍然依赖 kube-proxy 这个用户态组件去同步规则,并且与 netfilter 的 conntrack 模块配合时,会话状态管理仍有额外开销。此外,IPVS 模式对网络策略、会话保持等高级功能的支持并不完整,往往还需要配合其他 CNI 插件。

eBPF 数据路径的挂载点与转发逻辑

eBPF 数据路径不再通过修改 netfilter 规则来工作,而是将小型程序加载到内核的特定挂载点上,例如 TC 层的 clsact、套接字层的 sock_ops 或者 XDP 层。Cilium 是最典型的实现,它在每个节点上运行一个 cilium-agent,负责把 Service 和 Endpoints 信息编译成 BPF map,然后将 eBPF 程序附加到 Pod 虚拟网卡的 ingress 和 egress 路径上。

当数据包从 Pod 发出时,eBPF 程序会直接读取 BPF map 中的服务映射表,查找到目标 ClusterIP 和端口对应的后端列表,并完成 DNAT 或直接封装。由于决策在靠近网卡的内核上下文完成,数据包不需要排队进入用户态,也不需要遍历几百条 iptables 规则。负载均衡算法可以在 BPF map 中预计算权重,程序通过一次哈希或随机选择后端,时间复杂度接近 O(1)。

下面是一段简化的 eBPF 程序片段,展示如何在 TC 层处理服务转发。它假设 BPF map 中保存了后端地址和端口,并根据五元组做负载均衡。

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

struct backend_info {
    __u32 ip;
    __u16 port;
};

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 1024);
    __type(key, __u32);
    __type(value, struct backend_info);
} lb_map SEC(".maps");

SEC("classifier")
int svc_forward(struct __sk_buff *skb)
{
    __u32 key = 1;
    struct backend_info *backend = bpf_map_lookup_elem(&lb_map, &key);
    if (!backend)
        return TC_ACT_OK;

    // 这里省略对 skb 的解析、DNAT 和校验和更新
    // 实际实现会通过 bpf_skb_store_bytes 修改目标地址

    return TC_ACT_OK;
}

与 kube-proxy 相比,eBPF 数据路径最大的优势是绕过了 netfilter 的完整协议栈处理。kube-proxy 生成的 DNAT 规则依赖 conntrack 来保证反向流量回到同一个后端,而 eBPF 可以在 BPF map 中直接记录连接状态,或者使用更轻量的会话表。这样既减少了 conntrack 表的压力,也避免了因为 conntrack 满导致的新连接丢包问题。

另一个关键差异是规则更新方式。kube-proxy 需要全量刷写 iptables 或 IPVS 规则,eBPF 则通过 BPF map 的原子更新来修改服务端点。cilium-agent 收到 Kubernetes 事件后,只需要更新 map 中的键值对,数据面程序下一次查询时就会自动使用新地址。这种增量更新对长连接和突发流量更加友好,不会出现规则同步期间的数据面短暂不可用。

功能覆盖与可观测性对比

除了转发性能,功能覆盖也是选择数据路径的重要依据。kube-proxy 本身只负责 Service 的负载均衡和会话保持,网络策略通常需要额外的 CNI 插件通过 iptables 或 eBPF 实现。而 eBPF 数据路径可以与网络策略、服务网格、加密传输等能力共享同一套 map 和钩子,减少不同组件之间的规则冲突。

下面的表格从几个核心维度对比了两者的差异。

对比维度kube-proxyeBPF 数据路径
转发机制iptables 线性规则或 IPVS 哈希表BPF 程序在 TC/socket 层直接处理
规则更新全量刷新,存在锁竞争BPF map 增量更新
连接跟踪依赖 conntrack 模块可自定义会话表或绕过 conntrack
网络策略需要额外组件或 CNI 配合可与 eBPF 策略统一管理
可观测性依赖 iptables 计数器或 IPVS 统计原生支持细粒度指标和事件
调试难度iptables 规则直观但量大难读需要理解 BPF 程序与内核版本

可观测性方面,kube-proxy 提供的指标相对有限,通常只有规则同步延迟和错误计数。而 eBPF 数据路径可以在每个数据包经过时记录来源 Pod、目标 Service、后端选择结果和转发时延,配合 ring buffer 或 perf event 输出到用户态。Cilium 的 Hubble 组件就是基于这一能力实现服务依赖图和流量监控,这在排查微服务问题时比单纯查看 iptables 计数器要高效得多。

不过,eBPF 对内核版本和内核编译选项有要求。较新的 Linux 内核才能完整支持 BPF-to-BPF 调用、CO-RE 等特性。如果集群节点运行的是较旧的内核,或者某些云厂商对 eBPF 功能有限制,那么切换到 eBPF 数据路径前需要先评估兼容性。而 kube-proxy 的 iptables 模式几乎可以在任何 Linux 发行版上运行,这也是它长期作为默认组件的原因之一。

替换 kube-proxy 的实践路径

如果决定在集群中启用 eBPF 数据路径,通常不需要先卸载 kube-proxy,而是采用渐进式切换。以 Cilium 为例,可以通过 Helm 参数设置 kubeProxyReplacement=strict 来完全禁用 kube-proxy,或者使用 probe 模式自动检测节点能力并回退。完全替换后,Service 的 ClusterIP 转发、NodePort 访问以及外部流量策略都由 eBPF 程序处理。

下面是一个 Cilium 的配置片段,用于在安装时开启 kube-proxy 替换功能。

apiVersion: v1
kind: ConfigMap
metadata:
  name: cilium-config
  namespace: kube-system
data:
  kube-proxy-replacement: "strict"
  enable-bpf-masquerade: "true"
  enable-host-reachable-services: "true"

开启替换后,需要验证节点上的 iptables 规则是否已经不再包含 KUBE-SERVICES 链。可以在任意节点执行 iptables-save | grep KUBE-SVC,如果没有输出,说明 kube-proxy 规则已被移除。同时要检查 Service 的 ClusterIP 和 NodePort 访问是否正常,尤其是使用了外部流量策略或会话保持的服务。

从实际运行效果看,在几千个 Service 的大规模集群中,eBPF 数据路径通常能降低节点 CPU 使用率,因为不再需要频繁调用 iptables-restore。长连接在服务端点变化时也更加稳定,因为 BPF map 更新不会中断现有连接,而 iptables 全量刷新可能导致短暂的规则丢失。但这并不意味着 eBPF 在所有场景下都优于 kube-proxy。如果集群规模很小,Service 只有几十个,iptables 或 IPVS 的性能已经足够,引入 eBPF 反而增加了对内核和学习成本的要求。

总体而言,选择哪种数据路径取决于集群规模、内核版本、团队对 eBPF 的熟悉程度以及是否需要统一的可观测性平台。对于新建的、使用较新内核的生产集群,可以考虑直接采用 eBPF 数据路径;对于已有的、运行稳定的传统集群,可以先将 IPVS 作为过渡,再评估切换到 eBPF 的收益。

eBPFkube-proxyKubernetes 网络修改时间:2026-10-04 02:56:06

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1004/65358.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。