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 共享上下文。
eBPF 程序类型在网络观测中的挂载层级 应用层 内核协议栈 流量控制层(TC) 网卡驱动层 socket filter kprobe/tracepoint cgroup/skb TC ingress/egress XDP 观测粒度对比 XDP/TC:包级别,低开销 kprobe:函数级,灵活但慢 cgroup/skb:容器级,策略友好 socket filter:应用级,最慢

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 协作模式

  1. TC 程序 在包处理时,从 skb->cgroup_id 或自定义标记中提取租户 ID,查询 HASH Map 获取租户策略。
  2. kprobe/tracepoint 程序在 TCP 状态变更时,更新 LRU_HASH Map 中的连接状态。
  3. 用户态观测器 定期从 PERCPU_ARRAY 中汇总每租户的流量统计,并通过 RINGBUF 接收实时事件。
关键设计: 在灰度批次场景中,BPF_MAP_TYPE_HASH 用于存储“租户 → 灰度批次版本”的映射。TC 程序在包入口处查询此 Map,决定是否对包进行特殊标记或采样,从而实现灰度流量的精准识别。
eBPF Map 在网络观测中的协作流程 TC ingress 解析包 → 提取租户ID HASH Map 租户ID → 灰度版本 TC egress 标记/采样/转发 kprobe/tracepoint TCP 状态跟踪 LRU_HASH Map 连接跟踪表 RINGBUF 事件推送 → 用户态 用户态观测器(User-space Observer) 轮询 PERCPU_ARRAY 汇总统计 | 从 RINGBUF 消费事件 | 更新 HASH 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 如何协同:

  1. TC ingress 程序 通过 bpf_skb_load_bytes 解析 IP 包,提取源 IP 和目的端口。
  2. 通过 bpf_get_current_cgroup_id 获取包所属的 cgroup ID,映射为租户 ID。
  3. 调用 bpf_map_lookup_elem 查询 HASH Map,判断该租户是否属于灰度批次。
  4. 如果是灰度流量,调用 bpf_skb_store_bytes 在包头部写入灰度标记(如 DSCP 值)。
  5. 调用 bpf_map_update_elem 更新 PERCPU_ARRAY 中的灰度流量计数器。
  6. 调用 bpf_perf_event_output 将包摘要信息推送到 RINGBUF,供用户态拓扑留痕模块消费。
核心结论: 程序类型决定“在哪里观测”,Map 决定“观测数据如何存储与共享”,Helper 函数决定“观测程序能安全地做什么”。三者缺一不可,共同构成了 eBPF 网络观测的底层能力基座。