Kubernetes 把"部署"变得简单,却把"排障"变得复杂——一个 Pod 起不来,可能的原因横跨镜像、资源、调度、网络、存储、RBAC 半个体系。生产环境的故障排查不能靠"凭感觉试",必须有一套从现象到根因的系统方法。本文整理 mrgr.cn 运维组在真实生产中高频遇到的四类故障,给出标准排查路径与命令。
一、排查的总思路:分层下钻
K8s 故障排查遵循"由外向内、由上到下"的分层下钻思路:先看 Pod 状态与事件,再看节点与调度,再看网络与存储,最后看应用本身日志。核心是先定位"故障停在哪个阶段",再深入该阶段。切忌一上来就钻进应用日志——很多"应用报错"其实是调度或网络层的问题。
排障第一原则:先看kubectl describe pod的 Events 段和kubectl get pod -o wide的状态列。80% 的"起不来"问题,这两个命令就能定位方向。
- Pod 层:状态(Pending/Waiting/CrashLoopBackOff)、Events、容器日志。
- 节点层:节点状态、资源使用、kubelet 状态。
- 调度层:资源请求、节点亲和、污点容忍。
- 网络层:Service/Endpoints、CNI、DNS、网络策略。
二、CrashLoopBackOff:容器反复崩溃重启
CrashLoopBackOff 是最常见的故障之一,表现为容器启动后很快退出,Kubelet 反复重启并指数退避。根因几乎都是"容器内进程退出",但退出的原因千差万别。
2.1 标准排查路径
# 1. 看 Pod 状态与最近事件
kubectl describe pod <pod> -n <ns>
# 2. 看当前容器日志(容器还在时)
kubectl logs <pod> -n <ns> --tail=200
# 3. 看上一次崩溃的日志(容器已重启时关键)
kubectl logs <pod> -n <ns> --previous --tail=200
# 4. 看容器退出码
kubectl get pod <pod> -n <ns> -o jsonpath='{.status.containerStatuses[0].lastState}'
--previous 是排查 CrashLoopBackOff 的杀手锏——它输出的是上一次崩溃前的日志,往往直接包含报错堆栈。退出码也有强指示:0 是正常退出(多半是进程没前台常驻),127 是命令不存在,137 是 OOM 被杀,143 是被 SIGTERM。
2.2 高频根因
- 进程未前台常驻:容器主进程后台运行后立即退出,K8s 认为容器结束。需确保主进程前台运行(如
tail -f守护或直接 exec 主进程)。 - 配置/依赖缺失:启动时找不到配置文件、连不上数据库。看
--previous日志即可定位。 - OOM 被杀:退出码 137,
describe里会有 OOMKilled。需调大 resources.limits.memory 或排查内存泄漏。 - 健康检查失败:liveness probe 太激进,容器还没起来就被判定不健康重启。调大 initialDelaySeconds。
三、Pending 与调度失败:Pod 迟迟不调度
Pod 一直处于 Pending,说明它没被调度到任何节点。根因要么是"没有节点能满足条件",要么是"调度器没资源"。describe pod 的 Events 段会直接告诉你原因,但要读懂调度失败的提示语。
# 看 Pending 原因
kubectl describe pod <pod> -n <ns> | grep -A20 Events
# 常见提示:
# FailedScheduling: 0/5 nodes are available: 5 Insufficient cpu.
# FailedScheduling: 0/5 nodes are available: 3 node(s) had taints, 2 Insufficient memory.
3.1 调度失败的常见原因
- 资源不足:Pod 请求的 CPU/内存超过所有节点可用量。检查
resources.requests是否合理,节点是否已满。 - 节点污点未容忍:节点有 taint(如专用节点、未就绪节点),Pod 没有 toleration。看
kubectl get nodes -o custom-columns=...。 - 节点选择器/亲和性不匹配:nodeSelector 或 nodeAffinity 把 Pod 限制到了不存在的节点标签上。
- PVC 未就绪:Pod 依赖的 PVC 一直 Pending(StorageClass 问题),导致无法调度。
排查调度问题,先kubectl get nodes -o wide看节点是否都 Ready,再看kubectl describe node的 Allocatable 与已分配资源。很多"调度失败"其实是某几个节点 NotReady 被排除了。
四、节点 NotReady:节点失联
节点状态变 NotReady,意味着 Kubelet 停止向控制面上报状态。通常是节点本身出了问题:资源耗尽、kubelet 崩溃、容器运行时卡死、网络分区。describe node 的 Conditions 段会显示具体条件。
# 看节点条件
kubectl describe node <node> | grep -A10 Conditions
# 关键条件:
# Ready False KubeletNotReady ...
# MemoryPressure True ...
# DiskPressure True ...
# PIDPressure True ...
4.1 NotReady 的处理
- MemoryPressure/DiskPressure:节点内存或磁盘打满。登入节点清理(清日志、清无用镜像
crictl rmi --prune),或扩容。 - kubelet 停摆:
systemctl status kubelet看是否运行,journalctl -u kubelet看日志,常见是内存不足被 OOM 或证书过期。 - 容器运行时卡死:
crictl ps不响应说明 containerd/docker 卡死,需重启运行时(注意会重启节点上所有容器)。 - 长期 NotReady:设
pod-eviction-timeout让控制器自动驱逐 Pod,避免长期占用。
五、网络不通:Service 与 DNS 排查
网络问题是 K8s 排查里最隐蔽的一类。典型现象:Pod 之间访问超时、Service 访问不通、域名解析失败。排查要先确认"哪一段不通",再逐段定位。
5.1 Service 不通排查
# 1. Service 是否有 Endpoints(关键!)
kubectl get endpoints <svc> -n <ns>
# 若 Endpoints 为空 → 标签选择器没匹配到 Pod,或 Pod 不 Ready
# 2. Pod 内访问 Service
kubectl exec <pod> -- curl -v http://<svc>:<port>
# 3. 直接访问 Pod IP,绕过 Service
kubectl exec <pod> -- curl -v http://<pod-ip>:<port>
# Pod IP 通但 Service 不通 → kube-proxy/iptables 问题
# Pod IP 也不通 → CNI 或网络策略问题
Endpoints 为空是 Service 不通最高频的原因:标签选择器写错、Pod 没 Ready、Pod 端口与 Service targetPort 不一致都会导致。先确认 Endpoints 有值,再深入查网络。
5.2 DNS 解析失败
- 检查 CoreDNS 是否正常运行:
kubectl get pod -n kube-system -l k8s-app=kube-dns。 - Pod 内
nslookup <svc>与nslookup <svc>.<ns>.svc.cluster.local对比,确认是否短名解析问题。 - 检查 Pod 的
/etc/resolv.conf是否指向 CoreDNS Service IP。 - CoreDNS 配置 ConfigMap 是否被改坏,日志是否有
STSEReady错误。
六、排障工具箱与预防
- 必备命令:describe、logs(--previous)、get endpoints、exec、top、cordon/drain。
- 监控告警:Pod 重启率、节点 NotReady、CPU/内存水位、CrashLoopBackOff 必须有告警。
- 资源规范:所有 Pod 必须设 requests/limits,避免资源抢占导致节点压力。
- 演练:定期做节点故障、网络分区演练,验证自愈与告警是否生效。
K8s 排障能力的本质是"会读 Events"。Events 是 K8s 控制平面给你写的故障日志,几乎所有调度、启动、拉取镜像的问题都会在 Events 里留痕。养成先看 Events 的习惯,排障效率会提升一个量级。
结语
K8s 故障排查没有捷径,但有路径。从 Pod 状态、Events、日志到节点、调度、网络,按层下钻比盲目试错高效得多。mrgr.cn 运维组把本文的排查路径固化成了一份 runbook,每次故障按 runbook 走,平均定位时间缩短了 60%。欢迎在问答社区分享你遇到的奇葩故障与排查过程。