Cilium网络策略与Serverless性能调优实战 - 第4节

4.1 Pod 网络模型与 Cilium 的 CNI 实现

Kubernetes 的 Pod 网络模型要求每个 Pod 拥有独立的 IP 地址,且所有 Pod 之间可以不通过 NAT 直接通信。Cilium 作为基于 eBPF 的 CNI 插件,在实现这一模型时绕过了传统 iptables 和网桥方案,直接在网络栈的内核层完成数据包转发。

4.1.1 传统 Pod 网络 vs Cilium eBPF 路径

对比维度 传统方案(Flannel/Calico iptables) Cilium eBPF
数据路径 网桥 → iptables → 路由 → 隧道/veth 直接 eBPF 程序挂载 tc/XDP
网络策略 iptables 规则链,O(n) 匹配 eBPF map 哈希查找,O(1)
Service 转发 kube-proxy + iptables/IPVS eBPF 原生 DSR 或 kube-proxy 替代
延迟开销 多次内核态/用户态切换 单次 eBPF 程序执行
连接跟踪 conntrack 模块 eBPF map 自定义跟踪
关键结论:Cilium 通过 eBPF 将网络策略、Service 转发、负载均衡全部下沉到内核的 eBPF 虚拟机中执行,消除了 iptables 规则遍历和多次上下文切换的开销。

4.1.2 Cilium 的 Pod 网络数据路径

当 Pod 发送数据包到同一节点或跨节点 Pod 时,Cilium 的数据路径如下:

  1. 出站方向:Pod 的 veth 对端(cilium_host)触发 eBPF 程序(tc ingress)。
  2. 策略检查:eBPF 程序查询 cilium_policy map,判断源/目的身份是否允许通信。
  3. 封装/路由:跨节点时通过 VXLAN/Geneve 隧道或直接路由(Native Routing)转发。
  4. 入站方向:目标节点 eBPF 程序解封装后,将数据包交付到目标 Pod 的 veth 端。
# 查看 Cilium 为 Pod 分配的 identity
kubectl exec -n kube-system cilium-xxxxx -- cilium endpoint list
# 输出示例:
# ENDPOINT   IDENTITY   LABELS                    IP             NODE
# 1234       167        k8s:app=frontend          10.0.1.5       node-1
# 5678       289        k8s:app=backend           10.0.2.8       node-2
身份标识(Identity):Cilium 不依赖 IP 地址做策略匹配,而是为每个 Pod 分配基于标签的 Security Identity。即使 Pod 重启 IP 变化,策略依然生效。
Cilium Pod 网络数据路径(同节点通信) Pod A 10.0.1.5 veth cilium_host eBPF tc ingress eBPF Policy cilium_policy map cilium_host eBPF tc egress veth Pod B 10.0.1.8 数据路径说明: 1. Pod A 发送数据 → veth 对端 → cilium_host 触发 eBPF tc ingress 程序 2. eBPF 程序查询 cilium_policy map,检查身份是否允许通信 3. 允许后直接通过 cilium_host 转发到目标 Pod B 的 veth 端

4.2 Service 负载均衡:DSR 模式与 kube-proxy 替代

Cilium 提供了两种 Service 负载均衡模式:kube-proxy 替代模式和DSR(Direct Server Return)模式。两者都基于 eBPF 实现,但转发路径和性能特征不同。

4.2.1 kube-proxy 替代模式

Cilium 可以完全替代 kube-proxy,通过 eBPF 程序在节点网络接口上直接处理 Service 流量。相比 iptables 模式,它消除了 DNAT 规则链遍历和 conntrack 表竞争。

# 启用 Cilium 的 kube-proxy 替代
helm upgrade cilium cilium/cilium --namespace kube-system \
  --set kubeProxyReplacement=true \
  --set k8sServiceHost=192.168.1.100 \
  --set k8sServicePort=6443

# 验证 kube-proxy 已被替代
cilium status | grep "KubeProxyReplacement"
# 输出:KubeProxyReplacement:  Strict   [eth0 192.168.1.10]
特性 kube-proxy iptables Cilium kube-proxy 替代
规则存储 iptables 链表,O(n) 遍历 eBPF map,O(1) 哈希查找
连接跟踪 Linux conntrack(全局锁竞争) eBPF per-CPU map(无锁)
更新延迟 iptables-restore 全量刷新(秒级) eBPF map 原子更新(毫秒级)
CPU 开销 高(规则越多开销越大) 低(恒定 O(1))

4.2.2 DSR(Direct Server Return)模式

DSR 模式的核心思想是:入站流量经过负载均衡节点,但返回流量直接从后端 Pod 返回客户端,避免负载均衡节点成为瓶颈。Cilium 通过 eBPF 在数据链路层实现 DSR,无需修改 IP 头。

DSR 优势:减少负载均衡节点的带宽压力,降低延迟,特别适合 Serverless 场景下大量短连接请求。
# 启用 Cilium DSR 模式
helm upgrade cilium cilium/cilium --namespace kube-system \
  --set kubeProxyReplacement=true \
  --set loadBalancer.mode=dsr \
  --set loadBalancer.dsrDispatch=ipip

