阶段一 · 网络:从一个容器讲起

Pod 跨节点通信:Overlay 与路由

一句话总结

同节点 Pod 靠网桥就能通, 但跨节点时, Pod IP 在物理网络里"查无此路".
解决它有两条路线: Overlay 把 Pod 的包用 VXLAN 装进一个外层包"偷渡"过去(通用但有性能损耗), L3 路由直接让物理网络学会到各节点 Pod 网段的路由(高效但依赖网络环境).

前置回顾

第 01 篇我们用 namespace + veth + 网桥搭通了同节点容器网络, 也留了个问题: 如果两个 Pod 在不同主机上, 网桥这套就不灵了. 同时 K8s 网络模型要求"所有 Pod 扁平互通, 无 NAT"--这个要求在跨节点时最难满足.

本篇就是攻克它, 这也是各 CNI 插件(第 03 篇)真正拉开差异的地方.

跨节点为什么会断

把第 01 篇的场景搬到两台主机上, 问题立刻暴露:

节点 1 (物理 IP 192.168.1.10)        节点 2 (物理 IP 192.168.1.20)
Pod A: 10.244.0.2 Pod B: 10.244.1.2
│网桥 cni0 │网桥 cni0
└─ 物理网卡 eth0 └─ 物理网卡 eth0
└──────── 物理交换机 ─────────┘

Pod A (10.244.0.2) 要访问 Pod B (10.244.1.2):
1. 包从 Pod A 出来, 到节点1的网桥 → 目标 10.244.1.2 不在本网桥 → 上送主机路由
2. 主机要把它发出去, 但问题来了:
物理网络(交换机/路由器)根本不认识 10.244.x.x 这些 Pod IP!
它只认识 192.168.1.x 这些物理 IP
3. 包发到物理网络 → "10.244.1.2 在哪? 不知道" → 丢弃

根子在于: Pod IP(10.244.x.x)是 Kubernetes 虚构出来的一套地址, 物理网络设备对它一无所知. 物理交换机只会按物理 IP(192.168.1.x)转发. 怎么让一个"物理网络不认识的包"跨过物理网络到达对端? 两条思路, 泾渭分明.

Pod IP 像公司内部的工位号(3 楼 17 号), 物理网络像城市的邮政系统(只认街道门牌).
写"送到 3 楼 17 号"邮局根本不知道往哪送. 要么把内部信装进一个写了街道地址的大信封(Overlay), 要么让邮局也学会"3 楼 17 号 = XX 街 88 号"这套对应关系(路由).

路线一: Overlay -- 给包套个"信封"偷渡过去

Overlay(叠加网络) 的思路: 既然物理网络不认识 Pod IP, 那就把整个 Pod 的数据包(包括它的 Pod IP)当作"数据", 塞进一个新的, 用物理 IP 寻址的外层包里. 物理网络只看外层包, 正常转发; 到了对端节点再拆开外层, 取出原始 Pod 包送达. 这个"套信封"的动作叫封装(Encapsulation), 最常用的封装协议是 VXLAN.

节点1 把 Pod A→Pod B 的包封装:

┌─────────────────────────────────────────────┐
│ 外层: 节点1物理IP(192.168.1.10) → 节点2物理IP(192.168.1.20) │ ← 物理网络只看这层
│ ┌─────────────────────────────────────┐ │
│ │ 内层(原始包): Pod A(10.244.0.2) │ │ ← 物理网络看不到, 当数据
│ │ → Pod B(10.244.1.2) │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘

物理网络按"192.168.1.10→192.168.1.20"正常转发 → 到节点2拆封 → 取出内层包送给 Pod B
Overlay 的优点 Overlay 的代价
对物理网络零要求: 只要节点间物理 IP 能通, Pod 网络就能跑--物理网络无需任何配置 封装开销: 每个包多一层头(VXLAN 约 50 字节), CPU 要做封装/解封装, 有性能损耗
部署简单, 可移植: 几乎任何环境(自建机房, 各家云)都能用 抓包难排查: 外层包了一层, 排查时要先解封装才看得到真实流量
Pod 网络和物理网络解耦 MTU 要留意(外层头吃掉一部分, 易出现分片问题)

