机房网络和 K8S 网络,其实是同一套东西
从 Underlay 与 Overlay 的分层讲到 VXLAN 封装和 EVPN 控制平面,再对照 K8S 的 CNI、Service、EndpointSlice 和 Ingress,最后给出一张可以直接拿去排障的同构映射表。
有段时间我一直把这两件事分开看。机房网络是机房网络,交换机、BGP、VXLAN 是一套;K8S 网络是 K8S 网络,CNI、Service、Ingress 是另一套。直到有一次排查跨节点 Pod 访问超时,我从 Service 查到 Calico 的 IPIP 隧道,再查到 TOR 上的 BGP 邻居,最后发现是中间一台交换机的 MTU 少配了 4 个字节。那次之后才反应过来:这不是两套东西,是同一套东西换了个位置跑了第二遍。
这篇文章讲三件事:Underlay 和 Overlay 怎么分层,VXLAN 加 EVPN 到底在解决什么问题,以及 K8S 的 Service、EndpointSlice、Ingress 分别坐在机房网络的哪个位置上。把对应关系搞清楚之后,两边的技术是可以互相迁移着理解的,排障思路也能打通。
说明:文中的协议行为和默认值以主流厂商实现和常见发行版为准,具体参数请对照自己的设备型号、芯片和软件版本。生产环境改动前先确认配置备份和回滚路径。
#一、Underlay 只管一件事
#1. Spine-Leaf 和那张 /32 的表
现代机房的 underlay 目标非常单调:让所有 VTEP 的 loopback 地址互通,并且给出多条等价路径。它不认识租户,不认识虚机,也不认识 VLAN,只回答一个问题——这个 IP 我能不能送到。
拓扑是标准的 Spine-Leaf,Leaf 接服务器,Spine 只做转发,任意两台服务器之间有多条等长路径。每台设备一个 loopback /32,互联链路用 /31 或者干脆 unnumbered。所有链路全部激活,没有 STP 会来阻塞端口,带宽靠 ECMP 摊平。
判断一张 underlay 做得好不好,看三个指标就够:收敛时间、ECMP 哈希均匀度、故障域的收敛半径。第三个最容易被忽略,链路断了之后只有局部重算和全网重算,是两种完全不同的网络。
#2. OSPF 为什么在机房里待不住了
这个问题我被问过好几次。答案在 RFC 7938 里写得很直白,那份文档标题就叫《Use of BGP for Routing in Large-Scale Data Centers》,推荐用 eBGP 当数据中心内部唯一的路由协议。
区别在于两种协议同步状态的方式。OSPF 和 IS-IS 是链路状态协议,靠洪泛 LSA 让全网维护同一张拓扑图,任何一条链路抖动都会触发 SPF 重算。放在几百台交换机的 CLOS 里,LSA 满网飞,area 怎么划都别扭,调优手段基本只剩 cost 一个旋钮。
BGP 是增量通告的,链路抖动只影响相关的那几条路由。更关键的是策略能力:community、local-pref、AS-path、MED,想让某些流量走某条路、想给某个租户做引流,都有地方下手。加上 BGP Unnumbered(RFC 5549)配合自动邻居发现,配几行模板就能把整张网拉起来,对自动化运维友好得多。
一句话概括:OSPF 解决「怎么到」,BGP 解决「到不了、不许到、走哪条到」。
现实里的分布大致是这样:新建的云机房基本都是 eBGP;传统机房和园区网还在用 OSPF;运营商和超大规模厂商常见 IS-IS 做 underlay IGP,上面再套 iBGP 承载 overlay。如果你手里是存量网络,没必要为了追新把 OSPF 全拆了,能跑就行。
#二、Overlay 补的是 underlay 补不了的坑
#1. VXLAN 到底干了什么
Underlay 解决了可达性,但有四件事它解决不了,这四件事合起来就是 overlay 存在的全部理由。
VLAN 的 ID 只有 12 bit,4094 个,多租户场景下根本不够分。STP 会阻塞链路,把 CLOS 辛苦堆出来的带宽白白扔掉一半。虚机热迁移要求跨三层的二层域,迁移之后 IP 和 MAC 都不能变。多租户还需要 VRF(三层)和 VNI(二层)两层隔离,underlay 上只有一张路由表。
VXLAN 的做法很直接:把原始二层帧整个塞进一个 UDP 包里,目的端口固定 4789,外层 IP 头写的是 VTEP 的地址,然后交给 underlay 转发。VNI 有 24 bit,1600 万个,够用了。中间的交换机只看到外层 IP 和 UDP,对里面装的是什么完全无感。
#2. 外层 UDP 源端口不是随便填的
这是我觉得最容易被忽略、也最容易在生产上翻车的一个细节。
外层 UDP 的源端口必须由内层流的哈希算出来。如果所有隧道包的五元组都一样,ECMP 哈希就会极化,几千条流全被压到同一条 Spine 链路上。表现非常迷惑:带宽利用率看着只有百分之十几,业务却已经开始丢包。我第一次遇到的时候查了两天,最后抓包才看到所有包的外层源端口一模一样。
这个坑在 K8S 的 overlay CNI 里原样复现。所以如果你在选型时看到某个 CNI 文档里专门写了 UDP src port 的计算方式,那不是炫技,是真的踩过。
#3. 那 50 字节的代价
封装要加 14 字节外层以太头、20 字节外层 IP 头、8 字节 UDP 头、8 字节 VXLAN 头,一共 50 字节。主机或虚机还按 1500 发,到了 VTEP 封装就超了。
处理方式只有两条:开 jumbo,让 underlay 端到端跑 MTU 9000 以上;或者对 TCP 做 MSS clamp,让两端协商出合适的 MSS。选哪条取决于你能不能控制中间所有设备。跨机房、过专线、走公网隧道的场景下,中间经常有你控制不了的盒子,那就只能 clamp。
K8S 侧的数字我记一下,省得每次翻文档:Flannel VXLAN 默认 MTU 1450,Calico IPIP 默认 1480,Geneve 比 VXLAN 还要多吃几个字节。
#三、EVPN:把 MAC 地址当路由发出去
#1. flood-and-learn 撑不住
RFC 7348 定义的原生 VXLAN 是 flood-and-learn,靠数据平面泛洪来学 MAC 地址。小规模还能扛,规模一上去就三处崩:ARP 广播打满全网、未知单播到处复制、收敛只能等表项老化。
EVPN(RFC 7432 和 RFC 8365)的思路是一次范式转移:把 MAC 地址也当成路由来通告,用 MP-BGP 的 l2vpn evpn 地址族分发。BGP 从「通告网段」变成了「通告主机」。这个转变带来的收益比想象中大,因为主机的移动从此变成了一次普通的路由更新。
#2. 五类路由,真正要记住的只有两条
EVPN 定义了五类路由,实际排障和设计中高频出现的其实只有两条。
Type-2(MAC/IP Advertisement)通告的是主机 MAC 加 /32 主机 IP,再带上 VNI 和下一跳 VTEP。这就是「谁现在挂在哪台 Leaf 后面」的完整答案。有了它,Leaf 本地就能直接代答 ARP,全网不再需要泛洪,虚机迁移后新 Leaf 发一条 sequence 更大的 Type-2,全网立刻改指向。
Type-5(IP Prefix Route)通告的是网段,负责南北向和外部互联,跨机房的 DCI 场景主要靠它。
剩下三类:Type-1 处理 ESI 多归属和快速收敛,Type-3 用来自动建立隧道和维护 BUM 头端复制列表,Type-4 做多归场景下的 DF 选举。用到的时候再查也来得及,不用背。
#3. 任播网关
这条是理解 K8S Service 的钥匙,单独说。
传统三层网关是集中式的,所有跨网段流量都要绕到那台设备上。分布式任播网关的做法是:让所有 Leaf 配置同一个网关 IP 和同一个虚拟 MAC,虚机漂移到哪一台 Leaf 下面,网关都在本地,第一跳永远是最优路径。
请记住这个形态——一个 IP,在网络的每个接入点上都生效,流量就近完成转发。后面讲 Service 的时候你会发现,那完全是同一个东西。
#4. 对称 IRB 和非对称 IRB
跨网段转发有两种做法。非对称 IRB 要求每台 Leaf 上都有全部 VNI,配置量随租户数爆炸,现在基本没人用。对称 IRB 是源 Leaf 先查路由,再用 L3 VNI 封装送到目的 Leaf,只需要参与通信双方的 VNI,配合 VRF 做租户隔离,一次封装搞定。生产上见到的基本都是对称 IRB。
#四、K8S 把这套东西重做了一遍
#1. CNI 分两派
Pod 网络这一层,本质就是一个 overlay,而且分成了很明确的两派。
封装派和机房 VXLAN 完全同构:Flannel VXLAN、Calico VXLAN 和 IPIP、Cilium VXLAN 和 Geneve、OVN-Kubernetes。VTEP 从交换机芯片搬到了 Node 的虚拟网卡上,名字你大概率见过,flannel.1、cilium_vxlan、genev_sys。好处是对底层网络零要求,任何能通 IP 的环境都能跑起来。
路由派是真的把 Pod 网络并进了 underlay:Calico BGP、Cilium native-routing 和 BGP Control Plane、AWS VPC CNI。做法是让每个 Node 通过 BGP 把本机 Pod CIDR 通告给 TOR,或者直接让 Pod 拿到 VPC 里的 IP。优势很实在,零封装开销、MTU 不用打折、抓包所见即所得、可观测性好。代价是要底层配合,BGP 邻居、路由条目数、云厂商的路由表配额,每一项都有上限。
我的选择习惯:云上小集群用封装型最省心,大规模或者对性能敏感的场景走路由型,需要 L7 网络策略和可观测性就上 Cilium。
#2. Service 就是一个任播 VIP
这条是本文最想讲的一句。
kube-proxy 在每一个节点上都写一份「ClusterIP 到后端 Pod IP」的转发规则,流量出节点时就地完成转发,整个链路里不存在集中式的负载均衡设备。对比一下任播网关——一个 IP,在每个接入点上都生效,流量就近处理。这不是形似,是同一个设计换了个载体实现。
三种实现方式的差别主要在路径长度上:iptables 模式规则链线性遍历,万级 Service 时更新和匹配都很慢;IPVS 模式换成哈希表,性能大幅提升,但仍然要过 netfilter 和 conntrack;eBPF 模式在 socket 层就把目的地址改掉了,后面的路径完全不经过。
#3. EndpointSlice 就是那张表
早期的 Endpoints 是一个 Service 对应一个巨型对象,5000 个 Pod 的时候一次变更要序列化几 MB 的 JSON,直接把 apiserver 打爆。EndpointSlice 把它切成每片最多 100 条 endpoint 的多个对象,做增量更新。
它的定位和 EVPN Type-2 完全一致:告诉数据平面,这个名字背后现在有哪些真实的下一跳。区别只在于一个跑在 BGP 里,一个跑在 apiserver 的 watch 流里。
#4. Ingress、Gateway API 和机房的 ADC
Ingress 是 L7 入口,对应机房里的 ADC 或者 ADN,F5、A10、云的 CLB 加 WAF,同一个生态位。要注意 Ingress 本身只是一份 CRD 配置,真正转发流量的是 Ingress Controller,nginx、Envoy、Traefik 都在干这个活。
Gateway API 是它的继任者,把角色拆成了 GatewayClass、Gateway、HTTPRoute。拆开的好处是运维和业务的职责终于分清楚了,不用再往 Ingress 上糊一堆各家自定义的 annotation,也原生支持了 gRPC、TCP、UDP 和 TLS 路由。新项目我直接上 Gateway API。
#5. eBPF 干了什么
Cilium 的底座是 eBPF,核心动作是把转发逻辑从 netfilter 挪到内核的可编程点上,tc、XDP、sockops、cgroup 这几个位置都有挂载。
几个真正有用的能力:socket 层负载均衡在 connect() 阶段改写目的地址,整个转发不过 iptables、NAT 和 conntrack;host routing 让 Pod 出节点不走 bridge,直接 tc redirect;Hubble 提供 L3 到 L7 的流数据,还能看到 NetworkPolicy 的裁决记录;ClusterMesh 打通跨集群的 Pod 网络,对应的是机房里的 DCI。
顺带一提,Cilium 从 1.12 开始自带 BGP Control Plane,可以直接向 TOR 通告 Pod CIDR 和 Service VIP。自建机房里这基本等于把 MetalLB 的活并进了 CNI,少维护一个组件。
#五、一张对照表
把两边的东西摆在一起看,对应关系是这样的:
| 现代机房 | Kubernetes | 为什么是同一件事 |
|---|---|---|
| Spine-Leaf、eBGP、OSPF | 节点网络 | 只提供 IP 可达和 ECMP |
| VXLAN、VNI、VTEP | CNI overlay | 用封装换规模和隔离 |
| EVPN Type-2 路由 | EndpointSlice | 名字到下一跳的映射表 |
| 分布式任播网关 | Service ClusterIP | 每个接入点都生效的 VIP |
| L3 VNI 和 VRF | NetworkPolicy、Namespace 隔离 | 租户边界 |
| SLB 和 ADC | Service LB、Ingress、Gateway API | 南北向的两级负载 |
| Type-5 外部路由、DCI | LoadBalancer 通告、ClusterMesh | 跨域互联 |
| Leaf 交换机 | Node 上的 vSwitch | 接入层转发点 |
| BGP Route Reflector | kube-apiserver 加 etcd | 控制平面的状态源 |
| sFlow、IPFIX、INT | Hubble、SkyWalking、Prometheus | 观测平面 |
#六、etcd 和 BGP 不是一回事
对应归对应,有一处差异必须单独拎出来讲,因为它决定了两边完全不同的故障模型。
etcd 是 Raft 线性一致的分布式 KV,写不进去整个控制平面就停摆,所有变更全部卡住。BGP 是最终一致的增量路由协议,允许网络在一段时间内处于不一致状态,微环、短暂黑洞都是正常现象,路由慢慢收敛就好。
这个差别落到排障上非常具体。机房网络出问题,你看到的现象经常是「时好时坏」,因为表项正在收敛中,等一等可能自己就恢复了。K8S 出问题,你看到的现象经常是「完全没反应」,因为控制平面根本没在工作。这两种现象对应的排查方向完全不同,不能混着来。
至于 SkyWalking,它和 Hubble、机房里的 sFlow 属于同一层,都是观测平面。它不参与转发,但决定了你能不能看清链路上发生了什么。三个平面里缺任何一个,网络都谈不上可运维。
#七、K8S 比机房多了一层
还有个反直觉的点值得说一下:机房是两层,underlay 加 overlay,K8S 是四层。
多的那一层是 Service IP。机房里虚机的 IP 本身就是服务 IP,一个地址既是身份也是入口。K8S 因为 Pod 随时生灭,不得不在 Pod IP 之上再垫一层稳定的 VIP,让调用方有个不变的目标。
这层真正的对应物,其实是机房里 SLB 的 VIP 加健康检查。区别在实现方式:机房的 VIP 跑在专用硬件上,K8S 的 VIP 跑在每台机器的内核规则里。集中式设备被拆成了分布式的实现,这是 K8S 相对传统机房最本质的架构差异,也是它能扛住大规模 Pod 调度的原因。
#八、排障顺序
最后给一个我自己用的排查顺序,从下往上,每层验证完再往上走:
- Underlay:MTU、ECMP 哈希、BGP 邻居状态。这一层不通,上面全免谈。
- Overlay:VNI 配置、VTEP 之间是否可达、隧道状态。
- CNI:Node 上的路由表、封装网卡是否存在、IPAM 分配是否正常。
- Service:EndpointSlice 里有没有后端、NetworkPolicy 有没有拦、kube-proxy 规则是否同步。
- L7:Ingress Controller 的配置有没有生效、上游健康检查是否通过。
绝大多数「网络不通」的问题卡在第 1 层和第 4 层。第 1 层是 MTU,第 4 层是 EndpointSlice 里根本没有后端——Pod 起来了但 readinessProbe 没过,是最常见的一种。
#九、最后
回到开头那个 MTU 少配 4 个字节的问题。当时如果我脑子里有这张对照表,从 Service 一层层往下对,大概半小时就能定位,不至于查两天。
技术栈在变,从 ASIC 到 eBPF,从 BGP 到 etcd,但底下那套东西没怎么变:分层、解耦、用一层间接表把稳定的名字和可变的位置分开,再想办法让数据平面少绕路。记住这张表,下次遇到问题时至少知道该往哪一层看。
写于 2026 年 8 月 28 日
- 栏目
- 技术文章
- 约
- 12.7 分钟
- 字数
- 6.6K
- 阅读
- 85
本文为原创记录,转载请注明出处。如果这篇替你省了时间,欢迎留言说说你踩到的坑。
同题 · related
- H3C 企业园区与数据中心网络综合实验:VLAN、VXLAN EVPN、OSPF 与 BGP以 HCL 为实验环境,系统讲解 H3C 交换机、路由器、VLAN、VXLAN、EVPN、端口聚合、M-LAG、IRF、MSTP、静态路由、OSPF、BGP 和 ACL,并提供完整拓扑、配置模板、验收命令与故障排查流程。
- Kubernetes 核心组件详解与实战:从控制平面到应用上线系统讲解 kube-apiserver、etcd、Scheduler、Controller Manager、kubelet、容器运行时、CNI、Service、Ingress、存储、调度与安全,并提供完整的部署、验收和故障排查实践。
- 把博客从 docker-compose 搬到 k3s:单机迁移与上线全过程在 4C3.6G 的云主机上把博客从 docker-compose 迁到单机 k3s。附完整 YAML 清单(Deployment、Service、PVC、Ingress、Certificate 等)与全部配置命令,记录部署、数据迁移与切换上线的踩坑过程。
留言 · remarks
00 条还没有留言,来说点什么吧。