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/