WEBKT

万级 Pod 挑战:放弃 iptables,用 Cilium eBPF 实现超大规模 K8s 网络微隔离落地实践

5 0 0 0

在大规模 Kubernetes 集群中(例如 10,000+ Pod 规模),传统的网络微隔离方案往往会遇到难以逾越的性能瓶颈。如果你仍在使用基于组件如 kube-proxy 默认的 iptables,或者试图通过原生的 Kubernetes NetworkPolicy 来硬撑这种规模的安全隔离,那么集群可能正面临着频繁的网络抖动、控制面延迟、甚至节点 CPU 被软中断打满的隐患。

本文将深入探讨为什么在超大规模集群中 iptables 难以为继,并分享如何利用 Cilium(基于 eBPF 技术)构建高性能、可扩展的万级 Pod 网络微隔离架构。


一、 传统 iptables 方案在大规模场景下的“三宗罪”

原生的 Kubernetes NetworkPolicy 底层主要依赖节点的 iptables(或 IPVS 配合 ipset)。这种架构在集群规模较小时表现尚可,但在上万个 Pod 的高动态环境中,其缺陷会被无限放大:

  1. 线性查表带来的 $O(N)$ 性能衰退
    iptables 的规则链是线性的。当你有上万个 Pod,且彼此之间有复杂的微隔离策略时,节点上的 iptables 规则可能会膨胀至数万条。每个数据包在进入或发出时,都必须从头到尾逐条匹配。这种 $O(N)$ 的查找算法会导致网络延迟随规则数量呈线性上升。
  2. 规则更新时的“全量刷新”锁
    iptables 不支持增量更新。每次新增、删除或变更一个 Pod(这在频繁弹性伸缩的万级集群中每秒都在发生),系统都需要通过 iptables-restore 重新全量刷写规则。在刷写期间,内核空间会加锁,导致期间所有网络吞吐出现瞬间的延迟抖动(Kernel Lock Contention)。
  3. 标签膨胀与 IP 飘移带来的垃圾回收(GC)风暴
    基于 IP 的安全策略在云原生场景下天生存在缺陷。Pod 频繁重建导致 IP 不断变化,控制面必须疯狂地向所有节点同步最新的 IP 映射。这种高频的更新会直接把 Kubernetes APIServer 的 CPU 拖垮。

二、 为什么 Cilium 的 eBPF 能够解决这个问题?

Cilium 彻底抛弃了 iptables,转而使用 Linux 内核技术 eBPF (Extended Berkeley Packet Filter)。它在网络微隔离上的核心优势可以总结为两个词:Identity(身份)$O(1)$ 查找

+-------------------------------------------------------------+
|                     Cilium 控制面 (Operator)                |
|  Pod Labels (app=payment)  ====>  Security Identity: 105    |
+-------------------------------------------------------------+
                               | (同步身份)
                               v
+-------------------------------------------------------------+
|                     内核态 eBPF Map (O(1))                   |
|  [Source Identity] -> [Dest Identity] -> [Policy: ALLOW]    |
|  [      105      ] -> [      201     ] -> [   ALLOW   ]     |
+-------------------------------------------------------------+

1. 基于身份(Identity-Based)的安全机制

Cilium 不再关心具体的 IP 地址,而是将具有相同标签(Labels)的 Pod 抽象为一个“安全身份”(Security Identity,一个无符号整数,例如 ID: 105)。

  • 当 Pod A 发送数据包给 Pod B 时,Cilium 会在数据包的封装协议(如 VXLAN/Geneve 头部)中塞入源端的身标签 ID。
  • 接收端节点的 eBPF 程序只需提取这个 ID,即可在内核中直接做出放行或拦截决策。
  • 收益:即使 Pod 频繁重建、IP 频繁变换,只要它的 Label 没变,其安全身份就不会变,节点上无需更新任何过滤规则

2. eBPF Map 的 $O(1)$ 极速查找

Cilium 将安全策略直接存储在 Linux 内核的 eBPF Map(本质上是哈希表)中。无论集群里有 100 条还是 100,000 条隔离规则,eBPF 程序在收到报文时,只需要对哈希表进行一次查找即可。耗时是恒定的 $O(1)$,几乎没有任何额外延迟。


三、 万级 Pod 集群下的 Cilium 架构规划与性能调优

在超大规模生产环境中,直接套用 Cilium 的默认配置大概率会“踩坑”。要支撑万级 Pod 的微隔离,必须对控制面和数据面进行深度调优。

1. 关键策略:身份分配模式必须改用 KVStore 模式

默认情况下,Cilium 使用 Kubernetes CRD(CiliumIdentity)来同步和管理安全身份。在万级 Pod 且高频伸缩的场景下,这会给 K8s APIServer 带来极大的存储和监听压力。

优化方案
在部署 Cilium 时,强烈建议将 identity-allocation-mode 切换为 kvstore,并为其配置一个独立的、高可用的 etcd 集群(不要与 K8s 的 etcd 共用)。

# Helm values.yaml 关键配置
identityAllocationMode: kvstore
kvstore:
  backend: etcd
  etcd:
    endpoints:
    - https://cilium-etcd-0.local:2379
    - https://cilium-etcd-1.local:2379
    - https://cilium-etcd-2.local:2379

2. 扩容内核 BPF Map 的最大限制

Cilium 内部有多个 BPF Map 用于管理连接状态(Connection Tracking)和安全策略。默认的容量限制无法支撑万级 Pod 的并发流量。

