2023 年初我们 K8s 集群从 Calico 迁到 Cilium,理由是想用 eBPF 替代 iptables,顺便上 NetworkPolicy 七层规则。迁移走了一个季度,有惊喜也有翻车。本文复盘对比 Calico 和 Cilium,讲透两个方案的真实差异、迁移流程、性能对比和踩过的坑。
迁移背景
现状:K8s 1.27,500 个节点,5000+ Pod,Calico v3.24 over BGP
痛点:
- iptables 规则数 10w+,新增 Pod 慢(秒级延迟)
- NetworkPolicy 只能 L3/L4,要做 L7 规则得加 Envoy
- 排查丢包靠 conntrack,体验差
- 跨 AZ 流量观测能力弱
期望:
- eBPF-based 数据面,绕开 iptables
- L7 NetworkPolicy(HTTP 方法 + Path)
- Hubble 网络可观测性
- ServiceMesh 能力(不依赖 Istio)
候选:Cilium v1.14
架构对比
关键能力对比
维度 Calico Cilium
========================================================================
数据面 iptables / eBPF dataplane 纯 eBPF(tc/xdp)
NetworkPolicy L4 支持 支持
NetworkPolicy L7 不支持(要 Envoy) 原生支持(HTTP/Kafka/gRPC)
Service 实现 kube-proxy eBPF 替代 kube-proxy
Pod 间加密 WireGuard / IPsec WireGuard / IPsec
BGP 支持 原生(BIRD) v1.10+ 原生
观测能力 felix metrics + tcpdump Hubble(原生)
ServiceMesh 无 ClusterMesh(Cilium 自带)
默认 IP 分配 IPAM IPAM
跨集群 Cluster Mesh(Calico EE) ClusterMesh(开源)
学习成本 低(IPtables 大家都熟) 中(eBPF 概念多)
社区活跃度 高 很高(CNCF Graduated)
内核要求 3.10+ 4.9+(推荐 5.4+)
性能压测
压测环境:K8s 1.27, 3 节点 × 16C 32G, Pod 间 iperf3
测试 1:Pod 间 throughput(同节点)
Calico iptables : 22.5 Gbps
Calico eBPF data : 28.0 Gbps
Cilium : 32.5 Gbps
测试 2:Pod 间 throughput(跨节点)
Calico iptables : 9.2 Gbps
Calico eBPF data : 10.8 Gbps
Cilium : 11.5 Gbps
Cilium native routing: 12.1 Gbps
测试 3:Service 转发延迟(p99)
kube-proxy iptables: 850us
kube-proxy ipvs : 420us
Cilium eBPF : 180us
测试 4:NetworkPolicy 多规则
1000 条 policy:
- Calico iptables:每个新 Pod 5-8 秒生效
- Cilium:每个新 Pod 1 秒内生效
CPU 占用(满负载):
- Calico iptables felix:每节点 1.2 core
- Cilium agent:每节点 0.5 core
迁移流程
不能直接卸 Calico 装 Cilium,会全集群断网
正确流程:
1. 准备:Cilium 部署在 chained CNI 模式,与 Calico 共存
2. 节点滚动:逐节点替换 CNI(cordon → drain → 改 CNI → uncordon)
3. 验证:每滚 10 个节点跑一遍业务验证
4. 切换 NetworkPolicy:Calico CRD → Cilium CRD
5. 清理:卸载 Calico DaemonSet 和 CRD
Cilium 部署
# 1. cilium CLI
$ curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz
$ tar xzf cilium-linux-amd64.tar.gz && mv cilium /usr/local/bin/
# 2. 检查内核
$ uname -r # 5.4+ 推荐
$ ./cilium status
# 3. Helm 安装(参数最重要)
$ helm install cilium cilium/cilium \
--namespace kube-system \
--version 1.14.4 \
--set kubeProxyReplacement=true \
--set k8sServiceHost=k8s-api.internal \
--set k8sServicePort=6443 \
--set ipam.mode=kubernetes \
--set routingMode=native \
--set ipv4NativeRoutingCIDR=10.0.0.0/8 \
--set bpf.masquerade=true \
--set bandwidthManager.enabled=true \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set hubble.metrics.enabled='{dns,drop,tcp,flow,icmp,http}'
# 4. 验证
$ cilium status
$ cilium connectivity test
# 这个命令会跑一堆 Pod 测试连通性,跑通就 OK
替代 kube-proxy
# Cilium 完全替代 kube-proxy(不需要 kube-proxy DaemonSet)
$ helm upgrade cilium cilium/cilium \
--reuse-values \
--set kubeProxyReplacement=true
# 删掉 kube-proxy
$ kubectl -n kube-system delete ds kube-proxy
$ kubectl -n kube-system delete cm kube-proxy
$ for node in $(kubectl get node -o name); do
kubectl annotate $node --overwrite k8s.example.com/kube-proxy-replaced=true
done
# 节点执行(清理残留 iptables 规则)
$ iptables-save | grep -v KUBE | iptables-restore
$ ipvsadm -C
L7 NetworkPolicy
# 传统 NetworkPolicy(L3/L4)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
spec:
podSelector: {matchLabels: {app: backend}}
policyTypes: [Ingress]
ingress:
- from:
- podSelector: {matchLabels: {app: frontend}}
ports:
- port: 8080
protocol: TCP
# Cilium L7 NetworkPolicy(HTTP)
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: backend-l7
spec:
endpointSelector:
matchLabels: {app: backend}
ingress:
- fromEndpoints:
- matchLabels: {app: frontend}
toPorts:
- ports: [{port: "8080", protocol: TCP}]
rules:
http:
- method: GET
path: /api/v1/orders/.*
- method: POST
path: /api/v1/orders
headers:
- "X-User-Role: admin"
# 拒绝其他 HTTP
# Kafka L7 policy
- toPorts:
- ports: [{port: "9092", protocol: TCP}]
rules:
kafka:
- role: produce
topic: events
- role: consume
topic: events
consumerGroup: backend-consumer
Hubble 可观测性
# Hubble CLI
$ hubble observe --service productpage --since 1m
# Mar 15 14:23:01.123: default/frontend:42158 (ID:1234) -> default/backend:8080 (ID:5678) http-request FORWARDED (HTTP/1.1 GET /api/orders)
# Mar 15 14:23:01.180: default/backend:8080 (ID:5678) -> default/frontend:42158 (ID:1234) http-response FORWARDED (HTTP/1.1 200 57ms)
# 过滤特定问题
$ hubble observe --verdict DROPPED --since 5m
# 看所有被 NetworkPolicy 拒绝的流量
# Hubble UI(图形化)
$ kubectl port-forward -n kube-system svc/hubble-ui 8080:80
# Prometheus 指标
hubble_drop_total{reason="Policy denied"}
hubble_flows_processed_total
hubble_tcp_flags_total
踩过的坑
坑 1:kube-proxy 不能直接删,要先验证 service
直接删 kube-proxy 导致服务断流 5 分钟
正确步骤:先开 Cilium kubeProxyReplacement,验证 svc 都正常再删 kube-proxy
$ kubectl run test-svc --image=busybox --rm -it -- wget -O- http://kubernetes.default.svc
坑 2:LoadBalancer 类型 Service 不通
原因:云厂商 LB 通过 nodePort 转发,Cilium 替代 kube-proxy 后 nodePort 处理在 eBPF
某些云 LB 健康检查走 iptables 规则,Cilium 看不到
修法:配置 nodePort.directRoutingDevice 或 nodePort.localRedirectPolicy
nodePort:
enabled: true
directRoutingDevice: eth0
enableHealthCheck: true
bindProtection: true
坑 3:Pod 重启后 IP 变了影响连接
业务长连接,Pod 滚动重启 IP 变,客户端没重连
原因:NetworkPolicy 按 label 选,IP 变其实不影响 policy
问题在业务 client 端,不是 CNI 的锅
修复:client 加自动重连 + DNS TTL 调短
坑 4:DNS 流量被 L7 policy 干扰
# 默认 deny all 把 DNS 也 deny 了
# 必须显式放开 DNS
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-dns
spec:
endpointSelector:
matchLabels:
io.kubernetes.pod.namespace: default
egress:
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports: [{port: "53", protocol: UDP}]
rules:
dns:
- matchPattern: "*"
坑 5:Cilium agent OOM
现象:节点 Cilium agent 占用 4GB 内存
原因:endpoint 太多(每节点 80+ Pod),eBPF map 占用大
默认 map 大小可能撑不住
修复:调大 map 大小
bpf:
ctTcpMax: 524288
ctAnyMax: 262144
natMax: 524288
policyMapMax: 32768
authMapMax: 524288
resources:
limits:
memory: 8Gi
requests:
memory: 2Gi
坑 6:NetworkPolicy 调试难
# Pod 间不通,policy 是不是干扰?
$ cilium policy trace --src-pod default/frontend --dst-pod default/backend --dport 8080
# Verdict: DENIED
# Resolving ingress policy for [{app=backend}]
# Final verdict: DENIED
# Reason: No allowed rules
# 看具体被哪条规则拒绝
$ hubble observe --pod default/frontend --verdict DROPPED -f
迁移完后的实际收益
指标 Calico Cilium 变化
=============================================================
Pod 启动时延(NetworkPolicy 多) 5-8s 1s -85%
Service p99 延迟(集群内) 850us 180us -79%
节点 CPU 占用(网络组件) 1.2 core 0.5 core -58%
观测能力(诊断时长) 查 iptables 30min hubble 5min -83%
L7 policy 支持 无 原生
排障体验 翻 conntrack Hubble UI
新增能力:
- Hubble 实时流量观测
- ClusterMesh 跨集群通信
- BGP 原生(替代外部 MetalLB)
- WireGuard 透明加密(开启一行配置)
- ServiceMesh 能力(Cilium ServiceMesh)
什么时候不要换 Cilium
1. 内核 < 4.9 — 不支持
2. 团队对 eBPF 完全陌生 — 排障期会很痛苦
3. 现有 Calico 跑得很好 — 不要为换而换
4. 集群规模 < 50 节点 — 收益不明显
5. 用 Calico Enterprise 商业功能 — 商业支持是关键
6. 严重依赖 Calico Felix metric 报警 — 迁移要重写
避坑清单
- 迁移前升级内核到 5.4+,新特性 + BTF 支持
- 不能直接卸 Calico 装 Cilium,中间要共存阶段
- kubeProxyReplacement 开启前测试所有 Service 类型
- NetworkPolicy 默认 deny 模式下要放开 DNS
- Cilium agent 内存按 endpoint 数算,大集群调大 map
- Hubble 看流量很爽,但生产关掉 hubble.metrics 全开(高基数)
- L7 policy 性能比 L4 差,只对关键链路用
- BGP 模式注意 ASN 不冲突,跨集群规划好
- 升级 Cilium 跨 minor 版本要看 changelog,有 breaking change
- cilium connectivity test 是迁移完最值得跑的命令
总结
Calico 是 K8s 网络的"老朋友",成熟稳定文档全。Cilium 是新一代基于 eBPF 的方案,性能 + 观测 + L7 能力都更现代。但 eBPF 不是银弹:学习成本高,排障思路得换。我们换了一个季度,收益最明显的是 Hubble — 以前查"为什么 Pod 间不通"要翻 iptables + conntrack + tcpdump,现在 hubble observe 一行命令搞定。如果你的集群规模上千节点 + 团队愿意投入学习,Cilium 是明确推荐;如果只是几十节点的小集群,Calico 仍然是稳妥选择。
—— 别看了 · 2026