Kubernetes Pod异常重启排查与OOMKilled诊断流程

Pod异常状态识别与分类

Kubernetes环境中Pod异常是最常见的运维问题。通过kubectl get pods查看状态列,不同状态对应不同排查方向。CrashLoopBackOff表示容器启动后立即崩溃并反复重启;OOMKilled是内存不足被内核杀死;ImagePullBackOff是镜像拉取失败;Pending是调度失败或资源不足。

快速获取异常Pod的详细事件:

kubectl describe pod <pod-name> -n <namespace>

# 关注Events部分
# Events:
#   Type     Reason     Age                From               Message
#   ----     ------     ----               ----               -------
#   Normal   Scheduled  2m                 default-scheduler  Successfully assigned default/web-xxx to node-3
#   Warning  Failed     1m (x3 over 2m)    kubelet            Failed to pull image "registry.example.com/app:v2.1": rpc error
#   Warning  BackOff    30s (x5 over 2m)   kubelet            Back-off pulling image "registry.example.com/app:v2.1"

Events中按时间倒序排列,最早的异常通常是根因。

CrashLoopBackOff排查流程

容器启动后立即退出导致CrashLoopBackOff,排查顺序:先看容器日志,再看退出码,最后看探针配置。

查看容器日志:

# 当前容器日志
kubectl logs <pod-name> -n <namespace>

# 上一次崩溃的容器日志(关键)
kubectl logs <pod-name> -n <namespace> --previous

# 多容器Pod指定容器
kubectl logs <pod-name> -c <container-name> -n <namespace> --previous

容器退出码的含义:

| 退出码 | 含义 | 常见原因 |
|——–|——|———-|
| 0 | 正常退出 | 主进程执行完毕 |
| 1 | 应用错误 | 代码异常、配置错误 |
| 125 | Docker错误 | 容器启动失败 |
| 126 | 权限不足 | 可执行文件无执行权限 |
| 137 | OOMKilled | 内存超限被kill |
| 139 | Segfault | 程序段错误 |
| 143 | SIGTERM | 收到终止信号 |

退出码137是最常见的情况,确认方法:

kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.status.containerStatuses[0].lastState}'
# {"terminated":{"exitCode":137,"reason":"OOMKilled","message":"OOMKilled","startedAt":"...","finishedAt":"..."}}

OOMKilled内存问题诊断

确认OOMKilled后,需要区分是容器内存限制过低还是应用内存泄漏。

查看Pod资源限制和实际使用:

kubectl top pod <pod-name> -n <namespace>
# NAME         CPU(cores)   MEMORY(bytes)
# web-app-xxx  120m         487Mi

kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.containers[0].resources}'
# {"limits":{"memory":"512Mi"},"requests":{"memory":"256Mi"}}

实际使用487Mi接近限制512Mi,说明内存限制偏低。但如果持续增长不回落,应用侧可能存在内存泄漏。

Java应用OOM排查需要额外关注JVM堆内存配置。容器化Java应用必须使用容器感知参数,否则JVM会按宿主机内存计算堆大小:

# JDK 8u191+ 或 JDK 10+ 自动容器感知
java -XX:+UseContainerSupport \
     -XX:MaxRAMPercentage=75.0 \
     -XX:InitialRAMPercentage=50.0 \
     -jar app.jar

# 关键:MaxRAMPercentage不要设100%
# 容器限制512Mi,JVM堆设75%约384Mi
# 剩余128Mi给元空间、线程栈、GC等非堆内存

调整内存限制后重新部署:

kubectl set resources deployment/web-app -n <namespace> \
    --limits=memory=1Gi \
    --requests=memory=512Mi

Liveness/Readiness探针配置不当

探针配置错误导致Pod频繁重启。典型场景:Liveness探针超时时间太短,应用启动慢被误杀。

查看探针配置:

kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.containers[0].livenessProbe}'
# {"httpGet":{"path":"/health","port":8080},"initialDelaySeconds":10,
#  "periodSeconds":5,"timeoutSeconds":1,"failureThreshold":3}

这个配置问题明显:initialDelaySeconds只有10秒,如果应用启动需要30秒,Liveness探针在第10秒开始检查,3次失败(15秒)后Pod被重启,应用根本来不及启动。

合理的探针配置策略:

# Deployment探针配置
livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 60       # 给应用足够启动时间
  periodSeconds: 10             # 每10秒检查一次
  timeoutSeconds: 5             # 单次检查超时5秒
  failureThreshold: 3           # 连续3次失败才重启

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 10       # Readiness可以更早开始
  periodSeconds: 5
  timeoutSeconds: 3
  failureThreshold: 3

# 关键区别:
# Liveness失败 → Pod重启
# Readiness失败 → Pod从Service Endpoints移除,不重启

Liveness和Readiness应分开设计。Readiness端点检查应用是否准备好接收流量,Liveness端点检查应用是否存活。二者共用同一个端点会导致启动慢的应用被反复重启。

ImagePullBackOff镜像拉取失败

镜像拉取失败的原因分几类:镜像名称错误、仓库认证失败、网络不通。

诊断步骤:

# 查看详细错误
kubectl describe pod <pod-name> -n <namespace> | grep -A5 "Failed"

# 常见错误:
# 1. 镜像不存在
#   Failed to pull image "nginx:1.25.0": manifest not found
#   → 检查tag拼写

# 2. 认证失败
#   Failed to pull image: 401 Unauthorized
#   → 创建imagePullSecret

# 3. 网络超时
#   Failed to pull image: dial tcp: i/o timeout
#   → 检查DNS解析和防火墙

私有仓库认证:

# 创建拉取凭证
kubectl create secret docker-registry regcred \
    --docker-server=registry.example.com \
    --docker-username=admin \
    --docker-password=Harbor123 \
    --docker-email=ops@example.com \
    -n <namespace>

# 在Deployment中引用
spec:
  imagePullSecrets:
  - name: regcred
  containers:
  - image: registry.example.com/app:v2.1

Pod处于Pending状态

Pending状态表示Pod未被调度到任何节点。用describe查看调度失败原因:

kubectl describe pod <pod-name> -n <namespace>

# Events:
#   Warning  FailedScheduling  3m  default-scheduler  
#   0/6 nodes are available: 3 Insufficient memory, 2 node(s) had untolerated taint, 1 Insufficient cpu

这条信息直接说明了问题:3个节点内存不足,2个节点有taint未容忍,1个节点CPU不足。

资源不足时的处理方案:

# 查看节点资源使用
kubectl describe nodes | grep -A5 "Allocated resources"
# Allocated resources:
#   (Total limits may be over 100 percent, i.e., overcommitted.)
#   Resource          Requests     Limits
#   --------          --------     ------
#   cpu               7500m (94%)  10000m (125%)
#   memory            24Gi (80%)   32Gi (106%)

# 降低Pod资源请求
kubectl patch deployment web-app -n <namespace> --patch '{
  "spec": {
    "template": {
      "spec": {
        "containers": [{
          "name": "app",
          "resources": {
            "requests": {"cpu": "200m", "memory": "256Mi"}
          }
        }]
      }
    }
  }
}'

节点taint导致无法调度时,检查是否有对应的toleration:

# 查看节点taint
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints

# 查看Pod的toleration
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.tolerations}'

节点级故障导致Pod驱逐

节点NotReady或资源压力过大会触发kubelet驱逐Pod。查看驱逐事件:

kubectl get events -n <namespace> --field-selector reason=Evicted

# 驱逐原因常见:
# - 节点内存压力(memory.available < evictionHard阈值)
# - 节点磁盘压力(nodefs.available < 阈值)
# - 节点镜像垃圾回收压力(imagefs.available < 阈值)

# 查看节点状态
kubectl describe node <node-name> | grep -A10 "Conditions"
# Conditions:
#   Type             Status  LastHeartbeatTime  Reason
#   MemoryPressure   True    2m               kubelet has insufficient memory
#   DiskPressure     False   2m
#   PIDPressure      False   2m
#   Ready            False   2m               Kubelet stopped posting node status

节点NotReady后,默认300秒Pod才开始被驱逐到其他节点。可以通过修改kubelet配置调整这个时间:

# /var/lib/kubelet/config.yaml
evictionSoft:
  memory.available: "500Mi"
  nodefs.available: "10%"
evictionSoftGracePeriod:
  memory.available: "1m30s"
  nodefs.available: "2m"
evictionHard:
  memory.available: "200Mi"
  nodefs.available: "5%"
  imagefs.available: "5%"
evictionMaxPodGracePeriod: 30

软驱逐给应用30秒优雅退出时间,硬驱逐立即终止。关键服务配合PodDisruptionBudget确保最小副本数:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: web-app

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/kubernetespod-yi-chang-chong-qi-pai-cha-yu-oomkilled-zhen/

(0)
小编小编
上一篇 20小时前
下一篇 20小时前

相关推荐