跳到正文
复制成功
请遵守本站许可
报告
章节_文章 // 现场报告

文章编号: RL-EBPF原理与C

2026.09.25

eBPF 原理与 Cilium 网络数据面

Chavy
Chavy
#Kubernetes#eBPF#Cilium#网络
档案

摘要:理解 eBPF 程序、Map、XDP、TC 与 Socket Hook,结合 Cilium 分析 Service 转发、网络策略和 Hubble 流量观测的实现思路。

这一节继续深入 Cilium 的核心技术:eBPF(extended Berkeley Packet Filter)。

这部分知识将帮助你理解现代 Kubernetes 网络是如何在 Linux 内核中完成流量转发、Service 负载均衡、网络策略和流量观测的。

本节重点:

  1. eBPF 是什么,为什么 Linux 需要它。
  2. eBPF 程序如何加载和执行。
  3. XDP、TC、Socket 三种网络处理位置。
  4. eBPF Map 如何保存网络状态。
  5. Cilium 如何使用 eBPF 实现 Service 转发。
  6. Cilium NetworkPolicy 与 Hubble 的工作原理。

一、eBPF 是什么?#

可以先把 eBPF 理解为:一种允许经过验证的程序,在 Linux 内核支持的特定执行点运行的技术。

传统情况下,如果你希望修改 Linux 网络内核的某些行为,可能需要:

  • 修改 Linux 内核代码。
  • 编译或加载内核模块。
  • 使用内核已经提供的固定功能,例如 iptables。

eBPF 提供了另一种方式:通过程序描述处理逻辑,在内核支持的位置执行,而不必为每项自定义功能都修改内核源码。

flowchart TB
    subgraph G0["传统规则式处理"]
        direction TB
        N0["应用程序"]
        N1["iptables 规则"]
        N2["Netfilter"]
    end
    subgraph G1["eBPF 处理方式"]
        direction TB
        N3["用户态控制程序"]
        N4["eBPF 程序与 Map"]
        N5["内核执行点"]
    end
    N0 --> N1
    N1 --> N2
    N3 --> N4
    N4 --> N5

概念对比:两者并非互斥,实际系统可以同时使用 Netfilter、eBPF 和其他 Linux 网络机制。

例如,eBPF 可以用于:

  • 网络数据包过滤和转发。
  • 网络负载均衡。
  • 运行时安全监控。
  • 性能分析与内核事件追踪。
  • 系统调用观测。

但是 eBPF 不是一个可以任意执行所有内核操作的通用运行环境。程序类型、可用辅助函数、内存访问以及执行行为都受到内核约束。

二、eBPF 程序是如何运行的?#

以一个使用 C 编写的 eBPF 程序为例,整体流程如下:

flowchart TB
    N0["1. 编写 eBPF 程序<br/>通常使用 C,也有 Rust 等工具链"]
    N1["2. 编译为 eBPF 字节码<br/>例如使用 Clang/LLVM"]
    N2["3. 加载并通过 Verifier 验证<br/>内核检查程序是否合法、安全性约束是否满足"]
    N3["4. 挂载到内核执行点<br/>例如 XDP、TC 或 cgroup Socket Hook"]
    N4["5. 事件触发程序执行<br/>内核根据程序类型执行相应逻辑"]
    N0 --> N1
    N1 --> N2
    N2 --> N3
    N3 --> N4

这里有三个关键组件。

1. Verifier:验证器#

eBPF 程序在加载到内核时,需要通过验证器检查。

验证器会分析程序的控制流、内存访问、指针使用和辅助函数调用等,判断是否满足相应的安全约束。

例如,非法的越界内存访问可能导致程序被拒绝加载。

但验证器不能证明程序的所有业务逻辑都正确,也不能保证程序永远不会造成性能问题。

2. JIT:即时编译器#

在支持的架构和配置下,eBPF 字节码可以通过 JIT 编译为本机机器指令执行。

这样可以避免每次都通过字节码解释器逐条执行,提高执行效率。

