现象描述:
研发反馈有一批机器的CPU使用率极不均匀,主要集中在前面一半的核上,如下图所示:
(开局一张图,排障全靠蒙...)
cpu_usage
问题排查:
一开始怀疑可能是numa设定或者超线程不合理等原因导致的,在网上翻阅了许多资料与Troubleshooting文档后,仍然存在许多疑惑,期间同事也帮忙测试过确定不是numa设定的问题引起的,其实直接看/proc/cpuinfo也能发现0-19的核心是两颗CPU各占一半,基本排除了numa设定不合理或者内存条全插在一边导致另一颗CPU没有使用的问题(双物理核心CPU)。
可能Trobleshooting就是玄学+科学吧,在仍然毫无头绪的时候,我用top命令不断刷新CPU使用情况,忽然发现 %si 软中断的处理几乎都在0-19核上,而后一半核心的 %us 用户态使用率虽然比较低,但时不时也会运作,又通过ps命令验证了该机器上的代码进程没有设置CPU亲和性,于是开始猜测可能是软中断设置不合理引起的,此时又看到了一些类似问题的排障记录后,便开始进一步了解中断并定位问题。
##查看php进程的总数以及在不同核心上的分布情况
ps -eo psr,comm|grep php |wc -l;ps -eo psr,comm|grep php |sort -n|uniq -c
##mpstat可以监控多核系统下的CPU使用率以及处理的中断数量
mpstat -P ALL 1
References:
问题解决:
在查阅了大量文档并测试验证之后,已可以定位问题在于这批机器没有针对网卡中断做过优化。其实我们的机器在装机时默认都会做一系列的内核参数优化,也包括网卡多队列的亲和性优化,猜测这批机器在加上优化脚本之后没有重启运行脚本(因为该脚本是写在/etc/rc.local里开机运行的,同时发现系统默认网卡多队列只会绑定到CPU前一半的核心上,如该机器40核,默认绑定到0-19,重启了发现脚本是可以正常执行的),在手动运行脚本后,能看到后面的CPU在%us与%si上的使用率明显上升。
network_queues_tune.sh:
#!/bin/bash
# https://github.com/sklochkov/performance-tuner/blob/master/src/network-queues.sh
cpus=`grep -c processor /proc/cpuinfo`
if [ $cpus -ge 32 ] ; then
cpus=32
fi
q=`grep -E "eth|em|bond|virtio" /proc/interrupts | awk '{print $1}' | cut -d: -f 1`
ENTR=$[4096*$cpus]
sysctl net.core.rps_sock_flow_entries=$ENTR >/dev/null 2>&1
if [ $? -gt 0 ] ; then
echo "Kernel does not support net.core.rps_sock_flow_entries"
exit 0
fi
k=0
for i in $q ; do
affinity_mask=`printf "%01X" $[2**($k % $cpus)]`
echo $affinity_mask > /proc/irq/$i/smp_affinity
k=$[$k+1]
done
interfaces=`ifconfig | grep -E "^eth|^em|^bond" | awk '{print $1}' | grep -v :`
for t_iface in $interfaces ; do
if [ -d /sys/class/net/$t_iface/queues ] ; then
RX=`ls -ld /sys/class/net/$t_iface/queues/rx* 2>/dev/null | wc -l`
TX=`ls -ld /sys/class/net/$t_iface/queues/tx* 2>/dev/null | wc -l`
if [ $RX -gt 0 ] ; then
for i in `seq $RX` ; do
printf "%01X" $[2**$cpus - 1] > /sys/class/net/$t_iface/queues/rx-$[$i - 1]/rps_cpus
echo $[$ENTR / $RX] > /sys/class/net/$t_iface/queues/rx-$[$i - 1]/rps_flow_cnt
done
fi
if [ $TX -gt 0 ] ; then
for i in `seq $TX` ; do
printf "%01X" $[2**$cpus - 1] > /sys/class/net/$t_iface/queues/tx-$[$i - 1]/xps_cpus
done
fi
fi
done
方案优化:
读完这个脚本之后,发现了两个问题:
1,该脚本只适用于小于32核的机器,而我们现在新采购的机器基本都是40核起步,已不再适用;
2,调研了SMP irq affinity与 rps/rfs的具体作用后,发现它们是Linux内核不同时期的两个产物,目的都是为了优化cpu irq不均的问题以提高系统性能。rps/rfs技术是早期软件层面的一个实现,现在的SMP irq affinity技术完全能满足需求,尤其在CPU计算密集的场景中使用rps/rfs可能还会降低性能,因为中间多了一次性能消耗。
3,针对SMP irq affinity技术,调研并测试了网卡多队列绑定到所有核心随机使用与各队列分别绑定到单独核心两种情况,还是推荐大家绑定不同队列到单独核心,因为完全随机核心的话会出现软中断全集中在cpu 0上面以及某一颗物理核心负载过高的现象,如下图:
网卡多队列都绑定到0-39核
重写了一版仅使用SMP irq affinity技术的优化脚本(Python版本) :
#!/usr/bin/env python
# -*- coding: utf8 -*-
import re
from os import system
interrupts_file = '/proc/interrupts'
def file_hander(TARGET,VALUE='0'):
with open(TARGET,'w') as f_hander:
f_hander.write(VALUE)
def set_irq_affinity():
stop_irq_balance = 'service irqbalance stop'
system(stop_irq_balance)
interrupts_ct = open(interrupts_file)
cores_nr = len(interrupts_ct.readline().split())
irq_bit = 0
while True:
inter_line = interrupts_ct.readline()
if inter_line == "":
break
irq_list = inter_line.split()
if re.findall(r'eth|em|bond|virtio',irq_list[-1]):
irq_nr = irq_list[0][:-1]
TARGET = '/proc/irq/%s/smp_affinity_list' %(irq_nr)
VALUE = str(irq_bit)
file_hander(TARGET,VALUE)
irq_bit += 1
if irq_bit == cores_nr:
irq_bit = 0
if __name__ == '__main__':
set_irq_affinity()
References: