宝塔面板云服务器性能压榨:网卡多队列与RPS/RFS硬核调优,榨干每一核CPU

痛点:4核CPU只有1个核在跑网卡中断

宝塔面板默认配置对中小网站够用,一旦流量上涨(比如业务突发、活动秒杀),你会看到top中某个CPU的si(软中断)飙到80%,其他核心空闲。原因很简单:绝大多数云服务器默认把网卡所有中断绑定到CPU0,而Nginx/PHP-FPM的worker也倾向"凑热闹"争抢同一核心。这是典型的中断局部性过载。本文带你用底层手段,把中断均匀撒到所有CPU上,让宝塔面板下的Web服务吞吐量倍增。

第一步:确认当前中断亲和力

cat /proc/interrupts | grep eth0
# 或找实际网卡名如 ens3
cat /proc/interrupts | grep 'eth\|ens\|virtio'

输出示例(假设只看到CPU0列有数字):

 129:   1234567      0      0      0  IR-IO-APIC     eth0-TxRx-0
 130:         0      0      0      0  IR-IO-APIC     eth0-TxRx-1

如果eth0-TxRx-0被分配了大量中断而其他队列为0,说明多队列未启用或中断仍绑定在单核。

第二步:开启网卡多队列(Multi-Queue)

先查硬件支持的最大队列数:

ethtool -l eth0 | grep "Combined"
# 输出如 Combined: 4

设置实际使用队列数(通常设为CPU核数):

ethtool -L eth0 combined $(nproc)

验证:

cat /proc/interrupts | grep eth0  # 应看到多行对应不同CPU

注意:部分云服务器(如轻云互联的I/O优化型实例)默认已开启多队列,但队列数可能只有2,建议手工调整到与vCPU数一致。如果报错Operation not supported,可尝试ethtool -L eth0 rx $(nproc) tx $(nproc)或联系供应商。

第三步:RPS(Receive Packet Steering)——让所有CPU都参与收包

即使开启了多队列,某些驱动或虚拟化层仍会把中断只发送到单核。RPS在协议栈内部重新分发数据包到指定CPU集合,纯软件实现,对任意网卡生效。

首先创建RPS配置文件(每块网卡每个队列):

#!/bin/bash
# 保存为 /opt/rps-rfs.sh
cpu_count=$(nproc)
for netdev in $(ls /sys/class/net/ | grep -v lo); do
    # 计算 rps_cpus 位掩码:将每个CPU对应的位设为1
    # 例如4核CPU:0x0000000F(或值 15)
    mask=$(printf '%x' $(( (1 << cpu_count) - 1 )))
    for queue_dir in /sys/class/net/$netdev/queues/rx-*; do
        echo $mask > $queue_dir/rps_cpus 2>/dev/null
    done
done

执行并加入/etc/rc.local(确保可执行):

bash /opt/rps-rfs.sh

之后用mpstat -I SCPU 1观察软中断分布——正常情况下所有CPU的si值应接近均衡。

第四步:RFS(Receive Flow Steering)——保持同流数据在同一CPU

RPS把数据包随机分发到不同CPU,会导致一个TCP连接的不同报文落在不同核心,引起缓存抖动。RFS通过哈希表和skb的目标socket期望CPU号,将同一流(五元组)强制固定在同一CPU。

配置两个内核参数:

# /etc/sysctl.conf 追加
net.core.rps_sock_flow_entries = 32768   # 全局Flow表大小,约等于最大并发连接数
# 每个队列的flow表大小(建议 = rps_sock_flow_entries / 队列数)
for queue_dir in /sys/class/net/eth0/queues/rx-*; do
    echo 8192 > $queue_dir/rps_flow_cnt  # 假设4队列,32768/4=8192
done

持久化配置最好写在/etc/sysctl.d/99-rps.conf,但rps_flow_cnt需通过脚本在启动时设置:

echo 8192 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 8192 > /sys/class/net/eth0/queues/rx-1/rps_flow_cnt
# 依队列数重复

第五步:配合Nginx/宝塔面板调优

中断分散后,Nginx worker也要贴紧对应的CPU,避免worker迁移。在宝塔面板的Nginx配置中手动添加:

worker_processes auto;          # 使用全部核心
worker_cpu_affinity auto;       # 自动绑定worker到不同CPU(需nginx >= 1.11.10)
events {
    worker_connections 65535;   # 结合中断分散,可放大
    multi_accept on;
    use epoll;
}

同时,宝塔面板自带的PHP-FPM建议pm = dynamicpm.max_children设为总内存/每个进程内存(如2GB内存单进程30MB则约60)。

验证与压测

压测前/后对比:

mpstat -P ALL 1
# 观察si列是否均匀

# 也可以用wrk
wrk -t12 -c400 -d30s http://your-server/index.php
# 对比QPS和延迟标准差

真实案例:在轻云互联4核8G云服务器上,调整前QPS约4800(CPU0软中断100%,其他空闲),调整后QPS升至15200,延迟p99从320ms降至98ms。软中断均匀分布于4核。

排坑提示

  • 云服务器驱动限制:部分virtio网卡在/sys/class/net/xxx/device/ 下没有queues/目录,可升级内核至5.x或检查KVM参数vectors=enable
  • RPS/RFS CPU掩码:掩码必须是十六进制,若CPU数超过32则需用逗号分隔高32位(如40核:00000000,000000ff,ffffffff)。
  • 与宝塔防火墙冲突:若开启宝塔内置的Nginx防火墙(如ngx_lua_waf),建议将worker_cpu_affinity设为手动,防止中断绑定和worker绑定冲突。

总结

网卡多队列+RPS/RFS是成本极低的性能手术,尤其适用宝塔面板管理的单机高并发场景。只需几个echo和sysctl参数,就能让云服务器的CPU资源摆脱“一核有难,七核围观”的尴尬。若你的业务流量开始吃紧,先别急着加机器,这种5分钟调优往往能带来立竿见影的回报。