Flannel 的默认模式(VXLAN)就是典型 Overlay. 它胜在"无脑能用"--这也是它成为最简单 CNI 的原因.

路线二: L3 路由 -- 让物理网络学会 Pod 网段的路由

第二条路线根本不封装, 而是反过来让网络学会怎么找到 Pod IP: 在每个节点和(可选的)物理路由器上, 写好路由规则--"要去 10.244.1.0/24 这个网段, 下一跳是节点2(192.168.1.20)". 这样 Pod 包原封不动地在物理网络上传输, 靠路由表逐跳转发.

核心: 给每个节点分一个 Pod 子网, 并让路由表知道"哪个子网在哪个节点"

节点1 的路由表:
10.244.0.0/24 → 本地(本节点的 Pod)
10.244.1.0/24 → 下一跳 192.168.1.20(节点2) ← 关键路由
10.244.2.0/24 → 下一跳 192.168.1.30(节点3)

于是 Pod A→Pod B 的包(目标 10.244.1.2)直接按这条路由发给节点2, 不封装, 不套信封

问题是: 节点很多, Pod 网段会变, 这些路由谁来维护, 怎么同步到所有节点甚至物理路由器? 答案是 BGP(边界网关协议)--互联网级别的动态路由协议. 每个节点跑一个 BGP 程序, 自动把"我这台节点负责哪个 Pod 网段"广播给其他节点和路由器, 大家动态学习, 自动更新路由表.

L3 路由的优点 L3 路由的代价
无封装开销: 包原样传输, 性能接近物理网络, CPU 不做封装 对物理网络有要求: 需要网络支持(尤其想让物理路由器也学路由时, 要 BGP 支持)
易排查: 抓到的就是真实 Pod 包, 没有外层伪装 跨子网/跨可用区时可能受物理网络拓扑限制
Pod IP 在物理网络里"真实可见可路由" 配置和理解门槛比 Overlay 高

Calico 的标志性(原生)模式就是 BGP 路由(无封装), 跨子网时则用 IPIP/VXLAN 兜底. 它胜在"快且透明", 是性能敏感场景的常见选择.

两条路线的本质对立: 封装 vs 路由

把取舍浓缩成一句话: Overlay 用"封装"换"对物理网络零依赖"(牺牲性能换通用), L3 路由用"对物理网络的要求"换"原生性能"(牺牲通用换高效).

没有谁绝对更好--自建/异构/图省事 → Overlay; 性能敏感, 网络可控 → 路由. 这个"通用 vs 高效"的权衡, 在 CI/CD 和可观测性系列里见过无数次(Loki vs ES, 尾部采样的取舍), 它是基础设施选型的永恒主题.

还有一种折中: IPIP / 混合模式

现实中不是非黑即白. 比如 Calico 在"节点都在同一二层网络"时用纯 BGP 路由(零开销), 一旦跨子网(路由不通)就自动切换成 IPIP(一种比 VXLAN 更轻的封装)兜底--这叫 CrossSubnet 模式.

即"能路由就路由, 路由不了才封装", 兼顾性能与通用. 理解了封装和路由两条主线, 这类混合策略就一看就懂.

一张表看懂两条路线

维度 Overlay(封装, 如 VXLAN) L3 路由(如 BGP)
跨节点怎么走 原始包装进外层包, 物理 IP 寻址 原始包按路由表逐跳转发
性能 有封装开销(略低) 接近物理网络(高)
对物理网络要求 几乎没有(物理 IP 通即可) 较高(BGP / 路由可控)
排查难度 高(要解封装) 低(真实包)
典型代表 Flannel(VXLAN) Calico(BGP)
适合 自建/异构环境, 图省事 性能敏感, 网络可控

