今天处理了一个客户的故障,可能在web排错方面有一定的代表性,所以在这里跟大家分享。

  背景

  电商客户,系统架构是前端一台负载均衡设备,负载均衡算法是轮询(round robin);后面是两台web服务器,跑的应用是gunicorn(一个Python WSGI UNIX的HTTP服务器)。

  故障现象

  用户抱怨网站响应慢,有较多的页面打不开的情况。从系统监控来看,两台后端服务器的连接数(TCP)不相等。曲线图显示,其中一台的连接数在数十分钟内越来越偏离另一台正常服务器的连接数曲线。最严重的时刻,故障服务器的连接数是正常服务器的三倍。而前端负载均衡的请求分发始终是均等/轮询的。并不是负载均衡引发的连接数不同。

  处理过程

  在两台服务器上分别运行top命令,对比了性能状态数据。可以看到的区别是:正常服务器的load average大致在2以内,故障服务器接近6。从内存状态来看,正常服务器的物理内存是64GB,使用了50GB左右,未启用swap;故障服务器物理内存32GB全部使用,并启用了500多MB的swap。

  初步判断,故障是因为一台服务器只有32GB,内存资源不够,导致请求处理不够及时(连接数堆积和load average高)。然后检查了两台服务器上的连接数。运行ss -s命令,正常服务器TCP连接数2800左右,故障服务器TCP连接数3600左右。这也证实了监控图的信息。

  故障服务器的并发连接数高,并不是因为前端负载均衡分配的请求不均衡,只是因为自身处理的比正常服务器慢,所以并行存活的连接数就更高。这也使得故障持续恶化。

  于是我们建议用户升级故障服务器到64GB内存,保持跟另一台服务器相同。因为我们提供的是公有云服务,配置升级那是相当的快!升级后连接数、内存使用率等指标都很接近了,用户也不再遇到打不开页面的问题。事情到这里似乎就结束了。

  但是,到了下午,客户的网站访问量比上午大了很多。客户发现两台机器的load average差异较大(一台0.8,一台2.0),想寻求帮助,彻底找到这种负载不一致的原因。客户很担心下次“大促”的时候服务器还是会顶不住。

  于是我们不能仅仅从内存上面找根源了。既然是load average不一致,那应该是跟CPU负载有关系。top命令输出结果显示,CPU使用率指标上,两台服务器基本一致。由于这两台机器都是16核,这时候我们把焦点放在了CPU core跟进程的分布关系上。我们了解到客户使用的是gunicorn wscgi server,接着就查看这个服务的使用情况。运行ps -ef | grep gunicorn | wc -l,输出是35。去除grep一条,也就是客户在单台服务器上就跑了34个gunicorn进程。由于机器是16核的,平均来说,一个核应该跑两个gunicorn进程。我们接下来查看了进程和CPU核的分布情况。这时候需要使用ps -eF命令,其输出的第七列就是CPU核编号信息。

进程在多核CPU环境下分布不均导致TCP连接堆积_第1张图片

  在当前这个环境下,运行以下命令:

  ps -eF | grep gunicorn | sort -k7 -n

  这次我们看到,正常服务器上,34个gunicorn进程基本以3到5个进程分布在某个核上的形式运行着(很多核并没有跑gunicorn);而怀疑有故障的服务器上,某个核上集中了13个gunicorn进程。这显然会导致单个核负载过重,进而影响整体处理效率。

  找到原因后,方法也很快明确了。接下来就是让这些进程尽量均匀的分布在这些CPU核上。这个动作有shell脚本可以执行,大家有兴趣可以去找一下。这方面涉及的是linux CPU亲和性(affinity)的知识。参考链接:http://www.ibm.com/developerworks/cn/linux/l-cn-linuxkernelint/

  执行脚本之后,这些进程的分布情况得以明显改善。回头再用top等命令查看系统性能,用ss -s查看TCP连接数等指标,两台服务器都已经几乎一致了。客户那边也表示网页打不开等情况已经消失。“剁手党”们又可以在这个美好的夜晚尽兴网购了。