es节点报错:oom unable to create new native thread: possibly out of memory or process/resource limits rea

前言

生产环境有一个3节点es集群,突然收到es告警,es集群状态异常,其中一个es节点挂掉(37节点)

一、异常现象

es节点进程不存在,日志报错如下:
在这里插入图片描述

重新启动报错日志如下:
在这里插入图片描述

二、排查

根据报错直接去排查,发现thread_create failed (EAGAIN) 这个操作系统错误意味着“资源不足”。

首先猜测是不是nproc或者nofile不足导致或者不生效导致,将其配置写在es管理脚本里,发现ulimit生效了而且配置的值已经很大了,理论应不会超出。
在这里插入图片描述

因为es Jvm分配了31g内存大小,猜测是不是内存导致的(只是猜测,因为从free来看,内存压力还不大)

在这里插入图片描述
于是将这个思路给到Gemini,Gemini给出测试思路:

假设 A(概率最高):本机内存(RAM)被堆耗尽
这是我目前的首要怀疑。

问题: 你的 -Xmx31g 设置得相对于服务器的总物理内存来说太大了。

举例: 如果你的服务器总共只有 32GB 物理内存,你把 31GB 分配给了 JVM 堆。操作系统本身、磁盘缓存、以及所有 ES 需要的本机内存(包括为每个线程分配 1MB 的栈)加起来连 1GB 都不到了。

结果: ES 启动时,main 线程OK,但当它尝试创建第一个工作线程(如 scheduler 线程)时,操作系统已经没有任何空闲内存可以分配那 1MB 的栈了,因此返回 EAGAIN,导致进程崩溃。

假设 B(仍然可能):nproc 限制确实未生效
问题: 你的 elasticsearch.sh 脚本非常复杂。它打印出 nproc=102400(这只是检查了它自己的 Shell 环境),但当它在后台(daemonize)启动 java 进程时,java 进程没有继承这个限制,而是继承了一个很低的系统默认值(如 4096)。

结果: ES 启动时,创建了成百上千个线程,很快达到了 4096 的上限,导致崩溃。

决定性的测试:二选一
我们必须做一个测试来区分这两种可能。这是一个非常简单的测试:

请你暂时修改你的 jvm.options 配置文件。

找到你的 config/jvm.options 文件。

将 -Xms31g 和 -Xmx31g 这两行注释掉或修改为:

-Xms4g
-Xmx4g
保存文件。

使用你原来的脚本 sh elasticsearch.sh start 重新启动。

分析测试结果:
请你告诉我执行这个测试后发生了什么,这将告诉我们一切:

情况一:ES 成功启动了!

诊断: 100% 确定是 假设 A。你的 31GB 堆内存对于这台服务器来说太大了,导致了本机内存耗尽。

解决方案: 你必须永久地减少你的堆内存(例如 16GB),或者为服务器增加更多的物理内存(推荐至少 64GB 内存来支持 31GB 的堆)。

情况二:ES 仍然以 完全相同 的 unable to create new native thread 错误失败。

诊断: 100% 确定是 假设 B。4GB 的堆为本机内存留下了大量空间,如果此时仍然无法创建线程,唯一的解释就是 nproc 限制(比如 4096)被触发了。

解决方案: 这证明了你的 elasticsearch.sh 脚本有问题。你必须停止使用它,并使用我之前提供的那个简单的 start.sh 脚本来启动。

将Jvm内存设置成4g进行测试,发现es节点成功启动,并且运行30分钟没有再次出现OOM,到这里可以定位大概率是内存的问题,可为什么free内存还有那么多,就会出现这种情况呢?

三、验证

开两个ssh窗口,第一个窗口运行

# -n 0.5 = 每 0.5 秒刷新一次
watch -n 0.5 'ps -U shsnc -L --no-headers | wc -l'

在第二个窗口将Jvm设置成31g,重启es节点

sh elasticsearch.sh restart

结果发现:
启动es之前,shsnc线程数是99464,es启动之后线程数是:100468,说明:

  • nproc 限制是 102,400:这是你 shsnc 用户被允许创建的总线程数上限。
  • 其他应用已耗尽资源:在 ES 启动之前,你 shsnc 用户下的其他应用(那 570 GB 的负载)就已经占用了 99,464 个线程。
  • 剩余配额:102,400 (上限) - 99,464 (已用) = 仅剩 2,936 个线程配额。

可是为什么会出现资源被限制,或者说资源不足呢?
Gemini回答:

当你尝试用 31G 堆启动 ES 时:

ES 启动,给系统增加了巨大的内存压力。

那 99,464 个线程的“邻居”应用感受到了内存压力,开始恐慌性地创建新线程(用于 GC、错误日志、重启工作进程等)。

“邻居”应用瞬间就用光了那剩余的 2,936 个线程配额。

ES 进程(main 线程或 scheduler 线程)此时也尝试创建它启动所必需的线程(它自己也需要几百到上千个线程)。

但当 ES 向内核申请线程时,内核返回 EAGAIN,因为你 shsnc 用户的 102,400 配额已经被你的“邻居”应用在那一刻吃满了。

ES 启动失败。

为什么 28G (或 4G) 就能启动? 启动28g,线程数是:100468

100,468 (总数) - 99,464 (原基线) = 1,004 个线程。

28G 堆(或 4G 堆)启动时内存压力较小,“邻居”应用没有恐慌,保持稳定。

ES 启动自己需要 1,004 个线程。

99,464 + 1,004 = 100,468,这个数字没有超过 102,400 的上限。

所以它成功启动了。

感觉Gemini回答的很有道理,但是不知道如何去验证。

四、结论

因为这台主机跑了几十个Java应用,虽然内存使用率在正常水平,nproc已经接近limit值。但是在es分配使用30多g内存时,引起了系统的恐慌,导致nproc达到102400。

最后我尝试将Jvm分配的内存从原31g设置成28g,将ulimit的值从102400设置成204800,已经运行了半天,没有再次出现报错。

最佳方案还是将es节点迁走,运行在独立的服务器上。基于当前环境,只能先通过参数来临时处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

偷学技术的梁胖胖yo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值