一粟 · yisu.app
· 15 min read
Kubernetes

Kubernetes 网络模型深度剖析:CNI、Service Mesh 与 eBPF

把 K8s 的 iptables、IPVS、CNI、Sidecar、eBPF 拼成一张完整的图。理解它们各自的代价与延迟来源,是解决「Pod 之间为什么总有几毫秒抖动」问题的前置功课。

K8s 网络模型的两条铁律

Kubernetes 的网络模型看上去复杂,核心只有两条铁律:

  • Pod - Pod 直连:所有 Pod 在不使用 NAT 的前提下彼此可达。
  • 节点 - 节点直连:所有节点也可以和所有 Pod 直连。

这两条「看似简单」的要求带来了非常多的实现复杂度。CNI、Service Mesh、kube-proxy、eBPF 本质都是为这两条铁律服务。

CNI:Pod 网络的「网线」

CNI(Container Network Interface)解决的是「Pod 启动时拿到 IP、得到路由、与其他 Pod 通」。主流实现包括 Calico、Cilium、Flannel、Antrea。

它们的差别主要在:

实现封装策略性能
FlannelVXLAN不支持
CalicoBGP支持
CiliumeBPF支持最高
AntreaOVS支持
选 CNI 之前先想清楚:你是否需要 NetworkPolicy?是否需要高并发?是否需要链路加密?

kube-proxy:从 iptables 到 IPVS 到 eBPF

Service 是 K8s 给一组 Pod 提供「稳定入口」的能力。它的工作机制背后是 kube-proxy:

  • iptables 模式:为每条 service 写一组 iptables 规则。简单但慢,集群大时 P99 抖动明显。
  • IPVS 模式:用内核虚拟服务器 hash 表查。后端规模上万仍稳定。
  • eBPF 模式:Cilium 提供,绕过 iptables,性能与可见性最好。
# 检查 kube-proxy 模式
kubectl get kube-proxy -o yaml | grep mode
# 切换为 IPVS
kubectl -n kube-system patch ds kube-proxy --type merge \
  -p '{"spec":{"template":{"spec":{"containers":[{"name":"kube-proxy","args":["--proxy-mode=ipvs"]}]}}}}'

Service Mesh:把网络功能塞进 Sidecar

Service Mesh(Istio / Linkerd / Kuma)通过 Sidecar 把「超时、重试、熔断、加密、可观测」做到 Pod 内部。它的代价是「多一跳」,但收益是「业务代码完全解放」。

Sidecar 模式让请求多了 1 个本机内部跳转。每次跳转都增加 0.2–0.5ms 的延迟。当一个链路有 8 个微服务时,单纯 Mesh 就会增加 1.6–4ms。

这种代价对大多数业务是值得的,但对极致低延迟(如游戏后端、高频交易)就要谨慎评估。

eBPF:它替代了什么、它提供了什么

eBPF 允许在内核中运行沙箱程序,而无需修改内核源码或加载内核模块。它在 K8s 网络里最常见的应用是:

  • 替代 kube-proxy:用 eBPF 哈希表做 service 转发。
  • 替代 CNI 部分功能:Cilium 把网络策略、加密、负载均衡全部用 eBPF 实现。
  • 可观测性:Hubble 提供细粒度的网络流观测。

我们做过的对比:Cilium eBPF 模式下,集群内 P99 抖动从 3ms 降到 0.8ms。CPU 也明显降低。

Mesh or eBPF?二选一还是共存?

这两年最常见的争论是「eBPF 是否会取代 Service Mesh」。我的看法是:

  • eBPF 解决的是「网络转发」与「基础策略」。
  • Mesh 解决的是「应用层路由、灰度、流量镜像、精细治理」。

两者解决的问题域不一样。eBPF 会吃掉 Mesh 在「网络层」的需求,但应用层的精细能力 Mesh 仍然占优。

当下最稳的实践是:Cilium 做 CNI + 策略;Linkerd 或 Istio 做细粒度流量治理。

DNS:被忽视的抖动源

K8s 内 Pod 之间通信经常遇到「奇怪的几百毫秒延迟」。很多时候凶手是 CoreDNS:

  • Service 域名解析需要跨域查询。
  • 外部域名查询可能被 NAT 拦截。
  • 上游 DNS 限速会影响集群。

解决方案:在 NodeLocal DNSCache 部署本地缓存,把跨节点 DNS 查询转为本地命中。这是肉眼可见的延迟下降。

MTU 与分片:那些把链路毁掉的细节

在 VXLAN 模式下,每个 Pod 包被封装成 UDP 后总长度变大。如果 MTU 设置不当,会被中间网络分片。分片带来 100ms+ 抖动。

具体建议:

  • VLAN 环境下 MTU 设为 1500。
  • VXLAN 设为 1450 或更小。
  • ping -M do -s 1472 验证不分片上限。

真实案例:把 Pod 间延迟从 4ms 压到 0.8ms

某次我们遇到「Pod 间通信尾巴抖动 4ms」,用两周排查。问题定位在三个叠加点:

  1. kube-proxy 是 iptables 模式,几千条 Service 规则慢路径。
  2. Veth pair 没启用 checksum offload。
  3. CoreDNS 跨节点查询。

改造方案:

  • kube-proxy 切 IPVS。
  • 网卡 GRO/GSO 启用。
  • NodeLocal DNSCache。
  • Service Mesh Sidecar 切到 Ambient Mesh(去除本机跳转)。

改造后 P99 从 4ms 降到 0.8ms。这是把 K8s 网络模型「拆开看」才能换来的收益。

给团队的几条建议

  • 先理解 K8s 网络模型两条铁律,再选 CNI。
  • kube-proxy 模式下首选 IPVS。
  • eBPF 是趋势,但 Mesh 仍有不可替代的能力。
  • DNS 与 MTU 都是「看不见的抖动源」,必查。

K8s 网络不是一层协议。它是若干层叠加,每一层都有自己的代价。我们不能期待「换组件」解决一切,但理解结构能让我们「不再迷信任何单点」。