上周有台机器托我帮忙看性能,配置是 4 核 16G,网卡插在万兆口上,宿主机交换机也是万兆,客户自己的说法是"这条链路能跑满"。结果我一上手跑 iperf3,双向压测的最好成绩卡在 2.6 Gbps 左右就上不去了,对端换了两台机器、换到另一个机房,结果一模一样。客户一开始怀疑是交换机背板、怀疑是光模块、怀疑是运营商限速,我最后把网卡上的卸载特性全查了一遍,问题落在了一个特别不起眼的地方:TSO 被关掉了。那台机器跑在 KVM 虚拟机里,而虚拟机的 virtio 网卡默认没把 TSO/GSO 透传下去,内核只能老老实实一个包一个包地做协议栈处理,10G 的口就这么被压成了 2.6G。
如果你也在排查"带宽明明是万兆,怎么测出来只有零头"这类问题,先把机器和线路的规格摸清楚往往比什么都管用:zorocloud(原生IP精品线路,CN2GIA/9929/CMIN2 高端线路)买之前先问清带宽是共享还是独享、端口速率多少、是否独享一块 10G 物理口,比拿到手再猜要省事得多。
一、先别急着怀疑硬件:把"谁的瓶颈"分清楚
排查带宽问题,我的习惯是永远从三段拆开看:本机网卡到虚拟化宿主机、宿主机到交换机、交换机到对端机器。任何一段是共享的或者有队列限制,最终数字都会被压住,而 iperf3 的结果只告诉你"总数被压住了",不告诉你是哪一段。
先看本机这一段。用 ethtool eth0 看协商出来的速率是不是 10000Mb/s,如果显示 1000Mb/s 或者 100Mb/s,那是物理层或者交换机端口配错了,跟软件无关。速率对上了,再看 ethtool -i eth0,确认驱动是什么、固件版本是什么、在什么总线(PCIe)上。KVM 虚拟机里看到的一般是 virtio-net,而 OpenStack、KVM+Open vSwitch、Proxmox 里的表现又不一样,Docker 的 veth 又是另一回事。
然后是宿主机到对端。可以让对端起 iperf3 服务端,本机做 iperf3 -c 对端IP -R 跑反向流,再对着交换机端口看计数器。更有用的是看网卡自身的统计:ethtool -S eth0 会打出 rx_dropped、tx_dropped、nohandler 这些计数,如果它们在压测期间一路飙升,那就是本机侧在丢包,瓶颈基本可以锁定在宿主机或者虚拟化层;如果这些计数纹丝不动,那问题在链路或者对端。
二、卸载(offload)到底是什么,为什么它一关速度就掉一个档次
简单说,卸载就是把本该由 CPU 干的活交给网卡硬件去做。发送方向上,TSO(TCP Segmentation Offload)会让内核只写一个大的 TCP 段,把 MSS 级别的分片工作交给网卡;GSO(Generic Segmentation Offload)是它的泛化版本,UDP 也能用;接收方向上,GRO(Generic Receive Offload)让网卡把多个小包合并成一个大的交给内核;再加上 TX/RX checksum offload,校验和也由网卡算。
这一套机制的意义在于:不开卸载,一份 10GB 的数据在 1500 字节 MTU 下是六百多万个包,每个包都要进内核协议栈、走一次软中断、算一次校验和、组一次 sk_buff,CPU 很快就被这部分开销吃满,网卡本身的线速能力根本用不上;开了卸载,内核只处理几百个巨包,CPU 占用能掉到 10% 以下,带宽才有可能跑满。所以"关掉卸载 = 带宽掉一大截 + CPU 打满",从来不是玄学。
三、虚拟化为什么老是把卸载关掉
这是最关键的一节。物理机上 ethtool -k eth0 一般能看到 tx-checksumming: on、tso: on、gro: on 这些开关;可你换到虚拟机里再敲一遍同样的命令,会发现清一色是 off。原因在于虚拟化本身不"拥有"这块硬件,网卡是宿主机上的物理设备,虚拟机拿到的只是一个前端(virtio-net / vmxnet3 / e1000 之类),卸载能不能做,取决于宿主机后端驱动和虚拟化层愿不愿意替你转发这种大包。
具体来说,KVM 的 virtio-net 有纯 virtio 和 vhost-net 两种路径,前者每个数据包都要经过一次用户态拷贝和一次宿主机协议栈,后者把数据面放进了内核模块里(vhost-net),效率高得多。老的 e1000/rtl8139 模拟网卡则完全不支持卸载。Docker 更特殊:容器里看到的是 veth 对,veth 本质是虚拟设备,卸载能力取决于宿主机 bridge/物理网卡那一段是否真的在卸载,如果宿主机侧物理网卡关了卸载,容器里跑 iperf3 是怎么都跑不出高吞吐的。
还有一类容易忽略的情况:有些云厂商为了规避老版本 guest 内核的兼容性 bug,直接在宿主机侧把 TSO 关了,虚拟机里 ethtool -k 看到的也是 off,而且这是"设计如此",不是故障。这两种情况怎么区分?看宿主机上物理网卡的 ethtool -k:如果物理网卡是 on 而虚拟机里是 off,那是 virtio 前端没透传,属于可以尝试打开的;如果物理网卡本身就是 off,那就是厂商策略,客户端无解,只能换机型或者找客服。
四、怎么查:三个命令定位到卸载状态
第一个是 ethtool -k eth0,逐行看关键几项:rx-checksumming、tx-checksumming、tso、gso、gro、lro。凡是 off 的都是嫌疑人。注意 tx-checksumming 这类带 [fixed] 标记的项,说明能力被固件写死,改不了。
第二个是 ethtool -S eth0,看丢包和错误计数。除了常规的 drop,还有几个在卸载场景下特别值得盯的:rx_csum_errors(接收校验和错误,通常是接收侧校验和卸载没配合好导致的伪错)、tx_csum_errors、以及 virtio 后端的 vi_nic_queues 相关计数。跑一次压测前后各取一次,差值能直接告诉你问题出在收发哪一侧。
第三个是 ip -s link show eth0 和 nstat -az | grep -i drop,看内核层面的丢包。还有一个特别直观的指标是 ss -i,它会打印每个连接的 cubic、rtt、msec、以及 bytes_sent,配合压测看发送速率涨不涨,比看 top 里的 %steal 更准。
顺带说一个坑:如果 ethtool -k 显示 TSO 是 on 但压测仍然上不去,别忘了看 MTU。默认 1500 的 MTU 在万兆链路上是相当浪费的,巨型帧(ip link set dev eth0 mtu 9000)能把包数减少到 1/6,但前提是链路两端都支持,中间任何一台交换机不支持就会变成丢包。所以调 MTU 之前先跟对端和网络管理员确认,别自己单方面改。
五、怎么开:ethtool -K 的正确用法和它的副作用
在确认宿主机物理网卡确实是 on、只是虚拟机里没透传之后,可以尝试在虚拟机内手动打开:
ethtool -K eth0 tso on gso on gro on tx on rx on
这条命令会打印哪些开关改成功、哪些报 Could not change any device features。报错的项说明虚拟化前端根本不支持,改了也没用,别在这上面耗时间。另外要注意,ethtool -K eth0 tx off 是个很常见的"负优化":有些人在排查丢包时会把发送侧校验和卸载关掉,试图规避 tx_csum_errors,结果 CPU 直接被算校验和打满,吞吐反而掉一半。正确做法是保留卸载,把错误定位到具体是哪台机器的哪一侧。
还有一个更彻底的方向:让虚拟化层把卸载透传下来。KVM 里可以给虚拟机换 virtio-net + vhost-net 后端(virtio-net-pci 配 vhost=on),宿主机侧确认 vhost_net 模块已加载、网卡的 TSO/GRO 是 on,这样来宾里 ethtool -k 就会变成 on。如果你能管理宿主机,这是最干净的解法;如果是云厂商的实例,只能先用 veth 之外的方式绕开,比如把大流量走对象存储或走第三方中转。
六、开了卸载之后可能冒出来的两个新问题
第一个是 MTU 与卸载的组合问题。有些固件在 TSO 打开且 MTU 设成 9000 时会算出超过网卡能力的大包,表现为 tx_errors 上涨但 tx_dropped 不涨。我的处理顺序是:先把 MTU 调回 1500 验证吞吐是否恢复,再单独开巨型帧,一步一步来,不要同时改两个变量。
第二个是 LRO。GRO 是在内核协议栈里做合并,相对安全;而 LRO 是直接在网卡硬件里把包合并,历史上在某些老内核和特定驱动组合下会造成连接异常(报文粘包、TCP 状态错乱)。所以 lro 我一般保持 off,gro 留着,问题就少很多。
七、我的排查清单
把这几条逐项过一遍,八成能找到答案:
1. ethtool eth0 看协商速率和双工,确认物理层没被限到千兆;
2. ethtool -k eth0 看 tso/gso/gro/tx/rx 各自开关状态;
3. ethtool -i eth0 确认驱动类型(virtio-net 还是模拟网卡);
4. ethtool -S eth0 压测前后各取一次,看 drop 和 csum error 的增量;
5. 压测时同时在宿主机上看物理网卡的卸载状态,判断是"前端没透传"还是"厂商策略";
6. 确认 MTU 两端一致,改动前先回滚准备;
7. 记录 ip -s link 和 nstat 的基线,方便改动后对比;
8. 卸载开关会随重启失效,需要用 systemd 服务或 networkd 配置固化。
最后多说一句:卸载这东西在物理机上几乎是白送的,但在虚拟化环境里它经常是被"偷走"的,而且偷得悄无声息。带宽对不上的时候,先看 ethtool -k,再看宿主机,最后才去看交换机和运营商,能省下大量扯皮时间。

先确认物理层是万兆协商成功的,再往软件层查。速率对不上是配置问题,速率对得上却跑不满,才是卸载和虚拟化这一层的事。

卸载的核心思路是把分片和校验和这些机械活交给网卡,CPU 只处理少数几个大包,线速能力才用得上。

虚拟化环境里卸载能不能生效,取决于前端透传和后端驱动,关掉之后每包都要走完整协议栈,10G 的口也只能跑出零头。

确认宿主机侧物理网卡本来就是开着卸载之后,在虚拟机里用 ethtool -K 打开是可行的;报不支持的项就别硬改了。
另外补一句选机器的经验:如果你要做的是文件分发、镜像同步、大流量回源这类带宽吃紧的活儿,线路质量和带宽是否独享比配置堆得多高更重要。数脉科技(全球多线路接入,CMIN2/NTT/Level3/Cogent 等骨干网)这类接了多家国际运营商的方案,回程和去程都更可控,跑满端口比在一个共享百兆上反复调参数现实得多。


喜欢
顶
难过
囧
围观
无聊





