一、概述
1.1 背景介绍
集群跑着跑着,Pod 挂了。Slack 告警一刷屏,脑子一片空白。打开终端敲 kubectl get pods,看到一堆 CrashLoopBackOff、ImagePullBackOff、Pending,不知道从哪下手。这是每个刚接触 Kubernetes 运维的人都会遇到的场景。
Pod 是 Kubernetes 最小的调度单元,也是出问题最多的地方。一个 Pod 异常可能是镜像拉不下来,可能是 OOMKilled,可能是探针配错了,也可能是节点磁盘满了导致 Evicted。原因千差万别,但排查思路是有章可循的。
这篇文章把 Pod 异常排查拆成一条完整的链路:从 Pod 状态出发,看 Events,查容器日志,排节点资源,查网络策略,最后到存储挂载。每一步给命令,给判断依据,给修复手段。适用于 Kubernetes 1.32 + containerd 2.0 环境。
1.2 基础背景 / 核心语义
| 术语 | 含义 | 排障关注点 |
|---|---|---|
| Pending | Pod 已被 API Server 接受,但尚未被调度到节点 | 资源不足、nodeSelector/affinity 不匹配、PVC 未绑定 |
| ContainerCreating | 容器正在创建中,通常在拉镜像或挂载卷 | 镜像拉取超时、Secret 不存在、卷挂载失败 |
| CrashLoopBackOff | 容器反复启动又退出,退避时间逐步增加 | 应用启动失败、配置错误、依赖服务不可用 |
| ImagePullBackOff | 镜像拉取失败并进入退避 | 镜像不存在、认证失败、网络不通 |
| OOMKilled | 容器因内存超限被内核杀死 | limits 设置过低、内存泄漏 |
| Evicted | Pod 被节点驱逐 | 节点磁盘压力、内存压力、PID 压力 |
| Terminating | Pod 正在终止但未完成 | finalizer 卡住、preStop 超时、进程未响应 SIGTERM |
| Init:Error | Init 容器执行失败 | Init 容器命令错误、依赖检查失败 |
1.3 适用场景
生产环境 Pod 异常告警后的快速定位
新应用部署后 Pod 起不来的排查
节点故障导致大批 Pod 漂移后的状态检查
日常巡检中发现异常 Pod 的处理
升级 Kubernetes 版本后兼容性问题排查
1.4 环境要求
| 组件 | 版本 | 说明 |
|---|---|---|
| Kubernetes | 1.32.x | 支持 Sidecar Containers、改进的 Pod Lifecycle |
| containerd | 2.0.x | 默认运行时,移除了 v1 API 支持 |
| kubectl | 1.32.x | 与集群版本匹配 |
| crictl | 1.32.x | 用于直接与 containerd 交互排障 |
| Linux Kernel | 6.12+ | 支持 cgroup v2 |
| 操作系统 | Ubuntu 24.04 / Rocky 9.5 | 推荐 LTS 版本 |
1.5 排障坐标系
整个排查路径按以下顺序推进,每一层确认无问题后再进下一层:
Pod 状态 → Events → 容器日志 → 节点资源 → 网络策略 → 存储挂载 │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ Phase 调度/拉镜 应用异常 CPU/Mem/ CNI/DNS/ PVC/CSI/ Reason /挂载事件 /崩溃原因 Disk/PID NetworkPolicy Mount
排障核心原则:先广后深。先用最快的命令拿到全局视图,再根据线索逐层下钻。
二、详细步骤
2.1 先把观测面补齐
2.1.1 获取 Pod 全局视图
# 查看所有命名空间的异常 Pod kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded | head -50 # 查看特定命名空间的 Pod 状态(含 IP、节点信息) kubectl get pods -n-o wide # 查看 Pod 的详细状态(重点看 Status、Conditions、containerStatuses) kubectl get pod -n -o yaml | grep -A 30"status:"
2.1.2 获取 Events
# 查看特定 Pod 的 Events kubectl describe pod-n | tail -30 # 按时间排序查看命名空间下所有 Events kubectl get events -n --sort-by='.lastTimestamp'| tail -30 # 查看 Warning 级别 Events kubectl get events -n --field-selectortype=Warning --sort-by='.lastTimestamp'
2.1.3 获取容器日志
# 当前容器日志 kubectl logs-n -c # 上一次崩溃的容器日志(CrashLoopBackOff 时必用) kubectl logs -n -c --previous # 最近 100 行日志 kubectl logs -n --tail=100 # Init 容器日志 kubectl logs -n -c
2.1.4 使用 kubectl debug 进入临时调试容器
Kubernetes 1.32 的 Ephemeral Containers 已经 GA,可以直接往运行中的 Pod 注入调试容器:
# 注入一个 busybox 调试容器 kubectl debug -it-n --image=busybox:1.37 --target= # 使用 nicolaka/netshoot 排查网络问题 kubectl debug -it -n --image=nicolaka/netshoot:v0.13 --target= # 在节点上启动调试 Pod(排查节点级问题) kubectl debug node/ -it --image=busybox:1.37
2.2 第一轮判断
根据 Pod 状态快速分流:
| Pod 状态 | 第一步动作 | 预期线索来源 |
|---|---|---|
| Pending | kubectl describe pod 看 Events | 调度失败原因:资源不足、亲和性不匹配、PVC 未绑定 |
| ContainerCreating | kubectl describe pod 看 Events | 镜像拉取状态、卷挂载状态、Secret 获取状态 |
| ImagePullBackOff | kubectl describe pod 看 Events | 镜像名称/Tag 是否正确、Registry 认证、网络连通性 |
| CrashLoopBackOff | kubectl logs --previous | 应用启动报错、配置缺失、端口冲突 |
| OOMKilled | kubectl describe pod 看 lastState | limits.memory 值、应用实际内存用量 |
| Evicted | kubectl describe pod 看 message | 节点压力类型(DiskPressure/MemoryPressure) |
| Terminating 卡住 | kubectl describe pod 看 finalizers | finalizer 未清除、preStop hook 超时 |
| Running 但不健康 | kubectl describe pod 看 Conditions | readinessProbe/livenessProbe 失败 |
2.2.1 Pending 状态排查
# 查看调度失败的具体原因 kubectl describe pod-n | grep -A 5"Events" # 检查集群可用资源 kubectl top nodes # 检查是否有 Taints 阻止调度 kubectl describe nodes | grep -A 3"Taints" # 检查 PVC 绑定状态 kubectl get pvc -n
2.2.2 CrashLoopBackOff 排查
# 看上一次崩溃日志 kubectl logs-n --previous # 看容器退出码 kubectl get pod -n -o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}' # 常见退出码含义: # 1 = 应用错误 # 137 = OOMKilled (128+9=SIGKILL) # 139 = Segfault (128+11=SIGSEGV) # 143 = SIGTERM (128+15)
2.3 第二轮下钻
2.3.1 节点资源排查
# 查看节点资源使用情况 kubectl top nodes # 查看节点 Conditions kubectl describe node| grep -A 10"Conditions" # 检查节点上的 kubelet 日志 journalctl -u kubelet -n 100 --no-pager # 检查 containerd 状态 systemctl status containerd crictl info
2.3.2 网络排查
# 检查 Pod 网络连通性(从调试容器内) kubectl debug -it-n --image=nicolaka/netshoot:v0.13 -- bash # 在调试容器内执行 nslookup kubernetes.default.svc.cluster.local curl -v http:// . .svc.cluster.local: /health ping # 检查 NetworkPolicy kubectl get networkpolicy -n kubectl describe networkpolicy -n # 检查 DNS 是否正常 kubectl get pods -n kube-system -l k8s-app=kube-dns kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50
2.3.3 存储排查
# 检查 PVC 状态 kubectl get pvc -nkubectl describe pvc -n # 检查 PV 状态 kubectl get pv # 检查 StorageClass kubectl get sc # 检查 CSI driver 状态 kubectl get pods -n kube-system | grep csi # 查看挂载是否成功 kubectl describe pod -n | grep -A 10"Volumes"
2.4 根因矩阵
| 现象 | 可能根因 | 关键证据 | 优先级 |
|---|---|---|---|
| Pending 超过 5 分钟 | 资源不足 | Events: Insufficient cpu/memory | P1 |
| Pending 超过 5 分钟 | PVC 未绑定 | Events: pod has unbound immediate PersistentVolumeClaims | P1 |
| ImagePullBackOff | 镜像 Tag 不存在 | Events: manifest unknown | P2 |
| ImagePullBackOff | Registry 认证失败 | Events: unauthorized | P2 |
| CrashLoopBackOff (exit 1) | 应用启动失败 | --previous 日志中的错误栈 | P1 |
| CrashLoopBackOff (exit 137) | OOMKilled | describe pod 中 reason: OOMKilled | P1 |
| Evicted | 节点磁盘压力 | message: The node was low on resource: ephemeral-storage | P1 |
| Running 但 0/1 Ready | 探针失败 | Events: Readiness probe failed | P2 |
| Terminating 卡住 | Finalizer 未清除 | metadata.finalizers 不为空 | P3 |
| ContainerCreating 卡住 | Secret 不存在 | Events: secret "xxx" not found | P2 |
2.5 处理与验证
2.5.1 常见修复操作
# 强制删除 Terminating 卡住的 Pod kubectl delete pod-n --grace-period=0 --force # 手动清除 Finalizer(谨慎操作) kubectl patch pod -n -p'{"metadata":{"finalizers":null}}' # 扩容节点资源后重新触发调度 kubectl delete pod -n # 修复 ImagePullSecret kubectl create secret docker-registry regcred --docker-server= --docker-username= --docker-password= -n
2.5.2 修复后验证
# 确认 Pod 进入 Running 且 Ready kubectl get pod-n -w # 确认 Events 无 Warning kubectl get events -n --field-selectortype=Warning --sort-by='.lastTimestamp'| tail -10 # 确认应用健康 kubectlexec -n -- wget -qO- http://localhost: /health
三、示例代码和配置
3.1 配置样例
一个典型的 Pod 配置,包含资源限制、探针、优雅终止等排障相关字段:
# 文件:deployment-with-probes.yaml
# 说明:包含完整探针和资源限制的 Deployment 配置
apiVersion:apps/v1
kind:Deployment
metadata:
name:web-app
namespace:production
spec:
replicas:3
selector:
matchLabels:
app:web-app
template:
metadata:
labels:
app:web-app
spec:
terminationGracePeriodSeconds:30
containers:
-name:web
image:registry.internal/web-app:v2.1.0
ports:
-containerPort:8080
resources:
requests:
cpu:100m
memory:128Mi
limits:
cpu:500m
memory:512Mi
startupProbe:
httpGet:
path:/health/startup
port:8080
initialDelaySeconds:5
periodSeconds:5
failureThreshold:30
readinessProbe:
httpGet:
path:/health/ready
port:8080
periodSeconds:10
failureThreshold:3
livenessProbe:
httpGet:
path:/health/live
port:8080
periodSeconds:15
failureThreshold:3
lifecycle:
preStop:
exec:
command:["/bin/sh","-c","sleep 5"]
volumeMounts:
-name:config
mountPath:/etc/app/config.yaml
subPath:config.yaml
-name:data
mountPath:/data
volumes:
-name:config
configMap:
name:web-app-config
-name:data
persistentVolumeClaim:
claimName:web-app-data
3.2 脚本一:快速采集脚本
#!/bin/bash # 文件名:pod-diag-collect.sh # 作用:一键采集异常 Pod 的诊断信息,输出到指定目录 # 适用场景:Pod 异常时快速收集现场证据,避免信息丢失 # 使用方式:bash pod-diag-collect.sh[output-dir] # 输入参数: # $1 - Pod 名称(必填) # $2 - Namespace(必填) # $3 - 输出目录(可选,默认 /tmp/pod-diag) # 输出结果:在输出目录下生成包含 describe、logs、events 等信息的文件 # 风险提示:仅读取操作,不会修改任何资源;日志文件可能较大 POD_NAME="${1:?用法: $0 [output-dir]}" NAMESPACE="${2:?缺少 namespace 参数}" OUTPUT_DIR="${3:-/tmp/pod-diag}" TIMESTAMP=$(date +%Y%m%d_%H%M%S) DIAG_DIR="${OUTPUT_DIR}/${POD_NAME}_${TIMESTAMP}" mkdir -p"${DIAG_DIR}" echo"[INFO] 采集 Pod 描述信息..." kubectl describe pod"${POD_NAME}"-n"${NAMESPACE}">"${DIAG_DIR}/describe.txt"2>&1 echo"[INFO] 采集当前容器日志..." forCONTAINERin$(kubectl get pod"${POD_NAME}"-n"${NAMESPACE}"-o jsonpath='{.spec.containers[*].name}');do kubectl logs"${POD_NAME}"-n"${NAMESPACE}"-c"${CONTAINER}"--tail=500 >"${DIAG_DIR}/logs_${CONTAINER}.txt"2>&1 kubectl logs"${POD_NAME}"-n"${NAMESPACE}"-c"${CONTAINER}"--previous --tail=500 >"${DIAG_DIR}/logs_${CONTAINER}_previous.txt"2>&1 done echo"[INFO] 采集 Init 容器日志..." forCONTAINERin$(kubectl get pod"${POD_NAME}"-n"${NAMESPACE}"-o jsonpath='{.spec.initContainers[*].name}');do kubectl logs"${POD_NAME}"-n"${NAMESPACE}"-c"${CONTAINER}"--tail=500 >"${DIAG_DIR}/logs_init_${CONTAINER}.txt"2>&1 done echo"[INFO] 采集 Events..." kubectl get events -n"${NAMESPACE}"--field-selector"involvedObject.name=${POD_NAME}" --sort-by='.lastTimestamp'>"${DIAG_DIR}/events.txt"2>&1 echo"[INFO] 采集 Pod YAML..." kubectl get pod"${POD_NAME}"-n"${NAMESPACE}"-o yaml >"${DIAG_DIR}/pod.yaml"2>&1 echo"[INFO] 采集节点信息..." NODE=$(kubectl get pod"${POD_NAME}"-n"${NAMESPACE}"-o jsonpath='{.spec.nodeName}') if[ -n"${NODE}"];then kubectl describe node"${NODE}">"${DIAG_DIR}/node_describe.txt"2>&1 kubectl top node"${NODE}">"${DIAG_DIR}/node_top.txt"2>&1 fi echo"[DONE] 诊断数据已保存到:${DIAG_DIR}" ls -la"${DIAG_DIR}"
3.3 脚本二:日志归桶/诊断脚本
#!/bin/bash # 文件名:pod-log-classifier.sh # 作用:扫描指定命名空间下所有异常 Pod,按状态分类输出诊断建议 # 适用场景:集群升级后批量检查、日常巡检、大面积故障初筛 # 使用方式:bash pod-log-classifier.sh# 输入参数:$1 - Namespace(必填,使用 --all 扫描所有命名空间) # 输出结果:按异常类型分组输出 Pod 列表及初步诊断建议 # 风险提示:仅读取操作;大集群下可能执行时间较长 NAMESPACE="${1:?用法: $0 }" if["${NAMESPACE}"="--all"];then NS_FLAG="-A" else NS_FLAG="-n${NAMESPACE}" fi echo"==========================================" echo" Pod 异常分类诊断报告" echo" 时间:$(date '+%Y-%m-%d %H:%M:%S')" echo"==========================================" echo"" echo"--- CrashLoopBackOff ---" kubectl get pods${NS_FLAG}--no-headers 2>/dev/null | grep"CrashLoopBackOff"|whilereadline;do POD=$(echo"$line"| awk'{print $2}') NS_ACTUAL=$(echo"$line"| awk'{print $1}') EXIT_CODE=$(kubectl get pod"${POD}"-n"${NS_ACTUAL}"-o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}'2>/dev/null) RESTART_COUNT=$(echo"$line"| awk'{print $5}') echo" Pod:${NS_ACTUAL}/${POD}| 退出码:${EXIT_CODE}| 重启次数:${RESTART_COUNT}" case"${EXIT_CODE}"in 1) echo" -> 应用错误,检查 --previous 日志";; 137)echo" -> OOMKilled,检查 memory limits";; 139)echo" -> 段错误,检查应用二进制兼容性";; 143)echo" -> SIGTERM,可能是探针超时或优雅终止问题";; *) echo" -> 退出码${EXIT_CODE},检查应用日志";; esac done echo"" echo"--- ImagePullBackOff ---" kubectl get pods${NS_FLAG}--no-headers 2>/dev/null | grep"ImagePullBackOff|ErrImagePull"|whilereadline;do POD=$(echo"$line"| awk'{print $2}') NS_ACTUAL=$(echo"$line"| awk'{print $1}') IMAGE=$(kubectl get pod"${POD}"-n"${NS_ACTUAL}"-o jsonpath='{.spec.containers[0].image}'2>/dev/null) echo" Pod:${NS_ACTUAL}/${POD}| 镜像:${IMAGE}" echo" -> 检查镜像是否存在、Tag 是否正确、ImagePullSecret 是否配置" done echo"" echo"--- Pending ---" kubectl get pods${NS_FLAG}--no-headers 2>/dev/null | grep"Pending"|whilereadline;do POD=$(echo"$line"| awk'{print $2}') NS_ACTUAL=$(echo"$line"| awk'{print $1}') echo" Pod:${NS_ACTUAL}/${POD}" echo" -> 检查节点资源、PVC 绑定状态、nodeSelector/affinity 配置" done echo"" echo"--- Evicted ---" kubectl get pods${NS_FLAG}--no-headers 2>/dev/null | grep"Evicted"|whilereadline;do POD=$(echo"$line"| awk'{print $2}') NS_ACTUAL=$(echo"$line"| awk'{print $1}') echo" Pod:${NS_ACTUAL}/${POD}" echo" -> 节点资源压力导致驱逐,检查节点 Conditions" done echo"" echo"==========================================" echo" 扫描完成" echo"=========================================="
3.4 脚本三:验证脚本
#!/bin/bash # 文件名:pod-health-verify.sh # 作用:修复操作后,批量验证指定命名空间下 Pod 健康状态 # 适用场景:故障修复后的批量确认、部署后的健康检查 # 使用方式:bash pod-health-verify.sh[timeout-seconds] # 输入参数: # $1 - Namespace(必填) # $2 - 超时时间,单位秒(可选,默认 120) # 输出结果:输出各 Pod 的健康状态,最终给出通过/失败统计 # 风险提示:仅读取操作;对 Pod 执行 health 检查可能产生少量请求 NAMESPACE="${1:?用法: $0 [timeout-seconds]}" TIMEOUT="${2:-120}" PASS=0 FAIL=0 WARN=0 echo"[INFO] 开始验证${NAMESPACE}命名空间 Pod 健康状态 (超时:${TIMEOUT}s)" echo"---" kubectl get pods -n"${NAMESPACE}"--no-headers 2>/dev/null |whilereadline;do POD_NAME=$(echo"$line"| awk'{print $1}') STATUS=$(echo"$line"| awk'{print $3}') READY=$(echo"$line"| awk'{print $2}') READY_CURRENT=$(echo"${READY}"| cut -d'/'-f1) READY_TOTAL=$(echo"${READY}"| cut -d'/'-f2) if["${STATUS}"="Running"] && ["${READY_CURRENT}"="${READY_TOTAL}"];then echo"[PASS]${POD_NAME}-${STATUS}(${READY})" elif["${STATUS}"="Completed"] || ["${STATUS}"="Succeeded"];then echo"[PASS]${POD_NAME}-${STATUS}(Job 已完成)" elif["${STATUS}"="Running"] && ["${READY_CURRENT}"!="${READY_TOTAL}"];then echo"[WARN]${POD_NAME}-${STATUS}(${READY}) - 部分容器未就绪" else echo"[FAIL]${POD_NAME}-${STATUS}(${READY})" fi done echo"---" echo"[INFO] 检查 Warning Events..." WARNING_COUNT=$(kubectl get events -n"${NAMESPACE}"--field-selectortype=Warning --sort-by='.lastTimestamp'2>/dev/null | tail -5 | wc -l) if["${WARNING_COUNT}"-gt 1 ];then echo"[WARN] 存在${WARNING_COUNT}条 Warning Events,建议人工复核" kubectl get events -n"${NAMESPACE}"--field-selectortype=Warning --sort-by='.lastTimestamp'| tail -5 else echo"[PASS] 无 Warning Events" fi echo"---" echo"[INFO] 验证完成"
3.5 脚本四:回滚/批量探测脚本
#!/bin/bash # 文件名:deployment-rollback-check.sh # 作用:检查 Deployment 滚动更新状态,发现异常时提示回滚命令 # 适用场景:上线后观察滚动更新是否成功、批量检查多个 Deployment 状态 # 使用方式:bash deployment-rollback-check.sh# 输入参数:$1 - Namespace(必填) # 输出结果:输出每个 Deployment 的更新状态,异常时输出回滚命令 # 风险提示:仅读取和分析操作,不会自动执行回滚;回滚命令需人工确认后执行 NAMESPACE="${1:?用法: $0 }" echo"==========================================" echo" Deployment 滚动更新状态检查" echo" 命名空间:${NAMESPACE}" echo" 时间:$(date '+%Y-%m-%d %H:%M:%S')" echo"==========================================" kubectl get deployments -n"${NAMESPACE}"--no-headers 2>/dev/null |whilereadline;do DEPLOY=$(echo"$line"| awk'{print $1}') READY=$(echo"$line"| awk'{print $2}') UPTODATE=$(echo"$line"| awk'{print $3}') AVAILABLE=$(echo"$line"| awk'{print $4}') DESIRED=$(echo"${READY}"| cut -d'/'-f2) CURRENT_READY=$(echo"${READY}"| cut -d'/'-f1) # 检查是否有异常 Pod ABNORMAL=$(kubectl get pods -n"${NAMESPACE}"-l app="${DEPLOY}"--no-headers 2>/dev/null | grep -cE"CrashLoopBackOff|ImagePullBackOff|Error|OOMKilled") if["${CURRENT_READY}"="${DESIRED}"] && ["${ABNORMAL}"-eq 0 ];then echo"[OK]${DEPLOY}:${READY}ready,${UPTODATE}up-to-date" else echo"[ALERT]${DEPLOY}:${READY}ready, 异常 Pod 数量:${ABNORMAL}" echo" 最近更新历史:" kubectl rollouthistorydeployment/"${DEPLOY}"-n"${NAMESPACE}"| tail -5 echo"" echo" 建议回滚命令(需人工确认后执行):" echo" kubectl rollout undo deployment/${DEPLOY}-n${NAMESPACE}" echo" kubectl rollout status deployment/${DEPLOY}-n${NAMESPACE}--timeout=120s" echo"" fi done echo"==========================================" echo" 检查完成" echo"=========================================="
四、实际应用案例
案例一:镜像 Tag 不存在导致 ImagePullBackOff
现场现象:研发提交了新版本上线,Deployment 更新后新 Pod 一直停在 ImagePullBackOff,旧 Pod 正常运行。
第一轮判断:
kubectl get pods -n production | grep web-api # web-api-7d8f9c6b4-x2k9m 0/1 ImagePullBackOff 0 3m
状态明确是 ImagePullBackOff,直接看 Events。
第二轮下钻:
kubectl describe pod web-api-7d8f9c6b4-x2k9m -n production | tail -15
Events 输出:
Warning Failed 2m kubelet Failed to pull image"registry.internal/web-api:v3.2.1":
rpc error: code = NotFound desc = failed to pull and unpack image: manifest unknown
Warning Failed 2m kubelet Error: ErrImagePull
关键证据:manifest unknown说明镜像 Tag v3.2.1 在 Registry 中不存在。
根因:研发推送镜像时 CI 流水线失败,镜像没有 push 成功,但 Deployment YAML 已经更新了 Tag。
修复动作:
# 确认 Registry 中实际存在的 Tag skopeo list-tags docker://registry.internal/web-api | grep v3.2 # 实际最新 Tag 是 v3.2.0,先回滚到上一版本 kubectl rollout undo deployment/web-api -n production
修复后验证:
kubectl rollout status deployment/web-api -n production --timeout=60s kubectl get pods -n production | grep web-api # 所有 Pod 回到 Running 状态
防再发建议:CI 流水线增加镜像推送成功的校验步骤,推送失败时阻断后续 Deployment YAML 更新。
案例二:OOMKilled 引发 CrashLoopBackOff
现场现象:Java 微服务上线后频繁重启,Pod 状态反复在 Running 和 CrashLoopBackOff 之间切换,重启次数持续增长。
第一轮判断:
kubectl get pod order-svc-5f4d8c7a9-abc12 -n production -o jsonpath='{.status.containerStatuses[0].lastState.terminated}'
输出:
{"exitCode":137,"reason":"OOMKilled","startedAt":"2026-03-13T0200Z","finishedAt":"2026-03-13T0232Z"}
退出码 137,reason 明确是 OOMKilled。
第二轮下钻:
# 查看资源限制
kubectl get pod order-svc-5f4d8c7a9-abc12 -n production -o jsonpath='{.spec.containers[0].resources}'
输出:{"limits":{"cpu":"500m","memory":"256Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}
Java 应用 JVM 默认堆内存会根据容器 limits 计算,256Mi 对于该服务远远不够。
关键证据:应用日志(--previous)中可以看到 GC 频繁,最终被 OOM killer 杀掉。
根因:资源 limits.memory 设置过低,没有根据 Java 应用实际内存需求调整。
修复动作:
# 修改 Deployment 中 resources 配置 resources: requests: cpu:200m memory:512Mi limits: cpu:"1" memory:1Gi
同时在 Deployment 中通过环境变量显式设置 JVM 参数:
env: -name:JAVA_OPTS value:"-Xms384m -Xmx768m -XX:MaxMetaspaceSize=128m"
修复后验证:
kubectl top pod -n production | grep order-svc # 确认内存使用在 limits 范围内,无 OOMKilled 事件 kubectl get events -n production --field-selectortype=Warning | grep -i oom # 无输出,确认无 OOM 事件
防再发建议:为所有 Java 服务建立资源基线,通过 VPA Recommender 获取建议值;CI 模板中强制要求 JVM 参数显式配置。
案例三:节点磁盘满导致 Pod Evicted
现场现象:凌晨 3 点告警,某节点上 30+ Pod 被驱逐,状态全部变为 Evicted。
第一轮判断:
kubectl get pods -A --field-selector=status.phase=Failed | grep Evicted | head -10
大量 Pod 状态为 Evicted,集中在同一个节点 node-worker-03。
第二轮下钻:
kubectl describe node node-worker-03 | grep -A 5"Conditions"
输出:
Conditions: Type Status Reason ---- ------ ------ MemoryPressure False KubeletHasSufficientMemory DiskPressure True KubeletHasDiskPressure PIDPressure False KubeletHasSufficientPID Ready True KubeletReady
DiskPressure 为 True,确认是磁盘压力。
# SSH 到节点检查磁盘 df -h / # Filesystem Size Used Avail Use% Mounted on # /dev/sda1 100G 97G 3G 97% /
进一步排查磁盘占用:
du -sh /var/lib/containerd/* | sort -rh | head -5 du -sh /var/log/* | sort -rh | head -5
关键证据:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs 目录占用 60G,大量未清理的容器镜像层。
根因:镜像清理策略未配置,containerd GC 没有及时回收无用镜像层。
修复动作:
# 清理无用镜像 crictl rmi --prune # 清理已完成/失败的容器 crictl rm $(crictl ps -a --state exited -q) # 清理旧日志 journalctl --vacuum-size=500M
修复后验证:
df -h / # Use% 降至 55% kubectl describe node node-worker-03 | grep DiskPressure # DiskPressure False KubeletHasNoDiskPressure
清理 Evicted Pod:
kubectl get pods -A --field-selector=status.phase=Failed | grep Evicted | awk'{print $2 " -n " $1}'| xargs -L1 kubectl delete pod
防再发建议:配置 kubelet 的 imageGCHighThresholdPercent(默认 85%)和 imageGCLowThresholdPercent(默认 80%),在 containerd 配置中启用定期 GC;添加节点磁盘使用率告警(阈值 80%)。
案例四:NetworkPolicy 阻断导致 Pod 探针失败
现场现象:安全团队在生产命名空间启用了默认拒绝所有入站流量的 NetworkPolicy 后,大量 Pod 的 readinessProbe 失败,Service 后端没有可用 Endpoint。
第一轮判断:
kubectl get pods -n production # 所有 Pod 都是 Running 但 Ready 列显示 0/1
Pod 在跑但没有 Ready,说明探针失败。
kubectl describe pod web-app-xxx -n production | grep -A 5"Readiness"
输出:Warning Unhealthy Readiness probe failed: Get "http://10.244.3.15:8080/health/ready": dial tcp 10.244.3.15 i/o timeout
第二轮下钻:
探针超时说明网络不通。检查 NetworkPolicy:
kubectl get networkpolicy -n production # NAME POD-SELECTOR AGE # deny-all-ingress15m
kubectl describe networkpolicy deny-all-ingress -n production
输出确认该策略拒绝了所有入站流量,包括 kubelet 发起的探针请求。
关键证据:NetworkPolicy deny-all-ingress 没有豁免 kubelet 的探针请求源 IP。
根因:安全团队应用了 deny-all 基线策略但未添加 kubelet 探针的白名单规则。
修复动作:
# 文件:allow-kubelet-probes.yaml
apiVersion:networking.k8s.io/v1
kind:NetworkPolicy
metadata:
name:allow-kubelet-probes
namespace:production
spec:
podSelector:{}
policyTypes:
-Ingress
ingress:
-from:
-ipBlock:
cidr:10.0.0.0/8
ports:
-protocol:TCP
port:8080
kubectl apply -f allow-kubelet-probes.yaml
修复后验证:
# 等待探针恢复 kubectl get pods -n production -w # 几秒后所有 Pod 变为 1/1 Ready kubectl get endpoints web-app-svc -n production # 确认 Endpoints 列表有 IP
防再发建议:NetworkPolicy 变更纳入变更管理流程,应用前在 staging 环境验证;使用 Cilium/Calico 的 Policy Audit 模式先观察再执行。
五、最佳实践和注意事项
5.1 最佳实践
资源 requests/limits 必须配置:不配 requests 会导致调度不可预测,不配 limits 会导致单个 Pod 耗尽节点资源。使用 VPA Recommender 获取基线值。
三种探针都要配:startupProbe 给慢启动应用足够的时间,livenessProbe 检测死锁,readinessProbe 控制流量接入。不要用 livenessProbe 做 readinessProbe 的事。
terminationGracePeriodSeconds 要合理:默认 30 秒,对于需要排空连接的应用可能不够。配合 preStop hook 使用,确保应用有时间处理完存量请求。
日志输出到 stdout/stderr:不要只写文件。容器崩溃后 kubectl logs --previous 只能看到标准输出的内容。日志写文件的场景要配合 sidecar 收集。
镜像 Tag 不用 latest:每次部署使用确定的版本号或 Git SHA,方便回滚和追溯。imagePullPolicy 配合 IfNotPresent 使用。
Pod Disruption Budget 必须配置:避免节点维护或集群自动缩容时所有副本同时被驱逐。
节点资源告警前置:CPU、内存、磁盘使用率超过 80% 就应该告警,不要等到 Eviction 发生。
使用 kubectl debug 替代 exec + 安装工具:Ephemeral Containers 不会污染业务镜像,调试完自动清理。
5.2 注意事项
--force --grace-period=0 删除 Pod 是最后手段:强制删除可能导致存储卷未正常 unmount,造成数据损坏。
手动清除 Finalizer 要确认安全:Finalizer 存在是有原因的(比如需要清理外部资源),盲目删除可能导致资源泄漏。
Evicted Pod 不会自动清理:需要手动或通过 CronJob 定期清理,否则 etcd 中堆积大量无用对象。
NetworkPolicy 是命名空间级别的:跨命名空间通信需要明确在双方命名空间都配置策略。
containerd 2.0 移除了 v1 API:从低版本升级时,确认所有工具链(crictl、cri-dockerd 等)兼容 v2 API。
5.3 常见错误清单
| 错误现象 | 常见原因 | 修复方法 |
|---|---|---|
| 探针成功但 Pod 被重启 | livenessProbe 超时设置过短 | 增加 timeoutSeconds 和 failureThreshold |
| Pod 调度到意外节点 | 缺少 nodeAffinity 或 tolerations | 补充调度约束配置 |
| Init 容器卡住不退出 | Init 容器逻辑有死循环 | 检查 Init 容器命令和退出条件 |
| ConfigMap 更新后 Pod 没生效 | subPath 挂载不支持自动更新 | 不用 subPath 或重启 Pod |
| Service 无 Endpoints | selector 标签不匹配 | kubectl get endpoints 确认并修正 selector |
| HPA 不生效 | metrics-server 未部署或 Pod 无 requests | 部署 metrics-server 并配置 requests |
| Pod 启动慢 | 镜像太大,拉取耗时长 | 优化镜像体积,使用多阶段构建 |
| 滚动更新卡住 | maxUnavailable=0 且新 Pod 起不来 | 检查新版 Pod 日志,必要时手动回滚 |
六、故障排查和监控
6.1 关键指标
# Pod 重启次数 kubectl get pods -n--sort-by='.status.containerStatuses[0].restartCount' # 节点资源使用率 kubectl top nodes # Pod 资源使用率 kubectl top pods -n --sort-by=memory # 集群 Events 告警数 kubectl get events -A --field-selectortype=Warning | wc -l
6.2 指标说明
| 指标 | PromQL 表达式 | 正常范围 | 告警阈值 | 说明 |
|---|---|---|---|---|
| Pod 重启率 | rate(kube_pod_container_status_restarts_total[5m]) | 0 | > 0 持续 5 分钟 | 持续重启说明有未恢复的异常 |
| Pod 非 Ready 数 | kube_pod_status_ready{condition="false"} | 0 | > 0 持续 10 分钟 | 非 Ready Pod 不接收流量 |
| 节点 CPU 使用率 | 1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) | < 70% | > 85% | 高 CPU 可能导致调度失败 |
| 节点内存使用率 | 1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes | < 75% | > 85% | 高内存触发 Eviction |
| 节点磁盘使用率 | 1 - node_filesystem_avail_bytes / node_filesystem_size_bytes | < 80% | > 85% | 高磁盘触发 Eviction |
| Pending Pod 数 | kube_pod_status_phase{phase="Pending"} | 0 | > 0 持续 5 分钟 | 持续 Pending 说明调度有问题 |
| 容器 OOM 次数 | kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} | 0 | > 0 | 需要调整内存限制 |
| PVC Pending 数 | kube_persistentvolumeclaim_status_phase{phase="Pending"} | 0 | > 0 持续 5 分钟 | 存储卷绑定异常 |
6.3 告警规则
# Prometheus 告警规则:Pod 异常告警 # 文件:pod-alerts.yaml apiVersion:monitoring.coreos.com/v1 kind:PrometheusRule metadata: name:pod-alerts namespace:monitoring spec: groups: -name:pod.rules rules: -alert:PodCrashLooping expr:rate(kube_pod_container_status_restarts_total[15m])*60*15>0 for:5m labels: severity:warning annotations: summary:"Pod{{ $labels.namespace }}/{{ $labels.pod }}持续重启" description:"15 分钟内重启次数 > 0,当前值:{{ $value }}" -alert:PodNotReady expr:kube_pod_status_ready{condition="false"}==1 for:10m labels: severity:warning annotations: summary:"Pod{{ $labels.namespace }}/{{ $labels.pod }}持续未就绪" -alert:PodOOMKilled expr:kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}==1 for:0m labels: severity:critical annotations: summary:"Pod{{ $labels.namespace }}/{{ $labels.pod }}被 OOMKilled" -alert:NodeDiskPressure expr:kube_node_status_condition{condition="DiskPressure",status="true"}==1 for:2m labels: severity:critical annotations: summary:"节点{{ $labels.node }}磁盘压力" -alert:PersistentVolumeClaimPending expr:kube_persistentvolumeclaim_status_phase{phase="Pending"}==1 for:5m labels: severity:warning annotations: summary:"PVC{{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }}持续 Pending"
6.4 修复后验证
修复完成后执行以下验证清单:
| 验证项 | 命令 | 预期结果 |
|---|---|---|
| Pod 状态 |
kubectl get pods -n |
所有 Pod 为 Running 且 Ready |
| Events 无告警 |
kubectl get events -n |
无新增 Warning |
| 节点状态 | kubectl get nodes | 所有节点 Ready |
| Endpoint 注册 |
kubectl get endpoints |
Endpoints 列表包含 Pod IP |
| 业务验证 | curl 服务健康检查接口 | HTTP 200 |
| 监控指标恢复 | Grafana Dashboard | 无持续异常指标 |
七、总结
7.1 技术要点回顾
Pod 异常排查遵循固定链路:状态 -> Events -> 日志 -> 节点 -> 网络 -> 存储,先广后深。
kubectl describe pod 是排障起点,Events 部分提供了最直接的线索。
CrashLoopBackOff 时 --previous 参数是看到上次崩溃日志的关键。
退出码有明确含义:137=OOMKilled、139=Segfault、143=SIGTERM,根据退出码快速分流。
kubectl debug 的 Ephemeral Containers 在 1.32 已经 GA,应该作为首选调试手段。
节点级问题(磁盘满、内存压力)会导致大面积 Pod Eviction,要在节点层面前置告警。
NetworkPolicy 变更是隐蔽的故障源,deny-all 策略需要配套白名单。
资源 requests/limits 和探针配置是预防 Pod 异常的基础设施,不是可选项。
7.2 进阶学习方向
Pod 调度深入:理解 kube-scheduler 的打分机制、自定义调度器、调度框架插件。掌握 PriorityClass 和抢占调度。
实践建议:阅读 kube-scheduler 源码中的 Score 插件实现
容器运行时排障:深入 containerd 2.0 架构,掌握 ctr、crictl 的高级用法,排查 runtime-level 问题。
实践建议:搭建测试环境,模拟 containerd shim 进程泄漏场景
Kubernetes API 审计与安全:通过 Audit Log 追踪谁在什么时候做了什么操作,结合 RBAC 做权限最小化。
实践建议:配置 Audit Policy,观察生产集群的 API 调用模式
混沌工程:使用 Chaos Mesh 或 Litmus 主动注入故障,验证系统韧性。
实践建议:在 staging 环境注入 Pod Kill、网络延迟、磁盘填满等故障
7.3 参考资料
Kubernetes 官方文档 - Debug Pods: https://kubernetes.io/docs/tasks/debug/debug-application/debug-pods/
Kubernetes 官方文档 - Ephemeral Containers: https://kubernetes.io/docs/concepts/workloads/pods/ephemeral-containers/
containerd 2.0 Release Notes: https://github.com/containerd/containerd/releases
kubectl debug 命令参考: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_debug/
Kubernetes Failure Stories: https://k8s.af/
附录
A. 命令速查表
# 查看异常 Pod kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded # 查看 Pod 详情 kubectl describe pod-n # 查看上次崩溃日志 kubectl logs -n --previous # 查看退出码 kubectl get pod -n -o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}' # 注入调试容器 kubectl debug -it -n --image=busybox:1.37 --target= # 节点调试 Pod kubectl debug node/ -it --image=busybox:1.37 # 查看 Warning Events kubectl get events -n --field-selectortype=Warning --sort-by='.lastTimestamp' # 强制删除 Pod kubectl delete pod -n --grace-period=0 --force # 回滚 Deployment kubectl rollout undo deployment/ -n # 查看滚动更新状态 kubectl rollout status deployment/ -n # 查看节点资源使用 kubectl top nodes # 查看 Pod 资源使用 kubectl top pods -n --sort-by=memory # 清理 Evicted Pod kubectl get pods -A | grep Evicted | awk'{print $2 " -n " $1}'| xargs -L1 kubectl delete pod # 查看 PVC 状态 kubectl get pvc -n # 查看 NetworkPolicy kubectl get networkpolicy -n
B. 配置参数详解
| 参数 | 所属资源 | 默认值 | 建议值 | 说明 |
|---|---|---|---|---|
| terminationGracePeriodSeconds | Pod | 30 | 根据应用调整 | 收到 SIGTERM 到 SIGKILL 的等待时间 |
| startupProbe.failureThreshold | Container | 3 | 根据启动时间调整 | 慢启动应用设大一些 |
| startupProbe.periodSeconds | Container | 10 | 5-10 | 启动探测间隔 |
| readinessProbe.periodSeconds | Container | 10 | 5-10 | 就绪探测间隔 |
| readinessProbe.failureThreshold | Container | 3 | 3 | 连续失败多少次标记为未就绪 |
| livenessProbe.periodSeconds | Container | 10 | 10-30 | 存活探测间隔,不宜过短 |
| livenessProbe.failureThreshold | Container | 3 | 3-5 | 连续失败多少次重启容器 |
| resources.requests.cpu | Container | 无 | 根据实际用量设置 | 影响调度决策 |
| resources.requests.memory | Container | 无 | 根据实际用量设置 | 影响调度和 OOM 评分 |
| resources.limits.memory | Container | 无 | requests 的 1.5-2 倍 | 超限触发 OOMKilled |
| imagePullPolicy | Container | IfNotPresent | IfNotPresent | latest 标签会自动变为 Always |
| restartPolicy | Pod | Always | Always(Deployment) | Job 用 Never 或 OnFailure |
C. 术语表
| 术语 | 英文 | 解释 |
|---|---|---|
| 探针 | Probe | Kubernetes 检查容器健康状态的机制,包括 startup/readiness/liveness 三种 |
| 驱逐 | Eviction | 节点资源压力过大时,kubelet 主动终止低优先级 Pod 释放资源 |
| 临时容器 | Ephemeral Container | 注入运行中 Pod 的调试容器,不影响原有容器,退出后自动清理 |
| 终结器 | Finalizer | 资源删除前需要执行的清理逻辑标记,Finalizer 未移除则资源无法删除 |
| 调度 | Scheduling | kube-scheduler 将 Pod 分配到合适节点的过程 |
| 亲和性 | Affinity | 控制 Pod 调度到特定节点或与特定 Pod 同节点/不同节点的规则 |
| 污点/容忍 | Taint/Toleration | 节点标记不接受 Pod(Taint),Pod 声明可以接受(Toleration) |
| CSI | Container Storage Interface | 容器存储接口标准,用于对接各种存储后端 |
| CNI | Container Network Interface | 容器网络接口标准,用于配置 Pod 网络 |
D. 错误关键词速查
| 关键词 | 出现位置 | 含义 | 排查方向 |
|---|---|---|---|
| manifest unknown | Events | 镜像 Tag 不存在 | 检查 Tag 名称和 Registry |
| unauthorized | Events | Registry 认证失败 | 检查 ImagePullSecret |
| Insufficient cpu | Events | 节点 CPU 不足 | 扩容节点或清理闲置 Pod |
| Insufficient memory | Events | 节点内存不足 | 扩容节点或调整 requests |
| node(s) had taint | Events | 节点 Taint 不匹配 | 添加 Toleration 或移除 Taint |
| Back-off restarting | Events | 容器反复崩溃 | 查看 --previous 日志 |
| OOMKilled | containerStatuses | 内存超限被杀 | 增加 limits.memory |
| Evicted | status.reason | 节点资源压力驱逐 | 检查节点 Conditions |
| FailedMount | Events | 卷挂载失败 | 检查 PV/PVC/Secret 状态 |
| dial tcp ... timeout | Probe Events | 网络不通 | 检查 NetworkPolicy 和 CNI |
E. 排障顺序速记
1. kubectl get pods -n→ 确认异常 Pod 和状态 2. kubectl describe pod -n → 看 Events 拿线索 3. kubectl logs --previous → CrashLoop 时看崩溃日志 4. kubectl top nodes → 确认节点资源 5. kubectl get events -n → 全局 Events 扫描 6. kubectl debug -it → 注入调试容器深入排查 7. kubectl get networkpolicy -n → 排除网络策略影响 8. kubectl get pvc -n → 排除存储问题 9. 定位根因 → 修复 → 验证 → 记录
-
集群
+关注
关注
0文章
151浏览量
17684 -
镜像
+关注
关注
0文章
181浏览量
11699 -
kubernetes
+关注
关注
0文章
273浏览量
9530
原文标题:Kubernetes 运维入门:Pod 异常到底该怎么查
文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
Docker容器和Kubernetes退出码中文指南
阿里云容器Kubernetes监控(二) - 使用Grafana展现Pod监控数据
深入研究Kubernetes调度
Kubernetes组件pod核心原理
Kubernetes Pod多网卡方案MULTUS
在Kubernetes集群发生网络异常时如何排查
Kubernetes中的Pod简易理解
Kubernetes Pod如何独立工作
Kubernetes Pod如何获取IP地址呢?
Kubernetes Pod异常问题排查实战
评论