【Kubernets实践】15-在K8s中导出Java的Heap dump信息
场景:应用基于Kubernetes部署,应用开发者受限登陆集群节点下载文件。当Java(或者其他)应用出现OOM异常需要生成heap dump二进制文件。
Kubernetes部署负载时,如果不指定POD的重启策略(RestartPolicy),则默认为Always
策略。也即意味着,如果这个容器主进程出现故障,Kubelet会自动将这个容器重启。
遗憾的是,容器故障时,由于Kubelet会很快自动重启容器,所以我们没法人工进入做java的heap dump这个动作。那是不是就没法获取到应用的heap dump信息呢?当然不是,需要注意的是,容器重启但是POD并没有重启,也就是说POD被调度的节点没有变化,也就意味着,我们可以将容器生成的文件直接存放到节点中,集群管理员可以协助去下载heap dump文件。
1. 集群管理员获取heap dump文件
按照上面的思路,对于有整个集群权限的管理员而且,可以获取到节点中的任何数据,在容器退出前导出heap dump即可:
Deployment.yaml
1 | apiVersion: extensions/v1beta1 |
这里,设置lifecycle的preStop阶段执行heap dump动作,并将其写入容器的/dumps
目录,主机上有一个目录会被kubelet自动挂载到容器的dumps
目录下面,这样的话,即使容器重启了,POD没有重启,该目录依然存在,集群管理员依然可以到主机上下载该文件。
但这显然不是一个好的运维方式,如果应用开发者能够自助获取到文件就好了。其实,应用可以启一个Sidecar容器来做采集就好了,没有权限登陆节点,那我们把应用采集到OBS对象存储里就好了呀。
2. 应用开发者获取heap dump
顺着上面的思路,可以准备如下:
- 准备一个镜像,里面包含脚本能够上传文件到OBS存储。
- 业务部署的Deployment里加上一个Sidecar,使用上面的镜像,采集应用的文件到OBS存储。
这里,我们使用的是华为云OBS存储,基于python sdk写一个s3存储客户端,编写Dockerfile制作镜像。
2.1 步骤1:制作镜像
dockerfile
1 | # 设置Base镜像为alpine镜像,或者其他镜像 |
在这个镜像中装了inotify和python,其中python用于跑OBS客户端脚本,inotify用于检测jump dump文件变化。将OBS客户端到easy_s3_2.1.py
复制到与dockerfile所在目录,build镜像:
1 | docker build -f dockerfile -t myalpine:v1.1 . |
2.2 步骤2:应用定义sidecar容器
Deployment.yaml
1 | apiVersion: extensions/v1beta1 |
在上面的这段Deployment的定义中,比管理员获取heap dump文件 方案多了一个sidecar容器,其他内容完全一致,如下:
1 | - name: sidecar-container |
其中inotifywait
这个sidecar容器中的应用,主要是检测/dumps
目录,检测内容包括read / path / action 和file
, 检测方法是close_write
,即当dumps/
目录下的文件关闭了不再写入,就将文件通过s3客户端上传到sc桶s3-hc-dgg.xxx.xxx.com
中的/lmbucket
目录下并命名为oom.bin
。
这样,应用开发者即可自助到s3中下载heap dump文件了。
当然,这种方法适用于下载任何自己应用生成的文件。
另外,这里使用s3_client.py的时候是将obs桶访问的AK/SK写死在程序里了,建议使用secret对象写,能在一定程度上加密。
参考:
https://danlebrero.com/2018/11/20/how-to-do-java-jvm-heapdump-in-kubernetes/
https://github.com/aws-samples/kubernetes-for-java-developers/issues/12
