4. 隔离架构总览:eBPF租户隔离蓝图的三层模型

在构建多租户 eBPF 基础设施时,最核心的挑战在于:如何让多个租户共享同一套内核 eBPF 运行时,却彼此不可见、不可干扰、不可耗尽资源? 传统基于命名空间或 cgroup 的隔离方案在 eBPF 场景下存在明显短板——eBPF 程序可以访问全局内核数据结构,且其生命周期管理、资源配额、安全校验均缺乏租户维度的抽象。

为此,我们提出 eBPF 租户隔离蓝图的三层模型,将隔离能力拆解为 控制层(Control Plane)、数据层(Data Plane) 与 编排层(Orchestration Plane)。每一层承担不同的职责,通过清晰的接口契约协同工作,最终实现“租户感知”的 eBPF 资源管理。

核心设计原则: 控制层负责“能不能做”,数据层负责“怎么做”,编排层负责“如何调度与治理”。三层之间通过标准化的租户上下文(Tenant Context)传递身份与配额信息,避免跨层耦合。
eBPF 租户隔离三层模型架构图 eBPF 租户隔离蓝图:三层模型架构 编排层(Orchestration Plane) 租户生命周期管理 · 资源配额调度 · 策略冲突检测 · 审计与计量 API 网关 | 租户注册中心 | 配额引擎 | 策略仓库 面向运维/平台团队:定义租户、分配资源、下发策略 控制层(Control Plane) eBPF 程序校验 · 安全能力降权 · 资源限制注入 · 租户标签绑定 BPF Verifier 扩展 | 能力降权模块 | 资源限额注入器 | 租户上下文工厂 面向安全/内核团队:确保程序安全、资源可控、身份可追溯 数据层(Data Plane) 内核态 eBPF 运行时 · 隔离的 Map 与 Prog 实例 · 租户专属 Hook 点 BPF Map 隔离区 | 租户 Prog 链表 | 内核事件路由 | 数据面代理 面向内核运行时:实际执行 eBPF 程序、存储隔离数据、处理事件 策略下发 程序注入 事件/指标上报 状态/审计 租户上下文贯穿三层,实现端到端隔离

4.1 为什么需要三层?—— 单一平面模型的缺陷

在早期的 eBPF 多租户尝试中,许多团队试图将所有隔离逻辑塞进内核态(数据层),或完全依赖用户态管理工具(控制层)。这两种极端方案均暴露了严重问题:

方案 典型做法 核心缺陷
纯内核态隔离 在 BPF Verifier 中硬编码租户规则,所有逻辑在数据层完成 内核模块膨胀、升级困难、无法灵活调整配额、缺乏全局视图
纯用户态管理 用户态工具负责校验和注入,内核只做执行 无法感知内核运行时状态、资源竞争不可控、安全边界模糊
两层模型(控制+数据) 控制层校验,数据层执行,但缺少调度与治理 多租户策略冲突无法自动检测、资源配额无法动态调整、审计缺失
关键洞察: eBPF 租户隔离不仅仅是“能不能运行”的问题,更是“如何公平运行、如何审计、如何动态调整”的治理问题。三层模型将治理职责从执行层剥离,使每一层都能聚焦于自己的核心关注点。

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];       // 预留扩展字段
};
注意: 控制层必须运行在独立的、经过强化的用户态进程中,与编排层通过 gRPC 或 Unix Domain Socket 通信。切勿将控制层逻辑直接嵌入编排层,以避免编排层故障导致整个隔离体系失效。

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 程序仅凭上下文即可判断自己是否有权执行某个操作。

数据层隔离执行细节图 数据层隔离执行:租户上下文驱动 内核事件源 syscall / net / tracepoint 事件路由 & 打标 注入 tenant_id 标签 租户 A BPF Prog tenant_id = 0xA001 租户 A 专属 Map Map ID = 1001 ~ 1100 租户 B BPF Prog tenant_id = 0xB002 租户 B 专属 Map Map ID = 2001 ~ 2100 BPF 调度器(按租户时间片/优先级) 确保租户 A 与租户 B 公平共享 CPU 与 Map 带宽 租户上下文在事件路由时注入,BPF 程序通过 bpf_get_current_tenant_id() 读取

4.4 编排层(Orchestration Plane):治理与调度中枢

编排层是三层模型中最靠近“人”的一层,面向平台运维团队和租户自助服务。它不直接处理 eBPF 程序加载或内核执行,而是负责租户的完整生命周期管理以及全局策略的协调。

编排层的核心职责:

  • 租户注册与身份管理:创建/删除租户、分配 tenant_id、管理 与证书。
  • 资源配额调度:根据租户等级和集群负载,动态调整 CPU、内存、Map 容量等配额,并通过控制层下发给数据层。
  • 策略冲突检测:当多个租户的 eBPF 程序挂载到同一 Hook 点时,检测是否存在安全冲突(如一个租户试图阻止另一个租户的程序执行)。
  • 审计与计量:记录每个租户的 eBPF 程序执行次数、资源消耗、违规事件,用于计费和安全审计。
编排层与控制层的边界: 编排层只负责“决策”(如:给租户 A 分配 10ms CPU 时间片),不负责“执行”(如:如何将配额注入 BPF 程序)。执行由控制层完成。这种分离使得编排层可以独立升级,不影响内核稳定性。

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 实例实现租户隔离。数据层不暴露任何用户态接口,仅通过控制层间接管理。
推荐实践: 控制层 DaemonSet 应使用独立的 Linux 命名空间(PID、网络、挂载)运行,避免与租户容器共享任何内核资源。编排层与控制层之间的通信必须使用 mTLS 加密,防止租户身份被伪造。

通过三层模型的清晰分层,eBPF 租户隔离从“尽力而为”走向“可治理、可审计、可动态调整”。接下来的章节将深入每一层的具体实现技术,包括 BPF Verifier 扩展、Map 隔离算法、调度器设计等核心细节。