在部署时,必须手动调大以下内核参数,否则会遭遇 BPF map is full 的报错导致网络断连:

# Helm values.yaml
bpf:
  # 调整连接跟踪表大小(根据并发连接数调整,建议至少 100 万以上)
  ctMapMaxTCP: 1048576
  ctMapMaxUDP: 262144
  
  # 调整安全策略 Map 限制,防止因复杂隔离规则导致溢出
  policyMapMax: 32768

3. 避免滥用 L7 策略,主打 L3/L4 微隔离

Cilium 支持 HTTP/gRPC 等 L7 级别的微隔离,但 L7 策略的底层是依赖内置的 Envoy 代理进行流量劫持和解析。

  • 警告:在万级 Pod 规模下,如果对所有流量都开启 L7 策略,Envoy 的 CPU 和内存开销、以及带来的网络延迟将是无法承受的。
  • 最佳实践95% 以上的安全隔离应限制在 L3/L4 层级(直接由 eBPF 纯内核态转发)。只有极少数敏感的、低频的外部 API 调用,才引入 L7 策略。

四、 实战:编写高性能 CiliumNetworkPolicy (CNP)

我们以一个典型的微服务场景为例:只允许携带 role: frontend 标签的前端服务,通过 TCP 协议的 8080 端口访问携带 role: backend 标签的后端服务,同时拒绝其他一切未授权流量。

在 Cilium 中,我们应该使用性能更好的 CiliumNetworkPolicy(相比于 K8s 原生的 NetworkPolicy,它支持更丰富的匹配模式且在内核中运行效率更高):

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: secure-backend-microsegmentation
  namespace: production
spec:
  # 1. 选中要保护的目标 Pods(后端服务)
  endpointSelector:
    matchLabels:
      role: backend
  
  # 2. 入站规则白名单
  ingress:
  - fromEndpoints:
    - matchLabels:
        # 只允许具有 frontend 身份的 Pod 访问
        role: frontend
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP

为什么这个配置在万级节点中依然高效?

当这个 YAML 部署后,Cilium 仅仅做了一件事:
在 BPF 的 Policy Map 中插入了一条 Key-Value 记录:[Source_Identity_frontend] -> [Target_Identity_backend] -> Port_8080 -> ALLOW
无论你在集群里扩容出 5,000 个前端 Pod 还是 10,000 个后端 Pod,这条内核策略记录永远只有一条,查询耗时保持在纳秒级。


五、 零抖动:如何在生产环境中无感迁移?

如果你的集群目前正跑在传统的网络模式上,直接开启全局微隔离无异于在高速公路上给汽车换引擎。以下是一套经过验证的渐进式无感迁移策略:

Step 1: 启用 Hubble 可视化组件进行流量审计

在真正下发“Deny”(拒绝)策略前,先看清流量现状。Cilium 提供了强大的网络观测引擎 Hubble。

# 开启 Hubble 并启用审计模式
helm upgrade cilium cilium/cilium \
  --set hubble.enabled=true \
  --set hubble.metrics.enabled="{dns,drop,tcp,flow}"

利用 Hubble 观察一段时间,确保你找出了所有潜在的合法调用链,避免误杀。

Step 2: 善用 Cilium 的 Policy Audit Mode(审计模式)

Cilium 允许你在全局或单条策略上开启“审计模式”(Audit Mode)。在此模式下,违反策略的流量不会被真正拦截,但会向 Hubble 发送一条告警日志

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: audit-only-policy
  annotations:
    # 关键:开启审计模式
    cilium.io/policy-mode: "audit"
...

通过检索审计日志(寻找 action: audit 的流量记录),你可以极低成本地修正你的微隔离规则。

Step 3: 按 Namespace 灰度切换

切忌一次性在全局应用默认拒绝(Default Deny)策略。建议以 Namespace 为单位,逐个开启微隔离。
在确认该 Namespace 下所有服务的流量都在白名单覆盖中后,通过下发一个简单的 default-deny 策略完成闭环。

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: default-deny-all
  namespace: testing-space # 先在测试、预发环境灰度
spec:
  endpointSelector: {}
  ingress: [] # 留空代表拒绝一切未显式允许的入站流量

六、 总结与避坑指南

  1. 别让 IP 地址限制了想象力:在规划 Cilium 微隔离时,彻底忘记 IP 的概念,完全拥抱 K8s Labels。你的 Labels 设计得有多规范,你的微隔离策略就有多优雅。
  2. 警惕 DNS 解析导致的瓶颈:如果在策略中使用了 toFQDNs(基于域名的过滤),Cilium 会拦截并解析所有 Pod 的 DNS 请求以动态维护 IP 映射。在万级 Pod 规模下,这会对 CoreDNS 带来巨大冲击,务必做好 CoreDNS 的扩容与缓存配置(如启用 NodeLocal DNSCache)。
  3. 监控指标是生命线:上线后,重点监控 cilium_bpf_map_pressure(BPF map 空间水位)和 cilium_policy_import_errors_total 两个指标,时刻关注内核态的资源健康状况。

通过将网络微隔离的执行点从繁重的用户态/iptables 链条下沉到极致性能的 eBPF 内核空间,你不仅能够轻松降服万级 Pod 的安全隔离难题,还能顺带榨干节点的最后一分网络吞吐性能。

SRE铁哥 CiliumKubernetes网络微隔离

评论点评