2. eBPF 基础:技术原理、程序类型与内核观测性

在深入 Hubble 与 Cilium 之前,必须先理解 eBPF 这一底层核心技术。eBPF(extended Berkeley Packet Filter)是一种允许用户在 Linux 内核中安全运行沙箱化程序的技术,无需修改内核源码或加载内核模块。它从根本上改变了内核的可编程性与观测能力。

2.1 eBPF 技术原理

eBPF 的核心机制围绕“事件驱动”与“安全验证”展开。用户编写的 eBPF 程序通过 bpftrace、libbpf 或 Cilium 的 Go 库加载到内核,经过严格的验证器检查后,由 JIT(Just-In-Time)编译器转换为原生机器码,挂载到指定的内核钩子(kprobe、tracepoint、XDP 等)上执行。

关键流程: 编写 eBPF C 代码 → LLVM/Clang 编译为 BPF 字节码 → 内核验证器(安全性检查) → JIT 编译 → 挂载到钩子 → 事件触发执行。

eBPF 程序通过 Map 与用户态交互。Map 是内核中的键值存储结构,eBPF 程序将观测数据写入 Map,用户态程序通过 bpf() 系统调用读取。这种设计保证了数据路径的低延迟与安全性。

组件 作用 说明
验证器(Verifier) 安全审查 检查循环、指针、栈边界,拒绝危险程序
JIT 编译器 性能优化 将字节码转为 CPU 原生指令,接近内核模块性能
eBPF Map 数据交换 内核态与用户态共享数据,支持哈希、数组、LRU 等类型
辅助函数(Helper) 内核能力调用 eBPF 程序通过专用 API 访问内核数据,如获取 PID、网络包内容
运维提示: 在 Kubernetes 节点上执行 bpftool prog list 可查看当前加载的所有 eBPF 程序;bpftool map dump 可查看 Map 内容,用于调试 Hubble 数据采集是否正常。

2.2 eBPF 程序类型

eBPF 支持多种程序类型,每种类型对应不同的内核事件与挂载点。在 Kubernetes 网络观测场景中,最常用的是 XDP、TC、kprobe 和 tracepoint。

  • XDP(eXpress Data Path):在网卡驱动层处理数据包,性能极高,用于 DDoS 防护、负载均衡。
  • TC(Traffic Control):在内核网络栈的 ingress/egress 钩子处理包,Cilium 使用 TC 实现网络策略与转发。
  • kprobe/kretprobe:动态插桩到任意内核函数入口/返回点,用于追踪系统调用、网络协议栈行为。
  • tracepoint:静态预定义的内核事件点,稳定性高,Hubble 依赖 tracepoint 采集网络连接事件。
  • perf_event:采样 CPU 性能计数器,用于性能分析。
避坑指南: kprobe 依赖内核符号表,内核版本升级可能导致符号名变化,导致 eBPF 程序加载失败。生产环境优先使用 tracepoint 或 BPF CO-RE(一次编译,到处运行)技术。

下表对比了网络观测中最关键的三种程序类型:

类型 挂载点 性能开销 典型用途
XDP 网卡驱动层 极低 包过滤、重定向、DDoS 缓解
TC 网络栈 ingress/egress 低 网络策略、封装、负载均衡
tracepoint 内核静态事件点 低 连接追踪、系统调用监控
eBPF 程序类型与内核挂载点 用户态:Cilium Agent / Hubble CLI / bpftool 系统调用层(bpf() 系统调用) 内核态 XDP 网卡驱动层 TC 网络栈钩子 tracepoint 静态事件点 kprobe 动态插桩 perf_event 性能计数器 Hubble 核心依赖 tracepoint + kprobe

2.3 eBPF 与内核观测性

eBPF 将内核观测性提升到了新高度。传统观测手段(如 iptables 日志、tcpdump 抓包)存在性能开销大、数据不完整、无法关联上下文等问题。eBPF 通过在内核关键路径插入轻量级探针,实现了 全量、低开销、可编程 的观测能力。

在 Kubernetes 网络场景中,eBPF 观测性带来的核心能力包括:

  • 连接级可见性:追踪每个 TCP/UDP 连接的建立、关闭、重传、延迟,无需修改应用代码。
  • 协议解析:在 XDP/TC 层直接解析 HTTP/2、gRPC、DNS 等协议头部,提取服务名、状态码。
  • 关联容器身份:通过 cgroup ID 和 PID 映射,将网络流量精确关联到 Pod、Service、Namespace。
  • 零侵入:无需 Sidecar、无需重启 Pod,对业务容器完全透明。
Hubble 数据来源: Hubble 依赖 Cilium 加载的 eBPF 程序(主要是 tracepoint sock:inet_sock_set_state 和 kprobe tcp_retransmit_skb)来采集网络事件,然后通过 gRPC 流式输出给 Hubble Relay 和 CLI。

以下是一个简单的 eBPF 程序示例,用于追踪 TCP 连接状态变化(仅用于理解结构,实际 Hubble 使用 Cilium 封装好的程序):

// tcp_connect_trace.c (简化示例)
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

SEC("tracepoint/sock/inet_sock_set_state")
int trace_connect(struct trace_event_raw_inet_sock_set_state *ctx) {
    if (ctx->protocol != IPPROTO_TCP)
        return 0;
    // 记录连接四元组到 Map
    struct connect_key key = {
        .saddr = ctx->saddr,
        .daddr = ctx->daddr,
        .sport = ctx->sport,
        .dport = ctx->dport,
    };
    bpf_map_update_elem(&connect_map, &key, &ctx->newstate, BPF_ANY);
    return 0;
}