IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章助你少走弯路。在本系列的第一篇文章中我们从一个docker run hello-world起步。走到今天你已经能把一个 Flask Redis 应用完整地部署到 K8s 集群上配置好 Ingress、HPA、Prometheus 监控、Loki 日志甚至用 ArgoCD 实现了 GitOps 自动部署。你的集群和应用已经具备了生产级的运行能力。但如果让我说从“能用 K8s”到“能维护 K8s”最关键的跃迁是什么我的答案是排错能力。Pod 一直 Pending、容器反复 CrashLoopBackOff、Service 访问不通、节点突然 NotReady——这些场景你迟早会遇到。在 Docker Compose 时代排错通常只需docker logs和docker inspect因为所有容器都在一台机器上。而在 K8s 中故障可能发生在 Pod、节点、网络、存储、调度等任何一个层面排错需要一套系统性的方法论而不仅仅是记住几条命令。今天这篇就是你的K8s 排错实战手册。我将覆盖 5 类最高频的故障场景给出标准的排查路径和解决思路并结合贯穿案例的 Flask Redis 应用来模拟真实排错过程。一、排错方法论建立你的排查框架面对一个故障不要盲目地试命令。先建立一套清晰的排查框架能帮你更快地定位根因。我常用的框架是四层排查法第一层看状态kubectl get看资源状态kubectl describe看 Events 事件日志。这一层能解决 60% 的问题。第二层看日志kubectl logs看容器 stdout/stderrLoki 聚合查历史日志。这一层能再解决 20%。第三层看内部kubectl exec进入容器调试curl测试网络连通性nslookup检查 DNS 解析。这一层覆盖 15% 的疑难杂症。第四层看底层kubectl get events -A看集群全局事件kubectl top看资源消耗kubectl describe node看节点状态。这一层解决最后 5% 的深层次问题。每当你面对一个故障从第一层开始逐层深入不要跳级。二、场景一Pod 一直 Pending2.1 现象kubectl get pods显示某个 Pod 的STATUS为Pending且持续几分钟以上。2.2 排查路径与解决第一步查看 Pod 的 Events让 K8s 直接告诉你原因。kubectl describe podpod-name查看输出末尾的 Events 部分这里通常有最直接的线索。原因一资源不足Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 30s default-scheduler0/1 nodes are available:1Insufficient cpu.解决方案降低 Pod 的 CPU/内存 Requests或增加节点资源。原因二PVC 无法绑定Events: Warning FailedScheduling 30s default-scheduler0/1 nodes are available: persistentvolumeclaimredis-pvcnot found.这是因为 Pod 引用了不存在的 PVC。检查 PVC 是否已创建以及名称是否一致原因三镜像拉取失败ImagePullBackOffEvents: Warning Failed 60s kubelet Failed to pull imageflask-redis-counter:99.0:rpc error: codeNotFound descfailed to pull and unpack image Warning Failed 45s kubelet Error: ErrImagePull Normal BackOff 30s kubelet Back-off pulling imageflask-redis-counter:99.0ImagePullBackOff意味着 kubelet 尝试拉取镜像但失败了。常见原因有三个镜像标签写错了比如 3.0 写成了 99.0镜像是本地构建的但没有minikube image load私有镜像仓库需要配置imagePullSecrets解决方案确认镜像标签正确如果是 Minikube 本地镜像执行minikube image load 镜像名:标签如果是私有仓库创建 Secret 并在 Deployment 中引用imagePullSecrets。三、场景二Pod 反复重启CrashLoopBackOff3.1 现象kubectl get pods显示 Pod 的RESTARTS计数不断增长STATUS为CrashLoopBackOff。NAME READY STATUS RESTARTS AGE flask-deployment-xxxxxxxxx-xxxxx0/1 CrashLoopBackOff53m3.2 排查路径与解决CrashLoopBackOff表示容器启动后立即退出kubelet 根据重启策略不断重启但因为连续失败重启间隔被指数退避10s → 20s → 40s → …最长 5 分钟。首先要做的是查看容器日志搞清楚它为什么退出。kubectl logspod-name--previous--previous参数查看上一次容器的日志因为当前容器可能还没启动就退出了。常见原因一应用启动错误以 Flask 应用为例如果app.py中有语法错误容器会启动后立即退出。日志中会直接显示 Python traceback根据报错信息修改代码即可。常见原因二探针配置过严Events: Warning Unhealthy 45s kubelet Liveness probe failed: Gethttp://10.244.1.10:5000/health:context deadline exceeded(Client.Timeout exceeded)Liveness probe 反复失败kubelet 不断重启容器。解决方案是调整探针参数——增加initialDelaySeconds、periodSeconds或增大failureThreshold。常见原因三OOMKilled内存超限kubectl describe podpod-name|grep-A5StateState: Running Started: Mon,27May202510:00:00 0000 Last State: Terminated Reason: OOMKilled Exit Code:137OOMKilled且Exit Code: 137表示容器因内存超限被杀。解决方案是增大limits.memory或排查应用是否有内存泄漏。四、场景三Service 访问不通4.1 现象在 Pod 内通过 Service 名称访问另一个服务返回Could not resolve host或Connection refused。kubectlexecdeploy/flask-deployment --curlhttp://redis-service:6379# curl: (6) Could not resolve host: redis-service4.2 排查路径与解决Service 访问问题的排查遵循一条固定的验证链DNS → Endpoints → Pod 连通性。任何一环断了整个链路就不通。第一步检查 Service 是否存在kubectl get svc redis-service第二步检查 Service 的 Endpoints 是否有值kubectl get endpoints redis-service如果ENDPOINTS列为空说明 Service 没有匹配到任何健康的 Pod。检查 Service 的selector是否与 Pod 的labels完全一致kubectl get svc redis-service-ojsonpath{.spec.selector}kubectl get pods-l上面输出的标签第三步检查 DNS 解析kubectl run-it--rmdebug--imagealpine --nslookupredis-service如果 DNS 解析失败可能是 CoreDNS Pod 异常kubectl get pods-nkube-system-lk8s-appkube-dns第四步直接 ping Pod IP如果 DNS 正常但连接失败用 Pod IP 直接测试网络层是否通kubectl get endpoints redis-service# 记录 ENDPOINTS 列中的 IPkubectl run-it--rmdebug--imagealpine --pingPod IP五、场景四节点 NotReady5.1 现象kubectl get nodes显示某个节点STATUS为NotReady。NAME STATUS ROLES AGE VERSION minikube NotReady control-plane 1d v1.31.05.2 排查路径与解决节点NotReady意味着该节点上的所有 Pod 都面临被驱逐的风险。首先查看节点详情kubectl describenodenode-name在输出末尾的Conditions部分查看具体原因。常见原因一资源压力Conditions: Type Status Reason MemoryPressure True KubeletHasInsufficientMemory DiskPressure True KubeletHasDiskPressure节点内存或磁盘不足kubelet 自动将节点标记为不健康不再接受新 Pod 调度。解决方案是清理磁盘、释放内存或增加节点。常见原因二kubelet 停止在节点上检查 kubelet 服务状态minikubesshsudosystemctl status kubelet如果 kubelet 服务停止了重启它sudosystemctl restart kubelet六、结合监控与日志快速定位在真实生产环境中单个命令的排查效率太低。你需要结合前面搭建的可观测性体系来快速定位问题。6.1 通过 Prometheus Grafana 快速发现异常在 Grafana 仪表板中你可以一眼看到整个集群的健康状态。以我们贯穿案例的 Flask 应用为例Grafana 仪表板上的关键指标包括Pod CPU Usage面板显示某个 Flask Pod 的 CPU 突然飙升至 2 核远超过设定的 500m Limits同时Pod Restarts面板显示该 Pod 的重启次数从 0 跳升至 5。Container Memory Usage面板显示 Redis Pod 内存使用持续上涨至 256Mi接近 Limits而PVC Usage面板显示 Redis 数据卷使用率已达 92%。这些指标异常会在对应的 Alertmanager 告警规则中触发通知。例如我们之前配置的PodFrequentlyRestarting规则会在 Pod 在 5 分钟内重启超过 2 次时发送告警*“Alert: PodFrequentlyRestarting, Severity: warning, Pod flask-deployment-xxxxxxxx-xxxxx has restarted 5 times in the last 5 minutes.”*6.2 通过 Loki 快速查看相关日志当监控指标显示异常后你可以在 Grafana 中无缝切换到 Loki 查询页面。在 Explore 视图中选择 Loki 数据源输入 LogQL 查询{appflask-counter}|ERRORLoki 返回的日志会显示具体的错误堆栈。如果错误集中在某个特定 Pod 实例可以进一步缩小查询范围{appflask-counter,podflask-deployment-xxxxxxxx-xxxxx}监控指标告诉你“哪里出问题了”Loki 日志告诉你“为什么会出问题”。两者在同一个 Grafana 界面中协同工作形成完整的可观测性闭环。七、命令速查表八、本篇总结四层排查法看状态kubectl describe Events→ 看日志kubectl logs→ 看内部execcurlnslookup→ 看底层节点 全局事件逐层深入不跳级。Pod 级故障Pending资源不足/PVC 缺失/镜像拉取失败、CrashLoopBackOff探针过严/OOMKilled/应用自身错误每个状态都有固定的排查路径。网络故障Service 不通沿 DNS → Endpoints → Pod IP 逐层验证。Endpoints 为空检查 label selector 是否匹配DNS 失败检查 CoreDNS 状态。节点故障NotReady常见于资源压力MemoryPressure/DiskPressure或 kubelet 服务异常。可观测性联动Prometheus Grafana 快速发现异常Loki 日志精确定位根因。监控告诉你“发生了什么”日志告诉你“为什么发生”。如果你把这篇文章中每个场景的排查路径都实际操作一遍你就能自信地应对 K8s 中 80% 以上的日常故障。下一篇——第 49 篇服务网格入门Istio 简介我们将站在 NetworkPolicy 和 Ingress 的肩膀上向更高级的网络管理能力迈进。想了解更多还可以去各个平台搜索「IT策士」一起升级 IT 思维