首页 > 解决方案 > 当 K8s 健康检查重新启动 pod 时创建 JVM heapdump - 没有发生 OOM

问题描述

我有一种情况,突然发生很长的 GC 暂停,我需要找出突然分配内存的原因。长时间的 GC 暂停(大约 30 秒)导致 pod 连续多次失败 K8s 健康检查,并且 pod 重新启动,实际上没有发生 OOM。我想在 K8s 实际重新启动 pod 之前创建一个堆转储。我意识到应该对一些外部持久安装进行转储。

我对如何导致堆转储发生的唯一想法是使用 preStop 挂钩。问题是,当 Pod 因健康检查失败而重新启动时,是否会触发 preStop 钩子?

也许有一个更优雅的解决方案?

标签: javakubernetesjvmheap-dumpkubernetes-health-check

解决方案


问题是,当 Pod 因健康检查失败而重新启动时,是否会触发 preStop 钩子?

是的。根据定义perStop钩子在容器因 API 请求或管理事件(例如活动探测失败、抢占、资源争用等)而终止之前立即运行。


我应该在 pod 终止之前使用 preStop 挂钩来捕获 Java 堆转储吗?

是的。但是你需要小心,如果容器已经处于终止或完成状态,对 preStop 钩子的调用会失败。当Pod 终止时,它会等待默认的 30 秒宽限期(如果 PerStop 挂钩未完成,则额外等待 2 秒),然后再发送 KILL 信号。如果 preStop 挂钩需要比默认宽限期允许的时间更长的时间来完成,您必须进行修改terminationGracePeriodSeconds以适应这种情况。


对此有更优雅的解决方案吗?

我不知道。我想通过向 pod 添加一个空的 dir卷,并配置 JVM 以将堆转储到该目录command: ["java", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/dumps/oom.bin", "-jar", "yourapp.jar"]应该可以工作。

为什么上述解决方案会起作用?

当 kubernetes 因为没有响应健康检查而杀死你的容器时,kubernetes 只会重新启动容器,但不会重新调度 pod,因此不会将其移动到另一个节点。因此,在将 pod 移动到另一个节点之前,不会删除空的 dir 卷。因此,当容器重新启动时,新容器将挂载相同的空目录,该目录将包含上次运行的堆转储。因此,您可以kubectl cp在活动结束后随时查看这些文件。复制堆转储文件可能还有其他挑战,但它们是可以解决的。检查以获取更多信息。


推荐阅读