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，并绑定多核中断）？ 

  