4. 控制平面资源模型概述:资源模型的定义、分类与 eBPF 映射

在 eBPF 租户隔离架构中,控制平面资源模型是连接租户需求与内核执行引擎的桥梁。它定义了“哪些资源可以被隔离、如何描述这些资源、以及如何将资源描述转化为内核可执行的 BPF 程序与映射”。理解资源模型是设计多租户隔离策略的第一步。

4.1 资源模型的定义

资源模型是对系统可隔离资源的一种抽象化、结构化描述。在 eBPF 语境下,资源模型包含三个核心要素:

  • 资源标识:唯一确定一个资源实例(如 cgroup ID、网络命名空间、PID、设备号)。
  • 资源属性:描述资源的容量、配额、权限(如 CPU 时间片、内存上限、允许的系统调用列表)。
  • 资源生命周期:资源的创建、绑定、迁移、销毁规则。
关键认知:资源模型不是静态的配置文件,而是控制平面与数据平面之间的契约。控制平面通过资源模型生成 BPF 程序与映射,数据平面则依据这些程序执行隔离策略。

4.2 资源模型的分类:静态 vs 动态

根据资源描述与绑定时机,资源模型可分为两大类:

维度 静态资源模型 动态资源模型
定义时机 系统启动或租户创建时一次性定义 运行时根据负载、策略、事件实时调整
绑定方式 资源与租户强绑定,生命周期固定 资源与租户松耦合,支持热迁移与弹性伸缩
典型场景 系统调用白名单、固定 cgroup 路径、静态网络带宽预留 动态 CPU 配额、基于负载的 I/O 限流、自适应内存水位线
eBPF 开销 低(程序加载后几乎不变) 中高(需频繁更新 BPF 映射或替换 BPF 程序)
隔离粒度 粗粒度(容器级、Pod 级) 细粒度(进程级、请求级、甚至函数级)
典型代表 Seccomp 策略、固定 cgroup v2 层级 基于 BPF 的 CPU 调度器(CFS 带宽控制)、XDP 动态限速
选型陷阱:静态模型虽然性能好,但无法应对突发流量或恶意租户的资源抢占。动态模型灵活,但频繁更新 BPF 映射可能导致控制平面成为瓶颈。实际生产环境通常采用静态基线 + 动态补偿的混合策略。

4.3 资源模型在 eBPF 中的映射方式

资源模型最终需要映射到 eBPF 的三大核心组件:BPF 程序类型、BPF 映射(Map)、辅助函数(Helper)。映射方式决定了隔离策略的执行效率与可维护性。

4.3.1 映射到 BPF 程序类型

不同的资源模型对应不同的 BPF 挂载点:

  • 系统调用资源:映射到 BPF_PROG_TYPE_TRACING(如 sys_enter/sys_exit)或 BPF_PROG_TYPE_KPROBE。
  • 网络资源:映射到 BPF_PROG_TYPE_XDP、BPF_PROG_TYPE_SCHED_CLS、BPF_PROG_TYPE_CGROUP_SKB。
  • 进程/容器资源:映射到 BPF_PROG_TYPE_CGROUP_SYSCTL、BPF_PROG_TYPE_CGROUP_SOCKOPT。
  • 文件系统资源:映射到 BPF_PROG_TYPE_LSM(Linux Security Module)。

4.3.2 映射到 BPF 映射(Map)

资源模型的属性与状态通常存储在 BPF 映射中,供数据平面查询:

资源模型类型 推荐 BPF 映射类型 用途
静态白名单 BPF_MAP_TYPE_HASH 或 BPF_MAP_TYPE_ARRAY 存储允许的系统调用号、允许的设备号列表
动态配额 BPF_MAP_TYPE_PERCPU_HASH 记录每个租户的实时 CPU/内存/IO 使用量
层级结构 BPF_MAP_TYPE_CGROUP_ARRAY 关联 cgroup 路径与租户 ID
时间窗口限流 BPF_MAP_TYPE_LRU_HASH 实现基于时间戳的令牌桶或漏桶算法

4.3.3 映射到辅助函数

