K8s Pod 异常重启排查

背景

生产环境一台 MGT 服务 Pod 在无任何预警的情况下被重启, 多个接口响应十分慢。本文记录从发现到定位根因的完整排查过程,涵盖应用日志、kubectl describe、kubelet 探针日志、dmesg 节点日志和 error.log 五个维度的交叉分析。

环境信息

  • Pod : ***-dr-6dbcd6dfcc-9fg92
  • 命名空间: ``
  • 环境: dr(灾备)
  • 对比环境 : gray-***-dr(灰度环境,同期未发生重启)
  • JVM 配置 : -Xmx2G -Xms2G -XX:+UseG1GC
  • 容器内存限制: 16Gi
  • 探针配置 : HTTP GET ***/index.do, timeout=3s, interval=15s, failureThreshold=5

排查过程

第一步:应用日志分析

从应用日志发现,Pod 在 20:10:03 之后日志完全中断,直到 20:12:07 新 JVM 启动,中间有 124 秒的空白。

最后一条日志仍然是正常的业务请求:

复制代码
2026-08-20 20:10:03.749 [http-nio-8080-exec-73] INFO  
...path= ***//execute.do ...

第二步:Kubernetes 层面排查

kubectl describe pod 显示:

复制代码
Last State:     Terminated
  Reason:       Error
  Exit Code:    137
  Finished:     20:13:08

Exit Code 137 = SIGKILL,说明进程是被外部强制杀死的,不是 JVM 自己崩溃退出。

第三步:kubelet 探针日志分析

通过节点上的 kubelet 日志(journalctl -u kubelet),可以精确还原探针失败的全过程:

复制代码
2026-08-20T20:11:37.885343+08:00 kubelet prober.go:107 "Probe failed" probeType="Liveness"
  pod="/***-dr-6dbcd6dfcc-9fg92"
  output="Get "http://10.214.201.2:8080***/index.do": 
    context deadline exceeded (Client.Timeout exceeded while awaiting headers)"

2026-08-20T20:11:37.885343+08:00 kubelet prober.go:107 "Probe failed" probeType="Readiness"
  pod="/***-dr-6dbcd6dfcc-9fg92"
  output="Get "http://10.214.201.2:8080***/index.do": 
    context deadline exceeded (Client.Timeout exceeded while awaiting headers)"

2026-08-20T20:11:52.880973+08:00 kubelet prober.go:107 "Probe failed" probeType="Liveness"
  ... (同上,第2次失败)

2026-08-20T20:11:52.880973+08:00 kubelet prober.go:107 "Probe failed" probeType="Readiness"
  ... (第2次失败)

2026-08-20T20:12:07.885030+08:00 kubelet prober.go:107 "Probe failed" probeType="Liveness"
  ... (第3次失败)

2026-08-20T20:12:07.885030+08:00 kubelet prober.go:107 "Probe failed" probeType="Readiness"
  ... (第3次失败)

2026-08-20T20:12:22.881043+08:00 kubelet prober.go:107 "Probe failed" probeType="Liveness"
  ... (第4次失败)

2026-08-20T20:12:22.881043+08:00 kubelet prober.go:107 "Probe failed" probeType="Readiness"
  ... (第4次失败)

关键发现

  1. 探针错误类型一致 :每次都是 context deadline exceeded (Client.Timeout exceeded while awaiting headers),说明应用完全无法在 3 秒超时内返回 HTTP Header,处于彻底无响应状态
  2. Liveness 和 Readiness 同时失败:每次探测周期,两个探针同时失败,说明 Pod 已经整体不可用
  3. 失败间隔严格 15 秒:每次失败间隔正好 15 秒(与探针 interval 配置一致),说明 kubelet 的探测行为完全正常,问题出在应用侧
  4. 连续失败 4 次记录:日志中记录了 4 次失败(20:11:37、20:11:52、20:12:07、20:12:22),加上 failureThreshold=5,第 5 次失败后(约 20:12:37)kubelet 判定容器 unhealthy,触发重启

第四步:dmesg 节点日志分析

在宿主机上执行 dmesg 命令,查看内核环形缓冲区日志。通过分析 Pod 网络接口的创建和销毁事件,可以辅助判断 Pod 的生命周期变化:

复制代码
[Thu Aug 20 20:28:29 2026] device vvport2921 entered promiscuous mode
[Thu Aug 20 20:28:29 2026] eth0: renamed from vport2921
[Thu Aug 20 20:32:38 2026] device vvport2748 left promiscuous mode
[Thu Aug 20 20:33:29 2026] device vvport2922 entered promiscuous mode
[Thu Aug 20 20:33:30 2026] eth0: renamed from vport2922
[Thu Aug 20 20:44:13 2026] device vvport2921 left promiscuous mode
[Thu Aug 20 20:45:50 2026] device vvport2688 left promiscuous mode

dmesg 的作用 :虽然 dmesg 中并未直接显示 OOM Kill 事件,但通过 vport 网络接口的创建(entered promiscuous mode)和销毁(left promiscuous mode)事件,可以辅助判断 Pod 的启停时间点,帮助还原故障时间线。这也说明 Pod 并非被内核 OOM Killer 直接杀掉------如果是 OOM Kill,dmesg 中会有明确的 Out of memory: Kill process 记录。

第五步:HTTP线程分析

k8s探活请求没有被处理,我猜测是200个HTTP线程池被打满,但是观察prometheus监控面板,200个tomat线程并没有被打满

第六步:关键错误日志

