HYPER-V虚拟机网卡因流量过高当机分析与解决方法
昨天2台有30万访问量的虚拟机网卡当机,执行ping命令发现有这个报错:
```
ping: sendmsg: Operation not permitted
```
结合“虚拟机网卡在流量高峰时才出现,重启后恢复”,问题大概率和 **虚拟化平台的网卡/驱动/安全策略** 有关,而不是简单的网络不通。常见原因有:
---
### 🔎 可能原因
1. **虚拟化平台的安全策略限制**
* 比如 KVM、VMware、Hyper-V 的防火墙、vSwitch 安全规则触发,丢弃 ICMP 或阻止报文发送。
* 当流量高峰时,虚拟交换机可能触发 **端口安全(port security)** 或 **风暴控制(storm control)**,直接禁止虚拟机继续发包。
2. **iptables/ebtables/SELinux/AppArmor 限制**
* 在流量异常时,iptables 可能被防护脚本(比如 Fail2Ban、DDOS 防御)自动插入 DROP 规则,导致 ping 报错为 *Operation not permitted*。
* `sendmsg: Operation not permitted` 一般是内核禁止应用层发包,而不是网络栈完全不可达。
3. **网卡驱动/队列过载**
* virtio、e1000 等虚拟网卡驱动在高并发下容易队列塞满。
* 如果 ring buffer 被填满,可能触发驱动错误,报文无法下发。
4. **ARP/邻居表溢出**
* Linux 的 `neigh table` 在高并发时可能爆满,导致内核直接禁止发包。
* 可以通过 `ip -s neigh` 或 `dmesg` 检查是否有 “neighbour table overflow”。
5. **内核资源耗尽 (socket buffer/netfilter conntrack)**
* 高流量时,`net.ipv4.ip_local_port_range`、`conntrack` 表、`net.core.somaxconn`、`netdev_max_backlog` 被用光,系统无法再发 ICMP 报文。
* `sendmsg: Operation not permitted` 就是这种内核层拒绝。
---
### 🔧 建议排查步骤
1. **检查 dmesg / 内核日志**
```bash
dmesg | grep -i denied
dmesg | grep -i neigh
```
2. **确认防火墙**
```bash
iptables -nvL
iptables -t raw -nvL
iptables -t mangle -nvL
```
3. **查看 conntrack 是否溢出**
```bash
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
```
4. **检查邻居表是否爆满**
```bash
ip -s neigh
```
5. **检查虚拟机网卡驱动**
```bash
ethtool -i eth0
```
6. **提高内核资源参数(预防措施)**
在 `/etc/sysctl.conf` 增加:
```conf
net.core.netdev_max_backlog = 8192
net.ipv4.neigh.default.gc_thresh1 = 2048
net.ipv4.neigh.default.gc_thresh2 = 4096
net.ipv4.neigh.default.gc_thresh3 = 8192
net.netfilter.nf_conntrack_max = 262144
```
然后:
```bash
sysctl -p
```
---
⚠️ 你的情况 **重启后恢复**,说明资源是动态被耗尽/阻断的,不是永久配置问题。
最可能的两个方向:
* **conntrack 表溢出**(高峰时连接数过多,导致内核拒绝新连接/报文)。
* **虚拟交换机风暴控制/限流**(高峰时触发 vSwitch 安全机制)。
---
要不要我帮你写一个 **实时监控脚本**(收集 conntrack、neigh table、带宽利用率、dmesg 关键字),这样下次再复现时,你能立刻知道是哪一块资源耗尽?
👌 收到,你这里的信息点挺关键:
1. **网卡驱动:hv\_netvsc**
* 说明这台虚拟机是在 **Hyper-V / Azure** 上跑的。
* `hv_netvsc` 是 Hyper-V 的 paravirtualized 驱动,在高并发下偶尔会遇到队列溢出或转发 bug。
* 一旦内部 ring buffer / channel 被占满,报文就会被丢弃,应用层收到的就是
```
ping: sendmsg: Operation not permitted
```
(这是 Linux 内核在无法下发数据时的报错,不是防火墙拒绝)。
2. **ARP/邻居表**
* `ip -s neigh` 里没有看到 `FAILED`,大部分都是 `REACHABLE`,个别 `STALE` 正常。
* 说明不是邻居表爆满。
3. **sysctl 配置**
* 你已经把 `netdev_max_backlog`、`nf_conntrack_max`、`somaxconn` 等参数都调高了,说明连接跟 backlog 溢出的可能性已经大幅降低。
* `nf_conntrack_max = 524288` 足够大,conntrack 不太会是瓶颈。
---
### 📌 综合分析
结合 **虚拟机高峰时才触发** + **hv\_netvsc 驱动**,大概率是:
* **Hyper-V vSwitch / hv\_netvsc 在高并发流量下丢包或阻止新报文**。
* `sendmsg: Operation not permitted` 并不是权限问题,而是 **驱动/内核层报文没法发出**。
* 重启后恢复,是因为网卡队列 / vmbus 通道被清空,重新初始化了。
---
### 🔧 建议解决方案
1. **更新驱动 / 内核**
* Ubuntu 20.04 的默认内核里,`hv_netvsc` 有已知的稳定性 bug。
* 建议升级到 **5.15+ LTS 内核**,或者在 Azure/Hyper-V 环境,直接用 **最新的 cloud kernel**。
2. **增加多队列**
* 检查是否启用了多队列:
```bash
ethtool -l eth0
```
* 如果只有 1 队列,可以尝试增加:
```bash
ethtool -L eth0 combined 4
```
* 这样可以避免高并发时单队列被塞满。
3. **绕过 hv\_netvsc,用 SR-IOV / 合成网卡**
* 如果平台支持,启用 **SR-IOV** 网卡直通,绕过 hv\_netvsc。
* Azure 上是 **Accelerated Networking**,Hyper-V 上要在宿主机启用 SR-IOV。
4. **监控 vmbus 队列 / 网卡丢包情况**
* 查看驱动统计:
```bash
ethtool -S eth0
```
看有没有 `dropped`, `errors`, `busy` 的计数。
* 如果高峰时这些数暴增,就能确认是队列/驱动溢出。
---
👉 总结:
问题不是 Linux 配置不够,而是 **Hyper-V 的 hv\_netvsc 驱动在高流量时掉坑**。最有效的解决办法是 **升级内核/驱动** 或 **改用 SR-IOV 网卡**。
---
要不要我帮你写一个小脚本,可以定时采集 `ethtool -S eth0` 和 `dmesg` 日志,把高峰期网卡丢包情况抓下来?这样你能确证是 **hv\_netvsc driver bottleneck**。
root@rulr-4:/var/log/sysstat# ethtool -S eth0
NIC statistics:
tx_scattered: 0
tx_no_memory: 0
tx_no_space: 0
tx_too_big: 0
tx_busy: 0
tx_send_full: 1015
rx_comp_busy: 0
stop_queue: 0
wake_queue: 0
vf_rx_packets: 0
vf_rx_bytes: 0
vf_tx_packets: 0
vf_tx_bytes: 0
vf_tx_dropped: 0
tx_queue_0_packets: 8786385
tx_queue_0_bytes: 681809072
rx_queue_0_packets: 10353578
rx_queue_0_bytes: 783861988
tx_queue_1_packets: 10558838
tx_queue_1_bytes: 833217559
rx_queue_1_packets: 10700441
rx_queue_1_bytes: 807056969
tx_queue_2_packets: 9822662
tx_queue_2_bytes: 757879972
rx_queue_2_packets: 11035513
rx_queue_2_bytes: 835997833
tx_queue_3_packets: 7852471
tx_queue_3_bytes: 619014441
从你贴出的 **`ethtool -S eth0`** 输出看,有几个值得注意的指标:
```
tx_send_full: 1015
```
👉 这个计数器表示 **发送队列已满(driver ring buffer full)** 的次数,说明在流量高峰时,网卡的发送队列被打满,内核往驱动写包时被阻塞,导致出现你前面提到的 **`ping: sendmsg: Operation not permitted`** 问题。
其它计数器(如 `tx_no_memory`, `tx_no_space`, `rx_comp_busy`)都是 0,说明主要瓶颈出现在 **发送队列拥塞**。
---
### 可能原因
1. **虚拟化瓶颈**
* 如果是虚拟机(KVM/VMware/Hyper-V 等),往往因为 vNIC 队列过小或虚拟交换机转发压力过大,容易在高流量时堆积。
* 多队列(`tx_queue_x`)已经启用,但可能 **分配的中断/CPU 核数不够**,流量大时容易掉包。
2. **驱动/网卡队列太小**
* 默认 ring buffer(`txqueuelen` 和 `ethtool -G` 设置的 ring size)过小,无法承受突发流量。
3. **宿主机/物理网卡瓶颈**
* 如果虚拟机运行在物理机上,宿主机的物理网卡或 vSwitch 已经打满,也会反映到虚拟机侧。
---
### 建议排查 & 优化
1. **查看当前队列深度**
```bash
ethtool -g eth0
```
看 `TX` 和 `RX` ring buffer 默认值。很多虚拟网卡默认只有 256 或 512,很小。
2. **增大 ring buffer**
比如调到 4096:
```bash
ethtool -G eth0 rx 4096 tx 4096
```
(注意部分 vNIC 可能不支持这么大,要看 `ethtool -g` 输出支持的 max 值)
3. **调整 txqueuelen**
```bash
ifconfig eth0 txqueuelen 2000
```
或者用 ip 命令:
```bash
ip link set eth0 txqueuelen 2000
```
4. **检查 CPU 中断绑定**
```bash
cat /proc/interrupts | grep eth0
```
如果只落在一个 CPU 上,可以用 **irqbalance** 或手工 `smp_affinity` 做中断多核分担。
5. **虚拟化层排查**
* 如果是 KVM,确认 virtio-net 是否开了多队列(`mq=on`)。
* VMware/Hyper-V 也要确认虚拟交换机性能参数,必要时开启 **SR-IOV** 或 **DPDK** 提升性能。
6. **监控宿主机带宽利用率**
如果物理机本身已接近满速(比如 1Gbps 打满),虚拟机里当然会出现 send full。
---
📌 总结:
你的虚拟机 **高峰时网络不通、`sendmsg: Operation not permitted`**,结合 `tx_send_full: 1015`,几乎可以确认是 **发送队列打满**。
首要优化手段:**增大 ring buffer + txqueuelen**,再确认虚拟化层是否支持多队列分流。
要不要我帮你写一份 **Ubuntu20.04 下针对虚拟机网卡的优化脚本**(自动调整 ring buffer、txqueuelen,并绑定多核中断)?
No Comments