2. eBPF基础回顾:eBPF程序类型、eBPF Map、eBPF Helper函数在网络观测中的角色
在深入租户隔离与灰度批次拓扑留痕之前,必须重新夯实 eBPF 的三个核心支柱:程序类型(Program Type)、Map 与 Helper 函数。这三者决定了 eBPF 能观测什么、如何存储观测数据、以及如何安全地与内核交互。本节将聚焦它们在网络观测场景下的具体角色。
2.1 eBPF 程序类型:网络观测的挂载点
eBPF 程序类型决定了程序挂载的内核事件源。在网络观测中,最常用的类型如下:
| 程序类型 | 挂载点 | 网络观测角色 |
|---|---|---|
| XDP | 网卡驱动层(最早) | 高速包过滤、DDoS 防护、首包解析 |
| TC(ingress/egress) | 内核协议栈流量控制层 | 流量整形、隧道封装/解封装、包标记 |
| kprobe/kretprobe | 内核函数入口/返回 | 跟踪 TCP 状态机、socket 操作、内存分配 |
| tracepoint | 内核静态跟踪点 | 捕获 net:netif_receive_skb、tcp:tcp_retransmit_skb 等稳定事件 |
| socket filter | 单个 socket 上 | 应用层协议解析、socket 级别数据过滤 |
| cgroup/skb | cgroup 级别 | 容器/租户粒度网络策略与观测 |
选型建议: 在租户隔离场景中,TC 与 cgroup/skb 组合最为常见。TC 负责在主机侧标记租户流量,cgroup/skb 在容器侧执行策略与统计,两者通过 eBPF Map 共享上下文。
2.2 eBPF Map:观测数据的共享与持久化
Map 是 eBPF 程序与用户态之间、以及不同 eBPF 程序之间的数据交换通道。在网络观测中,Map 承担着连接跟踪表、统计计数器、租户标识映射等关键角色。
2.2.1 常用 Map 类型
| Map 类型 | 数据结构 | 网络观测典型用途 |
|---|---|---|
| BPF_MAP_TYPE_HASH | 哈希表(key-value) | 连接跟踪(5元组 → 元数据)、租户 ID 映射 |
| BPF_MAP_TYPE_ARRAY | 固定大小数组 | CPU 核心统计、每租户计数器 |
| BPF_MAP_TYPE_PERCPU_HASH | 每 CPU 哈希 | 高性能包计数,避免锁竞争 |
| BPF_MAP_TYPE_LRU_HASH | LRU 淘汰哈希 | 有限大小的连接跟踪表,自动淘汰旧连接 |
| BPF_MAP_TYPE_RINGBUF | 环形缓冲区 | 将观测事件高效推送到用户态 |
2.2.2 网络观测中的 Map 协作模式
- TC 程序 在包处理时,从
skb->cgroup_id或自定义标记中提取租户 ID,查询 HASH Map 获取租户策略。 - kprobe/tracepoint 程序在 TCP 状态变更时,更新 LRU_HASH Map 中的连接状态。
- 用户态观测器 定期从 PERCPU_ARRAY 中汇总每租户的流量统计,并通过 RINGBUF 接收实时事件。
关键设计: 在灰度批次场景中,BPF_MAP_TYPE_HASH 用于存储“租户 → 灰度批次版本”的映射。TC 程序在包入口处查询此 Map,决定是否对包进行特殊标记或采样,从而实现灰度流量的精准识别。
2.3 eBPF Helper 函数:安全的内核交互接口
Helper 函数是 eBPF 程序与内核交互的唯一合法途径。它们封装了内核功能,同时受 eBPF 验证器的严格检查。在网络观测中,以下 Helper 函数最为关键:
| Helper 函数 | 功能 | 网络观测角色 |
|---|---|---|
bpf_skb_load_bytes |
从 skb 中安全读取数据 | 解析包头部(IP、TCP、UDP) |
bpf_skb_store_bytes |
向 skb 中写入数据 | 修改包头部(如添加 VLAN 标记) |
bpf_map_lookup_elem |
查询 Map 中的元素 | 获取租户策略、连接状态 |
bpf_map_update_elem |
更新 Map 中的元素 | 记录新连接、更新计数器 |
bpf_get_current_cgroup_id |
获取当前 cgroup ID | 识别包所属的容器/租户 |
bpf_ktime_get_ns |
获取高精度时间戳 | 记录包到达时间、延迟计算 |
bpf_perf_event_output |
将数据发送到 perf 事件或 ringbuf | 将观测事件推送到用户态 |
验证器约束: Helper 函数的可用性取决于 eBPF 程序类型。例如,
bpf_skb_store_bytes 只能在 TC 和 XDP 程序中使用,而 bpf_get_current_cgroup_id 在 cgroup/skb 程序中才能正确获取容器 ID。编写程序前务必查阅 bpf_helpers.h 中的类型限制。
2.4 三者协同:一个网络观测的典型数据流
以“识别并统计某租户的灰度流量”为例,展示程序类型、Map、Helper 如何协同:
- TC ingress 程序 通过
bpf_skb_load_bytes解析 IP 包,提取源 IP 和目的端口。 - 通过
bpf_get_current_cgroup_id获取包所属的 cgroup ID,映射为租户 ID。 - 调用
bpf_map_lookup_elem查询 HASH Map,判断该租户是否属于灰度批次。 - 如果是灰度流量,调用
bpf_skb_store_bytes在包头部写入灰度标记(如 DSCP 值)。 - 调用
bpf_map_update_elem更新 PERCPU_ARRAY 中的灰度流量计数器。 - 调用
bpf_perf_event_output将包摘要信息推送到 RINGBUF,供用户态拓扑留痕模块消费。
核心结论: 程序类型决定“在哪里观测”,Map 决定“观测数据如何存储与共享”,Helper 函数决定“观测程序能安全地做什么”。三者缺一不可,共同构成了 eBPF 网络观测的底层能力基座。