昨天,项目的 ElasticSearch 服务挂了,我说的挂可不是进程没了,因为有 Supervisor 保护,而是服务不可用了。以前曾经出现过一次因为 ES_HEAP_SIZE 设置不当导致的服务不可用故障,于是我惯性的判断应该还是 ES_HEAP_SIZE 的问题,不过登录服务器后发现日志里显示大量的「Too many open files」错误信息。
那么 ElasticSearch 设置的最大文件数到底是多少呢?可以通过 proc 确认:
shell> cat /proc/
结果是「4096」,我们还可以进一步看看 ElasticSearch 打开的都是什么东西:
shell> ls /proc/
问题看上去非常简单,只要加大相应的配置项应该就可以了。此配置在 ElasticSearch 里叫做 MAX_OPEN_FILES,可惜配置后发现无效。
按我的经验,通常此类问题多半是由于操作系统限制所致,可是检查结果一切正常:
shell> cat /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
问题进入了死胡同,于是我开始尝试找一些奇技淫巧看看能不能先尽快缓解一下,我搜索到 @-神仙- 的一篇文章:动态修改运行中进程的 rlimit,里面介绍了如何动态修改阈值的方法,虽然我测试时都显示成功了,可惜 ElasticSearch 还是不能正常工作:
shell> echo -n 'Max open files=65535:65535' > /proc/
此外,我还检查了系统内核参数 fs.file-nr 及 fs.file-max,总之一切和文件有关的参数都查了,甚至在启动脚本里硬编码「ulimit -n 65535」,但一切努力都显得毫无意义。
正当山穷水尽疑无路的时候,同事 @轩脉刃 一语道破玄机:关闭 Supervisor 的进程管理机制,改用手动方式启动 ElasticSearch 进程试试看。结果一切恢复正常。
为什么会这样呢?因为使用 Supervisor 的进程管理机制,它会作为父进程 FORK 出子进程,也就是 ElasticSearch 进程,鉴于父子关系,子进程允许打开的最大文件数不能超过父进程的阈值限制,但是 Supervisor 中 minfds 指令缺省设置的允许打开的最大文件数过小,进而导致 ElasticSearch 进程出现故障。
此故障原因本来非常简单,但我却陷入了经验主义的固定思维,值得反思。
补充一个永久性解决方法:
1. 修改 /etc/security/limits.conf
增加如下
Shell
$user hard nofile 131072
$user是用来启动WLS的用户。2048是建议的数值,若遇到同样问题可能需要再次增加。
*表示所有用户:
Shell
* soft nofile 131072
* hard nofile 131072
参考 Oracle Enterprise Linux 的推荐设置:
Shell
oracle hard nofile 131072
oracle soft nofile 131072
oracle hard nproc 131072
oracle soft nproc 131072
oracle soft core unlimited
oracle hard core unlimited
oracle soft memlock 3500000
oracle hard memlock 3500000
# Recommended stack hard limit 32MB for oracle installations
# oracle hard stack 32768
2. 其他来自 Debian GNU/Linux 官方文档和 Oracle Technology Network 的解决方法,直接修改内核参数,无须重启系统。
Shell
sysctl -w fs.file-max 65536
# apply on-the-fly to proc
echo "65536" > /proc/sys/fs/file-max
# OR
echo 65536 | sudo tee /proc/sys/fs/file-max
两者作用是相同的,前者改内核参数,后者直接作用于内核参数在虚拟文件系统(procfs, psuedo file system)上对应的文件而已。
可以用下面的命令查看新的限制
Shell
sysctl -a | grep fs.file-max
# or use proc
cat /proc/sys/fs/file-max
修改内核参数
/etc/sysctl.conf Shell
echo "fs.file-max=65536" >> /etc/sysctl.conf
sysctl -p
查看当前file handles使用情况:
Shell
sysctl -a | grep fs.file-nr
# OR
cat /proc/sys/fs/file-nr
825 0 65536
输出格式: The number of allocated file handles, the number of free file handles, and the maximum number of file handles.
另外一个命令:
Shell
lsof | wc -l
有点让我困惑的是,以上两个命令获得的结果总是不相同的;-( 原因如下:
简单来说 file-nr 给出的是 File Descriptors (文件描述符,数据结构,程序用来打开文件所需要的 handle),而 lsof 列出的是 Open Files (文件),包括不是用文件描述符的。例如:当前目录,映射到内存中的 library 文件和可执行的文本文件(脚本?)。通常 lsof 输出要比 file-nr 大。
举个简单的例子:当前系统中Firefox打开的文件数:
Shell
lsof -p pid | wc -l
# or
lsof | grep pid | wc -l
再看一下这个进程 PID 所占用的文件描述符数
Shell
ls /proc/pid/fd | wc -l
对比一下就明白了,注:载入到内存的 library 文件详情可以看 /proc/pid/maps
此外,用 sysctl 来修改内核参数 fs.file-max 和用 ulimit 的区别,花了不少时间研究,讨教了 Linux/FreeBSD/Solaris/OpenSolaris 老鸟Jockey同学,得到点拨之后终于基本弄清楚其概念和区别了。
优先级(Open File Descriptors):
soft limit < hard limit < kernel (NR_OPEN => /proc/sys/fs/nr_open) < 实现最大file descriptor数采用的数据结构所导致的限制
The Linux kernel provides the getrlimit and setrlimit system calls to get and set resource limits per process. Each resource has an associated soft and hard limit. The soft limit is the value that the kernel enforces for the corresponding resource. The hard limit acts as a ceiling for the soft limit: an unprivileged process may only set its soft limit to a value in the range from 0 up to the hard limit, and (irreversibly) lower its hard limit. A privileged process (one with the CAP_SYS_RESOURCE capability) may make arbitrary changes to either limit value.
作为测试环境,尤其是用 VMWare guest OS 的形式,安装 OpenSSH Server, webmin, phpsysinfo 等工具可以提高效率。
针对 Oracle Enterprise Linux 和 Red Hat Enterprise Linux 的快捷解决方法:
另外 OEL 5 和 RHEL 5 可以直接安装 oracle-validated 包来解决安装 Oracle 数据库和中间件所需要的包依赖和系统配置问题,推荐!
yum install oracle-validated
或者下载后手动安装