服务器故障排查实战:CPU软中断飙高与网络包丢包定位

生产服务器出现CPU负载异常飙高但top中无明显占用进程时,软中断(softirq)往往是罪魁祸首。网络收包软中断NET_RX在万兆网卡满载时可消耗单核100%的CPU周期,导致应用进程响应延迟急剧上升。这类服务器故障排查需要从内核网络栈逐层定位,而非简单查看进程列表。

软中断飙升的现象与初步定位

典型的软中断飙高现象:top命令显示CPU的si(softirq)列达到50%以上,但us和sy列正常。通过/proc/interrupts和/proc/softirqs确认哪个软中断类型在飙高:

# 查看软中断统计(隔1秒取样两次对比)
cat /proc/softirqs; sleep 1; cat /proc/softirqs

# 输出示例:
#                 CPU0       CPU1       CPU2       CPU3
#   NET_RX:    1294382    1245661    1302917    1268930
#   NET_TX:      15832      14923      16281      15104
#   TIMER:     892341     889022     891563     888447

# 如果NET_RX增长远超其他类型,确认是网络收包软中断

# 查看中断分布
grep -E "eth0|ens|enp" /proc/interrupts

RSS(Receive Side Scaling)将网卡收包中断分散到多个CPU核心。如果所有中断都绑定到CPU0,单核会成为瓶颈:

# 查看网卡多队列配置
ethtool -l eth0
# Channel parameters for eth0:
# Combined:       8

# 查看中断亲和性
for irq in $(grep eth0 /proc/interrupts | cut -d: -f1); do
    smp_affinity=$(cat /proc/irq/$irq/smp_affinity)
    echo "IRQ $irq -> CPU mask: $smp_affinity"
done

# 修复:将队列均匀绑定到多核
i=0
for irq in $(grep eth0 /proc/interrupts | cut -d: -f1 | tr -d ' '); do
    mask=$(printf "%x" $((1 << i)))
    echo $mask > /proc/irq/$irq/smp_affinity
    echo "IRQ $irq -> CPU$i (mask: $mask)"
    ((i++))
done

RPS/RFS实现软件层负载分散

部分网卡不支持硬件多队列或队列数少于CPU核数,通过RPS(Receive Packet Steering)在软件层将收包分散到多个CPU处理:

# 启用RPS(以64核为例,所有核参与)
for i in /sys/class/net/eth0/queues/rx-*/rps_cpus; do
    echo ffffffff,ffffffff > $i
done

# RFS确保同一连接的包始终在同一CPU处理
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
for i in /sys/class/net/eth0/queues/rx-*/rps_flow_cnt; do
    echo 4096 > $i
done

# XPS优化发包路径
i=0
for i in /sys/class/net/eth0/queues/tx-*/xps_cpus; do
    mask=$(printf "%x" $((1 << (i % 64))))
    echo $mask > $i
    ((i++))
done

网络包丢包定位

软中断飙高往往伴随丢包。从网卡硬件层到内核协议栈逐层排查:

# 1. 网卡硬件层丢包
ethtool -S eth0 | grep -iE "drop|discard|miss|error"

# 2. 驱动层丢包 - 查看环形缓冲区
ethtool -g eth0
# Current hardware settings:
#   RX: 512   # 偏小,调大
#   TX: 512
ethtool -G eth0 rx 4096 tx 4096

# 3. 协议栈层丢包
netstat -su
# Udp:
#   packet receive errors
#   receive buffer errors
# TCP:
#   1342 resets received
#   56 SYN to LISTEN socket

# 4. conntrack表满导致丢包
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
# 临时扩容:
echo 524288 > /proc/sys/net/netfilter/nf_conntrack_max

tcpdump抓包定位网络层问题

# 抓取指定端口的TCP包
tcpdump -i eth0 -nn -w /tmp/capture.pcap port 443 and host 10.0.1.50

# 分析重传
tcpdump -r /tmp/capture.pcap -nn | grep -c "retransmission"

# 用ss查看socket队列积压
ss -tnp | awk '{print $1, $2, $3}' | sort | tail -20
# Recv-Q: 持续>0说明应用读取太慢
# Send-Q: 持续>0说明对端不收或网络拥塞

NAPI与中断轮询优化

NAPI机制在高负载时从中断驱动切换为轮询模式,减少中断风暴:

cat /proc/net/softnet_stat | head
# 第一列:已处理的包总数
# 第二列:轮询模式中处理的数量
# 第三列:因budget耗尽而退出轮询的次数

# 调整netdev_budget
sysctl -w net.core.netdev_budget=600
sysctl -w net.core.netdev_budget_usecs=8000

# 持久化配置
cat >> /etc/sysctl.d/99-network.conf << EOF
net.core.netdev_budget = 600
net.core.netdev_budget_usecs = 8000
net.core.netdev_max_backlog = 10000
EOF
sysctl -p /etc/sysctl.d/99-network.conf

eBPF精准定位内核网络栈丢包

传统工具只能看到丢包总量,eBPF能精确追踪每个包在内核网络栈中的路径和耗时:

# 追踪tcp_v4_rcv耗时
bpftrace -e '
kprobe:tcp_v4_rcv { @start[tid] = nsecs; }
kretprobe:tcp_v4_rcv /@start[tid]/ {
    $delta = (nsecs - @start[tid]) / 1000;
    printf("tcp_v4_rcv latency=%d us\n", $delta);
    delete(@start[tid]);
}'

# 追踪kfree_skb(内核丢弃skb的位置)
bpftrace -e '
kprobe:__kfree_skb {
    printf("skb dropped at %p, reason=%d\n", arg1, arg2);
}'

# dropwatch定位丢包位置
dropwatch -l kas
# 1 drops at tcp_rcv_established+0x1a2/0x3f0
# 15 drops at __udp4_lib_rcv+0x1b3/0x290

dropwatch直接显示内核函数级别的丢包位置,结合kernel symbol map可以定位到具体代码行。

生产案例:Nginx代理服务器丢包

某Nginx反向代理服务器在QPS达到5万时出现间歇性502错误。排查过程:

# 1. 现象:CPU si达到60%,但us+sy仅30%
top -d1
#   CPU0: 5.0%us, 3.0%sy, 62.0%si   <- 软中断异常

# 2. 确认NET_RX软中断
watch -n1 "grep NET_RX /proc/softirqs"
# NET_RX计数每秒增长约200万次,但连接数仅5万

# 3. 检查网卡中断绑定
grep eth0 /proc/interrupts | wc -l
# 只有2个队列,但CPU有16核

# 4. 修复:启用RPS分散到所有核
for i in /sys/class/net/eth0/queues/rx-*/rps_cpus; do
    echo ffff > $i
done

# 5. 调大接收缓冲区和backlog
sysctl -w net.core.netdev_max_backlog=250000
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216

# 修复后:CPU si降至15%,502错误消除,QPS提升到7万+

根因是网卡只有2个硬件队列,中断全部集中在2个CPU核上。启用RPS后将软中断处理分散到全部16核,配合backlog和缓冲区调优,问题彻底解决。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fu-wu-qi-gu-zhang-pai-cha-shi-zhan-cpu-ruan-zhong-duan-biao/

(0)
小编小编
上一篇 9小时前
下一篇 9小时前

相关推荐