Wiki LogoWiki - The Power of Many

eBPF

eBPF (Extended Berkeley Packet Filter)

什么是 eBPF?

eBPF 是一项革命性的技术, 它允许在 Linux 内核中运行沙盒程序, 而无需修改内核源代码或加载内核模块. 简单来说, eBPF 让 Linux 内核变得 "可编程".

它起源于 1992 年用于网络包过滤的 BPF (Berkeley Packet Filter), 但在 2014 年经过重新设计后, 它演变成了一个通用的执行引擎, 能够处理各种系统级事件.

核心架构与原理

1. 注入点 (Hook Points)

eBPF 程序是事件驱动的, 它们会被挂载 (Attach) 到内核或应用层的特定 "钩子" 上. 常见的钩子包括:

  • 系统调用 (System Calls): 监听应用层与内核的交互.
  • 函数进入/退出: 通过 kprobes (内核) 和 uprobes (用户态) 实现动态追踪.
  • 网络事件: 通过 XDP (高速数据面) 或流量控制 (TC) 直接处理数据包.
  • 追踪点 (Tracepoints): 内核中预定义的静态埋点.

2. 验证器 (Verifier)

安全性是 eBPF 的基石. 在程序加载到内核之前, 验证器会进行严格检查, 以确保:

  • 程序不会崩溃或导致内核死锁 (禁止无限循环).
  • 不会非法访问越权内存.
  • 程序必须在有限步骤内执行完毕.

3. JIT 编译 (Just-In-Time)

通过验证后, JIT 编译器会将通用的 eBPF 字节码编译成特定处理器的原生指令 (x86, ARM64 等), 从而实现与内核原生代码相当的执行效率.

4. BPF Map (存储与通信)

由于 eBPF 程序栈空间非常有限, Maps 提供了持久化存储和通信能力. 它允许 eBPF 程序:

  • 在多次事件触发之间保持状态.
  • 实现内核态与用户态之间的数据交换.
  • 聚合监控指标 (如直方图, 计数器).

为什么 eBPF 如此强大?

  • 无侵入性: 无需重启系统或重新编译内核即可观测和修改内核行为.
  • 安全性: 沙盒机制保证了即使程序出错也不会导致内核崩溃 (Kernel Panic).
  • 高性能: JIT 编译确保了在处理极高吞吐量数据时的最小开销.

典型应用场景

1. 基石网络 (Networking & Cilium)

高性能数据面处理. Cilium 利用 eBPF 为 Kubernetes 提供网络, 安全和可观测性. 此外, XDP 用于 DDoS 防御, 可以在数据包进入网络协议栈之前就将其丢弃.

2. 系统观测与性能调优

eBPF 正在取代传统的监控工具. 使用 bpftrace, 开发者可以用简单的脚本实时观测磁盘延迟, 文件打开频率或网络连接状态, 且开销极低.

3. 安全防护与运行时监控

Tetragon 和基于 LSM 钩子的 eBPF 程序可以根据进程行为在内核层强制执行安全策略, 提供深度的运行时安全防护.


进阶概念

1. eBPF 程序的生命周期 (Workflow)

从开发到运行的典型流程:

  1. 编写: 通常使用 C (限制子集) 或 Rust (使用 aya 等框架) 编写源码.
  2. 编译: 使用 LLVM/Clang 将源码编译为 eBPF 字节码.
  3. 加载: 用户态程序通过 bpf() 系统调用将字节码载入内核.
  4. 校验与编译: 内核验证器审计安全性, JIT 编译器生成原生机器码.
  5. 挂载: 将程序绑定到指定的 Hook 点开始工作.

2. BTF 与 CO-RE (编译一次, 到处运行)

在早期, eBPF 需要在目标机器上编译才能适配不同的内核结构偏移.

  • BTF (BPF Type Format): 记录了内核数据结构的元数据.
  • CO-RE (Compile Once – Run Everywhere): 通过 BTF 和 libbpf 的配合, eBPF 程序可以在加载时自动重定位结构值偏移, 实现跨内核版本运行.

