Skip to main content

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 命令:

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,并绑定多核中断)?