作者:夏志培
本文为原创文章,转载请注明作者及出处
背景:
c/c++应用程序进程由于各种原因会崩溃,人为上去的迅速干预处理也会造成一定的业务影响, 于是运维的同事将应用程序全部接入到supervisor,通过supervisor来防止应用进程宕掉短时间无人处理。经过一段时间后,supervisor带来一些收效,但同时引入一些问题。譬如应用程序崩溃后,在系统日志和内核的环形日志中留下一些报错信息(例如指令指针地址),虽说通过指令指针地址信息找到对应的出错行,但是由于该应用经过多次版本发布,即使通过指令指针地址找到了对应的代码行,也无法确定该出错的代码行就是当前代码仓库最新版本中的代码行。仍然不好定位bug, 形成较大的困扰。所以业务方仍然需要应用产生的core信息。于是介入参与,解决这个问题。(备注:环境信息:centos6.5 x64 ,supervisord-2.1-9)
过程和原因:
- 运维已经下线该Serve,保留了现场,于是登上目标服务器上查看,发现应用崩溃后在系统日志中仅存在少量的报错日志,(指令地址信息 ,文件的装载地址信息), 但是没有core文件。先检查core文件的名字生成规则有没有被修改。
\#sysctl -a |grep core_pattern
确认core的文件格式以core结尾。
然后检查系统参数core file size 是unlimited ,系统配置层未发现问题。
然后进行故障重现,在终端中手工启动该应用程序,崩溃后可以产生core文件。(向目标进程发SIGUSR2信号)
开始检查和对比 supervisor运行环境变量 和 终端中执行程序的环境变量 【可以借助gdb , 查看进程相关的内存】
对比环境后确实存在差异, supervisor接管后进程的 core file size 为0, 终端中执行的进程中的core file size 为ulimited, 其中0是不能产生core文件,ulimit是可以产生core文件。
- 为了所辖排查范围,用python写一个类似supervider的程序 和 产生core的C程序,并且放入到crontab中运行, 代码如下:
5.1 产生core的测试C代码: #cd /tmp/ && gcc test.c
#include<stdio.h>
int main()
{
char *str="this is a Segmentation fault";
str[0]='f';
return 0;
}
5.2 测试的python程序(类似supervisor作用)
#!/usr/bin/env python
#coding=utf8
from time import sleep
import os,signal
def child_handler(signum,stackframe):
while 1:
try:
result = os.waitpid(-1,os.WNOHANG)
except:
break
print "Reaped child process %d" % result[0]
signal.signal(signal.SIGCHLD,child_handler)
signal.signal(signal.SIGCHLD,child_handler)
try:
pid = os.fork()
if pid == 0:
os.execv("/tmp/a.out",[])
except OSError, e:
pass
sleep(30)
- 经过测试,C程序可以产生core文件,此时原因就是supervidoer程序本身了。
顺着/usr/bin/supervisord去查看源代码, 发现supervisord有一个set_rlimits函数,专门处理环境变量这一块的参数, 但supervisord的set_rlimits只会获取系统环境中两个变量:
RLIMIT_NOFILE 最大打开文件句柄数
RLIMIT_NOPROC 用户最大的进程数
,其他全部使用python.resource模块设置的默认值, 具体看https://pymotw.com/2/resource/,
为了验证这一点, 查看了当前未被supervisord接管某个应用进程的 max locked memory 和 supervisor接管进程的max locked memory, 正如预期中的一样,发现明显的差异。supervisor中由于core file size 这个参数也是使用python.resource模块设置的默认值(0),所以造成应用进程无法产生core文件。
解决办法:
- 不重启应用进程的3种解决办法:
1.1 借助gdb 直接修改ulimit的一些参数【不推荐】【内核>2.6】
1.2. 直接修改/proc/xx/limits的参数,echo -n ‘Max open files=unlimited:unlimited’ > /proc/15533/limits 【内核>2.6】
1.3. 借助prlimit工具 【内核>2.6.36】,prlimit –pid 25622 –nofile=10240:10240 - 根本解决办法, 修改supvisor模块中 options.py 文件第1293行下面加入以下代码
soft, hard = resource.getrlimit(resource.RLIMIT_CORE)
resource.setrlimit(resource.RLIMIT_CORE, (-1, hard))
示例:
#vim /usr/lib/python2.6/site-packages/supervisor/options.py
if hasattr(resource, 'RLIMIT_NPROC'):
limits.append(
{
'msg':('The minimum number of available processes required '
'to run this program is %(min)s as per the "minprocs" '
'command-line argument or config file setting. '
'The current environment will only allow you '
'to open %(hard)s processes. Either raise '
'the number of usable processes in your '
'environment (see README.rst) or lower the '
'minprocs setting in the config file to allow '
'the program to start.'),
'min':self.minprocs,
'resource':resource.RLIMIT_NPROC,
'name':'RLIMIT_NPROC',
})
###增加一下2行代码
soft, hard = resource.getrlimit(resource.RLIMIT_CORE)
resource.setrlimit(resource.RLIMIT_CORE, (-1, hard))
今天的故障分析到此啦。
更多运维技术文章,请关注「沪江技术学院」微信公众号。