PVE 万兆网卡只跑 1G?一次 ARP Flux 的完整排查记录
PVE 万兆网卡只跑 1G?一次 ARP Flux 的完整排查记录
dong4j背景
起因是家里 Homelab 的一台 NUC 升级,给它加了张 Intel X520-2 双口万兆,顺手把 NAS(DS923+)那边也换上了万兆。理论上这套组合应该是”一步到位、全屋万兆起飞”的那种。
但真正测下来,iperf3 从 NUC 打到 NAS 只有大概 950Mbps。
ethtool enp2s0f0 明明写着 Speed: 10000Mb/s 和 Link detected: yes,我盯着那一行看了半天。硬件、线缆、交换机、NAS 全都换过交叉验证过,问题死死卡在这台 NUC 上。
最后折腾下来才发现根本不是硬件的锅,是 Linux 网络里一个经典得不能再经典的坑:ARP Flux——多张网卡挂在同一个子网里,彼此抢答 ARP,导致流量走错路径。
这篇文章把完整的排查链路、每一步的命令和我当时的判断都写出来,既是给未来的自己留个底,也希望你以后踩到类似问题时能少走一圈弯路。
环境说明
先把桌面摆开,这台 NUC 上一共挂了 4 张网卡:
| 接口 | IP | 类型 | Bridge |
|---|---|---|---|
| enp2s0f0 | 192.168.31.99 | Intel X520 10G | vmbrt |
| enp2s0f1 | 192.168.21.99 | Intel X520 10G | vmbru |
| enp92s0 | 192.168.31.9 | 主板 2.5G | vmbr0 |
| enx00e04c68bbcf | 192.168.21.9 | USB 2.5G | vmbr1 |
目标很朴素:192.168.31.99(10G)作为主网卡连 NAS(DS923,192.168.31.2),192.168.31.9 做管理备用,两个都在 192.168.31.0/24 这个网段里。
为什么要这么摆?因为我希望管理网和存储网分别走不同的物理口,同时又懒得单独规划网段,就想着”反正都是 31 网段,系统会自己选最快的那个吧”。
这种想当然,后面吃了大亏。
问题现象
直接上 iperf3 做对比,横向看两台机器打同一台 NAS 的速度:
| 测试机 | 目标 | 结果 |
|---|---|---|
| PVE(192.168.31.9) | 192.168.31.2 | ~950 Mbps(不对劲) |
| 另一台万兆机器(192.168.31.6) | 192.168.31.2 | ~9.3 Gbps(正常) |
两台机器都是单线程 iperf3,NAS 端没任何特殊配置。既然另一台万兆能干到接近 10G,说明 NAS、交换机、线缆都没问题,瓶颈就在 NUC 自己。
第一轮排查:硬件
ethtool 明明协商到 10G,按常规思路先把硬件层扫一遍。
1. 驱动和型号
1 | $ ethtool -i enp2s0f0 |
Intel 82599ES / X520-2,ixgbe 驱动,都在预期内,没什么奇怪的。
2. PCIe 链路宽度
1 | $ lspci -vv -s 02:00.0 | grep -i width -A2 |
这里有点意思,理论 x8 实际跑在 x4(downgraded)。算一下 PCIe 2.0 x4 的理论带宽大约 16Gbps,跑 10G 足够,不构成瓶颈,所以暂时不追这条线。
顺便提一句,NUC 的 M.2 转接卡压到 x4 是常见现象,见得多了也就不奇怪。
3. 队列数量
1 | $ ethtool -l enp2s0f0 |
16 个队列,CPU 足够,不会单核瓶颈。
4. 中断分布
1 | $ cat /proc/interrupts | grep ixgbe |
这一条反而最关键——空的。
按理说只要流量真的在这张卡上跑,ixgbe 的中断计数肯定会噌噌往上涨。现在一条都没有,这是一个非常强的信号:流量压根没从这张卡上走。
当时我还没完全反应过来,以为是中断被绑到别的 CPU 或者 IRQ balance 出了问题,后来才知道是真字面意义上的”没流量”。
5. MTU
已配置 MTU 9000,和 NAS 对齐,这一项也正常。
第一轮扫完的结论:硬件没问题。
第二轮排查:是不是测错了接口
默认路由挂在 vmbr0(2.5G 那张),192.168.31.9 也绑在 vmbr0 上。于是很自然地有个怀疑:iperf 根本没走 10G 那张卡,流量被默认路由吸到 2.5G 去了。
强制绑源地址再测一遍:
1 | iperf3 -c 192.168.31.2 -B 192.168.31.99 |
结果还是 ~950 Mbps。源 IP 都绑定到万兆那一侧了,速度却完全没变,说明问题不是”走错接口”这么表层。
同时看一眼网卡的 byte 计数:
1 | $ ip -s link show enp2s0f0 |
iperf 作为 client 主要是发数据,理论上 TX 应该疯狂增长。但 TX 几乎不动,只有 RX 在涨。结合前面 /proc/interrupts 为空,结论已经很清楚:
iperf 发出去的包,没有真正从这张 10G 口出去,而是被路由到别的接口了。
第三轮排查:能不能把 2.5G 口 down 掉
为了验证是不是和 2.5G 口抢路径,我手贱试了一下:
1 | ip link set enp92s0 down |
下一秒 PVE 管理台立刻断开,因为我是通过 192.168.31.9 这个地址连进来的,管理流量走 vmbr0 → enp92s0。这一下虽然没解决问题,但是副产品:
坐实了管理流量确实走 2.5G 口。
然后赶紧切到服务器前面接显示器 up 回来,以后这种验证改用更安全的手法,不能这样莽。
破案:看邻居表
转折点在这一条命令上。既然流量路径选择不对,那就直接看二层邻居表:
1 | $ ip neigh show 192.168.31.2 |
看到这一行我就乐了。同一个 IP、同一个 MAC,同时出现在 vmbr0 和 vmbrt 两个接口上,这就是教科书级别的 ARP Flux。
再看路由表确认一下:
1 | $ ip route |
同一个 192.168.31.0/24 网段,在 vmbr0 和 vmbrt 两个接口上都有直连路由。这会带来几个后果:
- 两个接口都会应答 ARP 请求
- 邻居表里同一个 IP 出现在多个接口上
- TCP 包可能从 10G 口发出去,回程却从 2.5G 口收(非对称路径)
- 吞吐被卡在 ~1G,也就是慢那一侧的速率
这里有个关键点我当时也想明白过来:同网段通信不走默认路由,而是直接查邻居表选接口。所以哪怕我把默认路由改到 vmbrt 上,只要邻居表里 192.168.31.2 还绑在 vmbr0,iperf 该走哪还走哪。
之前白费那些力气调路由 metric 的,全都是无用功。
验证
临时把 2.5G 那边的 IP 摘掉(注意不是 down 接口,只是把 IP 去掉),然后清邻居表重测:
1 | ip addr del 192.168.31.9/24 dev vmbr0 |
速度一下就冲到 9.x Gbits/sec。
到这一步,故事已经很明朗:问题不是硬件,是同网段双接口 + Linux 默认 ARP 行为带来的 Flux。
根本原因:Linux 和 macOS 的设计差异
这个差别挺有意思的,顺便展开说两句。
- Linux 是多宿主模型(weak host model):默认允许多接口同子网、多接口应答 ARP,方便也容易出事
- macOS / BSD 是 strong host model:每个接口只响应自己 IP 的 ARP,多网卡挂同网段一般不会出这种事
这就解释了为什么同样的拓扑,Mac 上一切正常,Linux 上就会翻车。也解释了为什么你在 Mac 上调网卡优先级能生效,在 Linux 上却看不出效果——同网段根本不走默认路由,走的是邻居表。
我以前一直以为”多宿主”就是支持多网卡而已,这次算是结结实实理解了它在 ARP 层面的具体含义。
最终方案
梳一下目标:默认走 192.168.31.99(10G),另外三个 IP(192.168.31.9 / 192.168.21.9 / 192.168.21.99)都保留可用,四个网口全部在线,且两个子网也都保持原样。
1. 默认路由走 10G
把 gateway 配到 vmbrt(也就是万兆那个 bridge)上:
1 | vmbrt: gateway 192.168.31.1 |
2. 为备用网卡配策略路由
默认路由走 10G 之后,从 192.168.31.9 这个备用 IP 出去的流量就没网关了。解决办法是按源 IP 加一条策略路由,让它走自己的桌面:
1 | post-up ip rule add from 192.168.31.9 table 100 |
vmbr1、vmbru 同样操作,table 编号换成 101、102 之类就行。这些 post-up/post-down 钩子写在 /etc/network/interfaces 里,重启后能自动生效。
3. 关闭 ARP Flux(最核心的一步)
1 | echo "net.ipv4.conf.all.arp_ignore=1" >> /etc/sysctl.conf |
两个参数值得单独解释一下:
arp_ignore=1:只回答目标 IP 和接收接口 IP 匹配的 ARP 请求,也就是每个接口只为自己的 IP 回答 ARParp_announce=2:发出 ARP 请求时,源 IP 优先选择和接收接口在同一子网的地址
这两个加起来就是在告诉内核:各回各家,各找各妈,别抢答。
4. 验证
改完之后跑这几条:
ip route:默认路由应该是default via 192.168.31.1 dev vmbrtiperf3 -c 192.168.31.2:稳定在 9.x Gbits/secip neigh show 192.168.31.2:只在一个接口上出现
到这里就完事了。我还特意复核过,这些改动只动了主机层的网络行为,容器接在哪个 bridge 上不用动,PVE 里每个 LXC 和 VM 的配置也都不用改。
排查路径总览
把整个过程按”排查动作 → 结论”列一张表,以后遇到类似现象可以照着跑:
| 排查项 | 结果 |
|---|---|
| ethtool 协商 | 10000Mb/s,正常 |
| 驱动 | ixgbe,正常 |
| PCIe | x4,带宽足够 |
| 队列 | 16,正常 |
| 测错接口? | -B 192.168.31.99 仍 ~950M |
ip -s link 的 TX | 几乎不涨,流量没走 10G 口 |
/proc/interrupts | 无 ixgbe 中断,佐证上一条 |
ip neigh | 同一 IP 出现在 vmbr0 和 vmbrt 上 |
ip addr del 验证 | 删掉 31.9 后瞬间 9G+ |
| 最终方案 | arp_ignore + arp_announce + 策略路由 |
一些经验
- 同网段挂多张物理接口,在服务器环境下风险很大。管理网、存储网、业务网尽量分网段,Homelab 也一样
- Linux 不是 Mac,多宿主行为默认开放,多网卡同网段一定要配
arp_ignore+arp_announce,否则迟早踩 Flux - 协商 10G 但只跑 900M 出头的时候,不要第一时间怀疑硬件,优先排查:
ip neigh:同一 IP 是不是挂在多个接口上ip -s link的 TX 计数:看流量到底有没有真从目标网卡出去ip route:同网段是不是有多条直连路由/proc/interrupts:目标网卡的中断有没有在涨
- 多网卡 + 同子网 + 同交换机的组合,记住这个口诀:要么分网段,要么策略路由 + arp_ignore
网络问题里,很多时候不是硬件的锅,是拓扑设计的锅。硬件给你开了一条 10 车道的高速路,但你在入口放了一堆错的路标,车当然开不快。

















