K8s故障排查实战:从Pod Pending到节点NotReady的定位与解决

发布时间:2026/10/20 15:07:58
K8s故障排查实战:从Pod Pending到节点NotReady的定位与解决 K8s 跑久了谁还没踩过几个坑。从 Pod 起不来、节点 NotReady到 DNS 解析超时、网络不通故障类型翻来覆去就那么几类但每次排查都得花不少时间。这套 K8s 故障排查实战我整理了实际运维中最高频的一批故障场景把定位思路和解决方法串成一条线照着查基本能省一半功夫。写这篇文章的原因是上周帮某公司排查一个线上事故新版本发布后所有 Pod 都卡在 Pending业务方等着上线排查了半小时才发现是节点资源分配的问题。这种事不算罕见但每次现场都容易慌。如果你也负责 K8s 集群的运维或日常开发这篇内容适合你——既能当排查手册用也能帮你建立一套自己的故障定位思路。1. 故障排查的整体思路与定位方法论先聊点方法论因为排查 K8s 故障最怕的就是东一榔头西一棒子。我见过太多人一看 Pod 没起来就直接 describe看完日志还是一头雾水。其实 K8s 故障排查有清晰的层次按顺序走比乱试快得多。1.1 从下往上排查节点、网络、存储、Pod一个 Pod 能正常运行依赖的是底层基础设施全部就绪。所以排查故障我习惯从最底层开始逐层向上节点层节点是否 Readykubelet 是否活着磁盘、内存、PID 是否耗尽网络层CNI 插件是否正常工作Pod 网段是否能互通DNS 是否解析正常存储层PVC/PV 是否绑定成功存储驱动是否可用工作负载层Deployment/StatefulSet 是否正常Pod 是否调度到合适节点应用层容器是否启动成功健康检查是否通过日志是否有异常这个顺序不是我拍脑袋定的而是基于故障传播的依赖关系。底层出问题上层表现会五花八门上层出问题底层通常还是好的。所以先确认底层没问题再往上查能把排查范围快速缩小。1.2 时间线思维与变更驱动定位另一个重要思路是“变更驱动”。K8s 集群正常情况下不会无缘无故出问题绝大多数故障都和变更有关。排查时先问一句最近改了什么常见的变更类型包括发布新版本、修改镜像 Tag调整资源请求/限制修改 ConfigMap、Secret升级集群版本或更新 Node 节点更换或升级 CNI 插件、存储驱动扩容节点或缩容节点有一次某团队反馈 Pod 频繁重启查了半天最后发现是昨天改了健康检查的探针参数超时时间设置太短导致应用被反复杀掉。这就是典型的变更驱动问题。所以排查的第一步不是看日志而是回顾变更窗口很多时候能直接锁定嫌疑对象。1.3 建立排查基线事件、日志、监控三件套想要快速定位光靠临时看几个命令是不够的。日常就要把三件套配好故障发生时才能有据可查事件EventsK8s 自带的事件机制记录了调度、拉镜像、挂载存储等关键动作的结果。很多故障的根因直接在事件里就能看到。日志Logs容器标准输出日志以及 kubelet、容器运行时、CNI 组件的日志。应用日志让人看到症状组件日志让人看到原因。监控Metrics节点 CPU、内存、磁盘、网络等基础指标以及 Pod 的 CPU、内存使用率。很多“查不到原因”的故障其实早就通过监控指标给出了预警。这三样必须提前准备好等故障发生了再去找监控往往已经来不及。2. 常见故障分类与定位手段K8s 的故障虽然表现多样但按类型划分其实很清晰。我平时主要把故障分成基础设施故障、工作负载故障、网络故障、存储故障和控制器故障五类。分好类排查方向就有了。故障类型典型现象主要排查对象基础设施故障节点 NotReady、kubelet 异常、磁盘压力节点状态、kubelet 日志、系统资源工作负载故障Pod Pending、CrashLoopBackOff、ImagePullBackOffPod 事件、镜像地址、资源请求网络故障DNS 解析失败、Pod 间不通、Service 访问异常CoreDNS、CNI 插件、iptables 规则存储故障PVC Pending、挂载失败、IO 异常StorageClass、PV/PVC 状态、存储插件控制器故障ReplicaSet 不扩容、HPA 不生效、Job 不执行Deployment 状态、controller-manager 日志2.1 快速定位的常用命令与排查入口排查故障离不开几条核心命令。我几乎每次排查都会用到建议你也能熟练到条件反射的程度查看 Pod 整体状态kubectl get pods -A -o wide查看 Pod 的详细事件kubectl describe pod pod-name -n namespace查看容器日志kubectl logs pod-name -n namespace --tail100 kubectl logs pod-name -n namespace --previous # 查看崩溃前的日志查看节点状态kubectl get nodes -o wide kubectl describe node node-name查看所有资源的事件kubectl get events -A --sort-by.lastTimestamp这几条命令能覆盖大部分故障的第一现场。拿到现场信息后再根据症状去判断具体问题比盲目操作高效得多。2.2 用 kubectl get events 快速还原故障现场很多人忽略了一个关键点Pod 被删除后describe 里看到的事件可能就没了但集群事件还留着。所以遇到故障我第一件事通常是拉全集群事件按时间倒序看最近发生了什么。kubectl get events -A --sort-by.lastTimestamp | tail -50这个输出能告诉你哪个 Pod 在哪个节点上发生了什么是调度失败还是镜像拉取失败是健康检查失败还是被 OOMKilled是否有节点被标记为 NotReady有了这些信息再结合 Pod 的 yaml 和日志基本能还原出完整的故障链条。有时候事件里直接写着原因根本不需要再猜。3. Pod 层故障从 Pending 到 CrashLoopBackOff 的逐个击破Pod 是 K8s 的最小调度单位也是故障出现最多的地方。这一节把最常见的几种 Pod 故障拆开讲每一种都给到具体的排查步骤和解决方案。3.1 Pod 一直 Pending调度失败的核心原因分析Pending 意味着 Pod 还没有被调度到任何节点上或者调度后无法启动。常见原因有这么几类节点资源不足这是最常见的原因。Pod 声明的 requests 资源超过了集群内所有节点的可用资源调度器找不到合适的节点。排查方法kubectl describe pod pod-name -n namespace事件里会出现0/3 nodes are available: 1 Insufficient cpu, 2 Insufficient memory类似的提示。看到这个就知道是资源不够。解决思路扩容节点增加可用资源调整 Pod 的 requests 值降低资源要求删除一些不重要的 Pod释放资源通过 priorityClass 保证高优先级 Pod 优先调度节点亲和性或污点限制如果节点设置了 taints或者 Pod 配置了 nodeSelector、nodeAffinity可能导致符合条件的节点不存在。kubectl describe node node-name | grep -A5 Taints如果节点有污点而 Pod 没有对应的 toleration调度器就不会把 Pod 放上去。解决方法是给 Pod 添加 toleration或者去掉节点上的污点。存储类不存在或 PVC 无法绑定如果 Pod 使用了 PVC而 PVC 处于 Pending 状态Pod 也会一直 Pending。这时候要检查 StorageClass 是否存在、PV 是否可动态供应。kubectl get pvc -n namespace kubectl get storageclass我遇到过好几次都是 StorageClass 名字写错了明明集群里叫fast-diskyaml 里写成了fast-disk-ssd结果 PVC 一直 PendingPod 也跟着 Pending。这种问题事件里看得清清楚楚一眼就能发现。3.2 ImagePullBackOff 与 ErrImagePull镜像拉取失败的排查清单Pod 能调度成功但镜像拉不下来状态会显示 ImagePullBackOff 或 ErrImagePull。这类问题排查起来相对直接先看事件里的具体错误信息kubectl describe pod pod-name -n namespace常见原因镜像地址拼写错误或 Tag 不存在镜像仓库需要认证但 imagePullSecrets 没配置节点无法访问镜像仓库可能是网络不通或防火墙拦截镜像仓库限流或镜像过大导致拉取超时私有仓库证书不受信任处理方式按原因区分。如果是认证问题创建 Secret 并绑定到 Deploymentkubectl create secret docker-registry regcred \ --docker-serverregistry-server \ --docker-usernameusername \ --docker-passwordpassword \ -n namespace然后在 Deployment 的 spec 里添加spec: template: spec: imagePullSecrets: - name: regcred如果是网络问题可以先在节点上手动拉一下镜像看能不能成功。但要注意生产环境一般不建议直接 ssh 到节点手拉镜像因为这会绕过 K8s 的声明式管理导致节点上出现“游离镜像”。提示如果你用的是latest标签还会遇到缓存问题——节点上已经有同名的旧镜像K8s 不会每次都重新拉取。这时候要设置imagePullPolicy: Always或者改用带版本号的 tag。3.3 CrashLoopBackOff容器反复崩溃的定位方法CrashLoopBackOff 是让我又爱又恨的状态。恨是因为应用本身的 bug 藏在里面不好定位爱是因为它其实提供了一条非常清晰的排查路径。容器崩溃循环的原因主要分三类应用启动失败代码里抛异常、配置文件缺失、数据库连不上、启动命令参数错误等都会导致容器启动后立刻退出。kubectl logs pod-name -n namespace --previous--previous很重要因为容器崩溃后K8s 会重启容器当前容器的日志可能只有启动过程的片段。加上这个参数才能看到上一次容器生命的完整日志。健康检查失败livenessProbe 探测失败达到阈值后kubelet 会杀掉容器并重启。这种情况在应用日志里往往找不到错误因为应用本身是正常的只是探针配置不合理。典型的坑有两个探针超时时间太短应用启动稍慢就被判死探针路径写错返回 404 导致失败处理方法是调整探针参数比如把initialDelaySeconds调大给应用足够的启动时间。资源限制导致 OOMKilled容器申请的内存超过 limit被内核 OOM Killer 杀掉。通过kubectl describe pod能看到OOMKilled或Exit Code: 137。kubectl describe pod pod-name -n namespace如果看到退出码是 1371289对应 SIGKILL基本可以确认是 OOM。解决方法是增加内存 limit或者优化应用的内存占用。在事件和日志都查不到有效信息时还可以尝试进入容器手动启动进程看前台的报错输出kubectl exec -it pod-name -n namespace -- /bin/sh不过这要求容器里本身有 shell且应用支持前台调试。有时候直接在容器里跑一下启动命令错误信息比日志更直观。3.4 Pod 已运行但服务异常健康检查与动态配置的陷阱还有一类情况比较隐蔽Pod 状态是 Running但服务就是访问不正常比如请求超时、返回 500、部分功能不可用。先确认容器是否真的健康。Running状态只代表进程活着不代表应用能正常对外服务。kubectl get pod pod-name -n namespace -o yaml | grep -A10 readinessProbe如果 readinessProbe 配置了但一直失败Pod 的 Endpoints 里就不会有这个 PodService 自然不会把流量转给它。kubectl get endpoints service-name -n namespace如果 Endpoints 为空说明 Ready 的 Pod 数量为 0问题就出在 readinessProbe 上。这种情况要赶紧看应用日志和探针的访问日志确认是探针路径问题还是应用本身没起来。另一个隐蔽问题是 ConfigMap 更新后没有生效。K8s 挂载 ConfigMap 是符号链接切换机制更新 ConfigMap 后容器内的文件会更新但已经运行中的进程可能不会自动重新读取配置。很多人改了 ConfigMap发现 Pod 行为没变化就是因为这个。解决方法很简单更新 ConfigMap 后滚动重启关联的 Deployment。kubectl rollout restart deployment/deployment-name -n namespace4. 集群层故障从节点异常到网络安全隐患排查Pod 故障往往只是表象当同一个节点上的多个 Pod 同时出问题或者集群整体异常时就要把视角从 Pod 提升到集群层。这一层的问题影响面更大排查起来也更考验对组件原理的理解。4.1 Node 状态 NotReady 的排查与恢复步骤节点 NotReady 意味着 kubelet 无法正常向 API Server 上报心跳。可能的原因有kubelet 进程停止或崩溃节点负载过高kubelet 无响应网络分区节点与控制平面断开Docker/containerd 异常无法管理容器排查先从节点侧开始systemctl status kubelet journalctl -u kubelet -f --no-pager | tail -100kubelet 日志里如果出现频繁的failed to get node not found或connection refused说明 kubelet 与 API Server 通信异常。确认 kubelet 正常后再查容器运行时的状态systemctl status containerd journalctl -u containerd --no-pager | tail -100还有一个容易忽略的原因节点磁盘空间被占满。kubelet 会定期清理镜像和日志但如果容器日志量太大且 logrotate 又没配好/var 分区可能被写满。节点进入磁盘压力状态后会主动驱逐 Pod。df -h /var/lib/docker /var/log /var/lib/kubelet节点恢复 Ready 后被驱逐的 Pod 并不会自动回到原节点。如果这些 Pod 由 Deployment 管理且副本数没有变化控制器会重新创建 Pod调度到其他可用节点。如果节点恢复后希望你原来的 Pod 回到这个节点可以把节点上的 taint 去掉或者调整调度策略。4.2 CoreDNS 解析异常与 DNS 故障排查DNS 解析失败是集群内非常常见的问题。现象可能是服务间调用报域名无法解析、kubectl exec后 ping Service 名不通、应用日志里大量connection refused或no such host。排查 CoreDNSkubectl get pods -n kube-system -l k8s-appkube-dns kubectl logs -n kube-system -l k8s-appkube-dns --tail50如果 CoreDNS Pod 本身处于 CrashLoopBackOff 或 Pending要先解决 Pod 问题。CoreDNS 最常见的崩溃原因是内存压力因为 CoreDNS 会缓存 DNS 记录高并发下内存增长很快容易被 OOMKill。如果 CoreDNS 正常运行但解析还是失败试试在故障 Pod 里手动解析kubectl exec -it pod-name -n namespace -- nslookup kubernetes.default.svc.cluster.local如果解析失败检查 Pod 的/etc/resolv.conf配置kubectl exec -it pod-name -n namespace -- cat /etc/resolv.conf正常情况应该看到类似内容nameserver 10.96.0.10 search namespace.svc.cluster.local svc.cluster.local cluster.local options ndots:5如果 nameserver 不是 CoreDNS 的 Service IP可能是 kubelet 配置的--cluster-dns参数有问题或者 Pod 的 dnsPolicy 被设置成了Default。另一个常见的 DNS 坑是ndots:5。当域名中点的数量少于 5 个时系统会先尝试在 search 域里拼接解析导致一次查询要发起多次 DNS 请求出现超时。严格来说这不是故障但在高并发场景下会放大延迟表现就像 DNS 解析失败一样。注意不要把 CoreDNS 的副本数设置为 0 或同时在每个节点上跑太多副本。我见过一个集群CoreDNS 只有 1 个副本节点宕机后整个集群的 DNS 服务直接瘫痪。至少保持 2 个副本或者使用拓扑感知的调度让副本分布在不同节点上。4.3 Service 无法访问与 kube-proxy 的核查要点Service 是 K8s 内部服务发现的核心但 Service 访问不通的问题也特别多。排查顺序如下第一步确认 Service 和 Endpoints 的状态kubectl get svc -n namespace kubectl get endpoints service-name -n namespace如果 Endpoints 不为空说明后端 Pod 已经关联如果为空回到上一节说的 readinessProbe 问题。第二步检查 kube-proxy 是否正常工作kubectl get pods -n kube-system -l k8s-appkube-proxykube-proxy 负责维护节点上的 iptables/ipvs 规则将 Service 的 ClusterIP 转发到后端 Pod。如果它挂了或者规则异常Service 的 ClusterIP 就不可达。在节点上检查 iptables 规则iptables -t nat -L -n | grep cluster-ip这能看到是否存在 DNAT 规则。如果规则存在但仍然不通再检查后端 Pod 所在节点的网络策略或安全组是否放行流量。第三步在 Pod 内部分别测试 ClusterIP 和 PodIPkubectl exec -it pod-name -n namespace -- curl http://service-ip:port kubectl exec -it pod-name -n namespace -- curl http://pod-ip:portClusterIP 不通但 PodIP 通问题在 kube-proxy 或网络插件。两者都不通问题在应用本身或宿主机网络。这里我遇到过一种特别隐蔽的情况集群里所有 Service 访问都正常唯独某个命名空间的 Service 不通。查到最后发现是 Service 和某个 Pod 的标签选择器重复了导致 Service 后端混入了不该出现的 Pod流量被导到了完全陌生的应用上。所以 Service 的 selector 一定要仔细核对别以为写个 label 就万事大吉。4.4 CNI 网络插件故障与 Pod 间网络异常Pod 间网络不通、跨节点通信失败、网络策略失效这些问题的根源大多在 CNI 插件。常见的 CNI 插件有 Calico、Flannel、Cilium 等它们的故障排查思路有相似之处。先看 CNI 组件的 Pod 状态kubectl get pods -n kube-system | grep -E calico|flannel|cilium如果有 Pod 处于 CrashLoopBackOff 或 Pending优先解决。常见原因包括节点之间的 BGP 邻居建立失败网络策略依赖的 CRD 未安装或冲突CNI 配置文件损坏IP 地址池耗尽无法为新 Pod 分配 IP检查 IP 池的方法因插件而异。Calico 可以用calicoctl ipam show如果你用的是 Ciliumcilium status cilium bpf ip list网络插件出问题时节点上新建的 Pod 会一直处于 ContainerCreating 状态因为 CNI 在给 Pod 分配网络接口时失败。这时可以看 kubelet 日志或 CNI 插件的日志错误信息一般都很明确。有个细节值得注意CNI 插件跨节点通信依赖节点间的网络连通性。如果集群节点分布在不同机房或不同网段要保证底层网络链路是通的。我在一个混合云项目上遇到过节点都在一个集群里但不同云厂商的 VPC 之间没有打通结果跨节点的 Pod 通信就是不通单独看每个节点的 CNI 状态又都正常。5. 故障复现、工具与现场止血排查故障不能只靠看日志有时候需要主动复现问题才能找到真正的根因。另外故障发生时先止血再排查这个顺序要记牢。5.1 用临时 Pod 复现网络与 DNS 问题集群网络问题对业务的影响是全局性的但排查时又不想直接影响线上应用。我常用的方法是创建一个临时 Pod用干净的容器环境去测试网络和 DNS。apiVersion: v1 kind: Pod metadata: name: debug-pod namespace: default spec: containers: - name: debug image: nicolaka/netshoot:latest command: [sleep, 3600]netshoot 镜像里集成了 curl、dig、nslookup、tcpdump、nc 等常用网络排查工具非常适合用来调试网络问题。创建后进入 Podkubectl exec -it debug-pod -- /bin/bash然后在里面做测试dig kubernetes.default.svc.cluster.local验证 DNScurl http://service-ip:port验证 Service 访问ping pod-ip验证 Pod 间连通性tcpdump -i eth0 port 53抓 DNS 请求包这个临时 Pod 用完就删不产生任何副作用。比在业务 Pod 里装调试工具靠谱多了。5.2 故障现场的信息采集清单每次处理完故障我都习惯把现场信息保存下来方便复盘也方便写故障报告。信息采集清单如下故障发生时间、持续时间、影响范围相关 Pod 的 yaml 和事件输出节点状态和系统资源信息kubelet、容器运行时、CNI 插件的关键日志应用日志的关键片段监控指标截图或趋势数据变更记录和操作时间线不要嫌麻烦记这些。一个成熟的运维团队处理过的每个故障都是资产。下次再遇到类似问题翻出历史记录可以少走很多弯路。5.3 临时恢复的止血手段与后续根治故障发生时要分清楚紧急恢复和根治是不同的工作。现场人员最要紧的是恢复业务不是追根因。我常用的止血手段包括重启故障 Pod 或滚动重启 Deployment适用于临时性故障扩副本数把流量分散到更多实例上标记异常节点不可调度将业务 Pod 驱逐到其他节点回滚最近一次的变更比如镜像版本、配置、网络策略临时关闭故障的 CronJob 或批处理任务止血之后再做根因分析。有一种常见错误业务恢复后就没人管了导致同样的故障反复出现。一次故障至少要产出对应的监控告警、应急预案和修复措施这样才算闭环。6. 常见故障速查表与避坑经验最后把高频故障整理成速查表方便你放到收藏夹或者团队 Wiki 里。后面再补充几条踩坑经验算是我这些年的真实总结。6.1 高频故障速查对照表故障现象可能原因关键排查命令解决方向Pod Pending节点资源不足kubectl describe pod扩容节点、降低 requestsPod PendingPVC 未绑定kubectl get pvc检查 StorageClass 和 PVImagePullBackOff镜像地址错误kubectl describe pod修正镜像地址或 TagImagePullBackOff私有仓库认证失败kubectl get secrets配置 imagePullSecretsCrashLoopBackOff应用启动异常kubectl logs --previous修复应用代码或配置CrashLoopBackOff探针配置过严kubectl describe pod调整探针参数OOMKilled内存超出 limitkubectl describe pod增加 limit 或优化内存Node NotReadykubelet 异常journalctl -u kubelet恢复 kubeletNode NotReady磁盘压力df -h清理日志或扩容磁盘DNS 解析失败CoreDNS 异常kubectl logs -n kube-system -l k8s-appkube-dns修复 CoreDNS PodDNS 解析超时ndots 配置过深cat /etc/resolv.conf调整 ndots 或使用自建 DNSService 不通Endpoints 为空kubectl get endpoints修复 readinessProbeService 不通kube-proxy 异常kubectl get pods -n kube-system -l k8s-appkube-proxy重启 kube-proxyPod 间网络不通CNI 插件故障kubectl get pods -n kube-system查看插件日志并修复6.2 我的几条实测经验非常重要第一永远先看事件再看日志。事件的语义更贴近 K8s 本身日志是应用视角。先知道 K8s 做了什么再去看应用为什么失败两者对照效率最高。跳过事件直接看日志容易在细节里迷失方向。第二不要在故障现场频繁滚动刷新kubectl get pods看起来在忙实际没有获取有效信息。应该直接输出现场快照保存下来再慢慢分析。故障现场最需要的是冷静不是手速。第三日常养成检查变更窗口的习惯。把每次发布的镜像版本、配置改动、节点变更记录下来故障时对照时间线就能快速圈定范围。很多团队不做变更记录出了事只能全体排查效率特别低。第四监控告警要覆盖关键指标但不要过度告警。真正有价值的告警是节点 NotReadyPod 处于 CrashLoopBackOff 超过一定时长PVC 长时间 PendingCoreDNS 5xx 错误率上升节点磁盘使用率超过 85%告警不在于多而在于能让人在收到后立刻采取行动。太多无效告警只会让人麻木关键时刻反而漏掉真问题。第五也是我最想强调的不要害怕故障但一定要从故障里学到东西。每一次故障排查都是一次免费的架构评审。它逼你去理解集群的内部机制、组件间的依赖关系、应用与基础设施的耦合点。处理过几次复杂的网络故障之后你再回头看集群的架构设计会有完全不一样的认识。这是我运维 K8s 这些年来最真实的感受。希望这篇故障排查实战能帮你少踩一些坑遇到问题时能更快地定位到根因恢复业务然后从容地写下复盘报告。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询