不过,JIT 是否可用以及采用何种执行方式,取决于内核、体系结构与系统配置。

3. Hook:执行位置#

eBPF 程序不是加载后就随时自动运行。

它通常需要附着到特定事件或执行点,例如:

  • 网络设备收到数据包。
  • 数据包经过 TC 处理点。
  • 应用调用某些 Socket 操作。
  • 内核函数或系统调用触发跟踪事件。

不同 Hook 可以访问的信息和能够执行的操作不同。


三、XDP、TC 和 Socket Hook 有什么区别?#

这是理解 Cilium eBPF 数据面的重点。

flowchart TB
    N0["物理网卡接收数据包"]
    N1["XDP<br/>尽早处理进入网卡的数据包"]
    N2["Linux 网络协议栈<br/>包括 TC、路由、Netfilter 等处理机制"]
    N3["Socket / 应用进程<br/>连接建立、数据收发"]
    N0 --> N1
    N1 --> N2
    N2 --> N3

简化模型:TC ingress/egress 与 Socket Hook 属于不同的处理位置,不能把所有 Hook 理解为单一的线性执行顺序。

1. XDP:较早处理入站数据包#

XDP(eXpress Data Path)可以在网卡驱动的较早处理阶段执行 eBPF 程序。

典型应用:

  • 高性能数据包丢弃。
  • DDoS 流量过滤。
  • 快速转发。
  • 特定负载均衡加速。

在支持的环境中,XDP 可以让部分数据包不必经过完整的上层网络协议栈。

但是 XDP 不一定总是性能最优,也不是所有网卡驱动都支持同样的 XDP 模式。

2. TC:更灵活的网络数据包处理#

TC(Traffic Control)属于 Linux 网络流量处理机制。

eBPF 可以附着到 TC ingress 或 egress 等位置,处理经过网络接口的数据包。

典型应用:

  • 数据包过滤。
  • 网络策略执行。
  • 地址转换。
  • 流量重定向。
  • 容器网络转发。

相比 XDP,TC 所处位置通常可以使用更多 Linux 网络协议栈相关上下文,但具体能力取决于内核和程序类型。

3. Socket Hook:在连接或收发阶段处理#

Socket 层的 eBPF 程序可以附着到特定 cgroup Socket Hook 等执行点。

对于 Kubernetes Service,某些实现可以在应用建立连接时就完成 Service 地址到后端地址的转换。

例如,应用发起:

PRTCL // TEXT
connect(10.96.120.30:8080)

Cilium 的 Socket Load Balancing 可以在相关 Hook 中选择后端:

PRTCL // TEXT
10.244.2.20:80

这样,流量在进入后续网络路径之前,就可能已经绑定到实际后端地址。

三种 Hook 对比#

对比XDPTCSocket
主要执行位置网卡接收的早期阶段网络接口的流量处理位置特定 Socket 操作位置
典型用途快速过滤、转发策略、流量转发Socket 级负载均衡
处理对象数据包数据包Socket 操作或相关状态
Cilium 应用部分外部流量加速网络数据面与策略Service Socket LB

需要记住:eBPF 不是一种单独的网络转发方式,而是一套可以部署到多个内核执行点的可编程机制。

四、eBPF Map:内核里的网络状态表#

理解了程序如何运行,接下来还有一个问题:

eBPF 程序如何知道某个 Service 有哪些后端 Pod?

答案之一就是 eBPF Map。

Map 是内核管理的数据结构,可以供 eBPF 程序访问,也可以在权限允许的情况下由用户态程序进行管理。

常见 Map 类型包括:

  • Hash Map:键值映射。
  • Array Map:按索引访问数据。
  • LRU Hash Map:具备最近最少使用淘汰机制的哈希表。
  • LPM Trie:适合最长前缀匹配等场景。
  • Ring Buffer:常用于向用户态传递事件数据。

1. 以 Service 转发为例#

假设有一个 Service:

