4. Cilium 数据面核心:eBPF Map 与内存消耗

Cilium 数据面的核心运转依赖于内核中的 eBPF Map 结构。这些 Map 是 eBPF 程序与用户空间、以及 eBPF 程序之间共享数据和状态的关键桥梁。在生产环境中,Map 的大小直接决定了系统的内存开销、并发处理能力以及故障恢复速度。本章深入剖析三种核心 Map:Policy Map、CT Map 和 Endpoint Map,并量化其大小与内存消耗的关系。

4.1 三大核心 eBPF Map 概述

在 Cilium 的数据路径中,每个网络包的处理都会经过多个 eBPF 程序的串联。这些程序通过特定的 Map 来查找策略、跟踪连接状态和定位端点信息。下表总结了这三种 Map 的核心差异:

Map 名称 Key 类型 Value 类型 主要用途 默认最大条目
Policy Map Identity (uint32) Policy Entry (包含 L4/L7 策略) 基于安全标识的访问控制决策 16384
CT Map Tuple (5元组) CT Entry (连接跟踪状态) 跟踪 TCP/UDP/ICMP 连接状态 262144 (每CPU)
Endpoint Map IP 地址 (uint32) Endpoint Info (MAC, VNI, 标识等) 将目标 IP 解析为本机端点信息 65535
关键认知: Policy Map 是“查表决策”,CT Map 是“状态跟踪”,Endpoint Map 是“地址解析”。三者共同构成了 Cilium 数据面的状态机。

4.2 Policy Map:安全策略的查找表

Policy Map 是一个 BPF_MAP_TYPE_HASH,用于存储从 安全标识 (Security Identity) 到 策略条目 (Policy Entry) 的映射。当 Cilium 收到一个数据包时,它会提取源端和目标端的 Security Identity,然后查询 Policy Map 来决定是否允许通信。

  • Key 结构: 由源 Identity 和目标 Identity 组合而成,通常是一个 64 位整数。
  • Value 结构: 包含允许的 L4 端口列表、L7 协议规则以及是否允许转发等标志位。
  • 内存消耗公式: 总内存 ≈ (Key_Size + Value_Size + 哈希表开销) × 条目数。每个条目的哈希表开销约为 16-32 字节。
// 伪代码:Policy Map 条目结构
struct policy_key {
    __u32   src_identity;
    __u32   dst_identity;
};

struct policy_entry {
    __u64   packets;
    __u64   bytes;
    __u8    deny:1;
    __u8    l4_ports[16]; // 简化表示
    // ... 其他策略字段
};
资源画像要点: 在大型集群中,如果启用了“允许所有”的宽松策略,Policy Map 条目数会急剧膨胀。建议监控 cilium bpf policy list 的输出,确保条目数不超过默认最大值的 70%,以避免哈希冲突导致的性能下降。

4.3 CT Map:连接跟踪的状态引擎

CT Map 是 Cilium 数据面中 内存消耗最大 的 Map。它是一个 BPF_MAP_TYPE_LRU_HASH(最近最少使用哈希表),用于跟踪所有经过节点的网络连接状态。每个连接在 CT Map 中占用一个条目,直到连接超时或被主动清理。

  • Key 结构: 网络 5 元组(源 IP、目标 IP、源端口、目标端口、协议)。
  • Value 结构: 包含连接状态(ESTABLISHED, TIME_WAIT 等)、序列号、标志位、以及反向路径的指针。
  • 内存消耗公式: 总内存 ≈ (Key_Size + Value_Size + LRU 开销) × 条目数 × CPU 核心数。CT Map 是 per-CPU 的,因此内存消耗会随 CPU 核心数线性增长。
连接类型 典型条目大小 超时时间 内存影响
TCP ESTABLISHED ~168 字节 6 小时 (默认) 高,长连接会长期占用内存
TCP TIME_WAIT ~168 字节 60 秒 中等,短时间大量连接会冲击内存
UDP ~88 字节 30 秒 低,但高并发场景仍需关注
ICMP ~72 字节 30 秒 低
避坑指南: 默认的 CT Map 大小(262144 条目/CPU)在 32 核节点上意味着最多可容纳 800 万条连接。如果节点上运行了高并发的微服务(如 API Gateway),很容易触发 LRU 淘汰,导致连接被意外中断。建议根据实际并发连接数调整 bpf-ct-global-max 参数。

