后台程序出现Too-many-open-files是什么鬼?
一. 问题现象
打了一个小升级包,仅包含几个客户端DLL文件,然后用户设备中出现haserver,AsmSvr等模块状态为“STOP”的情况,系统运行不正常。
二. 问题分析
- 通过排查haserver和AsmSvr的日志发现,在服务不正常运行时,均打印出了"Too many open files.."的提示。
- 查看系统设置最大打开文件句柄数ulimit -n,发现该值为1000000
- 通过修改升级包update.bat,增加一行打印来获取ulimit -n的值 %sshcmd% %ip% %username% %pass% "ulimit -a > /tmp/u.txt " %port% 发现ulimit -n值被修改成了1024
- 进一步在windows命令行下手动执行命令,发现depends\sshcmd.exe目录下的程序报错
- 查看程序代码发现传入的sshcmd命令行参数没有问题,通过查看做包工具源码发现,其真正用的sshcmd.exe在depends目录下的AsmUpdate.bak压缩包中
- 修改AsmUpdate.bak后缀为.rar,解压出来,使用其中的sshcmd.exe来执行
命令行下执行如下:
提示All Done OK
登录172.22.112.9后发现/root目录下并没有ls.txt文件,说明命令不生效。 - 从svn co源码下来,重新编译ssh命令,放入第6步中解压出来的目录后执行,可以生效,
查看svn提交记录,有可能上一位作者没有提交最新的sshcmd.exe命令至压缩包AsmUpdate.bak中,至此sshcmd.exe执行不生效的问题告一段落
- 继续对比测试,直接将该命令放入升级包工具中执行,发现命令是生效的!!至此,不再纠结该问题。继续后续分析
三、寻找问题真相
- 为什么打个升级包会导致大量服务“异常停止”?
通过解压升级包后发现,该升级包中勾选了“大版本升级包”选项,但没有勾选“要求重启设备”,该选项中会停止所有服务。在执行完升级后最后的升级脚本中会有一个/asm/sh/asm.monitor把未启动的服务拉起来。
- 为什么打升级包后,最大文件打开句柄数会变成1024?
通过查阅资料发现,远程执行命令与登录执行命令是有区别的
- 为什么我通过ssh手动登录后查看ulimit -n文件句柄数是1000000?
ssh手工登录时,默认会加载/etc/profile全局环境配置,该配置中有一行命令"ulimit -n 1000000",由于我们升级时使用的是non-interactive 模式,使用的是sysupdate账户,此时查看/home/sysupdate/.bashrc文件,内容如下:
# .bashrc
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# User specific aliases and functions
其中/etc/bashrc中有一段加载/etc/profile.d/*.sh的代码:
# and interactive - otherwise just process them to set envvars
for i in /etc/profile.d/*.sh; do
if [ -r "i" ]; then
if [ "PS1" ]; then
. "i"
else
. "i" >/dev/null 2>&1
fi
fi
done
unset i
unset pathmunge
这一段代码中与/etc/profile中加载的是同一段,再来了解一下登录环境变量加载行为:
根据登录加载环境变量顺序来看/etc/profile -> /etc/profile.d/*.sh ->$HOME/.bashrc -> $HOME/.bash_profile
0x01 解决办法之一
目前系统中环境变量设置有两个地方我们进行了修改,一个是在/etc/profile末尾,一个是在/etc/profile.d/asm.sh,而且在/etc/profiles中我们发现有一行设置:ulimit -n 1000000
那我们可以做如下操作:
整理我们在/etc/profile中添加的一段asm自定义环境变量设置至/etc/profile.d/asm.sh中,制作一个升级包,更新下/etc/profile和/etc/profiles.d/asm.sh,问题得到解决
0x01 解决办法之二
从登录行为来解决:
- ulimit 是 bash 内置命令,所以修改配置,不能立即生效,需要注销重新登录
- 执行 ulimit 命令设置可以立即生效,但是仅仅针对当前会话生效
- 本地登录和 ssh 远程登录不是一个会话,本地的修改不能体现在ssh 远程登录上(并不是本地配置没有生效)
- /etc/ssh/sshd_config中UseLogin 默认是no,更改为 yes 后,SSH验证结束后会使用本地的/bin/login程序,会读取/etc/security/limits.conf,所以 ulimit -a 的结果和本地登录看到的一致;
对比测试用例:
- 测试步骤:
步骤A:Windows远程执行命令2-3次:ssh.exe "172.22.112.9" "ASMUPDATE" "enc____888888" "ulimit -a > /root/ls.txt" 22 使用ssh工具登录172.22.112.9查看
- 不对UseLogin 作任何修改,执行步骤A 预期结果:OPEN FILES (-N) 1024
- 修改UseLogin为Yes,执行步骤A 预期结果:OPEN FILES (-N) 1000000
- 重启ASM设备,执行步骤A 预期结果:OPEN FILES (-N) 1000000
- 实际结果:
- 符合预期结果
- 符合预期结果
- 符合预期结果
四、总结
- 来问题了并不可怕,可怕的是下次再出现同样的问题而依旧不知道原因。
- 重现问题现象能更好的理清问题发生的背景
- 好的测试用例和步骤对问题排查有很大帮助
- 来了问题不要慌,如果有点慌,重新看总结1 2 3 步
- 微信扫码赞助
-
- 支付宝赞助
-