诊断由于 ulimit 设置 core file 为 unlimited 而引起的应用事故
一般情况下, 我们会设置 core file size 的 limit 为0, 通常出于性能(产生 core dump 消耗 CPU), 磁盘空间(core 文件通常特别大), 敏感数据( core dump 包含应用进程运行时很多数据)的原因, 把 core file size 设置为0, 也就是不允许产生 core dump. 有时候为了诊断某些特定的问题, 专门打开这个设置. 对于 Java 应用程序, 如果不涉及 native 代码, 通常 heap dump 就足够了, 所以不需要产生 core dump.
某天, 生产环境的某个应用突然都不响应了, 应用进程好好的, 就是不响应. 初步查看, 发现磁盘用光了. 这很奇怪, 因为这些无状态 app, 通常情况下一旦运行正常, 只产生少量的日志文件到磁盘, 并且是 rotate 的, 所以只会占用少量空间.
首先 df -h 查看某个挂载的盘确实满了, 一路 du -sh 下去, 发现原来有几个 core dump 文件在 tomcat 的目录下, 每个文件大概27G 多的样子, 所以占满磁盘很轻松的事情.
那么, 为什么会产生这么多 core dump 文件呢, 查看日志系统, 发现有很多 OutOfMemoryError, 根据里面的信息, 我们可以看到, 这个 Java 进程尝试启动新的线程(Linux 系统上是有 LWP 实现线程)时候, 不能创建. 错误如下:
rootcause=OutOfMemoryError: unable to create new native thread
所以, 可以推断: 不能创建新线程导致JVM OutOfMemoryError, 然后系统做 Core dump, 然后磁盘占满, 然后 game over.
可是为什么之前从来没遇到过呢? 原来这个应用最近使用了最细的系统 image, 最新的 image 里面设置的 core file size 为 unlimited. 如下:
user1@myhost:~# ulimit -a
**core file size (blocks, -c) unlimited**
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
正常情况下, 我们因为最开始提到的原因, 会设置这个 core file size 为0.
另外 为什么无法创建线程呢? 这是另外一个单独的问题 -> docker --pids-limit 参数的问题.