PRTCL // TEXT
Service:10.96.120.30:8080
后端:
Pod A:10.244.1.10:80
Pod B:10.244.2.20:80

可以用两张逻辑表帮助理解:

Service 查找表(概念示意)

Service 地址后端集合
10.96.120.30:8080后端 A、B

后端映射表(概念示意)

后端 ID实际地址
A10.244.1.10:80
B10.244.2.20:80

真实 Cilium Map 的键、值结构会随版本、协议和功能配置变化,并不一定直接按照这两张表存储。

用户态的 Cilium Agent 可以观察 Kubernetes Service 和 EndpointSlice 的变化,随后维护相关数据面状态。

例如,Pod A 被删除后:

  1. Kubernetes 更新相应的 EndpointSlice。
  2. Cilium Agent 感知服务端点变化。
  3. Agent 更新 eBPF 相关 Map。
  4. 后续新连接选择当前可用的后端。

这是一种典型的控制面与数据面分离设计。

  • 控制面:负责管理期望状态和同步数据。
  • 数据面:负责根据已配置的状态处理实际网络流量。

2. Map 为什么很重要?#

因为数据包处理时,内核中的 eBPF 程序可以直接查询相关 Map,而不必为了每个请求都向 Kubernetes API Server 查询后端信息。

例如:

flowchart TB
    N0["请求进入"]
    N1["eBPF 程序"]
    N2["查询 Service Map"]
    N3["选择后端"]
    N4["转发或重定向"]
    N0 --> N1
    N1 --> N2
    N2 --> N3
    N3 --> N4

这样有利于减少频繁的用户态与内核态交互。

但 Map 不是无限容量的,其容量、内存占用和更新效率也需要监控。

五、Cilium 如何实现 kube-proxy 替代?#

传统 kube-proxy 的 iptables 模式通常依赖 Netfilter 规则进行 Service 匹配、后端选择和 NAT。

Cilium 在启用适用的 kube-proxy 替代配置时,可以通过 eBPF 实现 Service 负载均衡。

1. 两种数据面逻辑比较#

传统 iptables 方式

PRTCL // PLAINTEXT
** 客户端 Socket**
** 数据包进入协议栈 **
**Netfilter / DNAT**
**Pod 后端 **

Cilium Socket LB 方式

PRTCL // PLAINTEXT
** 客户端 Socket**
**eBPF Socket Hook**
** 后端地址已选定 **
**Pod 后端 **

Socket LB 的一个重要特点是:可以在应用建立连接阶段完成后端选择,而不是要求后续每个数据包都进行同样的 Service 目的地址转换。

但是,Cilium 的 Service 实现并不只有 Socket LB。

根据不同流量类型、网络配置和兼容性要求,它还可以通过 TC、XDP 等方式执行负载均衡。

2. 是否意味着 eBPF 一定比 iptables 快?#

不能简单这么认为。

eBPF 在某些工作负载下能够减少规则匹配或数据包处理开销,但实际性能受到很多因素影响,例如:

  • CPU 和网卡硬件。
  • Linux 内核版本。
  • Service 数量和连接规模。
  • 是否跨节点。
  • 是否进行 SNAT、DSR 或隧道封装。
  • 是否启用网络策略、监控和加密。
  • 工作负载主要受 CPU、内存还是网络带宽限制。

所以,正确的比较方式是:在相同硬件、网络拓扑和业务负载下,比较延迟、吞吐量、CPU 使用量和丢包等指标。


六、Cilium 如何实现 NetworkPolicy?#

传统 Kubernetes NetworkPolicy 的规则通常围绕 Pod 标签、Namespace、IP 地址和端口定义。

Cilium 进一步引入与工作负载身份相关的网络策略机制。

可以这样理解:

flowchart TB
    N0["Frontend Pod<br/>标签:app=frontend"]
    N1["Cilium eBPF 策略检查"]
    N2["Backend Pod<br/>标签:app=backend"]
    N3["拒绝:不符合策略"]
    N0 -->|"访问 TCP 8080"| N1
    N1 -->|"允许:符合策略"| N2
    N1 --x N3