eBPF 辅助函数是资源模型与内核交互的“API”:

  • bpf_get_current_cgroup_id():获取当前进程的 cgroup ID,用于静态资源模型的身份识别。
  • bpf_skb_cgroup_id():获取网络报文所属的 cgroup,用于网络资源隔离。
  • bpf_map_lookup_elem() / bpf_map_update_elem():动态资源模型的核心操作,用于读取/更新租户配额。
  • bpf_perf_event_output():将资源超限事件上报到用户空间,触发控制平面的动态调整。
映射原则:静态资源模型优先使用 BPF_MAP_TYPE_ARRAY 或 BPF_MAP_TYPE_HASH(只读加载),动态资源模型优先使用 BPF_MAP_TYPE_PERCPU_HASH(减少锁竞争)或 BPF_MAP_TYPE_LRU_HASH(自动淘汰过期条目)。
资源模型 → eBPF 三层映射架构 资源模型(静态 / 动态) 映射层(控制平面翻译) BPF 程序类型 (Kprobe / XDP / LSM) BPF 映射(Map) (Hash / Array / LRU) 辅助函数 (Helper API) 数据平面执行(内核 BPF 运行时) 挂载点触发 → 程序执行 → 映射查询/更新 → 隔离决策 (系统调用拦截 / 网络包处理 / cgroup 资源审计) 说明: • 静态模型:程序类型固定,映射内容只读加载,辅助函数仅用于身份识别。 • 动态模型:映射内容频繁更新,辅助函数用于配额读写与事件上报。

4.4 静态 vs 动态:映射方式对比

以下表格从 eBPF 实现角度对比两种资源模型的映射差异:

对比维度 静态资源模型映射 动态资源模型映射
BPF 程序加载 一次性加载,BPF_F_STATIC_FLAGS 只读 支持 BPF_F_REPLACE 热更新,或使用尾调用动态切换
映射更新频率 仅在租户创建/销毁时更新 秒级甚至毫秒级更新(如 CPU 配额调整)
并发控制 无需额外锁,映射为 BPF_F_RDONLY 需使用 BPF_MAP_TYPE_PERCPU 或 bpf_spin_lock
用户空间交互 通过 bpf(BPF_OBJ_GET) 读取,极少写入 通过 bpf(BPF_MAP_UPDATE_ELEM) 频繁写入,配合 perf_event 回调
典型代码模式
// 静态白名单检查
u32 syscall_nr = bpf_get_current_syscall_nr();
u32 *allowed = bpf_map_lookup_elem(&white_list, &syscall_nr);
if (!allowed) return -EPERM;
// 动态配额更新
u64 cgid = bpf_get_current_cgroup_id();
struct quota *q = bpf_map_lookup_elem("a_map, &cgid);
if (q && q->usage > q->limit) return -EBUSY;
q->usage += delta;
静态 vs 动态资源模型映射流程对比 静态资源模型映射流程 1. 租户创建 → 编译 BPF 程序 2. 加载 BPF 程序 + 只读映射 3. 挂载到固定钩子点 4. 运行时:只读查询,无更新 特点:低开销、高确定性、不可变 动态资源模型映射流程 1. 租户创建 → 编译 BPF 程序 2. 加载 BPF 程序 + 可写映射 3. 挂载到钩子点 + 注册事件回调 4. 运行时:频繁更新映射 + 事件上报 特点:灵活、弹性、控制平面交互频繁

4.5 映射方式选择决策树

在实际项目中,选择静态还是动态映射可参考以下决策逻辑:

  1. 资源是否在租户生命周期内变化?
    • 从不变化 → 静态映射(如系统调用白名单)
    • 可能变化 → 进入下一步
  2. 变化频率如何?
    • 小时/天级 → 静态映射 + 重建 BPF 程序(如容器启动时更新)
    • 秒/毫秒级 → 动态映射(如 CPU 带宽实时调整)
  3. 性能敏感度?
    • 极高(PPS > 10M)→ 优先静态映射,使用 BPF_MAP_TYPE_ARRAY 避免哈希计算
    • 中等 → 动态映射,使用 BPF_MAP_TYPE_PERCPU_HASH
最佳实践:在 Cilium 等生产级项目中,网络策略(NetworkPolicy)使用静态映射(加载时编译为 BPF 规则),而带宽管理(Bandwidth Manager)使用动态映射(基于 EDT 算法的速率限制)。这种“静态策略 + 动态调控”的组合是 eBPF 租户隔离的主流模式。