VxLAN 这三个字母,很多人是在看容器网络插件配置、云平台网络方案文档,或者排查跨机房的二层互通问题时第一次撞见的。真正上手去改配置的时候,往往是在别人已经铺好的环境里,改一个 VNI、加一条 FDB 表项,改完能通就收工,至于它到底为什么能通、封在里面的包长什么样,其实没几个人亲手验证过。我自己的习惯是,任何一层抽象,如果没在裸环境里手动搭过一遍,出问题的时候就只能靠猜。所以这篇就把它拆开:在几台普通 Linux 主机上,不用任何编排系统、不用任何网络插件,纯靠内核和 iproute2 手工把 VxLAN 隧道从零建到通,再把中间那些参数含义、MTU 计算、FDB 表项、抓包验证和踩坑记录一次性说清楚。适合已经能看懂 ip 命令、想搞明白 overlay 网络底座到底怎么运转的读者,也适合平时维护虚拟化环境、需要自己动手排网络问题的同行。
1. 先把 VxLAN 解决的问题想明白
1.1 VLAN 的天花板与二层网络的物理边界
传统 VLAN 的标签字段在以太帧头里只占 12 个比特,这 12 个比特能表达的范围是 0 到 4095,扣掉保留值和几个特殊用途,实际可用的 VLAN ID 是 4094 个。单看数字好像不少,但一个中等规模的数据中心,如果按业务线划 VLAN、按安全域划 VLAN、按环境(开发、测试、预发、生产)再切一刀,4094 这个上限是很容易被顶到的。更要命的是 VLAN 是个严格的二层概念,它要求参与同一个广播域的所有设备物理上连在同一套交换网络里,跨机房、跨楼层、跨不同网络供应商的链路,想透传 VLAN 就得在中间每一跳设备上都做放行配置,中间任何一跳不支持或者配错,整个二层域就断在那儿。
这两个限制叠在一起,就出现了很典型的场景:一批虚拟机需要保持在同一个二层网段里,因为应用里写死了 IP,或者依赖二层广播发现,但这批机器分布在两个不同的物理位置。用 VLAN 硬拉,得协调中间所有网络设备;改 IP 重部署,业务方不接受。VxLAN 就是在这种需求下被推到台前的。它把二层帧整体塞进一个 UDP 报文里,跨越三层网络传输,两端的隧道端点负责封装和解封装。对中间的路由设备来说,这只是一堆普通的 UDP 流量,不需要认识里面跑的是什么,也不需要为它做任何特殊配置。隧道的两个端点在逻辑上就相当于连了一根网线,中间隔着多少个路由跳数都无所谓。
1.2 一个 VxLAN 包长什么样:8 字节头里的关键字段
理解 VxLAN 最好的方式是直接看封装后的报文结构。从外往里剥,最外面是传输网络的以太头,接着是外层 IP 头(源地址和目的地址分别是两端隧道端点的地址),然后是 UDP 头,再往里才是 VxLAN 自己的 8 字节头,最后才是原始的内层以太帧。
| 层次 | 长度 | 关键内容 |
|---|---|---|
| 外层以太头 | 14 字节 | 传输网络的源/目的 MAC |
| 外层 IP 头 | 20 字节 | 两端隧道端点的 IP 地址 |
| UDP 头 | 8 字节 | 源端口随机,目的端口默认 4789 |
| VxLAN 头 | 8 字节 | Flags、VNI、保留字段 |
| 内层以太帧 | 原始长度 | 虚拟机或容器发出的完整二层帧 |
VxLAN 头这 8 个字节的构成是:第 1 个字节是标志位,其中最高位(值 0x08)叫 I 位,置 1 表示后面的 VNI 字段有效,抓包时看到这个字节是 0x08 就说明封装正常;紧接着 3 个字节保留,必须为 0;然后是 3 个字节的 VNI,这就是网络标识符,24 位,理论范围 1 到 16777215,这是 VxLAN 相对 VLAN 最直接的扩容,从 4094 个一下子跳到一千六百万;最后 1 个字节又是保留。VNI 是整件事的核心,两台主机上 VNI 相同、端点地址互相指对的隧道,才属于同一个逻辑二层网络;VNI 不同就是完全隔离的两张网,哪怕跑在同一块网卡、同一个 UDP 端口上。
关于 UDP 目的端口,这里有个必须记住的细节:IANA 给 VxLAN 分配的标准端口是 4789,但 Linux 内核的 vxlan 模块出于历史原因,如果建隧道时不显式指定dstport,默认用的是 8472。两端一端用默认值、一端显式写 4789,包发出去对方根本不认,抓包看是单向流量,非常容易被误判成"网络不通"。这个坑后面还会提到。
1.3 该用和不该用 VxLAN 的场合
先说适合的。跨三层网络的二层延伸是最典型的用途,虚拟机需要保持同网段又必须分布在不同物理位置时,VxLAN 是成本最低的解法。多租户隔离也很合适,每个租户一个 VNI,租户之间天然隔离,不用去协调底层交换机划 VLAN。容器和虚拟化平台的网络底座基本都建在它上面,你看到的那些覆盖网络方案,底层大多是这个东西。还有一种情况是同城两个机房做双活,业务希望保持二层连续,方便做地址漂移,这时候 VxLAN 也很顺手。
不适合的场合同样要说清楚。第一,如果两端根本不需要二层语义,只是想让两个网段的机器互相访问,那直接做三层路由就好,套一层 VxLAN 纯属给自己增加封装开销和排障难度。第二,如果底层网络质量很差,丢包和抖动明显,VxLAN 会把这些问题原样放大——它不做重传,内层 TCP 感知到的丢包就是实打实的丢包。第三,如果团队里没人愿意维护这套东西,那还是老老实实用三层方案,overlay 网络出问题的时候,不会抓包、看不懂 FDB 表的人基本无从下手。我见过不少团队上了覆盖网络之后,一出问题就整机重启碰运气,根因是没人真正理解这层的运转逻辑。
还有一点必须提醒:VxLAN 本身不带任何加密能力。它只是把帧换个包装,封装后的报文在传输网络上是完全明文的,谁在链路上抓包谁就能看到内层的全部内容。有安全要求的环境,得在它外面再叠一层加密手段,这是另一件事,和 VxLAN 参数无关。
2. 搭之前的环境规划与工具确认
2.1 拓扑与地址规划:先画图再动手
手动搭之前,先在纸上把地址规划清楚,这一步省不得。我习惯用两台主机起步,每台主机一块网卡接传输网络,再各配一个内层地址用于验证。传输网络我一般用 192.168.10.0/24 这种私有网段,内层用 10.0.100.0/24,两层地址完全分开,排障的时候一眼就能从地址判断这是内层还是外层。
具体规划是这样的:主机 A 的传输地址 192.168.10.11,主机 B 的传输地址 192.168.10.12,两台机器在传输网络里能互相 ping 通,网卡名假设都是 eth0。隧道建起来之后,主机 A 的 VxLAN 接口配 10.0.100.1/24,主机 B 配 10.0.100.2/24,VNI 统一用 100,UDP 端口统一 4789。这里"统一"两个字是重点,VNI、端口、协议(IPv4 还是 IPv6 承载)这三样,两端必须完全一致,任何一项对不上都不会通,而且报错信息往往指向别的地方。
地址规划里还有一件容易被忽略的事:主机上如果有多块网卡,local参数必须显式指定用哪块网卡的地址做隧道的源地址。不指定的话内核会根据路由表自己挑,挑错了就是源地址不对,对端 FDB 表里学到的远端地址是个无效地址,表现是单向通或者干脆不通。我搭环境时习惯把local和dev两个参数都写死,宁可多写几个字,也不留这个隐患。
2.2 内核能力与工具链自检
动手之前先确认内核里的 vxlan 模块是可用的。VxLAN 在 Linux 里属于内核模块,主流发行版的通用内核都编进去了,但精简过的镜像或者定制内核里可能没有。检查方式很简单:
lsmod | grep vxlan modprobe vxlan lsmod | grep vxlan第二条命令如果没有任何输出、返回码为 0,说明模块已经在内核里或者加载成功;如果报Module vxlan not found,那就是这个内核根本没编这个模块,得换内核,后面的步骤不用试了。加载之后可以在/sys/module/vxlan下看到目录,这说明能力已经就绪。
工具链方面需要 iproute2,ip命令的版本不能太老,太老的版本不支持ip link add ... type vxlan的完整参数。用ip -V看一眼版本,一般近几年的发行版自带的都没问题。另外准备 tcpdump 用于抓包验证,这个在后面的验证环节是不可替代的——只看ip link状态是看不出封装到底有没有发生的。
再确认一点:内核的网桥模块也要可用,如果你打算把虚拟机或容器接进来,需要bridge模块。这个模块几乎所有发行版都默认加载,lsmod | grep bridge确认一下即可。
2.3 底层连通性与 MTU 基线测量
在传输网络上先做基础连通性验证,两台主机互 ping 传输地址,确认通。这一步不通就别往下走了,VxLAN 是建在这层之上的,底层不通什么参数都没意义。
接着测底层链路的实际 MTU。这个值决定了 VxLAN 接口的 MTU 能开多大,也是后面大量诡异问题的根源。测法是发一个不允许分片的大包:
ping -M do -s 1472 -c 3 192.168.10.12-M do表示禁止分片,-s 1472是载荷长度,加上 8 字节 ICMP 头和 20 字节 IP 头正好 1500。如果这个能通,说明底层链路的标准 MTU 就是 1500,没有额外的隧道或者特殊配置在中间吃掉字节。如果报Message too long或者直接没回应,就逐步减小-s的值,找到能通的最大值,那个值加 28 就是底层实际 MTU。
这一步的意义在于:如果底层实际 MTU 只有 1400(比如链路中间又套了一层别的隧道),那 VxLAN 内层的 MTU 就得在 1400 的基础上再减,最后可能只剩 1350 左右。这个数字要提前算出来,因为一旦虚拟机按 1500 的 MTU 跑起来,小包能通、大包全挂,排查起来非常费劲。
3. 手工建隧道:从零到第一个 ping 通
3.1 拆解 ip link add 的每一个参数
创建 VxLAN 接口的命令本身很短,但每个参数都有它存在的理由。点对点单播模式的最小配置大概长这样:
ip link add vxlan100 type vxlan \ id 100 \ local 192.168.10.11 \ remote 192.168.10.12 \ dev eth0 \ dstport 4789 ip link set vxlan100 up ip addr add 10.0.100.1/24 dev vxlan100逐个说。vxlan100是接口名,内核里接口名最长 15 个字符,习惯上把 VNI 编进名字,多 VNI 环境下好认。type vxlan告诉内核这是个 VxLAN 设备。id 100就是 VNI,两端必须一致。local是本端隧道端点的源地址,remote是对端隧道端点地址,这两个参数决定了封装后外层 IP 头的源和目的。dev eth0指定封装报文从哪块网卡发出去,这个参数在有多块网卡或者有多条默认路由的主机上特别重要,不指定的话内核按路由表选,可能出现报文从一个口出去、回来的包从另一个口进来,导致对端学到的地址是错的。dstport 4789就是我前面强调过的,显式写死标准端口,不要依赖默认值。
创建完ip link set up拉起接口,然后配内层地址。这里有个细节:配地址这一步在单播点对点模式下是可选的,因为隧道本身建好就能转发二层帧,配 IP 只是为了方便用 ping 做验证。真正的生产用法是把 vxlan 接口挂进网桥,让虚拟机接在网桥上,那时候 vxlan 接口自己不需要 IP。
配置完可以用ip -d link show vxlan100查看详情,加-d参数才能看到 vxlan 的扩展信息,包括 id、local、remote、dstport、learning 状态等等,这条命令在后面排障时是第一步要看的东西。
3.2 两种典型组网:点对点单播与多播组
上面那种写法是单播模式,只指定了一个remote,意味着这个隧道只有一个对端。两台主机互连的场景这样写最省事,配置直白,行为可预测。但如果有三台以上主机需要同处一个二层域,单播模式的remote只能写一个,就没法表达了。这时候有三种做法。
第一种是把隧道接口配成不指定remote的形式,让内核自动学习远端地址。原理是数据面学习:收到某个 VNI 的包时,从外层源 IP 学到"这个内层 MAC 在哪个远端隧道端点后面",写进 FDB 表。这种模式配置最简单:
ip link add vxlan100 type vxlan id 100 local 192.168.10.11 dev eth0 dstport 4789但它有个前提,必须先有流量触发学习,而触发流量本身是广播或者未知单播,那就需要一种泛洪机制。这就引出了第二种。
第二种是组播模式,指定一个组播组地址:
ip link add vxlan100 type vxlan \ id 100 \ local 192.168.10.11 \ group 239.1.1.100 \ dev eth0 \ dstport 4789广播、未知单播、组播帧会被封装成组播包发到这个组地址,所有加入同组的隧道端点都能收到,从而实现多点的二层域。这个模式配置最少、概念最直观,但它强依赖底层网络支持组播转发,需要中间网络设备正确配置组播路由和 IGMP 侦听。公有云环境、多数虚拟化平台的默认网络里,组播是默认被禁掉的,这种情况下包发出去石沉大海,抓包能看到发出去了,但对端收不到。
第三种是我在实际环境里更推荐的做法:关掉自动学习,用nolearning参数,然后手工维护 FDB 表项,或者交给外层控制平面去下发表项。这么做看起来麻烦,但行为完全可控,不用去猜"这个 MAC 到底被学到哪去了",出问题时bridge fdb show一眼就能看出全貌。规模稍大的环境里,自动学习带来的不确定性和排障成本是很难接受的。
3.3 挂进网桥,让虚拟机和容器接进来
如果只是验证隧道本身,配个 IP ping 通就够了。但真实用途几乎都是给虚拟机或容器提供二层接入,这就需要一个网桥把 vxlan 接口和虚拟网卡桥接起来。做法是建一个网桥,把 vxlan 接口和虚拟机的虚拟网卡都挂上去:
ip link add name br100 type bridge ip link set dev br100 up ip link set dev vxlan100 master br100 ip link set dev vnet0 master br100 ip addr add 10.0.100.254/24 dev br100vnet0是虚拟机网卡在宿主机上的对应设备,容器场景下对应的是 veth 的一端。挂上去之后,虚拟机发出的帧从 vnet0 进入网桥,网桥看到目的 MAC 不在本机,就按 FDB 表从 vxlan100 转发出去,vxlan100 负责封装发往对端宿主机。对端解封装后进入对端网桥,再转发给对应的虚拟机。整条链路在虚拟机看来就是一根普通网线。
这里有个容易忽略的调整:网桥默认开着 STP,并且有 15 秒左右的转发延迟。在纯 overlay 场景里,STP 基本没用,还会让新建的桥在一段时间内不转发流量,表现是"配好了但不通,过十几秒又通了"。手动搭环境时我一般直接关掉:
ip link set dev br100 type bridge stp_state 0 forward_delay 0另外,如果网桥上挂了大量虚拟网卡,广播流量会有泛洪压力,可以给 vxlan 接口开启邻居抑制,让网桥不要把它已经知道的 MAC 再往隧道里泛:
bridge link set dev vxlan100 neigh_suppress on这个参数的作用是,当网桥要去某个 MAC 时,如果 vxlan 接口的 FDB 里已经有对应表项,就不再向内层泛洪广播,直接单播过去。在虚拟机数量多的环境里,这个小开关对广播风暴的抑制效果相当明显。
3.4 抓包验证:确认封装真的发生了
配置完能 ping 通,很多人就停了。但 ping 通只说明结果对,不说明过程对——有可能走的是别的路径,有可能封装根本没生效。我习惯一定要抓一次包,看到真实的报文。
在主机 A 上对传输网卡抓 UDP 4789:
tcpdump -i eth0 -nn -e -vvv udp port 4789 -c 3看到的东西应该是这样的:外层 IP 头里源地址是 192.168.10.11,目的地址是 192.168.10.12,UDP 源端口是随机高位端口,目的端口 4789,后面跟着 VxLAN 头,再往里是内层以太帧。加-e能看到以太头,加-XX能把报文以十六进制打出来,想核对 VxLAN 头那 8 个字节的话用这个:
tcpdump -i eth0 -nn -XX udp port 4789 -c 1在十六进制输出里找到 UDP 头之后的那 8 个字节,第一个字节应该是 0x08,说明 I 位置 1,VNI 有效;紧接着 3 个字节是 0x00;再往后 3 个字节是 VNI,为了凑足 24 位它会被拆开显示,VNI 100 对应的是 0x00 0x00 0x64。看不出来也没关系,用 Wireshark 打开更容易,它会把 VxLAN 层直接解析出来,VNI 显示得清清楚楚。
同一时刻在主机 B 上抓包,应该能抓到对应的入向报文。两边一对比,源和目的对调,VNI 一致,说明隧道是双向通的。这个验证过程看着啰嗦,但它是唯一能证明"确实封了、确实解了"的手段。
4. 规模上去之后的几件事
4.1 单主机多 VNI 的隔离与命名规范
实际环境里一台宿主机上不会只有一个 VNI。可能是多个租户,可能是同一个租户的不同网段,可能还有管理平面和业务平面的区分。实现方式很直接,一个 VNI 对应一个 vxlan 接口,每个接口配独立的 VNI、独立的内层地址:
ip link add vxlan100 type vxlan id 100 local 192.168.10.11 dev eth0 dstport 4789 nolearning ip link add vxlan200 type vxlan id 200 local 192.168.10.11 dev eth0 dstport 4789 nolearning多个 vxlan 接口共用同一个local地址和同一块dev网卡是完全允许的,内核靠 VNI 区分它们。反过来,同一个 VNI 在一台主机上建两个接口会冲突,内核会直接报错,这也是好事,避免配置混乱。
命名规范这块我给个建议:接口名里带上 VNI 编号,网桥名也对应上,比如 VNI 100 对应 vxlan100 和 br100。多 VNI 环境下如果名字和编号对不上,排障时找一个租户的网络要在几十个接口里翻,非常痛苦。另外建议留一份配置清单,记录每个 VNI 对应哪个业务、哪个网段、哪些主机上有端点,这东西在出事的时候能救命。
4.2 FDB 表项的自动学习与手工固化
FDB 表是这套机制里最核心也最容易让人迷糊的东西。它的作用是回答一个问题:要去某个 MAC 地址,应该把包封给哪个远端隧道端点。表项有两种来源,自动学习和手工添加。
自动学习在learning开启时生效。收到一个 VNI 的包,内核从外层源 IP 学到内层源 MAC 与远端端点的映射关系,写进 FDB。这套机制在小规模、流量模式稳定的环境里够用,但它有两个问题。一是初次通信时表是空的,得靠泛洪把包发出去,如果底层不支持组播,泛洪就没法实现,包直接被丢弃。二是表项会老化,长时间没流量就删掉,下次通信又要重新泛洪和学习。某些场景下这会表现为"偶尔断一下又好了"。
手工固化的做法是关掉学习,自己加表项。加两类:一类是全零 MAC 的泛洪表项,告诉内核"广播、未知单播这些要泛洪的帧,往这些远端端点都发一份":
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 192.168.10.12另一类是具体 MAC 的静态表项,如果你明确知道某个虚拟机的 MAC 在哪台宿主机后面:
bridge fdb append 52:54:00:aa:bb:cc dev vxlan100 dst 192.168.10.12查看当前表:
bridge fdb show dev vxlan100输出里会有self permanent之类的标记,permanent表示不会老化。手工加的表项默认就是 permanent,这一点比自动学习可靠得多。
在容器网络场景里,还有一种常见做法是nolearning配合external参数,把 FDB 表完全交给外层控制平面管理,内核这边只负责封装转发。这属于平台层面的设计,手动环境下用不上,但看到别人配置里有external的时候,你就知道它的意思了。
4.3 从二层到三层:让不同子网互通
VxLAN 天然是二层技术,它延伸的是广播域,不解决跨子网路由。如果两台虚拟机的内层地址不在同一个子网,光建隧道是通不了的,得靠三层转发。
最土但最直观的做法是在每个隧道端点上做路由。假设主机 A 的 vxlan 接口上还有一个地址 10.0.101.1/24,主机 B 上对应 10.0.101.2/24,想让 A 上的另一个网段访问 B 的子网,可以加静态路由指向对端的隧道端点地址:
ip route add 10.0.101.0/24 via 10.0.100.2 dev vxlan100前提是 vxlan 接口上配了 IP 并且打开了转发。用sysctl net.ipv4.ip_forward=1打开,同时注意内核默认可能开着反向路径校验,多网卡环境下会误杀合法流量,这种情况要单独调rp_filter。
另一种做法是通过邻居表直接指定,适合不想在隧道接口上配 IP 的场景。但说实话,手动环境里做三层互通,用路由是最清楚的,出问题ip route get一看就知道包往哪走。
真正大规模的三层互通会引入 EVPN 这类控制面协议,用 BGP 传递 MAC 和 IP 可达信息,不用手工维护 FDB 和路由。那属于另一个话题的范畴,手动搭环境阶段不建议一上来就碰,先用静态配置把行为摸清楚,后面上控制面才看得懂它在替你做什么。
4.4 接口持久化与重启后的恢复
手工用ip link add建的接口,重启之后就没了,这个必须有清醒认识。掉的直接后果是所有虚拟机失联,而且现象可能是"重启完网络就废了",如果没有持久化配置,得靠人重新敲命令。
持久化的路子有几条。用 systemd-networkd 的系统,可以写.netdev和.network文件,把隧道定义声明化。.netdev里写 VxLAN 定义:
[NetDev] Name=vxlan100 Kind=vxlan [VXLAN] ID=100 Local=192.168.10.11 Remote=192.168.10.12 DestinationPort=4789再配一个对应的.network文件声明它加入网桥。用 NetworkManager 的系统可以用 nmcli 建类型为 vxlan 的连接。还有更土的办法是把命令写进开机脚本,简单粗暴但不容易出错,适合自己维护的实验环境。
不管用哪种方式,有一点要验证到位:配置写完必须重启一次实测。只在当前会话里systemctl restart一下网络服务不算验证,因为那可能保留了旧接口。真重启之后ip -d link show vxlan100看一眼,bridge fdb show再看一眼,确认表项都在,才叫落实了。
5. 排查实录:那些让人抓狂的现场
5.1 症状到原因速查表
出问题的时候最怕没方向。我把这些年遇到的典型症状和对应原因整理成一张表,按这个顺序查能覆盖八成情况。查法是自下而上:先确认外层通不通,再确认封装有没有发生,再确认表项对不对,最后才看内层配置。
| 症状表现 | 最可能的原因 | 排查命令 |
|---|---|---|
建接口就报错Operation not supported | 内核没加载 vxlan 模块 | lsmod | grep vxlan |
| 内层 ping 完全没回应 | UDP 端口被拦或两端端口不一致 | tcpdump -i eth0 udp port 4789 |
| 抓包能看到发出去,对端收不到 | 传输网络不通、端口被防火墙拦 | 直接 ping 传输地址 |
| 小包通、大包不通 | MTU 配置不对 | ping -M do -s 1422 |
| 单向能通、反向不通 | local地址选错或 FDB 表项缺失 | ip -d link show、bridge fdb show |
| 配好十几秒后才通 | 网桥 STP 和转发延迟 | ip link set br100 type bridge stp_state 0 |
| 偶尔通偶尔不通 | FDB 表项老化后重新泛洪失败 | bridge fdb show(看表项是否还在) |
| 虚拟机迁移后不通 | FDB 表里残留旧端点地址 | 手工删旧表项或重新学习 |
表里最后一条值得多说一句。虚拟机热迁移之后,MAC 不变但位置变了,如果 FDB 里还留着指向旧宿主机的表项,流量会一直往老地方发,表现是迁移完成后业务长时间不恢复。自动学习模式下这个切换要靠新流量触发,手工模式下就得主动更新。虚拟机频繁迁移的环境,这个问题的优先级要提到很高的位置。
5.2 MTU 这个坑,几乎每个人都要踩一次
MTU 是 VxLAN 环境里最高频的故障源,没有之一。原因是它的表现太有欺骗性:ping 得通、SSH 能连、HTTP 请求小页面正常,一到传输大文件或者做数据库同步就卡死。很多人的第一反应是"网络丢包",然后去查交换机、查带宽,查半天没结果。
算一下这个账。底层链路 MTU 1500,封装 VxLAN 需要加上外层以太头 14 字节、外层 IP 头 20 字节、UDP 头 8 字节、VxLAN 头 8 字节,一共 50 字节。所以内层帧最长只能到 1450 字节,超过这个尺寸的帧封装后就会超出发送网卡的能力。如果内层接口还按 1500 配,超过 1450 的包要么被分片(如果允许),要么被丢弃。而实际业务里,TCP 发送方按自己的路径 MTU 发现逻辑来调,很多时候根本发现不了这个问题,就一直在重传。
做法很明确,把 VxLAN 接口和挂在上面的网桥、虚拟网卡的 MTU 都改小:
ip link set dev vxlan100 mtu 1450 ip link set dev br100 mtu 1450如果底层链路本身就不是 1500(比如中间还有别的封装),那还得再减。这种情况建议留余量,比如算出实际可用是 1420,那就配 1400,别再贴着上限用,路径上任何一个环节多几字节封装就会翻车。
验证方法就是禁止分片的 ping:
ping -M do -s 1422 -c 3 10.0.100.21422 是 1450 减去 20 字节 IP 头和 8 字节 ICMP 头。这个能通,说明 1450 是有效的。再加 1 字节应该失败,能确认边界就在这儿。这个测试做完,MTU 这个变量就基本可以排除了。
还有一层:虚拟机内部也得跟着改。虚拟机的网卡 MTU 如果还是 1500,宿主机网桥是 1450,从虚拟机发出的 1500 字节帧到网桥会被丢。所以虚拟机的配置也得同步,或者通过 DHCP 下发 MTU 值,别指望它自己发现。
5.3 我踩过的几个坑和对应手法
第一个坑是 UDP 校验和。VxLAN 封装的 UDP 头里,校验和字段默认可能填 0,表示不校验。IPv4 协议是允许这么做的,但某些网络设备或者虚拟化平台的虚拟交换机会把校验和为 0 的包当成异常丢弃。表现是内层完全不通,抓包看包发出去了,对端抓不到。遇到这种情况,建隧道时加udpcsum参数强制计算校验和:
ip link add vxlan100 type vxlan id 100 local 192.168.10.11 remote 192.168.10.12 dev eth0 dstport 4789 udpcsum这个参数很少出现在教程里,但确实能救命。反过来也有个udp6zerocsumrx之类的参数,IPv6 承载时用,情况类似。
第二个坑是卸载特性带来的假象。网卡和内核有各种 checksum offload、TSO、GRO 之类的优化,这些在物理网卡上工作良好,但叠加 VxLAN 之后某些组合会出问题,典型表现就是"本地抓包看不出来,实际发出去的包不对"。排查阶段可以先把这些关掉,排除干扰:
ethtool -K eth0 tx off rx off tso off gso off gro off注意这只是排查手段,不是长期配置。如果关掉之后问题消失,说明是卸载特性的兼容问题,那就得针对性处理,而不是一直关着,因为关掉会明显损失吞吐能力。
第三个坑是网桥上残留的旧接口。反复重建隧道的时候,如果只删了 vxlan 接口没删网桥,或者反过来,会留下悬空的配置,表现是新接口建好了但转发路径还是老的。我现在的习惯是重建之前按反序清理干净:
ip link set dev vxlan100 nomaster ip link del dev vxlan100 ip link del dev br100先解除从属关系再删接口,顺序不能反。另外删除之后用bridge fdb show和ip link show各看一遍,确认没有残留。
第四个算不上坑但值得提:防火墙。很多环境默认开了主机防火墙,UDP 4789 是没放行的。本机测试时为了排除干扰可以先临时放行或者关掉,但生产环境必须显式放行这个端口,并且限制来源地址范围。这条规则没配的话,表现为本机建隧道一切正常,跨机就是不通,非常容易误判成 VxLAN 配置问题。
最后一个经验,也是我花时间最多才想明白的:排 VxLAN 问题,顺序一定是自下而上,绝对不能跳到内层去找原因。先确认传输网络互通,再确认 UDP 端口通,再确认封装发生了,再确认 FDB 表项正确,最后才看内层地址配置。按这个顺序走,最多五分钟就能定位到是哪一层的问题。反过来,一上来就在内层 ping、改路由、重启虚拟机,折腾一小时也未必找到根因,还会引入新的不确定性。这套顺序我现在已经固化成习惯,每次动手之前先在心里过一遍,比任何工具都好用。