# 查看 DSR 是否生效
cilium status | grep "DSR"
# 输出:DSR:  Enabled
Cilium DSR 模式流量路径 客户端 10.0.0.1:8080 负载均衡节点 eBPF DSR 处理 后端 Pod 1 10.0.2.1:80 后端 Pod 2 10.0.2.2:80 入站 入站 出站(DSR 直接返回客户端,不经过负载均衡节点) DSR 关键流程: 1. 客户端请求到达负载均衡节点,eBPF 程序选择后端 Pod(如 Pod 1) 2. 负载均衡节点通过 IPIP 封装或 MAC 重写将请求转发到后端 Pod 3. 后端 Pod 处理请求后,直接将响应返回客户端(源 IP 为 Pod IP) 4. 负载均衡节点只处理入站流量,出站流量绕过负载均衡器

4.3 eBPF 数据路径详解

Cilium 的核心竞争力在于其 eBPF 数据路径。理解 eBPF 程序在数据包处理流程中的挂载点和执行逻辑,是掌握 Cilium 网络性能优化的基础。

4.3.1 eBPF 挂载点:XDP 与 tc

Cilium 在两种内核钩子处挂载 eBPF 程序:

  • XDP(eXpress Data Path):在网络驱动层、SKB 分配之前执行,性能最高,适合 DDoS 防护和早期丢弃。
  • tc(Traffic Control):在协议栈处理之后、SKB 分配之后执行,功能更丰富,适合策略执行和转发。
挂载点 执行时机 延迟 适用场景
XDP 网卡驱动层,SKB 分配前 < 1μs DDoS 防护、包过滤、负载均衡
tc ingress 协议栈处理,SKB 分配后 1-3μs 网络策略、Service 转发、封装/解封装
tc egress 协议栈处理,SKB 分配后 1-3μs 出站策略、带宽管理

4.3.2 eBPF 程序执行流程

以 Pod 访问 Service 为例,Cilium 的 eBPF 程序执行以下步骤:

  1. XDP 程序(可选):检查源/目的 MAC,如果是已知 Service IP,直接重定向到后端 Pod 的 MAC。
  2. tc ingress 程序:解析 IP 头,查询 cilium_lb4_services_v2 map 获取后端列表。
  3. 负载均衡选择:使用 Maglev 一致性哈希算法选择后端,查询 cilium_lb4_backends_v2 map 获取后端 IP。
  4. 封装/转发:如果是跨节点,通过 VXLAN 封装;同节点直接修改 MAC 转发。
  5. 策略检查:查询 cilium_policy map,验证源身份是否允许访问目标身份。
// 简化版 eBPF 程序:Service 负载均衡查找
// 完整代码见 cilium/bpf/lib/lb.h

static __always_inline int __lb4_lookup(struct __ctx_buff *ctx,
                                         struct ipv4_ct_tuple *tuple,
                                         struct lb4_service *svc,
                                         struct lb4_backend *backend,
                                         __u32 *cluster_id)
{
    // 1. 从数据包中提取 Service IP 和端口
    tuple->daddr = ip4->daddr;
    tuple->dport = bpf_ntohs(tcp->dest);

    // 2. 查询 Service map
    struct lb4_key key = {
        .address = tuple->daddr,
        .dport = tuple->dport,
    };
    svc = map_lookup_elem(&cilium_lb4_services_v2, &key);
    if (!svc)
        return CTX_ACT_OK;  // 非 Service 流量,直接放行

    // 3. Maglev 一致性哈希选择后端
    backend_id = maglev_lookup(svc, tuple->saddr, tuple->sport);
    backend = map_lookup_elem(&cilium_lb4_backends_v2, &backend_id);

    // 4. 修改目标 IP 为后端 Pod IP
    ctx_store_bytes(ctx, offsetof(struct iphdr, daddr),
                    &backend->address, 4, 0);
    return CTX_ACT_REDIRECT;
}
性能注意事项:eBPF 程序必须在内核验证器允许的指令数内完成(初始 4096 条,5.x 内核可扩展)。Cilium 通过 tail call(尾调用)将复杂逻辑拆分为多个 eBPF 程序,避免单程序超限。

4.3.3 eBPF map 与数据共享

Cilium 使用多种 eBPF map 在 eBPF 程序与用户态守护进程之间共享数据:

Map 名称 类型 用途
cilium_lb4_services_v2 哈希 存储 Service IP 到后端列表的映射
cilium_lb4_backends_v2 哈希 存储后端 Pod IP 和端口
cilium_policy 哈希 存储网络策略规则(身份 → 允许列表)
cilium_ct4 LRU 连接跟踪表,记录五元组和状态
cilium_metrics 数组 eBPF 程序统计计数器(丢包、转发等)
Cilium eBPF 数据路径与 Map 交互 数据包入口 XDP / tc eBPF 程序 尾调用链 Service Map Policy Map CT Map 转发/丢弃 REDIRECT / DROP eBPF 数据路径关键点: 1. 数据包进入 XDP/tc 钩子,触发 eBPF 程序执行 2. eBPF 程序通过尾调用链依次查询 Service Map、Policy Map、CT Map 3. 所有 map 操作在 eBPF 虚拟机内完成,无需切换到用户态 4. 最终执行转发(REDIRECT)或丢弃(DROP)动作 5. 用户态 cilium-agent 通过 bpf() 系统调用更新 map,实现策略热加载
性能调优建议:在 Serverless 场景下,建议启用 XDP 加速(loadBalancer.acceleration=native)并配合 DSR 模式,可将 Service 转发延迟降低至 1μs 以下。同时监控 cilium_metrics map 中的丢包计数器,判断是否需要调整 eBPF map 大小。