eBPF 网络性能优化实战:用 Cilium 替换 kube-proxy
结论先行: 在 Kubernetes 集群中,用 Cilium 基于 eBPF 的 kube-proxy 替换模式,能让网络转发延迟降低 30%-50%,同时消除 iptables 规则维护带来的性能抖动。我花了 4 周时间在三个不同规模的生产集群上完成了迁移,踩了所有能踩的坑,今天就把这些经验分享给你。
为什么必须换掉 kube-proxy

说实话,kube-proxy 默认的 iptables 模式在早期确实够用,但随着 Service 数量增长,问题就来了——iptables 规则呈指数级膨胀。当集群 Service 超过 1000 个时,每个新连接都需要遍历上万条规则,CPU 飙高、延迟抖动简直是家常便饭。我亲眼见过一个 2000 Service 的集群,每次规则更新时节点 CPU 直接冲到 80% 以上。
IPVS 模式稍好一些,但仍有局限:它不支持 externalTrafficPolicy: Local 的优雅处理,而且规则更新需要全量刷新,这在大型集群里简直是噩梦。
而 Cilium 利用 eBPF 直接在内核层面完成 Service 负载均衡,完全绕过了 iptables 和 IPVS 的整个路径。这就像从骑自行车直接升级到坐高铁,体验完全是两个世界。
迁移前的环境准备
我的测试集群信息如下,供你参考:
- Kubernetes 1.24
- 3 个 master + 10 个 worker 节点
- 内核版本 5.10(eBPF 最低要求 4.19,但强烈推荐 5.10+)
- 当前网络方案:Calico + kube-proxy (iptables)
注意: 内核版本低于 5.10 的节点,eBPF 的
bpf_skb_adjust_room等功能不可用,Cilium 会降级到标准模式,性能优势就大打折扣了。所以升级内核是第一步,千万别偷懒。
第一步:安装 Cilium CLI
安装 CLI 工具非常简单,一条命令搞定:

curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz
sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz
验证安装是否成功:
cilium version
# 输出: cilium-cli: v0.15.0
看到版本号就说明安装成功了。顺便说一句,我建议你定期检查更新,Cilium 社区迭代很快,新版本经常带来性能提升和 bug 修复。
第二步:部署 Cilium 并启用 kube-proxy 替换
我选择直接替换现有网络方案,而不是并行部署。这是最激进的方案,但也是最能体现性能差异的方式。当然,如果你对稳定性要求极高,也可以先部署 Cilium 并保留 kube-proxy,验证没问题后再切换。
创建 values.yaml 配置文件:
# cilium-values.yaml
kubeProxyReplacement: strict
k8sServiceHost: 192.168.1.100 # 替换为你的 API Server 地址
k8sServicePort: 6443
rollOutCiliumPods: true
ipam:
mode: kubernetes
routingMode: native
autoDirectNodeRoutes: true
ipv4NativeRoutingCIDR: 10.0.0.0/16 # 你的 Pod CIDR
这里有几个关键参数我想特别说明一下:
- kubeProxyReplacement: strict — 完全接管 kube-proxy 功能,这是性能提升的核心
- rollOutCiliumPods: true — 自动滚动更新现有 Pod(注意这不会重启 Pod,但会触发重新调度,所以建议在业务低峰期操作)
部署命令:
cilium install --values cilium-values.yaml
等待 2-3 分钟,喝杯咖啡的功夫,Cilium 就部署好了。
第三步:验证部署状态
部署完成后,先检查组件状态:
cilium status --wait
# 预期输出:
# /¯¯\
# /¯¯\__/¯¯\ Cilium: OK
# \__/¯¯\__/ Operator: OK
# /¯¯\__/¯¯\ Hubble: disabled
# \__/¯¯\__/ ClusterMesh: disabled
# \__/
关键验证项——检查 kube-proxy 替换是否生效:
cilium status | grep "KubeProxyReplacement"
# 输出: KubeProxyReplacement: Strict [eth0 192.168.1.101]
如果看到 Disabled 或 Partial,说明节点内核不支持或者配置有误。这时候别慌,先检查内核版本和 eBPF 功能是否开启。
第四步:卸载旧的 kube-proxy
这是最容易出事的步骤。我建议先保留 kube-proxy 但停止其工作,而不是直接删除 DaemonSet。相信我,这个谨慎的步骤能救你一命。
方法一(推荐):停止 kube-proxy 但不删除
kubectl -n kube-system scale deployment/kube-proxy --replicas=0
方法二(激进):完全删除
kubectl -n kube-system delete daemonset kube-proxy
我当初选了方法二,然后立刻发现节点上的 kube-proxy 残留 iptables 规则导致冲突。那种感觉就像刚拆了旧水管却发现新水管还没接好,水喷得到处都是。所以需要手动清理残留规则:
# 在每个节点上执行
iptables-save | grep -v "KUBE-" | iptables-restore
ip6tables-save | grep -v "KUBE-" | ip6tables-restore
注意: 清理 iptables 时一定要保留非 KUBE 开头的规则,否则会把节点的防火墙规则也清掉,那可就麻烦了。
第五步:验证网络功能
创建测试 Service 来验证网络是否正常工作:
# test-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: test-svc
spec:
selector:
app: test
ports:
- port: 80
targetPort: 8080
type: ClusterIP
验证负载均衡:
# 从任意 Pod 内访问 Service
kubectl run test-pod --image=busybox --rm -it -- wget -O- http://test-svc
检查 eBPF 规则是否生效:
cilium service list
# 输出类似:
# ID Frontend Service Type Backend
# 1 10.96.0.1:443 ClusterIP 1 => 192.168.1.100:6443
# 2 10.96.0.10:53 ClusterIP 1 => 10.0.0.5:53
看到这些规则,说明 Cilium 已经在内核层面接管了 Service 负载均衡,性能提升指日可待。
性能对比:实测数据
我使用 wrk 工具在相同硬件上做了压测,测试场景:1000 个 Service,每个 Service 2 个后端 Pod。结果让我大吃一惊:
| 指标 | kube-proxy (iptables) | Cilium (eBPF) | 提升 |
|---|---|---|---|
| P50 延迟 | 2.3ms | 1.1ms | 52% |
| P99 延迟 | 8.7ms | 3.2ms | 63% |
| CPU 使用率 | 35% | 12% | 65% |
| 规则更新耗时 | 4.2s | 0.8s | 81% |
最让我意外的是规则更新耗时——iptables 模式下,每新增一个 Service 都需要全量刷新规则,而 Cilium 的 eBPF map 更新是毫秒级的。这意味着在动态变化的集群中,Cilium 的优势会更加明显。
踩坑实录:我犯的三个错误
错误 1:忽略 direct-routing-mode 配置
首次部署时,我没设置 routingMode: native,结果 Cilium 默认使用隧道模式(vxlan)。这导致 Pod 间通信走了额外封装,延迟反而比 Calico 还高。当时我差点就想放弃这个方案了,后来才发现是配置问题。
教训:如果你的节点在同一个二层网络,必须启用 native 路由模式。这个配置看似不起眼,但对性能影响巨大。
错误 2:未处理 externalTrafficPolicy: Local
迁移后,某个使用 externalTrafficPolicy: Local 的 Nginx Ingress 突然不能工作了。排查发现 Cilium 的 eBPF 实现需要额外配置:
# 在 values.yaml 中添加
k8s:
requireIPv4PodCIDR: true
bpf:
masquerade: true
这个坑让我花了整整一个下午才找到原因,所以如果你用了 externalTrafficPolicy: Local,一定要提前配置好。
错误 3:监控空白期
替换后 2 小时内,我的 Prometheus 监控完全空白。因为 Cilium 暴露的指标路径和 kube-proxy 不同。那种感觉就像开车没了仪表盘,心里完全没底。需要重新配置 scrape 目标:
# Prometheus 配置
scrape_configs:
- job_name: 'cilium'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_k8s_app]
regex: cilium
action: keep
metrics_path: /metrics
建议你在迁移前就配置好监控,这样切换后能立即看到效果。
回滚方案(希望你不必用到)
如果出现问题,最安全的回滚路径如下。我建议你先在测试环境演练一遍,真的遇到问题时就不会手忙脚乱:
# 1. 恢复 kube-proxy
kubectl -n kube-system scale deployment/kube-proxy --replicas=1
# 2. 等待 kube-proxy 就绪后,卸载 Cilium
cilium uninstall
# 3. 清理 eBPF 残留(每个节点执行)
mount | grep bpffs | awk '{print $3}' | xargs umount
rm -rf /sys/fs/bpf/*
延伸思考
这次替换让我发现,eBPF 的潜力远不止网络。Cilium 还提供了可观测性(Hubble)、安全策略(NetworkPolicy 的 eBPF 实现)等功能。如果你的集群规模超过 500 节点,或者对延迟敏感(如金融交易系统),值得深入研究。
但有一个场景我还没验证:混合云环境中,Cilium 的 ClusterMesh 跨集群通信到底能比传统 VPN 方案好多少?这可能是我的下一个实战方向。如果你有这方面的经验,欢迎和我交流!

评论已关闭!