在 error.log 中找到了决定性证据:

复制代码
2026-08-20 20:09:40.389 [http-nio-8080-exec-15] ERROR 
o.a.c.h.Http11NioProtocol.log - Failed to complete processing of a request
java.lang.OutOfMemoryError: Metaspace

根因分析

Metaspace OOM

有问题的应用代码导致不断的产生新的类,最终metaspace被消耗完

完整时间线

时间 来源 事件
20:09:40.379 app log 最后一次存活探针成功
20:09:40.389 error.log Metaspace OOM 发生
20:09:42.952 app log 就绪探针仍成功,最后一批业务请求正常处理
20:09:55 ~ 20:10:03 app log 探针全部失败,业务请求量骤降,最后一条应用日志
20:11:37 kubelet log kubelet 首次记录探针失败(prober.go:107),Liveness/Readiness 同时失败
20:11:52 kubelet log 第2次探针失败
20:12:07 kubelet log 第3次探针失败
20:12:07 app log 新 JVM 启动(旧容器仍在运行)
20:12:22 kubelet log 第4次探针失败
20:12:37 kubectl kubelet 判定存活探针失败(连续 5 次),触发重启
20:12:38 kubectl preStop hook 执行失败(stop.sh 缺失)
20:13:08 kubectl/dockerd SIGTERM 超时,发送 SIGKILL,旧容器终止
20:13:08 kubectl 新容器开始启动,服务逐步恢复

为什么 Metaspace OOM 会导致整体不可用?

Metaspace 存储的是类的元数据(类名、方法、字段等)。虽然 OOM 只发生在一个线程上,Tomcat 的 ConnectionHandler 也 catch 了这个 Error(见 AbstractProtocol.java 源码),但 Metaspace 满了是 JVM 全局状态。后续任何需要加载新类或反射的操作都会失败,导致 Tomcat 的 NIO 处理层逐步退化,最终完全无法处理任何请求。

关键点:从 SIGTERM 发出到 SIGKILL 的 30 秒内,容器无法正常退出,说明 JVM 内部已经严重僵死,连 shutdown hook 都无法正常执行。

灰度环境对比

gray-***-dr 同期未发生任何异常重启。两个环境镜像版本、配置基本一致,差异在于灰度环境流量较小,Metaspace 的增长速度更慢。这进一步印证了"长期运行 + 流量压力 → Metaspace 累积增长 → 最终溢出"的根因链条。

补充说明:Pod 并非被内核 OOM Killer 杀掉

通过 dmesg 日志可以确认,宿主机内核并未触发 OOM Killer。如果是 cgroup 内存超限导致的内核 OOM Kill,dmesg 中会有明确的 Out of memory: Kill process 记录,且 Exit Code 会是 137(SIGKILL from OOM Killer)。本次 Exit Code 137 的来源是 kubelet 因探针连续失败而发出的 SIGKILL,根因是 Metaspace OOM 导致 JVM 僵死 → 探针超时 → kubelet 杀容器

解决方案

1. 设置 Metaspace 上限(立即生效)

bash 复制代码
-XX:MaxMetaspaceSize=1024m

2. 添加 OOM 自动 dump 和快速失败参数

bash 复制代码
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=app/logs/
-XX:+ExitOnOutOfMemoryError

ExitOnOutOfMemoryError 可以让 JVM 在 OOM 时立即退出,避免进入"僵尸"状态,配合 K8s 自动重启机制可以更快恢复服务。

3. 排查类加载泄漏

长期运行后 Metaspace 持续增长,排查方向:

  • 动态代理(CGLIB、Javassist)
  • 反射调用
  • Groovy 脚本引擎
  • 频繁的 ClassLoader 创建

总结

这次排查的关键收获:

  1. ExitOnOutOfMemoryError 很重要:OOM 后让 JVM 快速失败,而非进入僵尸状态,配合 K8s 自动重启更快恢复
  2. dmesg 辅助排除:dmesg 虽然没有直接显示 OOM 事件,但通过 vport 网络接口事件帮助排除内核 OOM Killer,确认 Pod 并非被内核因内存超限而杀
  3. Exit Code 137 不等于 OOM Kill:也可能是 kubelet 因探针失败发出的 SIGKILL。需要结合 kubelet 探针日志、dmesg、error.log 和 kubectl describe 综合判断
  4. 探针失败是结果,不是原因:不要停留在"探针失败导致重启",要追问"为什么探针会失败"
相关推荐
MetaLite1 小时前
微服务认证选型-SpringSecurity-SaToken与自研方案
微服务·云原生·架构
FoldWinCard2 小时前
基于k8s高可用web集群
前端·容器·kubernetes
cui_hao_nan2 小时前
Docker学习3
docker·容器
玉&心3 小时前
在Docker中部署前端和后端的实现
运维·docker·容器
wdfk_prog3 小时前
Docker 常用语法与命令:CLI、Dockerfile 与 Compose 速查
运维·docker·容器
无敌糖果3 小时前
K8S network namespace错误分析
docker·容器·kubernetes
郝开4 小时前
Docker Compose 模块化多环境配置规范指南:示例搭建Kibana 9.4.2
docker·容器·kibana
运维老郭4 小时前
Prometheus 监控 K8s 从入门到入坑:工作原理 + 部署实操 + 避坑指南
云原生·容器·kubernetes
AAA@峥4 小时前
从零搭建 Prometheus 完整监控告警体系|Linux 部署 + node_exporter+Grafana 可视化
云原生·grafana·prometheus