ulimit 未正确设置是很多线上故障的根源:
Too many open files
;java.lang.OutOfMemoryError: unable to create new native thread
;
对于生产环境来说, ulimit 的调参优化至关重要;
本文详细介绍并梳理一下与 ulimit 相关的林林总总;
ulimit 是 linux 对于每个通过 PAM 登录的用户 ( 每个进程 ) 的资源最大使用限制的设置;
注意, 这里仅仅对通过 PAM 登陆的用户起作用, 而对于那些随系统启动而启动的 daemon service, ulimit 是不会去限制其资源使用的;
在 /etc/security/limits.conf
文件中的第一段注释如下:
This file sets the resource limits for the users logged in via PAM.
It does not affect resource limits of the system services.
关于 linux PAM 相关的内容, 可以前往另外一篇文章: pam 认证与配置;
ulimit 基本信息
1 | # 查看所有 ulimit 设置 |
ulimit 需要优化的场景及待优化参数
linux 默认的 ulimit 限制, 是出于安全考虑, 设置的有些保守; 实际的生产环境下, 往往需要对其作出适当的调整, 方可发挥机器的最大性能;
场景1: tomcat web 容器
一台 4C4G60G 的标准虚拟主机, 其上部署了一个 tomcat 实例, 启动 catalina 进程的是 tomcat:tomcat 用户;
如果该服务是一个网络 IO 密集的应用, 需要打开的 socket file 远不止 1024, ulimit 设置的 max open files 就会限制其性能; 另外, 该主机只部署了这一个服务, tomcat 用户是唯一一个需要占用大量资源的用户, ulimit 对单个用户的限制便会造成机器资源闲置, 极低的使用率, 降低 web 服务的性能;
所以, 可以对该机器的 ulimit 作出如下调整:
1 | 1. max memory size -> unlimit |
另外, 我们还遇到一种特殊的情况, 用标准配置虚拟机跑 dubbo 的服务治理: 当时发现, 如果服务注册到 zookeeper 的数量达到一定级别, 线上就会报 java.lang.OutOfMemoryError: unable to create new native thread
的异常;
最后确定问题的原因是 ulimit -u
max user processes 的数量配置过低, 增大后解决问题:
1 | 4. max user processes -> 65535 |
具体的情况可以参见这篇文章: dubbo 服务治理系统设计;
场景2: elasticsearch data node
32C64G4T 的配置, 为确保指针压缩特性被打开, 一般我们都会控制 jvm 的最大堆内存与最小堆内存: ‘-Xmx30g -Xms30g’, 并希望能锁住所有的内存, 避免堆内存被 swap 到磁盘, 降低了搜索性能; 这种场景下我们当然不希望 ulimit 限制了 max memory size 以及 max locked memory;
所以, 可以对该机器的 ulimit 作出如下调整:
1 | 1. max locked memory -> unlimit |
对于 max locked memory, elasticsearch.yml 本身有一个配置项 bootstrap.mlockall
/bootstrap.memory_lock
= true, 其背后实现就是通过类似于 ulimit -l unlimit 的方法完成的; 只是, elasticsearch 试图自己主动改变该配置能生效的前提, 是 ulimit 配置文件里要允许其这样设置, 具体的逻辑请看本文下下节: #ulimit 的永久修改;
另外, 还有其他的一些场景, 可能需要调整其他参数以作优化, 此处不一而论;
以上是需要调整 ulimit 参数的场景举例, 下面的内容是关于如何 临时/永久 修改 ulimit 设置;
ulimit 当前 session 下的临时修改
ulimit 的临时调整, 只对当前 session 下的当前用户, 以及当前用户所起的进程生效;
其调整方法也已经在 ulimit -a
中被注明了:
1 | # max locked mem |
ulimit 的永久修改
上一节的方法, 只能在当前 session 下对当前用户作临时调整, 而 要想对 ulimit 作永久调整, 需要修改一些配置文件:
/etc/security/limits.conf
;/etc/security/limits.d 目录
;
这些文件用于持久化每个用户的资源限制设置;
其中, /etc/security/limits.conf
自不必说, 这是配置 ulimit 的主要文件:
1 | domain 限制的目标: |
以下是一个具体的例子:
1 | #<domain> <type> <item> <value> |
如上所示, 系统允许 elastic 用户的最大 memlock 为 unlimit, 如果这个值被设置为了一个比较小的值, 那么上上节 elasticsearch 试图将其改成 unlimit 便会失败;
而对于 /etc/security/limits.d
目录的作用, /etc/security/limits.conf
文件中的第二段与第三段有如下注释:
Also note that configuration files in /etc/security/limits.d directory,
which are read in alphabetical order, override the settings in this
file in case the domain is the same or more specific.
That means for example that setting a limit for wildcard domain here
can be overriden with a wildcard setting in a config file in the
subdirectory, but a user specific setting here can be overriden only
with a user specific setting in the subdirectory.
也就是说, limits.conf 配置文件, 可以在用户级别上被 limits.d 目录下的配置文件覆盖;
举一个例子, 在 redhat/centos 各发行版本中, limits.d 目录下就有一个文件 20-nproc.conf
:
1 | # Default limit for number of user's processes to prevent |
这里面对除了 root 用户之外的所有用户作了一个最大进程/线程数目的 soft 限制;
如果修改 limits.conf 文件:
1 | * hard nproc 65535 |
这时会发现, 除非自己试图 ulimit -u
修改 max processes, 否则这个值会依然被限制为 4096;
而要想将该值默认放到 65535, 就必须修改 20-nproc.conf
文件方才生效;