eBPF vs. 内核模块 (LKM)

特性eBPF 程序内核模块 (LKM)
安全性极高 (验证器审计, 沙盒运行)较低 (由于 Bug 可能导致 Kernel Panic)
复杂度较低 (通过辅助函数操作)很高 (涉及底层 API)
更新能力动态加载/卸载, 无需重启复杂 (需要卸载依赖, 可能需重启)
访问权限受限 (仅能通过 Helper 调用内核)无限制 (可访问所有内核符号)
生命周期随 Hook 事件触发持续常驻直到卸载

开发约束与限制

在使用 eBPF 时需要注意:

  • 栈空间限制: 通常只有 512 字节 (大数据请使用 Map).
  • 指令数上限: 现代内核虽有提升, 但仍有最大指令条数限制.
  • 控制流限制: 虽然新内核支持有界循环, 但复杂的逻辑分叉仍受限.
  • 执行权限: 大部分操作需要 CAP_SYS_ADMINCAP_BPF 权限.

常用工具链推荐

  • BCC (BPF Compiler Collection): 包含大量现成的性能分析脚本.
  • bpftrace: 基于 DSL 的动态追踪利器, 适合单行脚本.
  • Cilium / Tetragon: 云原生场景下的网络与安全标杆.
  • libbpf-bootstrap: 开发独立 eBPF 应用的最佳实践起点.

eBPF vs. Cgroup v2: 区别与协同

很多开发者会将这两者混淆, 因为它们都用于容​​器化和系统管理, 但其本质和侧重点完全不同.

核心区别 (Key Differences)

维度Cgroup v2eBPF
本质资源配额/管理 (Resource Management)可编程引擎 (Programmability Engine)
功能侧重限制 CPU/内存/IO, 进程层级组织.网络过滤, 系统监控, 安全审计, 行为修改.
操作方式通过修改 /sys/fs/cgroup 下的文件进行配置.编写代码, 编译为字节码后加载至内核钩子.
执行逻辑被动规则 (声明式配置).主动代码 (命令式逻辑执行).
可见性宏观统计 (Per-group stats).微观追踪 (Per-event/instruction tracing).

协同作战 (Synergy)

在现代 Linux 内核中, eBPF 和 Cgroup v2 并不是孤立的, 而是 "基础设施" (Cgroup) 与 "智能大脑" (eBPF) 的关系:

  1. 基于 Cgroup 的网络过滤 (eBPF on Cgroups):

    • eBPF 程序可以挂载到特定的 Cgroup 上 (BPF_PROG_TYPE_CGROUP_SKB).
    • 这意味着你可以针对某个特定的 Pod 或 Container 编写自定义的网络防火墙规则, 而不影响宿主机或其他容器.
  2. 资源压力监控 (PSI + eBPF):

    • Cgroup v2 提供了 PSI (Pressure Stall Information), 而 eBPF 能够实时分析这些指标并触发告警或自动扩缩容.
  3. 安全加固 (LSM + Cgroups):

    • 利用 eBPF LSM 钩子, 你可以结合 Cgroup 身份标识来限制特定容器的危险系统调用, 实现极细颗粒度的安全隔离.

总结:

  • Cgroup v2 负责 "把人关进房间并分配水电" (资源配额与组织).
  • eBPF 负责 "给房间装上智能监控和自定义安保" (逻辑注入与深度观测).

火焰图 (Flame Graph)

火焰图是由性能大师 Brendan Gregg 发明的,用于可视化 CPU 耗时分布。

  • X 轴:代表抽样到的函数在 CPU 上运行的总时长。越宽,代表该函数运行时间越长。
  • Y 轴:代表函数调用栈。最顶端是当前正在运行的函数,下面是它的父调用。
  • 颜色:通常是随机的(红色调),没有特殊含义,只是为了区分不同的函数。

On this page