服务器带宽够了还是卡(细数那些服务器卡顿往事)
服务器带宽够了还是卡(细数那些服务器卡顿往事)因为是多核,我们说到卡,可能也并不是全局的,比如负载,我们第一眼基本就是看平均负载,是针对系统而言的,但是针对某个进程来说,平均负载可能就没有那么大的代表性了,典型的多核利用问题。大多数程序运行的时候是单核利用的,比如8核的机器,虽然负载只有1,但是top或者mpstat看到的cpu核利用就是具体的一个核被榨干了,显示idle为零了(0.0%id)。所以我们在编译一个大型软件的时候,指定多核利用会比单核快多了,比如make命令的-j参数指定N个任务进程同时进行,速度顿时提高。如果不说我们服务器cpu支持内存的ECC校验,支持超大内存,支持十二核心、二十四线程、超线程技术等,这还真有点汗颜。上面的例子核心问题其实是硬盘IO的瓶颈导致的。很多卡的问题,并不直接是cpu的问题,而是我们不怎么入眼的硬盘问题,所以平常说的硬盘多厉害,基本都是说有多快,而不是看容量有多大。说到硬盘容量,其实也是个技术
1 不惧,勇于上阵
自我激励下:碰到问题先上机器到处逛逛看下再说,可能就是个小问题点引起的。
先给个很简单的问题,两台机器之间传输一个大文件,传输速度的瓶颈是哪个因素决定的呢?
大家入门时候会针对四大组件做些基础的指标分析,这次我们这次少谈基础命令,来闲聊下这4个组件,然后重点复习下云主机上关于cpu的一个重要特性:软中断。
1.1 硬盘
平常碰到的卡的问题,在外行眼里可能很容易被误解,比如我们最常见的电脑开机时间。十多年前电脑开机时间普遍在50秒以上,但这个主要是cpu慢导致的吗,看似是的,电脑没那么快的响应能力,那就是i3i5之类的cpu问题,其实不是的,花个300块钱换个SSD硬盘,开机时间轻松到10秒。
上面的例子核心问题其实是硬盘IO的瓶颈导致的。很多卡的问题,并不直接是cpu的问题,而是我们不怎么入眼的硬盘问题,所以平常说的硬盘多厉害,基本都是说有多快,而不是看容量有多大。
说到硬盘容量,其实也是个技术细节问题,多年前主流配置还是R410的centos5普遍用146G硬盘。后来发现R410居然最大只支持识别2T的硬盘,再后来做raid之后发现ext4居然最大可分区空间只有16T,被PC玩家鄙视了。
1.2 CPU
说到cpu,估计又要被PC玩家鄙视了,我们服务器的cpu主频一般就1.9GHz到2.40 GHz,能申请个3.20GHz的就当个宝来用了。相比平常的PC机器,经常就是2.90GHz以上,还随便就冒出个4.20GHz,这就导致用来app打包的那些同学就非常鄙视服务器了,用PC机那打包速度确实不是一般的快,这个在内网还是很推荐用PC代替服务器的。
如果不说我们服务器cpu支持内存的ECC校验,支持超大内存,支持十二核心、二十四线程、超线程技术等,这还真有点汗颜。
因为是多核,我们说到卡,可能也并不是全局的,比如负载,我们第一眼基本就是看平均负载,是针对系统而言的,但是针对某个进程来说,平均负载可能就没有那么大的代表性了,典型的多核利用问题。大多数程序运行的时候是单核利用的,比如8核的机器,虽然负载只有1,但是top或者mpstat看到的cpu核利用就是具体的一个核被榨干了,显示idle为零了(0.0%id)。所以我们在编译一个大型软件的时候,指定多核利用会比单核快多了,比如make命令的-j参数指定N个任务进程同时进行,速度顿时提高。
1.3 内存
经过硬件的多次迭代更新,cpu厉害了,硬盘也是固态了,可能还会有什么瓶颈短板呢?
大家都知道内存速度很快,但是快也是有个速度的,也有频率区别,即使再快,加载192G的内存也是需要时间的,这里可能也会导致某些时候给人的印象就是好慢,特别是物理机开机的内存自检会让人怀疑这个是服务器应有的速度么。
内存瓶颈感觉不怎么会影响到我们,说明平常业务量很小,平常小打小闹地用起来是没什么问题,当到了一定的量级之后,内存的问题就突显了,特别是大数据库和openstack之类的应用。有个很重要的NUMA特性设计就会影响到cpu核的利用,比如负载偶尔跳动厉害引起服务器卡顿。
1.4 网络
还有些情况只是网络问题导致卡顿误区的:
比如ping某个ip的时候,半天不响应,可能不是ip不通,而只是ping命令在进行IP反解析,加个-n就可以解决问题了。
类似的ssh登录慢,并不是负载高机器卡,只是在尝试IP反解析,或者在尝试类似GSSAPIAuthentication的认证方式。
还有就是纯粹的办公网络问题,导致远程操作响应慢,远远不是卡顿问题。
当然还有个很重要的卡顿来源是网络设备。
外行可能认为交换机就只是插上网线就可以用的设备,但是如果接入的设备多了,流量跑高了也很容易导致交换机卡顿,甚至是达到出口设备的NAT瓶颈,如果网络拓扑异常还容易导致全局莫名其妙的卡顿。
1.5 小结
回到上面的传输问题,分析的逻辑虽然简单,但凭空想象出来的结果可能没那么准,通过来回对比下容易得出结论:
- 如果网卡是千兆口,可能可以达到120MB/s,如果是万兆网卡呢,速度可以到1200MB/s?
- 如果是万兆网卡,速度没达到预期,硬盘IO可能就到100%了,瓶颈就是硬盘了。
- 如果IO很低,硬盘不是问题,可以top看下cpu占用,很可能cpu是100%了,而且是用的单核,这个时候多核cpu不管用,主频高才是王道。
- 传输途径如果是scp,涉及到openssl加密的问题,速度可能才100MB/s就把cpu耗上去了,改为rsync会把这个cpu问题减轻,但是用nginx做个web服务然后再用wget下载才能把速度发挥起来,可以达到650MB/s。
- 然后就涉及到系统的调优了,比如之前写的tcp_sack值、tcp_wmem和tcp_rmem等。
总的来说上面几个组件最容易坏的是硬盘,而且危害也最大,CPU貌似是用不坏,内存虽然也很坚挺,但是故障率随着年限增加而增大,还好有个ECC校验,甚至可以提前把故障的内存条剔除,不至于大概率让服务器崩溃,除非是内存用的毫无空闲。最不起眼的是网络了,很容易让人误解除非公网抖动,网络是不会存在问题的,其实不然,业务量一上来,服务器网卡性能就和硬盘IO一样重要,千兆虽然感觉够用,但是很多场景会让人想上万兆网卡,而且服务器接入的网络设备也不是无限强大的。
上面的大文件传输不会涉及到SI软中断的性能,但是现实中服务端的链接很多都是小数据包,是另外一番天地了。
2 软中断
下面来谈谈我们今天的话题重点:软中断。
当某台高配置的机器,撑起大量业务时候,我们会时刻关注性能短板在哪。当内存和硬盘还空闲的时候,负载也不高的时候,一个我们多年前很少关注的一个指标以大写的方式出场了:PPS,网卡的包转发率。
我们平常说的千兆网卡,很多人以为瓶颈就是带宽最多只能跑1000Mb,其实除非是纯下载业务,比如CDN节点,否则按我们数据交互频繁的游戏业务来说,大量小包的转发就会让网卡疲于奔命了,能跑个两三百兆就不错了,这就是PPS指标做的怪。现象就是网络延迟增大,服务器top可以看到ksoftirqd进程排名靠前,si%软中断值加大。
对于包大小的查看,wireshark里面可以用ip.len来手动过滤,然后看底部匹配数目的百分比,或者用包长度的分析功能(Statistics -> Packet Lengths):
游戏服务端一般都是小包交互:
CDN节点有大量长度大于1k的包
这种大包很容易就可以把网卡带宽跑满,但是小包就很难了。
怎么测试呢,可以用hping3的flood方式来压测对方,看下网卡带宽能跑多大,调节-d的数据大小就可以看到效果了,当每个数据包携带的数据很小的时候,比如我们游戏60字节的头加上10字节的数据,海量70字节的包大小是不能把网卡是跑满的,PPS率先达到瓶颈,并占用大量cpu的处理能力从而把服务器负载带高。
这次外行的PC玩家表示从来没把网卡瓶颈当回事,就像搞技术的我们从来不把声卡麦克风当回事一样。
2.1 多队列软中断
多年前只有把Linux当网关来跑高流量的NAT才出现的软中断问题在现在高配机器跑大量游戏业务重新出现了。这个和cpu关系密切的短板怎么破?
以前只需要重新分配下irq中断号到具体的cpu就可以立竿见影地把延迟降下来,但是为什么重新提一些这个事情呢,因为我们改用云主机并在同一台机器加大业务量把这个pps的问题放大了,以前物理机一般可以到130万PPS以上,普通的腾讯云是30-50万PPS,我们最开始在openstack用NAT来做网络组件,PPS更是掉到惨不忍睹的局面,到后来改为SR-IOV的网卡算是勉强把PPS带到了10万PPS,但远远不够,然后就有了针对云主机的软中断探索。
云主机的软中断碰到了哪些不一样呢,根据网卡类型,比如如果openstack那里是多队列的网卡模式的话,中断里面网卡的命名会和物理机不一样,比如virtio0-input.队列号和virtio0-output.队列号,不再是物理机的em1-队列号的方式,不过也还算比较相似。
但很多情况只有1个队列号,这就需要进一步地探究了:
# ethtool -l eth0
Channel parameters for eth0:
Cannot get device channel parameters
: Operation not supported
这种就是不支持,一般的虚拟机是不支持的,需要用策略来支持虚拟机的多队列功能来加大pps性能,如果ethtool提示没有-l参数的话就得自行升级下ethtool版本了,同时注意下和内核版本也有些关系,已知2.6.32-754是支持的。
像现在几个大厂常见的云主机应该都是支持多队列,但默认基本上没开多队列,比如:
# ethtool -l eth0
Channel parameters for eth0:
Pre-set maximums:
Combined: 4
Current hardware settings:
Combined: 1
这种Current 的Combined是1,而且小于Pre-set maximums的Combined时候表示没有开启,这种情况跑按照以前那种根据/proc/interrupts来调整也是会跑到一个cpu上,因为当前队列只有1个,需要先用ethtool -L eth0 combined 4 来手动调整增加。
当网卡变成了多队列的方式就容易调整了,按照大家熟悉的先找到irq号,然后设置cpu的亲和力就可以了:
- 在/proc/interrupts文件查看网卡对应的IRQ号
- 在/proc/irq/$IRQ/smp_affinity_list文件里面指定对应的cpu序列号
这次说的是smp_affinity_list非smp_affinity,其实都一样,smp_affinity_list用比较容易读的十进制方式来设置,常用的格式比如1、1-3 6、1-4,但是smp_affinity属于不直观的十六进制。
问题点就是那些没有队列号的软中断要怎么处理。
2.2 RPS新特性
比如我们用SR-IOV模式的网卡是没法直接通过中断号来调整软中断的。需要引入新的概念叫做RPS,接收端包控制,用于在软件层把将数据包指派至特定的 CPU 进行处理。
参考红帽文档:
rps_cpus 文件的默认值为 0。这会禁用 RPS,以便处理网络中断的 CPU 也能处理数据包。要启用 RPS,配置适当的 rps_cpus 文件以及特定网络设备和接收队列中须处理数据包的 CPU 。rps_cpus 文件使用以逗号隔开的 CPU 位图。因此,要让 CPU 在一个接口为接收队列处理中断,请将它们在位图里的位置值设为 1。例如,用 CPU 0、1、2 和 3 处理中断,将 rps_cpus 的值设为 00001111 (1 2 4 8),或 f(十六进制的值为 15)。
默认路径:/sys/class/net/*/queues/rx-*/rps_cpus和/sys/class/net/*/queues/rx-*/rps_flow_cnt
RPS参考脚本:
#定义rfc数值
rfc=4096
cc=$(grep -c processor /proc/cpuinfo)
#根据cpu核数的不同,计算对应的rps_value 第一个cpu,即cpu0不使用
tmp=$(for i in `seq 1 $((cc-1))`; do echo -n 1; done)
bin_rps_value=$tmp"0"
# 二进制转十六进制,每八位十六进制需要用逗号隔开,该算法最多支持16位 十六进制,也就是CPU小于等于64核的都没问题
rps_value=`printf \'%x\n\' "$((2#$bin_rps_value))" | sed "s@fffffffe\\$@ fffffffe@g;s@ffffffff @ ffffffff @g;s@^ @@g"`
# 设置rps总数
rsfe=$(echo $cc*$rfc | bc)
sysctl -w net.core.rps_sock_flow_entries=$rsfe
for fileRps in $(ls /sys/class/net/*/queues/rx-*/rps_cpus|grep -v bond)
do
echo $rps_value > $fileRps
done
for fileRfc in $(ls /sys/class/net/*/queues/rx-*/rps_flow_cnt|grep -v bond)
do
echo $rfc > $fileRfc
done
2.3 测试结果统计
测试方法用hping3或者netpef都可以,最直观的感受就是在PPS很高的时候,ping的延迟是20多毫秒或者更高,通过软中断优化,可以立马降到个位数。
RPS特性弥补了老旧网卡或者某些虚拟机不支持多队列的网卡性能,但是在支持多队列的情况下优先通过普通修改IRQ的CPU亲和力来缓解软中断问题。
测试的时候PPS的查看可以用zabbix监控,实时看的话用sar -n DEV 2的方式,但是centos6自带的sar经常出现数据不准确的情况,可以自行编译升级到最新版,或者直接用ifconfig来手动计算,计算方式也很简单,计算每秒RX packets和TX packets的包总量差值即可。
3 小结
虽然上面更倾向于是很常规的故障排除,但不可否认的是在硬件足够好的时候,会少很多性能问题和后续的优化处理成本,所以在成本可控的情况下,经过多年风雨的技术控也想说句话:长久的核心应用尽量使用配置好一点的服务器来搭建。这样给业务有比较大的弹性伸缩空间,不至于容易故障或者频繁被动升级。
附录
上面没有涉及到命令细节,下面随带经典的命令集供大家复习参考: