返回首页

9.2 常见 Pod 异常排查

Pod 异常是 Kubernetes 故障排查里最常见的一类问题。表面上看,现象可能只是 OOMKilledCrashLoopBackOffPendingImagePullBackOff 或探针失败;但真正的排查重点,是先识别问题发生在 调度前启动中运行期,还是 依赖检查阶段。Kubernetes v1.32 中,Pod 生命周期仍然围绕 PendingRunningSucceededFailed 等阶段展开,因此排障时先判断 Pod 停在生命周期的哪一段,会极大提升定位效率::cite[373]。

9.2.1 OOMKilled 的典型原因与处理方式

OOMKilled 往往意味着容器进程因为内存不足被系统杀掉。它不是“应用自己退出”,而是“被动终止”。排查这种问题时,重点不是只看最后一条日志,而是要回答:到底是应用内存持续上涨,还是 limit 设得过小,或节点本身已经资源紧张?

常见触发原因

原因 典型现象 排查重点
内存 limit 过小 启动后不久即退出 容器峰值内存与 limit 是否匹配
应用内存泄漏 运行一段时间后反复退出 内存曲线是否持续上升
突发流量或大对象处理 高峰期 OOM 请求大小、缓存、并发是否异常
JVM / Go / Python 参数不合理 实际可用内存被高估 堆参数、GC 参数、worker 数量
节点整体内存吃紧 同节点多个 Pod 受影响 节点内存压力与驱逐信息

一组标准排查命令

kubectl get pod payment-6c9d8f7b7d-abcde -n prod
kubectl describe pod payment-6c9d8f7b7d-abcde -n prod
kubectl logs payment-6c9d8f7b7d-abcde -n prod --previous
kubectl top pod payment-6c9d8f7b7d-abcde -n prod
kubectl top node

重点观察:

  1. describe pod 中容器上一次退出原因是否为 OOMKilled
  2. logs --previous 是否能看到分配内存、加载大文件、缓存暴涨等线索
  3. top pod 是否已经逼近 limit
  4. 同节点其它 Pod 是否也出现异常

典型处理思路

情况 1:limit 明显偏小

例如应用启动就要加载模型、字典或大配置,但 limit 只给了 256Mi,这时最直接的处理就是先把 limit 调高,再观察峰值。

resources:
  requests:
    cpu: "200m"
    memory: "512Mi"
  limits:
    cpu: "1"
    memory: "1Gi"

情况 2:应用确实有泄漏或缓存不受控

这种情况下,只加内存通常只能“延后爆炸”,不能真正解决问题。应同时做:

  • 打开应用侧内存指标
  • 识别大对象分配路径
  • 限制缓存上限
  • 分页读取大结果集
  • 调整 worker / 并发数

情况 3:节点内存整体吃紧

如果同节点多个 Pod 都在抖动,就不要只盯单 Pod,而要切到节点排查:

kubectl get pod -A -o wide | grep worker-01
kubectl describe node worker-01

OOMKilled 的实战判断口诀

  • 启动即炸:先怀疑 limit 太小或初始化加载过重
  • 跑久才炸:先怀疑泄漏、缓存失控、慢性堆积
  • 高峰期炸:先怀疑流量、并发、请求模型变化
  • 同节点一起炸:先怀疑节点资源压力

9.2.2 CrashLoopBackOff 的定位思路

CrashLoopBackOff 可以简单理解为:容器在不断启动、退出、再启动,Kubernetes 为了避免无休止重试,会逐步拉长重启退避时间。排查运行中反复崩溃的 Pod 时,应从容器退出原因、上一次日志、启动命令与探针结果共同分析,而不是只看当前状态::cite[458]。

典型原因分类

类别 示例
进程自身退出 启动参数错误、配置缺失、依赖不可达
启动命令有误 command / args 写错
探针失败导致重启 liveness probe 配置过严
权限或挂载异常 进程启动时无法读取文件或绑定端口
OOM 日志不一定明显,但退出原因为 OOMKilled

推荐排查顺序

kubectl get pod web-abcde -n prod
kubectl describe pod web-abcde -n prod
kubectl logs web-abcde -n prod --previous
kubectl logs web-abcde -n prod -c app

重点关注下面这些位置:

  1. Container State / Last State:确认是 ErrorOOMKilled 还是被 probe 拉起重启
  2. Events:看是否有 Back-off restarting failed container
  3. 上一轮日志:看进程退出前做了什么
  4. 配置与环境变量:确认 Secret、ConfigMap、命令行参数是否正确

一个常见例子:配置缺失导致启动即退

kubectl describe pod api-7b9f6f8ddf-xyz12 -n prod
kubectl logs api-7b9f6f8ddf-xyz12 -n prod --previous
kubectl exec -it api-7b9f6f8ddf-xyz12 -n prod -- env

如果日志里出现:

FATAL: missing DATABASE_URL

那根因可能并不在容器本身,而是:

  • Secret 没挂进去
  • 环境变量键名写错
  • Pod 使用了错误的 ServiceAccount,导致无法读取依赖资源

CrashLoopBackOff 的关键思维

不要把 CrashLoopBackOff 当作根因,它只是“重启中的一种状态描述”。真正的根因,往往藏在:

  • 退出码
  • 上一轮日志
  • 探针事件
  • 启动参数
  • 依赖项可用性

9.2.3 Pending 状态的常见触发因素

Pending 表示 Pod 已经被 API Server 接收,但还没有进入稳定运行阶段。官方文档指出,Pod 在这个阶段可能仍在等待调度、等待镜像拉取,或等待某些前置条件完成::cite[373]。

最常见的 5 类原因

原因 典型事件 说明
节点资源不足 FailedScheduling CPU、内存、GPU 不够
污点 / 容忍不匹配 untolerated taint Pod 不允许落到目标节点
亲和性 / 反亲和冲突 0/3 nodes are available 规则太严格导致无节点可选
PVC 未绑定 pod has unbound immediate PersistentVolumeClaims 存储前置条件未满足
镜像拉取异常 ErrImagePull / ImagePullBackOff 有些场景仍表现为 Pending 阶段

排查命令

kubectl get pod job-abcde -n prod
kubectl describe pod job-abcde -n prod
kubectl get nodes
kubectl get pvc -n prod

describe pod 的 Events 区域

很多 Pending 问题,答案其实已经在 Events 里:

  • 0/5 nodes are available: 3 Insufficient memory, 2 node(s) had untolerated taint
  • persistentvolumeclaim "data-pvc" not found
  • Failed to pull image

一个典型例子:调度条件太苛刻

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: node-role.kubernetes.io/high-mem
          operator: In
          values:
          - "true"

如果集群里根本没有满足条件的节点,那么 Pod 就会一直 Pending。解决方法通常是二选一:

  • 放宽调度约束
  • 补充符合条件的节点

9.2.4 镜像拉取失败、探针失败与权限异常排查

这一节把最常见但容易混淆的 3 类异常放在一起讲,因为它们都经常发生在“Pod 看起来没起来”的阶段。

一、镜像拉取失败

镜像问题最常见的现象是:

  • ErrImagePull
  • ImagePullBackOff
  • Failed to pull image

排查命令:

kubectl describe pod web-abcde -n prod
kubectl get secret -n prod
kubectl get sa default -n prod -o yaml

重点检查:

  1. 镜像名、tag 是否拼错
  2. 私有仓库凭据 imagePullSecrets 是否存在
  3. Pod 使用的 ServiceAccount 是否带上正确的拉镜像凭据
  4. 节点到镜像仓库的网络是否可达

二、探针失败

Kubernetes v1.32 的探针机制仍分为 liveness、readiness、startup 三类:

  • liveness probe 失败,容器会被重启
  • readiness probe 失败,Pod 会从 Service 后端摘除
  • startup probe 适合启动慢的应用,用来保护启动阶段,避免 liveness 过早误杀::cite[251]

排查命令:

kubectl describe pod payment-abcde -n prod
kubectl logs payment-abcde -n prod --previous
kubectl exec -it payment-abcde -n prod -- wget -S -O- http://127.0.0.1:8080/healthz

一个常见误区是:应用启动很慢,但配置了非常激进的 liveness probe,于是容器还没真正准备好,就被 kubelet 判死并重启。此时应优先考虑:

  • 增大 initialDelaySeconds
  • 调整 timeoutSeconds
  • 增大 failureThreshold
  • 对慢启动应用使用 startupProbe

探针文档还特别提醒:TCP 探针连接是由节点上的 kubelet 发起,而不是在 Pod 内发起,因此 host 参数不能简单依赖 Pod 内的 Service 名称解析::cite[251]。

三、权限异常

权限问题通常比想象中更“隐蔽”。Pod 看起来像是启动失败,但真正原因可能是:

  • 没权限读取挂载目录
  • 非 root 进程无法写入某个路径
  • ServiceAccount 权限不足,访问 API 被拒绝
  • 容器安全上下文过严,导致启动命令无法执行

推荐排查动作:

kubectl describe pod task-abcde -n prod
kubectl exec -it task-abcde -n prod -- id
kubectl exec -it task-abcde -n prod -- ls -l /data
kubectl auth can-i get secrets --as=system:serviceaccount:prod:app-sa -n prod

如果容器根本起不来,就从 describe 里的事件和安全上下文字段反推;如果能进入容器,就直接检查用户、目录权限和挂载点。

一套 Pod 异常排查顺序

无论你面对的是 OOM、CrashLoop、Pending 还是探针失败,都可以先走下面这条路径:

kubectl get pod <pod> -n <ns>
kubectl describe pod <pod> -n <ns>
kubectl logs <pod> -n <ns> --previous
kubectl get events -n <ns> --sort-by='.metadata.creationTimestamp'

如果还不够,再补充:

kubectl exec -it <pod> -n <ns> -- /bin/sh
kubectl debug <pod> -n <ns> -it --image=busybox:1.36 --target=<container>

其中,ephemeral container / kubectl debug 特别适合以下场景:

  • 容器镜像里没有调试工具
  • 主容器已经频繁崩溃
  • 需要临时抓包、做 DNS 或端口探测::cite[458]

本章小结

Pod 异常排查的关键,不在于记住多少状态名,而在于你能否迅速判断问题卡在:

  • 调度前:Pending、PVC、资源、污点、亲和性
  • 启动中:镜像拉取、配置缺失、权限错误
  • 运行期:OOMKilled、CrashLoopBackOff、探针失败
  • 依赖侧:数据库、配置中心、DNS、Service 不可达

掌握这种分层思路后,Pod 问题通常都能在较短时间内定位到具体环节。


📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

2.2 Deployment 与 ReplicaSet:无状态应用管理

下一篇

9.3 网络故障排查