在 Cilium 的身份模型中,工作负载可以根据安全相关标签关联到数字安全身份。相应的 eBPF 数据面利用这些身份信息和策略状态进行判断。

一个重要优势是:策略可以围绕工作负载身份表达,而不必仅仅依赖某个临时的 Pod IP。

例如,后端 Pod 被重新创建:

PRTCL // TEXT
旧 IP:10.244.2.20
新 IP:10.244.2.35

如果它保持同样的安全相关标签,策略语义可以继续表达“允许 frontend 访问 backend”,而不必让用户逐个维护临时 Pod IP。

NetworkPolicy 是如何执行的?#

概念上:

  1. 用户创建 NetworkPolicy。
  2. Cilium Agent 读取相关策略与工作负载信息。
  3. 将策略转换为适合数据面的状态。
  4. 更新相关 eBPF 程序所使用的 Map 等数据。
  5. 数据包经过相应 Hook 时执行策略判断。

不过,网络策略的实际执行还涉及流量方向、连接状态、身份识别和特定数据面配置,不能把所有策略简化为一个固定的 IP 匹配过程。

七、Hubble:如何观察 eBPF 网络流量?#

学习 eBPF 最有价值的部分之一,就是理解 Cilium 如何通过 Hubble 提供网络可观测性。

Hubble 是建立在 Cilium 之上的网络可观测性工具,可以帮助观察:

  • 哪个 Pod 访问了哪个 Pod。
  • 使用了什么协议和端口。
  • 流量是否被允许或拒绝。
  • 部分 DNS 和 HTTP 层面的请求信息。
  • 某些流量为什么被网络策略拒绝。
flowchart TB
    N0["Pod 网络流量"]
    N1["Cilium eBPF 数据面<br/>产生网络流量与策略相关事件"]
    N2["Cilium Agent / Hubble<br/>收集和提供流量事件"]
    N3["Hubble CLI / Hubble UI<br/>查询流量、查看拒绝原因和调用关系"]
    N0 --> N1
    N1 --> N2
    N2 --> N3

注意:Hubble 的观察范围取决于部署方式、功能配置和事件采集限制。它不能保证记录每一个数据包,也不能直接看到所有加密应用流量的明文内容。

八、eBPF、iptables 与 Kubernetes 网络的关系总结#

维度iptables / NetfiltereBPF
配置模型规则、链和匹配条件可加载程序与 Map
执行位置Netfilter 处理路径XDP、TC、Socket 等多个 Hook
Service 转发传统 kube-proxy 的一种实现Cilium 等方案可实现
网络策略网络插件可通过规则实现网络插件可通过 eBPF 实现
可观测性计数器、日志及相关工具可结合 eBPF 事件和 Hubble
主要优势成熟、工具生态完善可编程性与灵活的执行位置
主要挑战大规模规则管理与排障复杂度内核兼容性、程序与 Map 调试

不要认为 eBPF 完全取代了 iptables,也不要认为 eBPF 一定绕过所有 Linux 网络协议栈处理。

两者可以在同一台 Linux 主机上共存,实际走哪条路径由系统配置和流量类型决定。

九、本节总结#

你现在应该能够区分:

  • eBPF 程序:决定在特定内核执行点处理什么。
  • Verifier:验证 eBPF 程序是否满足加载约束。
  • XDP、TC、Socket Hook:对应不同的内核执行位置。
  • eBPF Map:保存数据面需要的状态。
  • Cilium Agent:负责维护相应的数据面配置。
  • Hubble:用于观察 Cilium 管理的网络流量与策略事件。

推荐深入阅读:

归档于 2026.09.25
// 档案结束
Classified
章节_06
协议编号: CC-BY-NC-SA-4.0

eBPF 原理与 Cilium 网络数据面

作者:Chavy发布日期:2026.09.25

基于 CC BY-NC-SA 4.0

© 2026 Chavy
Powered by Astro