Cilium eBPF Maps 容量规划与调优指南:默认上限、动态尺寸与 Service LB 映射规模计算
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
eBPF Maps 是 Cilium 数据通路(datapath)的核心存储结构,连接跟踪、NAT、策略、负载均衡等关键能力都依赖这些内核映射,而每个 Map 都带有上限容量。本文以 Documentation/network/ebpf/maps.rst 为主线,系统梳理 Cilium 各类 BPF Map 的默认容量、作用域与扩展性含义,讲解如何通过cilium-agent命令行参数覆盖默认上限、利用--bpf-map-dynamic-size-ratio依据系统内存自动计算容量,并给出 Service Load Balancer Map 的精确容量估算公式与扩容注意事项。读完本文,你将能根据节点规模为 Cilium 数据通路做出合理的容量规划与调优决策。
eBPF Maps 与容量上限的基本概念
Cilium 将所有 BPF Map 创建为带有**上限容量(upper capacity limit)**的映射。当插入条目的数量超过该上限时,插入操作会失败,进而限制数据通路的扩展能力。因此,容量规划是部署 Cilium 前必须考虑的问题:每个限制都可以在源码中调整,cilium-agent也提供了对应的命令行选项(配置选项会按需陆续补充)。
各 Map 的容量上限与扩容影响,体现在数据通路实际处理连接、转发数据包和加载策略的各个环节。下面先看核心 Map 的默认上限总表,再逐一展开容量覆盖与动态计算机制。
BPF Map 默认上限总表
下表汇总了 Cilium 各类 BPF Map 的默认上限值、作用域(Scope)与扩展性含义(数据来源于 maps.rst):
| Map 名称 | 作用域 | 默认上限 | 扩展性含义 |
|---|---|---|---|
| Auth(认证) | node | 512k | 每个节点最多 512k 条已认证关系 |
| Connection Tracking(连接跟踪) | node | 512k TCP / 256k UDP | 每个节点最多 512k 条并发 TCP 连接、256k 条预期的 UDP 应答 |
| NAT | node | 512k | 最多 512k 条 NAT 条目 |
| Neighbor Table(邻居表) | node | 512k | 最多 512k 条邻居条目 |
| Endpoints(端点) | node | 64k | 每个节点最多 64k 个本地端点 + 主机 IP |
| IP cache | node | 512k | 跨所有集群最多 256k 个端点(IPv4+IPv6 混合),或最多 512k 个端点(仅 IPv4 或仅 IPv6) |
| Service Load Balancer(服务负载均衡) | node | 64k | 跨所有集群最多约 3k 个 clusterIP/nodePort Service(详见下文 Service LB Map Sizing 一节) |
| Service Backends(服务后端) | node | 64k | 跨所有集群的所有服务累计最多 64k 个唯一后端 |
| Service Source Ranges | node | 64k | 跨所有服务累计最多 64k 条 LB 源地址范围 |
| Service Session Affinity | node | 64k | 来自不同客户端的最多 64k 条亲和性记录 |
| Policy(策略) | endpoint | 16k | 单个特定端点的最多 16k 个"身份 + 端口 + 协议"允许组合 |
| IPv4 Fragmentation(IPv4 分片) | node | 8k | 节点上同时最多 8k 个在途分片数据报 |
| IPv6 Fragmentation(IPv6 分片) | node | 8k | 节点上同时最多 8k 个在途分片数据报 |
| IPv4 Masq | node | 16k | 供 BPF 版 ip-masq-agent 使用的最多 16k 条 IPv4 CIDR |
| IPv6 Masq | node | 16k | 供 BPF 版 ip-masq-agent 使用的最多 16k 条 IPv6 CIDR |
| Egress Policy(出站策略) | node | 16k | 跨所有集群的所有目标 CIDR 上最多 16k 个端点 |
| Node | node | 16k | 跨所有集群最多 16k 个不同的节点 IP(IPv4 与 IPv6) |
从上表可以归纳出几个规划要点:
- 作用域差异:绝大多数 Map 是节点级(node)共享的,唯独 Policy Map 是**每端点(endpoint)**独立的——它的上限(16k)表示单个端点最多允许的策略组合数,而非整个节点。
- 连接跟踪是扩容瓶颈:CT(Connection Tracking)Map 的 TCP/UDP 分离设计(512k TCP + 256k UDP)直接决定了节点能支撑的最大并发连接数。
- 负载均衡相关 Map 的规模:Service LB、Backends、Source Ranges、Session Affinity 四个 Map 都受服务规模约束,具体估算方法见后文。
从源码确认默认值
这些默认上限在 pkg/option/config.go 中以常量形式定义,例如:
CTMapEntriesGlobalTCPDefault = 2 << 18(即 512Ki 条目,对应文档的 512k TCP);CTMapEntriesGlobalAnyDefault = 2 << 17(即 256Ki 条目,对应 256k UDP);NATMapEntriesGlobalDefault = int((CTMapEntriesGlobalTCPDefault + CTMapEntriesGlobalAnyDefault) * 2 / 3)(即默认按 CT 合计的 2/3 计算);PolicyMapMax = 1 << 16、FragmentsMapMax = 1 << 16等(见 pkg/option/config.go)。
同时,pkg/datapath/maps/maps_generated.go中登记了各 Map 的实际内核名称,如cilium_ct4_global、cilium_ct6_global、cilium_ct_any4_global、cilium_ct_any6_global、cilium_auth_map、cilium_lb4_services_v2、cilium_snat_v4_external、cilium_nodeport_neigh4等(见 maps_generated.go),可用于在节点上通过bpftool map list对照检查实际生效的容量。
使用命令行参数覆盖 Map 容量上限
对于部分 BPF Map,可以通过cilium-agent的命令行选项覆盖默认容量上限。文档明确支持的参数包括:
| 命令行选项 | 作用 |
|---|---|
--bpf-auth-map-max | Auth Map 最大条目数 |
--bpf-ct-global-tcp-max | 全局 TCP 连接跟踪 Map 最大条目数 |
--bpf-ct-global-any-max | 全局非 TCP(UDP 等)连接跟踪 Map 最大条目数 |
--bpf-nat-global-max | NAT Map 最大条目数 |
--bpf-neigh-global-max | 邻居表 Map 最大条目数 |
--bpf-policy-map-max | 每端点策略 Map 最大条目数 |
--bpf-fragments-map-max | 分片 Map 最大条目数 |
--bpf-lb-map-max | Service Load Balancer Map 最大条目数 |
这些选项在源码中有完整登记:CT/NAT/Neigh 选项定义于 pkg/option/config.go(CTMapEntriesGlobalTCPName = "bpf-ct-global-tcp-max"、NATMapEntriesGlobalName = "bpf-nat-global-max"、NeighMapEntriesGlobalName = "bpf-neigh-global-max"等),并在Populate阶段通过vp.GetInt(...)读取后写入DaemonConfig(见 pkg/option/config.go);--bpf-lb-map-max定义于 pkg/loadbalancer/config.go,--bpf-policy-map-max定义于 pkg/maps/policymap/cell.go。
CT 与 NAT 表之间的 2/3 约束
这里有一条容易被忽略的硬性约束:当显式指定了--bpf-ct-global-tcp-max和/或--bpf-ct-global-any-max时,NAT 表大小(--bpf-nat-global-max)不得超过组合 CT 表大小(TCP + UDP)的 2/3。以下两种情况会自动生效:
- 未显式设置
--bpf-nat-global-max; - 使用了动态 BPF Map 尺寸(见下文)。
源码层面的实现位于 pkg/option/config.go:当 NAT 大小超过CTMapEntriesGlobalTCP + CTMapEntriesGlobalAny时,会自动封顶为(CTMapEntriesGlobalTCP + CTMapEntriesGlobalAny) * 2 / 3,并输出告警日志。此外,pkg/option/config.go 的校验逻辑还保证 CT/NAT 条目数落在LimitTableMin(1Ki)到LimitTableMax(16Mi,即单个 Map 约 1GiB 条目)之间,超出范围会直接报错。
一个配置示例
假设要为每节点 8 vCPU、30GiB 内存的节点显式配置容量:
cilium-agent \ --bpf-ct-global-tcp-max=524288 \ --bpf-ct-global-any-max=262144 \ --bpf-nat-global-max=524288 \ --bpf-neigh-global-max=524288 \ --bpf-policy-map-max=16384 \ --bpf-lb-map-max=65536注意此时 NAT 表(524288)不能超过 CT 合计(524288 + 262144)的 2/3,即最大应为 524288,示例中恰好满足约束。
基于内存的动态 Map 尺寸:--bpf-map-dynamic-size-ratio
手动规划每个 Map 的上限既繁琐又容易出错。Cilium 提供了--bpf-map-dynamic-size-ratio选项:在 agent 启动时,根据给定比例的系统总内存自动确定多个大型 BPF Map 的上限。例如,比例0.0025表示这些 Map 最多使用系统总内存的 0.25%。
该选项影响以下内存占用最大的 BPF Map:
cilium_ct_{4,6}_global(TCP 连接跟踪)cilium_ct_{4,6}_any(UDP/任意协议连接跟踪)cilium_nodeport_neigh{4,6}(NodePort 邻居表)cilium_snat_v{4,6}_external(外部 SNAT)cilium_lb{4,6}_reverse_sk(sock reverse NAT)
这些内核 Map 名称均可在 pkg/datapath/maps/maps_generated.go 中找到对应登记。
动态计算原理(源码级)
动态尺寸的计算逻辑位于 pkg/option/config.go 的getDynamicSizeCalculator:
- 计算可用内存:
memoryAvailableForMaps = totalMemory * dynamicSizeRatio; - 计算默认内存基线:将各 Map 默认条目数乘以其单元素大小(
SizeofCTElement、SizeofNATElement、SizeofNeighElement、SizeofSockRevElement)求和,得到totalMapMemoryDefault; - 按比例分配:每个 Map 的条目数按
(entriesDefault * memoryAvailableForMaps) / totalMapMemoryDefault计算,并受最小值(如 TCP CT 为 128Ki、UDP CT 为 64Ki、NAT 为 128Ki)与最大值LimitTableMax约束; - CPU 对齐:启用分布式 LRU 时,条目数会向上取整到
num_possible_cpus()的倍数,与内核htab_map_alloc()的行为保持一致,避免 Map 属性不匹配导致反复重建(见 pkg/option/config.go)。
注释中还给出了直观的计算示例:512MB 内存 → CT TCP 约 33140 条;1GB → 约 66280 条;4GB → 约 265121 条;16GB → 约 1060485 条。
显式设置优先:如果某个 Map 已通过命令行选项显式指定,则动态尺寸对该 Map 失效,使用用户提供的值(见calculateDynamicBPFMapSizes中if !vp.IsSet(...)的分支逻辑,pkg/option/config.go)。
与 kube-proxy 的连接跟踪对比
kube-proxy根据机器 CPU 核数设置 Linux 连接跟踪表(nf_conntrack)的最大条目数:每个核默认 32768 条,且无论核数多少,最低为 131072 条。Cilium 则有自己基于 BPF Map 的连接跟踪表,条目数根据节点总内存计算,且无论内存多少,最低为 131072 条。
当 Cilium 配置--bpf-map-dynamic-size-ratio: 0.0025时,两种方案的连接跟踪条目数对比如下(数据来自 maps.rst):
| vCPU | 内存(GiB) | Kube-proxy CT 条目 | Cilium CT 条目 |
|---|---|---|---|
| 1 | 3.75 | 131072 | 131072 |
| 2 | 7.5 | 131072 | 131072 |
| 4 | 15 | 131072 | 131072 |
| 8 | 30 | 262144 | 284560 |
| 16 | 60 | 524288 | 569120 |
| 32 | 120 | 1048576 | 1138240 |
| 64 | 240 | 2097152 | 2276480 |
| 96 | 360 | 3145728 | 4552960 |
可以观察到:小内存节点上两者都收敛于 131072 的下限;从 8 vCPU / 30GiB 开始,Cilium 由于基于内存而非核数计算,CT 条目数开始超过 kube-proxy,且在内存增长更快的高端节点上优势更明显(96 vCPU / 360GiB 时 Cilium 约 455 万条,而 kube-proxy 约 314 万条)。这印证了 BPF 数据通路在高并发场景下连接跟踪容量的扩展性优势。
Service LB Map Sizing:服务负载均衡容量估算
核心 Map 与容量影响
Cilium 使用名为cilium_lb{4,6}_services_v2的 LB services Map 存放 clusterIP 与 nodePort 类型的 Service 负载均衡条目,通过--bpf-lb-map-max配置,默认 64k。如果该 Map 写满,Cilium 可能无法对 Service 更新进行调和(reconcile),从而影响 Service IP 的连通性,或导致无法创建新 Service。
在节点上可通过bpftool map list | grep cilium_lb找到cilium_lb4_services_v2/cilium_lb6_services_v2两个实例(对应 maps_generated.go 中的登记)。
容量计算公式
单个 Service 在 LB Map 中产生的条目数取决于两个因素:Service 选中的 Pod 后端数量,以及Service spec 中的端口/协议条目数量:
每个 Service 的 LB Map 条目数 =(每个 Service 的端点数量)×(每个 Service 的端口/协议数量)
由此可以粗略估算所需的整体 Map 大小:
LB Map 条目数 ≈(LB Service 数量)×(每个 Service 的平均端点数量)×(每个 Service 的平均端口/协议数量)
例如:假设有 1000 个 Service,平均每个 Service 选中 3 个 Pod 后端、暴露 2 个端口/协议,则所需条目约为1000 × 3 × 2 = 6000,远低于 64k 默认上限;但如果单个 Service 选中了数千个 Pod 后端,则必须单独核算。
注意:该启发式估算假设各 Service 选中的 Pod 数量与端口/协议条目大致呈正态分布。如果你的场景存在较大离群值(例如某个 Service 选中了非常庞大的 Pod 后端集合),则可能需要做更精确的逐项估算。
扩容的代价:连接中断风险
一旦 Cilium 在某个节点上创建了 Service LB Map(即该节点首次运行 Cilium agent 之后),再尝试修改容量参数并重启 Cilium,将导致连接中断——因为新 Map 需要重新填充现有 Service 条目。因此,如果连接中断不可接受,务必在安装 Cilium 之前仔细评估 Map 需求,将容量规划前置。
小结与容量规划建议
| 规划要点 | 建议 |
|---|---|
| 默认上限足够大多数场景 | 512k CT、512k NAT、64k Service LB 是常见节点规模的合理起点 |
| 高并发节点优先用动态尺寸 | 通过--bpf-map-dynamic-size-ratio=0.0025让 agent 按内存自动分配,避免手工估算 |
| 显式配置必须满足 CT/NAT 2/3 约束 | NAT 表 ≤ (CT TCP + CT UDP) × 2/3,否则会被自动封顶或启动校验失败 |
| Service 规模巨大时提前核算 | 使用"Service 数 × 平均后端数 × 平均端口数"估算cilium_lb{4,6}_services_v2所需容量 |
| 扩容动作尽量前置 | 首次运行 agent 后调整 LB Map 容量并重启会中断连接,尽量在安装阶段定稿 |
| 参考源码核对实际生效值 | 常量默认值见 pkg/option/config.go,Map 内核名称见 pkg/datapath/maps/maps_generated.go |
容量规划的核心原则是:默认值适合作为起点,动态尺寸适合作为通用基线,显式覆盖用于已知的极端场景,而 Service LB 必须结合真实服务拓扑在部署前完成核算。理解这些 Map 上限及其背后的源码实现(pkg/option/config.go 中的默认常量、校验与动态计算逻辑),将帮助你在规模增长时做出有依据的调优决策。
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考