4. 隔离架构总览:eBPF租户隔离蓝图的三层模型
在构建多租户 eBPF 基础设施时,最核心的挑战在于:如何让多个租户共享同一套内核 eBPF 运行时,却彼此不可见、不可干扰、不可耗尽资源? 传统基于命名空间或 cgroup 的隔离方案在 eBPF 场景下存在明显短板——eBPF 程序可以访问全局内核数据结构,且其生命周期管理、资源配额、安全校验均缺乏租户维度的抽象。
为此,我们提出 eBPF 租户隔离蓝图的三层模型,将隔离能力拆解为 控制层(Control Plane)、数据层(Data Plane) 与 编排层(Orchestration Plane)。每一层承担不同的职责,通过清晰的接口契约协同工作,最终实现“租户感知”的 eBPF 资源管理。
4.1 为什么需要三层?—— 单一平面模型的缺陷
在早期的 eBPF 多租户尝试中,许多团队试图将所有隔离逻辑塞进内核态(数据层),或完全依赖用户态管理工具(控制层)。这两种极端方案均暴露了严重问题:
| 方案 | 典型做法 | 核心缺陷 |
|---|---|---|
| 纯内核态隔离 | 在 BPF Verifier 中硬编码租户规则,所有逻辑在数据层完成 | 内核模块膨胀、升级困难、无法灵活调整配额、缺乏全局视图 |
| 纯用户态管理 | 用户态工具负责校验和注入,内核只做执行 | 无法感知内核运行时状态、资源竞争不可控、安全边界模糊 |
| 两层模型(控制+数据) | 控制层校验,数据层执行,但缺少调度与治理 | 多租户策略冲突无法自动检测、资源配额无法动态调整、审计缺失 |
4.2 控制层(Control Plane):安全准入与资源契约
控制层是租户隔离的第一道闸门。它运行在用户态特权进程中,负责在 eBPF 程序加载前完成所有安全校验与资源绑定。控制层不直接参与内核数据面的执行,而是通过 租户上下文(Tenant Context) 将隔离策略“注入”到数据层。
控制层的核心组件包括:
- BPF Verifier 扩展:在标准校验器基础上增加租户规则检查,例如禁止访问其他租户的 Map、限制 helper 函数调用范围。
- 能力降权模块:根据租户等级(如免费/付费/内部)动态降低 eBPF 程序的能力集,例如限制 kprobe 挂载点、限制 bpf_trace_printk 输出。
- 资源限额注入器:将租户的 CPU 预算、内存上限、Map 容量等配额编码为 BPF 程序可读的只读数据结构,随程序一起加载。
- 租户上下文工厂:为每个租户生成唯一的
tenant_id和capability_,并嵌入到 BPF 程序的.rodata段中。
// 控制层生成的租户上下文结构体(示例)
struct tenant_context {
__u64 tenant_id; // 全局唯一租户 ID
__u32 capability_flags; // 能力位图(如允许 kprobe、允许读取 /etc/passwd 等)
__u32 max_map_entries; // 该租户所有 Map 的总条目上限
__u32 max_prog_insns; // 单程序指令数上限
__u32 cpu_time_budget_ms; // 每 100ms 窗口的 CPU 时间预算(毫秒)
__u8 reserved[32]; // 预留扩展字段
};
4.3 数据层(Data Plane):内核态隔离执行
数据层是 eBPF 程序实际运行的地方——即内核 BPF 子系统。在三层模型中,数据层不再“裸奔”,而是通过控制层注入的租户上下文实现运行时隔离。数据层的隔离机制包括:
| 隔离维度 | 实现方式 | 说明 |
|---|---|---|
| Map 隔离 | 租户专属 Map 实例 + 访问控制 | 每个租户拥有独立的 BPF Map 树,Map ID 与 tenant_id 绑定;跨租户访问在 Verifier 阶段即被拒绝 |
| Prog 隔离 | 租户 Prog 链表 + 调度配额 | 同一 Hook 点挂载多个租户的 Prog,通过 BPF 调度器按时间片或事件优先级轮转执行 |
| 事件路由 | Perf Event 按租户打标 | 内核事件(如网络包、系统调用)在进入 BPF 程序前被标记 tenant_id,确保数据不串流 |
| 资源上限 | BPF 程序内嵌配额检查 | 控制层注入的配额数据在运行时由 BPF 程序自身校验,超出则丢弃或降级处理 |
数据层的关键设计原则是:所有隔离逻辑必须在内核态完成,不依赖用户态进程的实时干预。这意味着控制层注入的租户上下文必须是“自包含”的——BPF 程序仅凭上下文即可判断自己是否有权执行某个操作。
4.4 编排层(Orchestration Plane):治理与调度中枢
编排层是三层模型中最靠近“人”的一层,面向平台运维团队和租户自助服务。它不直接处理 eBPF 程序加载或内核执行,而是负责租户的完整生命周期管理以及全局策略的协调。
编排层的核心职责:
- 租户注册与身份管理:创建/删除租户、分配 tenant_id、管理 与证书。
- 资源配额调度:根据租户等级和集群负载,动态调整 CPU、内存、Map 容量等配额,并通过控制层下发给数据层。
- 策略冲突检测:当多个租户的 eBPF 程序挂载到同一 Hook 点时,检测是否存在安全冲突(如一个租户试图阻止另一个租户的程序执行)。
- 审计与计量:记录每个租户的 eBPF 程序执行次数、资源消耗、违规事件,用于计费和安全审计。
4.5 三层之间的数据流与契约
三层模型的有效性依赖于层间清晰的数据流契约。以下是典型的数据流路径:
| 步骤 | 源层 | 目标层 | 数据内容 | 协议/接口 |
|---|---|---|---|---|
| 1 | 编排层 | 控制层 | 租户创建请求、配额策略、能力位图 | gRPC / REST API |
| 2 | 控制层 | 数据层 | BPF 程序字节码 + 租户上下文(.rodata) | bpf() syscall + BPF_PROG_LOAD |
| 3 | 数据层 | 控制层 | 程序加载结果、运行时指标、违规事件 | Perf Event / BPF Ring Buffer |
| 4 | 控制层 | 编排层 | 聚合后的租户状态、审计日志、资源使用统计 | gRPC / 消息队列 |
其中,租户上下文是贯穿三层的核心数据结构。它在编排层定义 schema,在控制层填充具体值,在数据层被 BPF 程序读取。任何对租户上下文的修改都必须经过编排层审批,控制层只负责“忠实注入”。
4.6 三层模型的部署拓扑
在实际部署中,三层模型可以映射到不同的进程/容器/节点上:
- 编排层:通常部署为独立的微服务集群,与 Kubernetes 控制面集成,提供 REST API 供租户自助服务。
- 控制层:作为 DaemonSet 运行在每个节点上,负责该节点上所有租户的 eBPF 程序校验与注入。控制层进程需要 CAP_BPF 和 CAP_SYS_ADMIN 权限。
- 数据层:运行在内核中,通过 BPF 调度器和隔离的 Map 实例实现租户隔离。数据层不暴露任何用户态接口,仅通过控制层间接管理。
通过三层模型的清晰分层,eBPF 租户隔离从“尽力而为”走向“可治理、可审计、可动态调整”。接下来的章节将深入每一层的具体实现技术,包括 BPF Verifier 扩展、Map 隔离算法、调度器设计等核心细节。