通过宿主机查看K8S或者是容器内的Java程序的简单方法

发布时间 2023-12-10 17:22:37作者: 济南小老虎

通过宿主机查看K8S或者是容器内的Java程序的简单方法


背景

最近一个项目的环境出现了 cannot create native process 的错误提示
出现这个错误提示时, docker exec 或者是 kube exec 进入容器/POD内部后,无法使用jstack等的命令. 

然后想简单查看一下问题原因都无从下手. 
这次报错的提示信息其实比较奇怪, 也是提示了OOM
但是看 K8S的监控 内存才 12G (POD限制内存27G) CPU都没有用到 1个核心(POD限制 6个CPU)

看到这些数据时感觉并没有出现堆区内存泄漏导致的OOM, 怀疑是线程池资源未回收导致的线程池数量到达上限.
不过一直是没有任何证据, 所以想想办法从外部进行一下信息的简要查看. 

第一部分现成信息查看

容器是通过cgroup和namespace实现了资源限制和资源隔离. 
虽然Java是跑在K8S管理下面的POD里面的, 但是实际上依旧是Linux系统里面的一个 process
所以理论上可以通过 host 查看 pod内的contianer的部分信息. 

所以可以连上host K8S运行此应用的worker节点. 
这里吐槽一下 美帝的政治正确下, master 节点都改名 control-plane 了. 不适应. 

ps -ef |grep java 可以看到一个进程id

最简单的方法可以使用
top -Hp $pid -bn 1 > docker_jvm.info
就可以查看进程信息. 

第二部分尝试进行dump的获取

先安装 gdb
yum install gdb -y 
然后可以使用 gcore 命令

然后使用 gcore $pid 的方式形成 dump 文件
但是注意可能会失败
注意在执行gcore 之前需要先执行一下
ulimit -c unlimited
设置 core 无限制, 不然会报错:
warning: Memory read failed for corefile section, 1048576 bytes at 0x7fb380c18000.
具体的执行命令为:
ulimit -c unlimited
time gcore 3019830 -o core 

注意这个命令非常耗时, 根据jvm占用内存大小不同而不同. 
配置好的机器速度会快一些.

注意 可能会有一些报错:
warning: Target and debugger are in different PID namespaces; thread lists and other data are likely unreliable.  Connect to gdbserver inside the container.
warning: Expected absolute pathname for libpthread in the inferior, but got target:/lib/x86_64-linux-gnu/libpthread.so.0.

容器内和host内的进程进行 core 获取是不太一样的. 

Core转换为dump文件

之前之前总结过, 这里直接拿来.

需要注意, 如果是 jstack的话 需要安装如下的包才可以, 单独安装 java 是没有的

yum install java-1.8.0-openjdk-devel-* 

jstack `which java` core.3019830
可以进行简单的查看

time jmap -dump:format=b,file=heap.hprof `which java` core.3019830
可以使用这个命令从 core文件中获取一下对应的jvm dump 文件.

获取之后就可以使用 mat jprofiler 工具进行分析了.

之前总结的地址:
https://mp.weixin.qq.com/s?__biz=MzU2MTA2MzQyNw==&mid=2247485503&idx=1&sn=5e8454f65a2e0c8dcb336b5ff295476b&chksm=fc7f3b53cb08b245bb02348687d6f7b400861f7b5409a340e103f452b37dea4f8c67e85c9434&token=1159504462&lang=zh_CN#rd

注意事项

除非是jdk的版本完全一样, 不然是无法解析的, 会报错:
sun.jvm.hotspot.debugger.DebuggerException: get_thread_regs failed for a lwp

这是可以使用 docker 内部的 jdk进行相关的处理
方式为:
docker run -it -v /root:/root jdk:$tag

然后在内部执行就可以了

备注

可能依旧会失败. 并且转换的速度非常缓慢

[root@k8sipv6 ~]# docker exec -it tender_curran bash    
root@ef38f1ee8e4f:~# ps -ef |grep jmap
root          33       1 99 17:07 pts/0    00:11:32 jmap -dump:format=b,file=heap.hprof /opt/java/openjdk/bin/java core.3019830
root          63      54  0 17:18 pts/1    00:00:00 grep jmap

可以看到会一直在进行转换, 速度其实挺慢的.