【Cilium / eBPF】BPF 挂载与程序生命周期:TC、XDP、cgroup 在路径上的角色
内容提要
本文介绍Cilium v1.20的BPF挂载机制,核心是XDP、TC、socket等hook在数据路径上的分工:XDP负责早过滤,TC处理策略与转发,socket加速同节点连接。程序经Go loader加载替换,与map世代耦合,能力不足时回退iptables。排障需确认程序是否实际挂载,而非仅看Pod状态。
延伸解读
挂载点决定排障抓取点
文章强调,排障时不能只看Pod状态,而应确认BPF程序是否真正挂载到接口或cgroup。XDP预过滤若启用,非法流量在驱动层即被丢弃,故障现象会从TC层转移到XDP层,此时tcpdump或Hubble的抓取点需相应调整。若eBPF能力不足回退到iptables,原本在TC drop reason或Hubble verdict上可见的拒绝会变成netfilter计数,观测坐标随之改变。
程序替换与map世代耦合
BPF程序加载与替换并非孤立操作,而是与map世代紧密耦合。v1.20中policy map改名为cilium_policy_v3_,即使格式未变,语义变化也迫使升级或降级时重建map,并与新程序原子切换,以减轻旧map缺身份导致的丢包。加载失败时,控制面可能仍显示agent Running,但实际程序未上岗,需通过bpftool等工具检查接口或cgroup上的实际挂载情况。
多hook分工而非单一巨型hook
Cilium选择XDP、TC、socket等hook分工协作,而非将所有逻辑塞入XDP。XDP负责最早过滤与部分LB加速,TC处理策略与转发,socket层加速同节点连接。这种设计源于策略与连接状态所需的元数据在TC/socket层更易获取。工程上需注意,论文中的XDP加速效果并不等于生产环境每张网卡的实际能力,应以内核特性矩阵为准,能力不足时允许回退iptables。
Q&A
Cilium 在数据路径上主要使用哪些 BPF 挂载点?它们各自的作用是什么?
Cilium 主要使用 XDP、TC(ingress/egress)和 socket operations(cgroup)等挂载点。XDP 在驱动最早收包点进行预过滤和 NodePort 等早路径加速;TC 挂在设备 qdisc 分类点(容器侧常挂在 veth 主机端),用于 L3/L4 策略、重定向到 endpoint 以及与 host/overlay 衔接;socket operations 在 cgroup 上监控 TCP 状态变迁,用于发现可加速的 ESTABLISHED 连接,并通过 sk_msg 实现同节点 socket 直通。
Cilium 的 BPF 程序是如何被加载和替换的?
Cilium 的 BPF 程序由 Go 侧的 loader(pkg/datapath/loader)负责加载和替换。加载过程大致为:根据节点/endpoint 配置生成或选择 BPF 对象,创建或打开依赖的 map,通过 BPF_PROG_LOAD 加载程序,然后 attach 到设备或 cgroup。替换时,新程序就位后切换流量视角,但可能存在窗口,例如 bpf_lxc 可能在 bpf_host 策略程序尚未就绪时加载,导致 DROP_HOST_NOT_READY 错误。此外,程序替换与 map 世代耦合,升级/降级时可能需要重建 map。
Cilium 在什么情况下会回退到 iptables?
当 eBPF 数据面缺少所需能力时,Cilium 可能回退到 legacy iptables 实现。具体取决于内核特性矩阵,如果某些功能无法通过 eBPF 实现,就会使用 iptables 作为替代。排障时,宿主机上存在 iptables 规则并不一定意味着 Cilium 未使用 eBPF,可能是互操作、回退或主机防火墙路径。
如何判断 Cilium 的 BPF 程序是否真正挂载成功?
不能仅看 Pod 状态或 agent 是否 Running,因为加载失败时控制面可能仍显示 agent Running。需要检查接口或 cgroup 上是否真的有期望的 BPF 程序,可以使用 bpftool 或 cilium bpf 等工具查看。如果程序未挂载,应区分是 verifier 拒绝、attach 失败还是 map 不兼容等问题。
Cilium 中同节点 Pod 到 Pod 的流量路径是怎样的?socket 加速如何影响该路径?
同节点 Endpoint 到 Endpoint 的流量在两个 veth/endpoint 程序之间传递,可选 L7 时旁路到代理。启用 socket layer enforcement 后,TCP 握手阶段仍需穿越 endpoint policy,直到 ESTABLISHED;之后同节点路径可主要留在 L7(若有)与 socket 加速,避免反复跑完整策略块。socket 加速通过 sockops 和 sk_msg 实现,可减少重复策略遍历。
Cilium 的 BPF 程序与 BPF map 之间有什么关系?为什么说它们“同生共死”?
BPF 程序与 BPF map 是耦合的,程序加载时需要创建或打开依赖的 map,程序替换时往往伴随 map 世代更新。例如 v1.20 的 policy map 改名为 cilium_policy_v3_,升级/降级时需要重建 map,并与新程序原子切换,以减轻旧 map 缺身份导致已有连接丢包的问题。因此,程序与 map 的版本必须匹配,否则可能导致功能异常。