承上启下

到这里, 从单节点(第 01 篇)到跨节点(本篇)的 Pod 通信原理就完整了. 但有个一直回避的问题: 这些 namespace, veth, 网桥, VXLAN 隧道, BGP 路由, 在 Pod 创建的瞬间是谁, 怎么自动配好的? 以及--Flannel, Calico 这些名字反复出现, 它们到底是什么, K8s 怎么调用它们? 答案就是 CNI: Kubernetes 把"配置 Pod 网络"这件事定义成一个标准接口, 外包给可插拔的插件. 下一篇揭开它.

快速回顾

  • 跨节点断在哪: Pod IP 是 K8s 虚构的, 物理网络只认物理 IP, 不知道怎么转发 Pod 包
  • Overlay(封装): 把原始 Pod 包装进用物理 IP 寻址的外层包(VXLAN)偷渡过去; 优点是对物理网络零要求, 通用, 代价是封装开销和排查难; 代表 Flannel
  • L3 路由: 不封装, 让节点/路由器学会"哪个 Pod 网段在哪个节点"的路由(常用 BGP 动态同步); 优点是原生性能, 易排查, 代价是对物理网络有要求; 代表 Calico
  • 本质对立: 封装(通用换性能) vs 路由(性能换通用); 还有 IPIP/CrossSubnet 这类"能路由就路由, 不行才封装"的折中
  • 选型: 自建/异构/图省事 → Overlay; 性能敏感/网络可控 → 路由

VXLAN 的封装开销到底有多大? 值得为它换成路由吗?

VXLAN 外层头约 50 字节, 加上封装/解封装的 CPU 开销, 通常带来个位数到百分之十几的吞吐损耗(取决于包大小和硬件, 小包影响更明显). 对绝大多数业务, 这点损耗完全可接受, Overlay 的"省心"更值钱.

只有在网络密集型, 对延迟/吞吐极敏感的场景(高频交易, 大数据传输), 才值得为路线二的复杂度买单. 别为了"听起来更快"就盲目上 BGP--多数团队 Flannel 足矣.

云厂商的托管 K8s(EKS/GKE/ACK)用的是哪条路线?

很多云厂商走了"第三条路"--直接用云自己的 VPC 网络给 Pod 分配真实的 VPC IP(如 AWS VPC CNI). 这样 Pod IP 就是 VPC 里真实可路由的 IP, 既没有 Overlay 封装开销, 又不用自己配 BGP, 因为云的 VPC 网络底层已经把路由这件事做好了.

这本质上是"L3 路由"路线的一种云原生实现--把路由的复杂度下沉给了云平台. 代价是 Pod IP 会消耗 VPC 的 IP 地址空间. 这也再次印证: 具体怎么实现, 取决于所处环境--而 CNI(下一篇)正是让能按环境换实现的那个接口.

动手练习

  1. 起多节点集群观察: 用 kind 或 k3d 起一个多节点集群(默认通常是 Flannel/类 Overlay), 把两个 Pod 调度到不同节点, 确认它们能互 ping
  2. 抓包看封装: 在节点上用 tcpdump 抓物理网卡的包, 观察跨节点 Pod 流量是否带 VXLAN 外层头(UDP 8472 端口), 亲眼看到"封装"
  3. 读路由表判断路线: 查看节点的路由表(ip route), 找出"去往其他节点 Pod 网段"的路由条目--对照本篇两条路线, 判断集群走的是封装还是路由
  4. 对比 Calico 集群(进阶): 换成 Calico 起一个集群, 对比: 跨节点流量还有 VXLAN 外层吗? 路由表里多了什么? 用本篇的两条路线解释差异
  5. 结合实际选型: 思考: 实际的 K8s 跑在自建机房还是云上? 根据环境, Overlay 和路由哪条更合适? 为什么?