4.4 Endpoint Map:本地端点的地址簿

Endpoint Map 是一个 BPF_MAP_TYPE_HASH,用于将 IP 地址 映射到 本地端点信息。当数据包的目标 IP 是本机管理的 Pod 或主机时,Cilium 通过查询 Endpoint Map 来获取 MAC 地址、VXLAN VNI、Security Identity 等信息,从而完成最终的转发。

  • Key 结构: 目标 IP 地址(32 位 IPv4 或 128 位 IPv6)。
  • Value 结构: 包含端点标识、MAC 地址、LXC ID、以及策略相关的元数据。
  • 内存消耗公式: 总内存 ≈ (Key_Size + Value_Size + 哈希表开销) × 本地 Pod 数量。该 Map 的大小与节点上运行的 Pod 数量成正比。
// 伪代码:Endpoint Map 条目结构
struct endpoint_key {
    __u32   ip;          // IPv4 地址
    // 对于 IPv6,使用 __u8 ip[16]
};

struct endpoint_info {
    __u32   sec_id;      // 安全标识
    __u8    mac[6];      // MAC 地址
    __u32   vni;         // VXLAN 网络标识
    __u32   ifindex;     // 宿主机网卡索引
    // ... 其他元数据
};
资源画像要点: Endpoint Map 的内存消耗相对稳定,与节点上的 Pod 密度直接相关。在 100 Pod/节点的典型场景下,内存消耗通常在 1-2 MB 以内。但在大规模集群中,如果频繁创建和销毁 Pod,需要关注 Map 的更新频率对 CPU 的影响。

4.5 Map 大小与内存消耗的量化关系

为了帮助您进行精确的资源规划,以下是一个基于默认配置的量化示例(假设节点为 32 核,运行 100 个 Pod,并发连接数 50 万):

Map 大小与内存消耗量化关系图 Policy Map 类型: BPF_MAP_TYPE_HASH 条目数: 16,384 (默认) 单条目: ~64 字节 总内存: ~1.5 MB CT Map (per-CPU) 类型: BPF_MAP_TYPE_LRU_HASH 条目数: 262,144 × 32 CPU 单条目: ~168 字节 总内存: ~1.4 GB Endpoint Map 类型: BPF_MAP_TYPE_HASH 条目数: 100 (本地 Pod) 单条目: ~96 字节 总内存: ~0.01 MB 内存消耗关键结论 1. CT Map 是内存消耗的主力,占总 Map 内存的 99% 以上。 2. CT Map 内存与 CPU 核心数线性相关,多核节点需重点规划。 3. Policy Map 在策略数量多时(如 500+ 条策略)会显著增长。 4. Endpoint Map 内存最小,但频繁的 Pod 创建/删除会导致 Map 抖动。 5. 建议预留节点总内存的 5%-10% 用于 eBPF Map,避免 OOM。

4.6 内存优化与监控实践

针对上述 Map 的内存消耗特性,以下是具体的优化和监控建议:

  1. 调整 CT Map 超时时间: 对于 TCP 长连接,将 bpf-ct-tcp-max 从默认的 6 小时缩短到 1 小时,可以显著降低内存峰值。
  2. 启用 CT Map GC 加速: 设置 bpf-ct-gc-interval=30s,加快过期连接的回收速度。
  3. 监控 Map 使用率: 使用 cilium bpf ct list 和 cilium bpf policy list 定期检查条目数,并设置告警阈值(如 80%)。
  4. 使用 per-CPU 的 Map 变体: 对于 CT Map,Cilium 已经默认使用 per-CPU 的 LRU 哈希表,这减少了锁竞争,但增加了内存消耗。在内存敏感的场景下,可以评估是否使用全局 LRU 哈希表。
最佳实践: 在 Kubernetes 集群中,建议将 --bpf-ct-global-max 设置为 节点最大 Pod 数 × 每个 Pod 预期并发连接数 × 1.5。例如,100 Pod × 5000 连接/Pod × 1.5 = 750,000 条目/CPU。