容器 25:跨主机路径为什么会出现小包通大包失败
两端都要记录
直路由路径要使两端宿主知道对端 Pod 子网经哪个下一跳到达;封装路径则由 overlay 端点把内层包封装为外层 UDP/IP,并靠 underlay 递送到另一端。无论哪种,先在两台独立 VM 固定 IP、路由表、接口 MTU 和过滤规则,双端抓包,再逐次提高自建探针请求负载的报文大小。若小包成功、大包失败,比较内层 DF/分片、外层包长、ICMP Packet Too Big 和 PMTU 发现,不能只说“网络不稳定”。
正常例应验证小包与不超过路径 MTU 的大包均到达;边界例将受控 overlay 接口 MTU 配错,使请求在确定阈值附近失败,再调整仅属于实验的配置并复核双端结果。计时与丢包只报告真实抓包次数,不估算“合理的”性能数。本环境没有两台独立 VM、ip 或 CNI 插件,直路由与 VXLAN 对照、双端抓包、故障与恢复全部 NOT_RUN;补跑方案和探针源码包仍可独立取得。
内层地址不替 underlay 转发
两台 VM 上的进程若位于不同宿主,连接不可能只凭在各自本地创建 veth 就跳到对方内存中的网络栈。22 的 veth 把帧送到本机 peer,23 的 bridge 可以让同一节点上的端口转发;从节点 A 到节点 B,仍要让宿主网络对目标有路由和能到达的下一跳。所谓“Pod IP 在两台机器上看起来连号”,不过是逻辑地址安排,不能推出物理交换机已把两个节点的容器网卡接进同一 VLAN。实验先分别保存 A/B 的宿主 underlay 地址与路由,确认 ip route get <对端宿主地址> 的实际出接口,再谈 overlay;否则 VXLAN 外层包根本到不了对端,即使本地 VXLAN 接口显示 up 也没有意义。
直路由方案让 underlay 知道去对端容器网段的下一跳。A 往 B 的 Pod 网段发包时,A 的路由选择对端 VM,B 接收并送到它拥有的本地 endpoint;回包也需要从 B 知道如何抵达 A 的网段。只在 A 增加去 B 的路由,可以制造“请求到了 B,但响应回不来”的不对称故障。真正验收要在两端读目的、源地址选择和返回方向;A 的 TCP 连接未建立不自动证明 A→B 方向丢了包,B→A 也可能有过滤或错误路由。计划要求的“两台 VM 直路由与封装对照”必须分开保存各阶段双端抓包,不能用单宿主上 ping localhost 代替。
VXLAN 路径则由 VTEP 把内层以太网帧装进外层 UDP/IP,外层的源/目的通常是两个 underlay 可达的端点;对端收到并解封装后再在其本地网络继续转发。RFC 7348 描述封装格式与 VNI 所标识的虚拟网络,Linux kernel VXLAN 文档展示配置对象与工具入口。这里选一个专用 VM 内的新建 VXLAN 设备和测试 VNI,不声称 Kubernetes 的任何 CNI 一定这样实现,也不认为“发现 UDP 4789”就证明容器流量正在走预定 Pod 网络;VM 的隧道端点、VNI、MAC 学习方式及出入口抓包必须相符。若实际环境有硬件卸载,抓包所在位置可能看到封装前或解封装后的帧,报告应标清接口与抓包点,不为凑一张理论图修饰原始记录。
封装也有安全与作用域条件:只在两台独占 VM 的自建虚拟网络上建立 VXLAN,不改默认路由、默认防火墙或正在服务其它任务的 Pod 网段。VM 管理者需要分配 underlay IP、允许两机在专用端口互访并保证测试网段不与现有业务冲突;如果只能借共享生产 VTEP,实验维持 NOT_RUN。一侧关闭本篇自建 VXLAN 接口后,可以看到本机 VTEP 停止发送/接收;另一侧的具体 TCP 错误和抓包结果要实测,不能把“理论上应超时”填成实际日志。恢复时仅对自建接口重新设置 up,验证两端路由、probe-app HTTP 结果及抓包,然后按创建记录删除这两个设备。
MTU 是哪一层报文的最大值
MTU 描述链路接口可承载的网络层报文大小;应用一次 POST /state 的 body 大小、一次 TCP write 的字节数和真正送上 underlay 的单个报文大小并非一一对应。TCP 会协商 MSS、分段和重传,网卡卸载还可能改变抓包时看到的报文形态。因此只把 probe-app 的 POST 从 100 字节增到 4 KiB,就把某次失败归因为“超过 MTU”,是不成立的。为了单独测试 MTU,VM 可以用 iputils ping -M do 指定 DF 策略与受控 -s payload,在 IPv4 下逐项记录实际生成的报文长度与工具返回;应用 HTTP 只证明业务路径在相同拓扑上可用,不证明它发出了与 ICMP 同样长度的一只 IP 包。
封装后外层必须容纳内层帧和额外头部;IPv4/IPv6 外层、UDP、VXLAN、内层以太网头、可选扩展头及底层承载都会影响净可用值。RFC 7348 提供 VXLAN 帧格式,不要求所有环境都用同一数值 MTU。先从 A/B 的 ip -d link show 读 underlay/VXLAN/veth 的真实 MTU,从双端抓包计算本次外层 IP 总长与内层报文的关系,再据最小一跳估计可承载范围;没抓到实际头部和路径限制前,不用教材里一个固定“减 50 字节”替换 VM 的数值。若二层还加 VLAN 或隧道里继续套隧道,开销和数据路径又会变化,必须重新记录而不能外推。
在 Linux VM 上将本篇新建的 VXLAN 接口 MTU 设得偏大,可以构造“本地认为能发,而 underlay 较小”的边界;若端系统收到合适的 ICMP Packet Too Big/Fragmentation Needed 并调整路径 MTU,它可能正常恢复或以较小包继续,不能预先断言所有大包必失败。RFC 1191 的 IPv4 Path MTU Discovery 解释了 DF 报文遇到较小链路及反馈的关系。若过滤丢掉必要的反馈,小包成功而大包等待/重传的表现就更容易出现;但本系列禁止在共享宿主关掉安全机制或随意操作全局过滤,专用 VM 也只允许针对自建测试端口和接口的可恢复规则。每一轮把报文大小、DF 选项、实际 ICMP 回包和出入口抓包放一起,才可能证明失败阈值属于 MTU 而非应用超时、路由错误或 listener 未启动。
正反两条路径怎样记录
在两台 VM 建直路由基线时,不要先堆满规则。先从 A 请求 B 的宿主 underlay IP 上的自建 probe-app /health,确认两机之间可达;然后在各自实验网段加入针对另一端测试网段的路由,让 A/B 从受控源地址访问彼此;保存每次 ip route get、应用监听、curl 状态与两端抓包。两个 VM 如已有生产默认路由,禁止改动,只新增和删除本篇前缀的路由。任何一次路由添加失败都要保存命令退出码和当时对象,不把一次普通宿主 IP 的成功当作直路由容器网段通过。如果缺 Pod sandbox/容器 ID,这仍只是两机网络模型对照,不能声称真实 Kubernetes Pod 已跨节点通信。
再在两台 VM 上各建一只只属于本篇的 VXLAN 端点,记录 VNI、本地/对端 underlay IP、端口、隧道接口与 MTU,给 overlay 测试接口分配互通地址,并确认两边各自到对端 VTEP 有 underlay 路由。probe-app 使用同一归档、同一端口、同一 /health,只是客户端从 B underlay 地址切换到 B overlay 地址;成功返回可证明这条请求经过了配置的逻辑目标,却仍需在 underlay 抓到本次对应的 UDP 封装报文、在 overlay 内侧看到原始请求,才能证明具体网络路径。若 TCP 流量被错误路由至其它接口,一个 HTTP 200 不能自动代替封装路径的实际抓包。
双端抓包至少要有 A 发送端 underlay 与 B 接收端 underlay 两点;如有权限和隔离条件,在 VXLAN 接口额外采内层报文。记录抓包时间、网卡名称、过滤表达式、实际报文总长与端点地址。两端的抓包时间可能偏移,不能用不同主机的绝对时间直接计算毫秒级延迟;通过测试流唯一端口、IP、VNI 和包序列关联,必要时记时钟同步状态。tcpdump -c 200 与 timeout 10 双重限制采样量和时长,退出非零要按实际原因解释。无 tcpdump 的 VM 留抓包 NOT_RUN,不能用 ip link 的计数增长代替“已经观察到外层 UDP”。
报文大小试验要在同一种路径上先测试较小 payload,再增加 ping -M do -s <大小> -c 3 的长度,各轮参数、成功数、工具退出码、ICMP 错误及外层包长并列。在 VXLAN 端点 MTU 1400 的示意配置下,1372 字节 ICMP payload 加常见 IPv4/ICMP 基本头部只是帮助选择临界输入,实际 IP 头可能有选项,VM 工具实现/路径 MTU 又可能影响结果,不能提前写入“1372 成功、1373 失败”。判断阈值应以 VM 实际抓包和返回信息为准,而不是按脚本预期填写。IPv6 的 PMTU 报文与分片规则不同,若第二条练习要求比较 IPv6,需要确认两端开通 IPv6 和具备对应捕获工具,否则老实标该分支 NOT_RUN。
还有一项容易漏掉的对照:每次修改自建 VXLAN 接口 MTU 之前与之后,都要分别保存 A/B 的内外层接口 MTU,以及 A 到 B、B 到 A 的实测结果。只记录发送端的 ping 退出码,无法排除回程方向的端点配置不同;只看到客户端收到了 ICMP 错误,也不能确认是谁生成的。若宿主启用了分段或封装卸载,发送端抓到的单个大报文可能不是物理线上真实的单包大小。此时应补对端接收点或关闭本次实验接口的相关卸载功能再对照,并恢复其原值;不能未经授权改动共享 underlay 设备来取得预想的抓包形态。
当小包正常、大包在 VXLAN 路径失败时,先核对 A 发包时是否已被本地接口 MTU 拦住:本地报 message too long 与报文确实从 A 的 underlay 出口离开是两种事件。若 A 出口看见外层包而 B 没看见,查 underlay 中间设备或过滤规则,不能归到 B 的 probe-app;若 B underlay 看见包但 B overlay 不见内层,查 VNI、VTEP 接收、端口和解封装;若 B 内层已收到而 A 收不到响应,查反向路径和 ICMP/重传。只有两端都能看到符合预期的包、应用却返回 503 时,才回到程序就绪语义。这个顺序通过“包最后在哪一跳被证实存在”定位责任,比“跨机网络一定是 MTU”更可迁移。
另一个反例是让一侧自建 VXLAN 接口 down,再从 A/B 分别发有上限的请求并保存抓包;恢复接口 up 后再执行相同命令。停的是本篇虚拟接口,不是 VM 的 underlay 网卡,也不改变 SSH/宿主控制台。若故障阶段连 underlay 测试请求都失败,说明隔离边界没按计划成立,要停止实验并恢复宿主 VM 网络,不能继续向 Pod 网络结论推进。清理前记录所有自建接口、地址和路由,只删除本篇增加的对象,之后与基线快照比较,确认两台 VM 均恢复;若其中一端无法登录,应使用预留的控制台而不是发往未知节点的批量删除命令。
把实际开销和性能结论分开
从 RFC 7348 算出某种格式的封装头部开销,是协议字节核算;“VXLAN 导致百分之几吞吐下降”则需要控制机器、CPU、网卡卸载、镜像、连接复用、缓存与多轮负载,完全是另一组实验。这个章节只验网络可达与 MTU 失配,不从短时 ping 的往返时间推导真实业务 99 分位。即使这两台 VM 的 overlay 路径恰好更慢,也不能推广所有内核和网络设备。34–35 会在明确样本量、负载和失败率后讨论性能证据;这里每个数字都来自网卡状态、抓包、工具输出与 VM 环境,不通过预设速率填“实验结果”。
要由本篇迁移到多节点 Kubernetes,应先保留 Pod UID 到 sandbox ID、netns 和 IP 的链,再把该节点使用的 CNI 配置、VTEP 或路由与实际跨节点请求对应。25 只证明 underlay/overlay 的网络原则,不会自动证明某个 CNI 以 VXLAN 实现,或 kubectl get pod 的 IP 真在指定 VTEP 上出站。若在一个无 Pod 的 VM 里从虚拟接口能访问另一个 VM,应称为“自建两机网络模型通过”,而不是“Pod 网络通过”。真正需要 kubelet/CRI 的那一层会在 26–30 逐步补证。
直路由和 VXLAN 路径的比较还要固定源 IP,否则看到的差别可能来自来源路由而不是封装。例如从 VM 的宿主默认地址访问对端 underlay,而从 VXLAN 设备的 198.51.100.x 访问另一端,两个请求的源网段已经不同;安全组与本机过滤规则可以分别允许/拒绝。报告应明确两条路径不能满足“只改一项”的严格性能对照,只能作为网络拓扑机制对照。若后续做吞吐比较,要进一步匹配应用服务、源身份、过滤策略、测量方法和时间窗口;本篇只在请求级别说明走哪种入口、在哪里第一次观察到失配,不报通用 overhead 数字。
VXLAN 外层的 UDP 头让 B 能找到对应隧道端点,但不能说所有底层交换机都知道内层 Pod IP。它们通常只沿外层 VM IP 处理路由,而内层逻辑地址在隧道的入口/出口一侧解释;这也是 VXLAN 能让不具备原生容器网段路由的 underlay 承载一个逻辑网络的原因。外层到得了 B,却因为 VNI 不匹配而无法把内层交给 B 的预定网段,是一个与“underlay 不可达”不同的失败层。检查 VNI、目的 UDP 端口和接口实际状态能区分;仅从客户端 TCP 超时不能区分这两种情况。由于 VM 的 VXLAN 端点由本篇自己创建,故障测试只改自己的 VNI/接口参数,不能在已有生产 overlay 上做交叉 VNI 探测。
跨机的路由与封装都要考虑回程。封装入口 A 发往 B 时,B 可以收到内层请求,但对 A 的返回可能选择不同的本地路由,形成非对称回程。A 出口有包、B 内层有包而 curl 仍超时,就应该增加 B 到 A 方向的抓包,不应把 MTU 或防火墙当作唯一嫌疑。IP 转发、反向路径过滤和多宿主策略路由都可能影响回程;本篇既不关闭宿主的安全检查,也不在共享网卡上改全局策略来追求实验成功。专用 VM 若存在这些条件,记实际配置并把不可执行的分支标 NOT_RUN,不在一张逻辑图上把它们隐藏。故障恢复成功的标准是相同输入下往返报文和 HTTP 返回重新出现,而非两边的虚拟设备名字再次出现在 ip link 中。
PMTU 反馈路径也可能与普通业务 TCP 请求走不同方向和协议。发现一个小请求能成功,只能说明它在这个时刻所用的段大小与路径条件兼容;它没有验证更大 IPv4 DF 报文、IPv6 Packet Too Big 反馈或大流量下的拥塞情况。curl 返回超时同时 ping -M do 返回明确 MTU 错误,是两个互相补充的症状,但要核对它们是否从同一个接口和同一个源地址出发。若 ping 实际从宿主默认路由出站、HTTP 却经 VXLAN,两个现象不能拼成一条因果链。实验命令要先运行 ip route get 记录出口,再谈应用请求与包长。
清理输出也不能依赖一个“接口已删除”的总句子。分别查 A/B 的 ip -d link show、ip route 和对端专用 UDP 端口是否还绑定本篇 VTEP;如果测试过程中创建了额外 netns、路由或抓包文件,逐一列名和删除范围,保留清理命令/返回码。无权限访问 VM 时,不能编造“清理完毕”,只能写下尚未创建对象的本机状态 NOT_RUN。反过来一旦在专用 VM 真正运行,报错后也不能因为后文还有 26–39 等待写作就跳过失败清理;这会让下次运行的路由/接口残留改变结论。
如何判断该补路由还是调 MTU
当最小 ICMP/TCP 测试都失败,首先查 underlay 对端地址是否可达、路由与 ACL 是否允许;这是连接是否有路径的问题,暂时没有证据支持“只是 MTU”。当小报文稳定而某个大报文失败,再读本地接口 MTU、实际 DF 与错误/ICMP 反馈,观察外层包是否到达对端;如果本地工具因为接口上限拒绝发送,根本无须从 B 的应用日志找答案。若只有 HTTP 大请求失败而相同源接口的 DF ping 能往返,重点转向 TCP MSS、应用 body 限额、代理超时或服务器写入行为,不能因“大的不行”便统一改 VXLAN MTU。probe-app /state 本身只接受不超过 4 KiB 的 Content-Length,超过时返回 413 是应用定义的限制,不能当网络 PMTU 黑洞。
两机 VM 是否共享相同 underlay MTU 也要检查:A 的出口 1500、B 的出口 9000,不表示 A→B 就能发送 9000 的 IP 包,路径还包含中间网络设备,而实际路径 MTU 由其中较窄的一跳决定。只读两端接口而看不到中间节点时,应通过边界探测和 ICMP/抓包判断,保留“无法确定隐藏一跳”的不确定性。VXLAN 接口通常应配置与 underlay 开销兼容的上限;若示例将它刻意设大只是故障注入,要记录调整前后原值并在清理时恢复,不把异常值留给下一个实验。这里也不建议关闭分片/PMTU 机制来换取表面成功:那会改变故障条件,却不能定位问题根源。
如果选择对比 IPv4 外层与 IPv6 外层,必须先确认两台 VM 的 underlay 确有 IPv6 路由和专用实验地址,并在两端明确标出外层类型、VXLAN 端口、内层协议与抓包位置。IPv6 在中间路由器处对过大报文的处理和 IPv4 的分片语义不同,不能把一次 IPv4 的 -M do 结果直接复制进 IPv6 栏。若没有 IPv6 实验环境,可根据 RFC 与设备 MTU 解释开销方向,但验收仍是 NOT_RUN;在没有真实报文头时不写“同一 MTU 下恰好多出固定 N 字节”的实验结论。这样的边界比给读者一个不适用于其环境的固定表格更有用。
因此这篇的反例至少包含两个互不替代的条件:直接路由少了回程路由时的小请求失败,与 VXLAN 在较窄 underlay 上处理较大 DF 报文的边界。前者应从两端路由和往返抓包解释,后者应从实际 MTU、DF、外层包长及错误反馈解释;两个故障可能都表现为 curl 超时,却不能靠同一条配置命令修复。两轮均需在各自基线请求成功后只改一项,恢复原值、再次复核请求和清理自建对象。条件无法满足时留出空白数据栏和补跑入口,不能用协议文档的示例数值冒充本次测量。
| 现象 | 核查数据 | 不可直接推出 |
|---|---|---|
| 小请求正常、大请求卡住 | 两端抓包、MTU、ICMP | 一定是应用超时 |
| 两端 Pod 子网地址相似 | 各宿主路由及 overlay 端点 | 共享物理二层 |
| 外层丢包 | underlay 路由/过滤 | 内侧 veth 已坏 |
练习
- 先预测同一内层报文走 IPv4 和 IPv6 外层封装时可用 MTU 是否相同,按真实抓包计算外层头部并验证不硬编码数值。
- 预测停掉一侧受控 VXLAN 端点后两端分别看到哪些包;在专用 VM 抓包、恢复、确认所有自建接口删除。
上一篇:24:CNI;下一篇:26:Pod 到达节点。
系列总目录:从进程隔离到运行时与编排。



