【Kubernetes】记录一次K8S容器内程序OOM排查过程:unable to create new native thread
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
项目背景
基于k8s的容器化kafka PaaS管理平台业务团队申请kafka通过一系列操作封装crd调用operator创建集群当然还包括其他功能、topic管理、group管理、监控告警、集群扩容、分区管理等等。
后台会对每个集群启动定时任务扫描kafka的元数据变化主要是使用zk客户端Curator。
问题初现
在集群增长到一定数量后有一天突然再访问PaaS平台就报错了报错信息竟然是OOMunable to create new native thread
原因可能是
-
线程数过多无法再申请新的线程
-
线程数超过机器每个对进程的线程数的限制
问题排查
首先查看程序占用的线程数
由于种种原因用了比较笨的方法
jstack 1 | grep 'java.lang.Thread.State' | wc -l
得到的数量是2300+而容器对每个pod的线程数限制为2000这就是问题所在
通过观察之后大部分线程都是以“Curator-”为前缀的进一步过滤
jstack 1 |grep "Curator" |wc -l
得到的数量是1800+
所以可以定位到这些线程都是和zk客户端有关系。
问题定位
首先看是哪个地方创建的线程根据一顿搜索找到了创建线程池的代码其中线程池核心数的取值是这样的
Math.max(Runtime.getRuntime().availableProcessors(), 16)
平平无奇毫无波澜但是这就是罪魁祸首
我们项目的Dockerfile如下
FROM java:8
WORKDIR /
ADD target/xadd-kafka-console.jar app.jar
RUN bash -c 'cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && touch /app.jar'
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-server","-Xms4096m","-Xmx4096m","-XX:NewSize=1500m","-XX:+UseConcMarkSweepGC","-XX:CMSInitiatingOccupancyFraction=70","-jar","/app.jar"]
没有指定具体的java版本这么写到容器内会拉取哪个版本呢
root@xadd-consumer-bbb464c4-qp7q8:/# java -version
openjdk version "1.8.0_111"
OpenJDK Runtime Environment (build 1.8.0_111-8u111-b14-2~bpo8+1-b14)
OpenJDK 64-Bit Server VM (build 25.111-b14, mixed mode)
root@xadd-consumer-bbb464c4-qp7q8:/#
root@xadd-consumer-bbb464c4-qp7q8:/#
答案是较低版本的1.8.0_111
低版本的jdk对容器环境支持并不友好使用代码Runtime.getRuntime().availableProcessors()获取到的CPU数量是宿主机的真实核数而不是pod所分配的核数我们K8S node节点的CPU核数为100左右所以导致线程池的核心线程数是100这是一个很大的坑并不容易发现
这个问题解决了还有一个问题为什么线程数会一直不断上涨呢
查看项目代码
通过查看项目代码发现每个Curator客户端都需要一个线程池作为参数也就是每次有新的集群创建都会创建一个新的线程池这个无法避免但是可以将核心线程数改小保证线程数的增长在可控范围内
问题解决
-
替换基础为openjdk:8u332-jdk
-
修改核心线程数为4CPU limit为2
一个集群会创建4个线程 以集群接入速度来看 目前是可以接受的。