引言
运维KVM虚拟化平台时,你是否遇到过这样的怪事:虚拟机里用 ethtool 查看网卡,速度显示只有 100Mb/s,可虚拟机到宿主机之间的数据传输却轻松跑出 23Gbps 的惊人速度?这种“百兆网卡”与“万兆吞吐”的矛盾,着实令人困惑。
作为一名长期与虚拟化打交道的博主,我决定抽丝剥茧,彻底还原这个问题的真相,并给出经过实战检验的修复方案。
问题现象:矛盾的指标
在一台运行着多个 KVM 虚拟机的宿主机上,我进入某台 Ubuntu 虚拟机执行:
ethtool eth0
输出显示 Speed: 100Mb/s。但当我从宿主机向这台虚拟机拷贝大文件,或使用 iperf3 测试内部带宽时,速度竟高达 23Gbps。显然,网卡真实性能远超“百兆”。
为什么 ethtool 会给出如此离谱的数值?这还得从 Virtio 的原理说起。
排查过程:从虚拟到物理,逐层击破
1. 确认 Virtio 多队列配置
首先检查虚拟机是否启用了多队列,这直接影响 vCPU 并发处理网络包的能力。在宿主机执行:
sudo virsh dumpxml 虚拟机名 | grep -A 15 "<interface"
sudo virsh vcpucount 虚拟机名
理想配置如下,其中 queues 应等于 vCPU 核数:
<interface type='bridge'>
<model type='virtio'/>
<driver name='vhost' queues='4'/>
</interface>
2. 安装诊断工具
在虚拟机内安装必要工具:
# Ubuntu/Debian
sudo apt install ethtool iperf3 -y
# CentOS/RHEL
sudo yum install ethtool iperf3 -y
3. 测试虚拟机内部通信
在目标虚拟机启动服务端,从另一台机器测试:
# 服务端
iperf3 -s
# 客户端
iperf3 -c 虚拟机IP
结果显示 23Gbps,说明虚拟机网络栈和 Virtio 驱动本身无问题。问题焦点转向外部访问路径。
4. 测试外部访问速度
从局域网另一台物理机测试:
iperf3 -c 虚拟机IP
速度骤降至 100Mbps 左右。至此,瓶颈锁定在物理网卡或网桥转发环节。
5. 检查物理宿主机网卡硬件卸载状态
在宿主机上查询网卡卸载能力:
sudo ethtool -k 物理网卡名 | grep -E "scatter-gather|tcp-segmentation|generic-receive"
典型的问题输出:
scatter-gather: off
tcp-segmentation-offload: off
generic-receive-offload: on
scatter-gather 和 tcp-segmentation-offload 被关闭时,所有 TCP 分段和内存整理都由 CPU 软件处理,虚拟化环境下这种开销会被急剧放大。
6. 检查物理网卡协商速度
执行 ethtool 物理网卡名,确认物理网卡确实协商在千兆或更高,排除物理链路本身降速的可能。
解决方案:三步修复性能
方案一:立即启用硬件卸载(核心修复)
在宿主机上执行(以 enp2s0 为例):
sudo ethtool -K enp2s0 sg on tso on gso on gro on
验证:
sudo ethtool -k enp2s0 | grep -E "scatter-gather|tcp-segmentation|generic-receive|generic-segmentation"
预期输出全为 on。
方案二:配置永久生效
NetworkManager dispatcher 方式:
sudo nano /etc/NetworkManager/dispatcher.d/99-enable-offload
写入(替换网卡名):
#!/bin/bash
if [ "$1" = "enp2s0" ] && [ "$2" = "up" ]; then
ethtool -K enp2s0 sg on tso on gso on gro on
fi
赋予执行权限:
sudo chmod +x /etc/NetworkManager/dispatcher.d/99-enable-offload
rc.local 方式:编辑 /etc/rc.local,在 exit 0 前添加相同命令,并 chmod +x /etc/rc.local。
方案三:优化网桥性能(可选)
sudo brctl setfd brg0 0
sudo sysctl -w net.bridge.bridge-nf-call-iptables=0
sudo sysctl -w net.bridge.bridge-nf-call-ip6tables=0
echo "net.bridge.bridge-nf-call-iptables=0" | sudo tee -a /etc/sysctl.conf
echo "net.bridge.bridge-nf-call-ip6tables=0" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
原理说明:为什么显示百兆却跑出万兆?
Virtio 的“百兆”只是占位符
Virtio 是半虚拟化设备,不模拟物理网卡的链路协商。内核在无法获取真实链路速率时,会返回默认值 100Mb/s 作为占位。因此,ethtool 的速度显示在 Virtio 上毫无参考意义,唯一的真相只有 iperf3 实测带宽。
TSO 与 Scatter-Gather 为何关键?
- TSO(TCP Segmentation Offload):将 TCP 分段工作从 CPU 卸载到网卡硬件,显著减少中断与协议栈开销。
- Scatter-Gather:允许网卡直接跨不连续内存缓冲区收发数据,避免多次内存拷贝。
关闭这两项后,CPU 被迫处理所有分段和内存拼接,在虚拟化转发链路(虚拟机→vhost→网桥→物理网卡)中成为绝对瓶颈。
内部快、外部慢的本质
| 路径 | 是否经过物理网卡 | 主要开销 | 速度表现 |
|---|---|---|---|
| 虚拟↔宿主机 | 否 | 内存拷贝 | 20Gbps+ |
| 虚拟↔外部 | 是 | 物理网卡卸载能力 | 100Mbps~900Mbps |
内部通信不触碰物理网卡,自然不受 TSO/SG 影响;外部访问则完全取决于物理网卡的卸载功能是否开启。
最终验证与后续提醒
修复后再次从外部运行:
iperf3 -c 虚拟机IP
速度应提升至 300~900Mbps。若仍不理想,请检查网线类别(至少 Cat6)、交换机端口、对端网卡能力,以及物理网卡是否跑在完整的 PCIe 带宽上。
结语
Virtio 网卡显示“百兆”并非故障,而是半虚拟化设计的必然。真正的问题是物理网卡硬件卸载被关闭,导致虚拟化场景下性能雪崩。掌握这一排查思路,你就能在虚拟化网络优化的道路上少走弯路,彻底告别“百兆”困局。
希望这篇文章能帮你理清思路,如果你也曾在虚拟化网络中踩过坑,欢迎在评论区分享交流。

评论