Cilium eBPF Datapath 深度解析:BPF 钩子、数据包生命周期与 Map 容量调优
Cilium eBPF Datapath 深度解析BPF 钩子、数据包生命周期与 Map 容量调优【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/ciliumCilium 的 eBPF Datapath 是其在网络、安全与可观测性能力上的核心引擎它利用 Linux 内核网络栈中的一系列 BPF 钩子XDP、TC Ingress/Egress、Socket Operations、Socket Send/Recv加载 BPF 程序并组合出端点策略、服务负载均衡、IPsec 加密、L7 策略等高层网络对象。本文基于仓库 Documentation/network/ebpf/ 目录下的官方文档结合 bpf/ 与 pkg/option/config.go 等源码完整讲解 BPF 钩子与数据路径组件、数据包从进入到离开节点所经历的三条典型流程以及决定集群规模上限的 BPF Map 容量与动态调优方法。读完本文你将掌握 Cilium 数据路径的工作原理、如何评估和调整各类 BPF Map 上限以及 kube-proxy 与 iptables 在其中的协作边界。一、eBPF Datapath 总览内核中的 BPF 钩子Linux 内核网络栈提供了一组 BPF 钩子用于挂载并运行 BPF 程序。Cilium 数据路径正是利用这些钩子将多个 BPF 程序串联起来从而构建出更高层次的网络抽象。文档 intro.rst 列出了 Cilium 使用的四类核心钩子。1. XDPeXpress Data PathXDP 钩子位于网卡驱动收包路径的最早位置数据包到达即触发 BPF 程序执行。由于程序直接作用于原始报文数据、先于任何其他处理发生因此可以获得最佳的数据包处理性能。该钩子非常适合运行过滤程序——丢弃恶意或异常流量也是常见 DDoS 防护机制的基础。Cilium 的 Prefilter 对象正是运行在此钩子上。2. Traffic Control Ingress/EgressTC 收发路径TC ingress 钩子与 XDP 一样附着在网络接口上但它在网络栈完成报文初步处理后、进入 L3 层之前运行此时可以访问与报文关联的大部分元数据适合执行本地节点处理例如应用 L3/L4 端点策略并将流量重定向到端点。对于面向网络的设备TC ingress 常与 XDP 配合此时可以合理假设到达 TC 层的绝大多数流量是合法的、目的地为主机的流量。容器通常使用名为 veth pair 的虚拟设备它如同一根连接容器与主机的虚拟网线。Cilium 通过在 veth pair 主机侧的一端附着 TC ingress 程序可以监控并强制所有离开容器的流量遵循策略同时通过为每个容器关联的 veth pair 附加 BPF 程序并将所有网络流量路由到主机侧虚拟设备另一端同样附着 TC ingress 程序Cilium 即可监控并强制节点进出流量的策略。从源码看这些路径对应cil_from_netdev、cil_to_netdev、cil_from_host、cil_from_container、cil_to_container等程序例如 bpf/bpf_host.c 定义了在宿主机设备 TC ingress 上运行的cil_from_netdev测试代码 bpf/tests/lib/bpf_host.h 与 bpf/tests/lib/bpf_lxc.h 分别映射了这些程序的跳转表。此外在cilium_host与cilium_net这对虚拟接口之间BPF 程序会重写目的 MAC 地址见 bpf/bpf_host.c确保报文按预期在主机与容器网络命名空间之间传递。3. Socket OperationsSocket 操作该钩子附着在指定的 cgroup 上针对 TCP 事件运行。Cilium 将 socket operations 程序附着到根 cgroup用于监控 TCP 状态迁移尤其是 ESTABLISHED 状态。当某个 socket 进入 ESTABLISHED 状态且其 TCP 对端位于本节点可能是本地代理时会为其附加一个 socket send/recv 程序。4. Socket Send/RecvSocket 收发该钩子在 TCP socket 的每次发送操作时运行可以检查消息内容并选择丢弃消息、将消息送入 TCP 层、或把消息重定向到另一个 socket。Cilium 用它来加速数据路径重定向即下文 Socket Layer Enforcement 快速重定向从而在连接建立后绕过部分策略检查路径。二、由钩子组合出的数据路径对象将上述钩子与虚拟接口cilium_host、cilium_net、可选的 overlay 接口cilium_vxlan、Linux 内核加密支持以及用户态代理Envoy相结合Cilium 构建出以下六个核心网络对象。1. Prefilter预过滤Prefilter 对象运行 XDP 程序提供一组用于过滤来自网络的流量的预过滤规则以获得最佳性能。具体而言它使用 Cilium agent 提供的 CIDR 映射做查找当目的地址不是合法端点时直接丢弃否则放行交给协议栈继续处理。该机制可以根据需要轻松扩展以构建新的预过滤条件。2. Endpoint Policy端点策略Endpoint Policy 对象实现 Cilium 的端点强制。它通过一次映射查找即可得到报文关联的身份与策略因此在大量端点下依然具有良好的扩展性。根据策略该层可能丢弃报文、转发到本地端点、转发到 Service 对象或转发到 L7 Policy 对象做进一步规则匹配。它是数据路径中负责将报文映射到身份、并执行 L3/L4 策略的最主要对象。对应的策略映射cilium_policy定义于 bpf/lib/policy.h。3. Service服务负载均衡Service 对象对每个收到的报文以目的 IP可选地加上目的端口做一次映射查找。若命中则将报文转发到配置的 L3/L4 后端之一。Service 块可以附着在任意接口的 TC ingress 钩子上实现独立负载均衡器也可以集成进 Endpoint Policy 对象中。服务相关的映射cilium_lb4_services_v2、cilium_lb6_services_v2定义于 bpf/lib/lb.h 与 bpf/lib/lb.h。4. L3 EncryptionL3 加密Ingress 方向L3 Encryption 对象将报文标记为待解密交给 Linux xfrmtransform层解密解密完成后该对象接收报文并将其送交协议栈做进一步处理。根据模式direct routing 或 overlay后续传递可能通过 BPF tail call也可能通过 Linux 路由栈。解密所需的密钥编码在 IPsec 头中因此 ingress 方向无需映射查找即可获得密钥。Egress 方向先以目的 IP 做映射查找判断报文是否需要加密以及目的节点有哪些可用密钥选择两端节点都持有的最新密钥将报文标记为加密后交给 Linux xfrm 层加密完成后若使用 overlay 则通过直接 tail call、否则送回 Linux 路由栈传递给下一层。5. Socket Layer EnforcementSocket 层强制Socket Layer Enforcement 组合使用 socket operations 与 socket send/recv 两个钩子监控并附着所有与 Cilium 管理端点包括 L7 代理关联的 TCP socket。socket operations 钩子识别适合加速的候选 socket——包括所有本地节点连接端点到端点以及任何到 Cilium 代理的连接随后这些连接的所有消息均由 socket send/recv 钩子处理。快速重定向会先确保所执行的策略在 socket/端点映射下仍然有效然后直接将消息发送给对端 socket从而显著减少路径中的处理环节。6. L7 PolicyL7 策略L7 Policy 对象将代理流量重定向到 Cilium 的用户态代理实例。Cilium 使用 Envoy 实例作为其用户态代理Envoy 根据配置的 L7 策略决定转发流量或生成适当的拒绝响应。关于 L7 策略扩展的更多细节见 Documentation/network/servicemesh/envoy注更具体路径可查阅 Documentation/network 下的 Envoy 相关章节。以上组件相互连接构成了 Cilium 灵活高效的 eBPF 数据路径。三、数据包的生命周期三条典型路径文档 lifeofapacket.rst 从 eBPF 数据路径视角通过三个场景完整呈现数据包的生命周期。场景一Endpoint 到 Endpoint同节点首先是同节点端点之间的本地流量可在入口与出口按需启用 L7 Policy。随后展示启用 Socket Layer Enforcement 后的同路径对于 TCP 流量建立连接的握手阶段仍会穿越 Endpoint Policy 对象直到 TCP 状态进入 ESTABLISHED连接建立之后唯一仍需经过的就只有 L7 Policy 对象。场景二从 Endpoint 出向 Egress可选 overlay其次是从本地端点出站到外部网络的路径可选 overlay 网络。使用 overlay 时流量经与 overlay 对应的 Linux 网络接口转发出去默认情况下该接口名为cilium_vxlan。与场景一类似当启用 Socket Layer Enforcement 且使用 L7 代理时对于 TCP 流量可以跳过端点与 L7 Policy 之间的 Endpoint Policy 处理块若启用了 L3 加密则数据包在出站前会被加密块处理。场景三Ingress 到 Endpoint可选 overlay最后是外部流量进入本地端点的路径同样可选 overlay 网络。与场景二类似Socket Layer Enforcement 可以省去代理与端点 socket 之间的一组策略遍历。若收到的报文是加密的则先解密再进入正常处理流程。可以看到Socket Layer Enforcement 的核心价值在于连接握手完成后将报文直接从源 socket 重定向到目标 socket从而避开一系列策略块与协议栈处理这在端到端延迟与吞吐上都有显著收益。四、BPF Maps容量上限、默认值与本节点规模边界所有 BPF map 在创建时都带有容量上限超出上限的插入会失败因此 map 容量直接限制了数据路径的可扩展性。文档 maps.rst 给出了各 map 的默认上限表每一项均可在源码中调高如有需求官方也会按需增加配置项。Map 名称作用域默认上限规模影响Authnode512k每节点最多 512k 条已认证关系Connection Trackingnode512k TCP / 256k UDP最多 512k 条并发 TCP 连接最多 256k 个预期的 UDP 应答NATnode512k最多 512k 条 NAT 条目Neighbor Tablenode512k最多 512k 条邻居条目Endpointsnode64k每节点最多 64k 个本地端点 主机 IPIP cachenode512k全部集群最多 256k 个端点IPv4IPv6或最多 512k 个端点仅 IPv4 或仅 IPv6Service Load Balancernode64k全部集群最多约 3k 个 clusterIP/nodePort Service详见下文 Service LB Map SizingService Backendsnode64k全部集群所有 Service 累计最多 64k 个唯一后端Service Source Rangesnode64k所有 Service 累计最多 64k 条 LB 源地址范围Service Session Affinitynode64k最多 64k 个来自不同客户端的亲和关系Policyendpoint16k单个端点最多 16k 个允许的 身份 端口 协议 组合IPv4 Fragmentationnode8k节点上最多 8k 个同时在途的 IPv4 分片报文IPv6 Fragmentationnode8k节点上最多 8k 个同时在途的 IPv6 分片报文IPv4 Masqnode16kBPF 版 ip-masq-agent 最多使用 16k 个 IPv4 CIDRIPv6 Masqnode16kBPF 版 ip-masq-agent 最多使用 16k 个 IPv6 CIDREgress Policynode16k全部集群所有目的 CIDR 下最多 16k 个端点Nodenode16k全部集群最多 16k 个不同节点 IPIPv4 与 IPv6其中Connection Tracking、NAT、Neighbor Table 等映射在源码中的定义位置分别为 bpf/lib/conntrack_map.hcilium_ct4_global、bpf/lib/nat.hcilium_snat_v4_external、bpf/lib/neigh.hcilium_nodeport_neigh4而动态 sizing 涉及的 socket 反向 NAT 映射cilium_lb4_reverse_sk定义于 bpf/lib/sock.h。1. 通过 cilium-agent 命令行覆盖容量部分 BPF map 的上限可以通过cilium-agent的命令行选项覆盖包括--bpf-auth-map-maxAuth map 上限对应选项定义见 pkg/option/config.go--bpf-ct-global-tcp-max全局 TCP 连接跟踪表上限pkg/option/config.go--bpf-ct-global-any-max全局 UDPany连接跟踪表上限--bpf-nat-global-maxNAT 表上限pkg/option/config.go--bpf-neigh-global-max邻居表上限pkg/option/config.go--bpf-policy-map-max策略 map 上限--bpf-fragments-map-max分片 map 上限pkg/option/config.go--bpf-lb-map-max负载均衡 map 上限注意若指定了--bpf-ct-global-tcp-max和/或--bpf-ct-global-any-max则 NAT 表大小--bpf-nat-global-max不得超过 TCP UDP 合计 CT 表大小的 2/3。当未显式设置--bpf-nat-global-max或使用了动态 BPF map sizing见下文时该限制会被自动应用。在实现层面agent 还会对这些值做上限校验例如 pkg/option/config.go 会将超出FragmentsMapMax定义为1 16即 64Ki的碎片表条目数截断到上限。2. 动态 BPF Map Sizing通过--bpf-map-dynamic-size-ratio标志agent 会在启动时根据给定比例与系统总内存决定若干大型 BPF map 的上限。例如 ratio 为 0.0025 表示将系统总内存的 0.25% 用于这些 map。该标志影响的 map 是系统中内存消耗最大的几个cilium_ct_{4,6}_global、cilium_ct_{4,6}_any、cilium_nodeport_neigh{4,6}、cilium_snat_v{4,6}_external与cilium_lb{4,6}_reverse_sk——它们的定义均可在 bpf/lib 下的conntrack_map.h、neigh.h、nat.h、sock.h中找到。源码 pkg/option/config.go 揭示了其实现细节仅当 agent 已通过SetMapElementSizespkg/option/config.go填充各 map 的元素大小keyvalue后才执行动态计算合法的 ratio 取值范围为(0.0, 1.0]。由于计算后的动态大小最终会被钳制到各表的上限之内因此即使 ratio 取 0.98 也不会真的把总内存的 98% 分配给 BPF map若同时启用了BPFDistributedLRUdistributed LRU而没有指定动态 ratio启动会直接报错计算器getDynamicSizeCalculatorpkg/option/config.go采用的启发式是按各 map 的默认条目数在多个 map 之间按比例分配总内存预算并将每个 map 的大小封顶在各自最大值用户显式给定的 map 大小将覆盖计算值。3. Cilium 与 kube-proxy 的 CT 表对比kube-proxy根据机器的 CPU 核数设置 Linux 连接跟踪表的最大条目数默认每核 32768 条且无论核数多少最低为 131072 条。Cilium 则使用自己的 BPF map 作为连接跟踪表条目数基于节点总内存计算且无论内存多少最低为 131072 条。下表对比了kube-proxy与 Cilium 在--bpf-map-dynamic-size-ratio: 0.0025配置下各自 CT 表的条目数vCPU内存 (GiB)Kube-proxy CT entriesCilium CT entries13.7513107213107227.51310721310724151310721310728302621442845601660524288569120321201048576113824064240209715222764809636031457284552960可以看出在大内存节点上Cilium 基于内存比例计算的 CT 表比 kube-proxy 基于核数计算的表更大为高并发场景预留了更多连接跟踪容量。五、Service LB Map 容量规划Cilium 使用名为cilium_lb{4,6}_services_v2的负载均衡服务 map 来保存 clusterIP 与 nodePort 类型 Service 的 LB 条目map 定义见 bpf/lib/lb.h 与 bpf/lib/lb.h。这些 map 通过--bpf-lb-map-max配置默认 64k。若该 map 被写满Cilium 可能无法协调 Service 更新进而影响 Service IP 的连通性或新 Service 的创建。所需 map 大小取决于多个因素每个 clusterIP/nodePort Service 创建的条目数等于该 Service 选中的 Pod 后端数乘以 Service spec 中的端口/协议条目数LB map entries per Service 每个 Service 的端点数×每个 Service 的端口/协议数由此可以粗略估算整体所需大小LB map entries ≈LB Service 数量×平均每 Service 端点数×平均每 Service 端口/协议数需要注意两点该启发式估算假设各 Service 选中的 Pod 数量与端口/协议条目数大致服从正态分布若存在极端离群值例如某个 Service 选中了数量巨大的 Pod 后端则需要进行更细致的估算。一旦 Cilium 在某节点上创建了 Service LB map即该节点上首次运行 Cilium agent 之后再尝试调整 map 大小参数并重启 Cilium 会导致连接中断——因为新 map 需要用现有 Service 条目重新填充。因此若无法容忍此类中断必须在安装 Cilium 之前仔细评估 map 需求。六、iptables 回退与 kube-proxy 互操作根据 Linux 内核版本的不同eBPF 数据路径能在 eBPF 中实现的功能集存在差异。当某些必需能力不可用时对应功能会回退到传统 iptables 实现详见内核特性支持矩阵。下图展示了 kube-proxy 安装的 iptables 规则与 Cilium 安装的 iptables 规则在节点上的集成关系理解这一协作关系对排查问题很有帮助在 eBPF 能力覆盖不到的场景例如特定内核版本上某些功能缺失下Cilium 会与 kube-proxy 的规则共存而在 eBPF 完全覆盖的功能路径上Cilium 的数据路径可以完全绕开 iptables这也是其性能优势的来源之一。七、总结与进一步阅读Cilium 的 eBPF Datapath 通过在 XDP、TC Ingress/Egress、Socket Operations、Socket Send/Recv 四类钩子上运行 BPF 程序组合出 Prefilter、Endpoint Policy、Service、L3 Encryption、Socket Layer Enforcement 与 L7 Policy 六大对象同节点端点互访、端点出站 Egress 与外部流量入站三条典型路径覆盖了绝大多数报文流转场景而 BPF Map 的容量规划默认上限、cilium-agent命令行覆盖、--bpf-map-dynamic-size-ratio动态 sizing 以及 Service LB map 的条目估算则是保障大规模集群可用性的关键前置工作。如需继续深入可在仓库中查看Documentation/network/ebpf/lifeofapacket.rst三条数据路径的完整图解bpf/bpf_host.c宿主路径cil_from_netdev等入口程序bpf/lib/lb.hLB 服务 map 定义与查找逻辑bpf/lib/conntrack_map.h连接跟踪 map 定义pkg/option/config.go动态 map sizing 的校验与计算实现bpf/tests/lib/bpf_host.h数据路径程序跳转表的测试夹具【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考