K8s故障排查实战:从Pod Pending到节点NotReady的体系化路径
简介这份文档面向云计算运维工程师、K8s 初学者及需要快速排障的一线人员系统梳理了 Kubernetes 集群常见故障的诊断与处理思路。内容按连接异常、通信异常、节点内部异常、应用异常四大模块展开涵盖 Pod 处于 ContainerCreating、Pending、ImagePullBackOff 等典型状态的成因分析并给出 kubectl get/describe/logs、kubelet 服务状态与日志查看等排查流程还涉及节点重置重新加入集群、PV 存储卷未挂载、网络插件与 ceph 存储插件等实战场景。资源包为 1 个 docx 文档压缩后约 11.58MB内容以图文笔记形式组织目录清晰、便于按故障类型检索。目前已有 2877 人学习下载适合作为日常运维排障的速查手册与知识沉淀参考。1. k8s 故障处理从 Pod 起不来、网络不通到节点失联的排查路径线上 k8s 集群出问题时最怕的不是报错而是不知道从哪一层开始查。Pod 一直 Pending、Service 访问超时、节点突然 NotReady这三类故障占了日常运维的大头。很多人第一反应是重启但重启只能掩盖症状下次换个 Pod 照样翻车。这篇笔记按「控制面 → 工作节点 → Pod 调度 → 网络插件 → 存储与配置」的层次把每一层的典型故障现象、排查命令和修复动作串起来。适合已经搭过集群、能跑 kubectl 的运维和开发也适合正在准备 k8s 面试题、想把零散命令串成体系的人。读完你至少能拿到一套可复用的排查顺序而不是靠玄学重启。2. 控制面故障apiserver、etcd 与调度器的失联排查控制面是集群的大脑它出问题时kubectl 会直接超时或返回Unable to connect to the server。这一层故障的排查不能靠猜必须按组件逐个确认进程、端口和证书状态。2.1 先确认 apiserver 是否在监听apiserver 是唯一对外暴露的入口它挂了整个集群就失去控制能力。用systemctl或容器运行时命令确认进程状态再检查 6443 端口是否监听。# 查看 kube-apiserver 容器状态kubeadm 部署方式 crictl ps -a | grep kube-apiserver # 检查 6443 端口监听 ss -lntp | grep 6443 # 查看 apiserver 最近日志 crictl logs apiserver-container-id --tail 100逻辑说明crictl ps -a能看到容器是否反复重启如果状态是Exited说明进程启动后崩溃。ss -lntp确认端口是否被监听如果端口不存在apiserver 根本没起来。日志里重点看x509证书错误和etcd连接超时这两个是 apiserver 起不来的最常见原因。参数说明--tail 100只取最后 100 行避免日志刷屏。如果 apiserver 是静态 Pod 方式部署日志路径在/var/log/pods/kube-system_kube-apiserver-*/下用tail -f实时观察。2.2 etcd 健康检查与证书过期处理etcd 存了集群所有状态它一旦不可用apiserver 会报etcdserver: request timed out。etcd 的排查分两步先看成员健康再看证书有效期。# 设置 etcd 客户端环境变量 export ETCDCTL_API3 export ETCDCTL_CACERT/etc/kubernetes/pki/etcd/ca.crt export ETCDCTL_CERT/etc/kubernetes/pki/etcd/server.crt export ETCDCTL_KEY/etc/kubernetes/pki/etcd/server.key # 查看 etcd 成员健康状态 etcdctl endpoint health --endpointshttps://127.0.0.1:2379 # 查看证书过期时间 openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -noout -dates逻辑说明endpoint health返回healthy说明 etcd 本身正常如果返回unhealthy或超时先看 etcd 容器日志。证书检查是关键k8s 默认证书有效期一年过期后 apiserver 和 etcd 之间会直接断连现象是集群突然整体不可用。参数说明--endpoints指定 etcd 地址单节点就是127.0.0.1:2379多节点要逐个检查。openssl x509 -dates输出notBefore和notAfter如果notAfter已经过期需要用kubeadm certs renew续期后重启对应组件。2.3 调度器与控制器管理器的排查调度器挂了新 Pod 会一直 Pending控制器管理器挂了Deployment 不会自动补副本。这两个组件通常和 apiserver 部署在同一批节点上排查方式类似。# 查看调度器日志中的调度失败原因 crictl logs scheduler-container-id --tail 50 | grep -i failed\|error # 查看控制器管理器日志 crictl logs controller-manager-container-id --tail 50 | grep -i failed\|error # 确认组件是否正常选主 kubectl get leases -n kube-system逻辑说明调度器日志里如果出现no nodes available或insufficient cpu/memory说明是资源不足而非调度器本身故障。控制器管理器日志里常见的是leader election失败多 master 环境下选主异常会导致控制器不工作。kubectl get leases能看到各组件是否成功持有租约。参数说明grep -i忽略大小写方便过滤Failed和failed两种写法。leases资源在 kube-system 命名空间下正常状态是每个组件一条记录HOLDER字段显示当前持有者。3. 工作节点故障NotReady、kubelet 与容器运行时排查节点 NotReady 是集群里第二高频的故障。现象是kubectl get nodes显示某节点状态为NotReady该节点上的 Pod 可能被驱逐也可能卡在原地不动。排查顺序是先看节点状态详情再看 kubelet最后看容器运行时。3.1 用 describe 定位 NotReady 的直接原因kubectl describe node会输出节点的 Conditions 和 Events这是最快拿到线索的地方。# 查看节点状态详情 kubectl describe node node-name | grep -A 10 Conditions: # 查看节点最近事件 kubectl describe node node-name | grep -A 20 Events: # 查看节点上的 Pod 分布 kubectl get pods --all-namespaces --field-selector spec.nodeNamenode-name逻辑说明Conditions 里重点看Ready、MemoryPressure、DiskPressure、PIDPressure四个字段。如果Ready是False原因通常在Message字段里比如kubelet stopped posting node status说明 kubelet 挂了container runtime not ready说明容器运行时有问题。Events 里能看到节点最近发生的驱逐、镜像拉取失败等事件。参数说明--field-selector spec.nodeNamenode-name按节点过滤 Pod比grep更准确。-A 10表示显示匹配行后 10 行方便看完整信息。3.2 kubelet 服务状态与日志排查kubelet 是节点上的核心代理它负责 Pod 生命周期管理、上报节点状态。kubelet 挂了节点会在几分钟内变成 NotReady。# 查看 kubelet 服务状态 systemctl status kubelet # 查看 kubelet 最近日志 journalctl -u kubelet -n 100 --no-pager # 查看 kubelet 配置中的关键参数 cat /var/lib/kubelet/config.yaml | grep -E clusterDNS|clusterDomain|runtimeEndpoint逻辑说明systemctl status能看到服务是否 active如果显示failed或activating说明 kubelet 启动失败。journalctl日志里重点看failed to run Kubelet和connection refused前者通常是配置错误后者是容器运行时没起来。config.yaml里的runtimeEndpoint必须和实际容器运行时地址一致写错会导致 kubelet 连不上运行时。参数说明-n 100取最近 100 行日志--no-pager避免进入分页模式。grep -E支持正则一次过滤多个关键词。3.3 容器运行时故障containerd 与 Docker 的差异k8s 1.24 之后默认用 containerd不再直接支持 Docker。容器运行时挂了kubelet 会报container runtime not ready节点上的 Pod 全部异常。# 查看 containerd 服务状态 systemctl status containerd # 查看 containerd 日志 journalctl -u containerd -n 100 --no-pager # 用 crictl 检查运行时连通性 crictl info # 查看运行时配置 cat /etc/containerd/config.toml | grep -E SystemdCgroup|sandbox_image逻辑说明crictl info是判断容器运行时是否可用的直接命令如果返回Runtime service is not running说明 containerd 没起来。config.toml里SystemdCgroup必须和 kubelet 的 cgroup 驱动一致不一致会导致 Pod 启动后立即退出。sandbox_image是 pause 镜像地址如果拉不到Pod 会卡在ContainerCreating。参数说明SystemdCgroup true是推荐配置和 kubelet 的cgroupDriver: systemd对应。sandbox_image在国内环境需要换成可访问的镜像仓库地址否则 Pod 创建会超时。4. Pod 故障Pending、CrashLoopBackOff 与 OOMKilled 的排查Pod 是 k8s 最小的调度单元它的故障现象最直观但原因可能分布在调度、镜像、配置、资源四个层面。这一章按 Pod 生命周期阶段拆解每个阶段对应一类典型故障。4.1 Pending 状态调度失败还是资源不足Pod 一直 Pending说明调度器没有把它分配到节点上。用describe pod看 Events最后一行通常就是原因。# 查看 Pod 详情和事件 kubectl describe pod pod-name -n namespace # 查看节点资源分配情况 kubectl describe node node-name | grep -A 5 Allocated resources # 查看是否有污点导致无法调度 kubectl get nodes -o custom-columnsNAME:.metadata.name,TAINTS:.spec.taints逻辑说明describe pod的 Events 里FailedScheduling后面会跟具体原因。常见的有0/3 nodes are available: 3 Insufficient cpu资源不足、node(s) had taint污点未容忍、node(s) didnt match node selector标签不匹配。Allocated resources能看到节点已分配和总量判断是否真的资源不够。参数说明-o custom-columns自定义输出列TAINTS列显示节点污点。如果污点存在且 Pod 没有对应 toleration调度器会跳过该节点。4.2 CrashLoopBackOff容器反复重启的定位方法CrashLoopBackOff 表示容器启动后很快退出kubelet 按指数退避重启它。排查关键是看容器退出前的日志和退出码。# 查看 Pod 当前状态和重启次数 kubectl get pod pod-name -n namespace -o wide # 查看容器上一次退出的日志 kubectl logs pod-name -n namespace --previous # 查看容器退出码和原因 kubectl describe pod pod-name -n namespace | grep -A 5 Last State # 进入容器调试如果容器还在运行 kubectl exec -it pod-name -n namespace -- /bin/sh逻辑说明--previous看的是上一次容器实例的日志因为 CrashLoopBackOff 时当前实例可能还没输出日志就退了。Last State里的Exit Code是关键1通常是应用报错137是 OOMKilled 或被 SIGKILL143是 SIGTERM。如果容器能短暂运行用exec进去手动执行启动命令看报错是否复现。参数说明-o wide显示 Pod 所在节点和 IP方便确认是否集中在某节点。--previous只在容器重启过之后有效第一次启动失败时用不了。4.3 OOMKilled 与资源限制的配置检查OOMKilled 是容器内存超过 limit 被内核杀掉退出码 137。它和 CrashLoopBackOff 的区别是OOMKilled 有明确的内存超限记录。# 查看 Pod 的 resources 配置 kubectl get pod pod-name -n namespace -o jsonpath{.spec.containers[*].resources} # 查看节点内存压力 kubectl describe node node-name | grep -A 5 MemoryPressure # 查看容器实际内存使用需要 metrics-server kubectl top pod pod-name -n namespace逻辑说明resources里limits.memory是硬限制超过就 OOM。requests.memory影响调度设置过小会导致节点超卖。kubectl top需要集群部署了 metrics-server没有的话用crictl stats在节点上直接看容器资源占用。参数说明jsonpath精确提取字段避免输出整个 YAML。limits和requests的比例建议控制在 1.5 以内差距过大容易导致节点内存碎片化。4.4 镜像拉取失败与 ImagePullBackOffImagePullBackOff 表示 kubelet 拉不到镜像原因可能是镜像名写错、私有仓库认证失败、网络不通。# 查看 Pod 事件中的拉取错误 kubectl describe pod pod-name -n namespace | grep -A 10 Events: # 在节点上手动拉取镜像测试 crictl pull image-name # 查看 imagePullSecrets 配置 kubectl get pod pod-name -n namespace -o jsonpath{.spec.imagePullSecrets}逻辑说明Events 里会显示Failed to pull image和具体错误比如manifest unknown镜像不存在、unauthorized认证失败、i/o timeout网络超时。在节点上用crictl pull手动拉一次能区分是 kubelet 配置问题还是镜像本身问题。imagePullSecrets为空但镜像是私有的就会认证失败。参数说明crictl pull用的是节点上的容器运行时配置和 kubelet 拉取路径一致。如果手动能拉下来但 Pod 拉不到检查 kubelet 的--image-pull-progress-deadline和镜像仓库地址配置。5. 网络与存储故障Service 不通、DNS 解析失败与 PVC 挂载异常网络和存储是 k8s 里最容易出玄学问题的两块。Service 访问不通可能涉及 kube-proxy、网络插件、Endpoint 三个层面PVC 挂载失败则和 StorageClass、PV、节点挂载点有关。5.1 Service 访问不通的排查链路Service 不通时按「Service → Endpoint → Pod → 网络插件」的顺序查不要跳步。# 查看 Service 详情 kubectl describe svc service-name -n namespace # 查看 Endpoint 是否正常 kubectl get endpoints service-name -n namespace # 查看 kube-proxy 日志 kubectl logs -n kube-system -l k8s-appkube-proxy --tail 50 # 在 Pod 内测试 DNS 解析 kubectl exec -it pod-name -n namespace -- nslookup service-name逻辑说明describe svc看Selector是否和 Pod 标签匹配Endpoints为空说明没有 Pod 被选中。kube-proxy日志里看是否有Failed to list *v1.EndpointSlice错误这通常是 RBAC 或 apiserver 连接问题。nslookup在 Pod 内执行能区分是 DNS 问题还是网络转发问题。参数说明-l k8s-appkube-proxy按标签选择 kube-proxy Pod不同部署方式标签可能不同用kubectl get pods -n kube-system确认。nslookup需要 Pod 内有这个命令没有的话用getent hosts或cat /etc/resolv.conf检查 DNS 配置。5.2 CoreDNS 解析失败的常见原因DNS 解析失败表现为 Pod 内nslookup超时或返回SERVFAIL。CoreDNS 本身、网络插件、节点 resolv.conf 都可能是原因。# 查看 CoreDNS Pod 状态 kubectl get pods -n kube-system -l k8s-appkube-dns # 查看 CoreDNS 日志 kubectl logs -n kube-system -l k8s-appkube-dns --tail 50 # 查看 CoreDNS ConfigMap 配置 kubectl get configmap coredns -n kube-system -o yaml # 检查 Pod 的 DNS 配置 kubectl exec -it pod-name -n namespace -- cat /etc/resolv.conf逻辑说明CoreDNS Pod 如果是CrashLoopBackOff先看日志里的插件加载错误。ConfigMap里forward . /etc/resolv.conf表示上游 DNS如果上游不可达解析会超时。Pod 的/etc/resolv.conf里nameserver应该是 CoreDNS 的 ClusterIP如果不是检查 kubelet 的clusterDNS配置。参数说明-l k8s-appkube-dns是 CoreDNS 的默认标签部分版本用k8s-appcoredns用kubectl get pods -n kube-system确认实际标签。forward插件的上游地址如果是节点本地 resolv.conf节点 DNS 故障会直接传导到集群内。5.3 PVC 挂载失败与 StorageClass 排查PVC 一直 Pending 或 Pod 挂载失败原因通常在 StorageClass、PV 匹配、节点挂载点三个环节。# 查看 PVC 状态和事件 kubectl describe pvc pvc-name -n namespace # 查看 StorageClass 列表 kubectl get storageclass # 查看 PV 绑定情况 kubectl get pv # 查看节点上的挂载点 df -h | grep mount-path逻辑说明describe pvc的 Events 里会显示waiting for a volume to be created或no persistent volumes available。StorageClass的Provisioner决定动态供给方式如果 Provisioner Pod 没运行PVC 会一直 Pending。df -h在节点上确认挂载点是否存在NFS 或 Ceph 挂载失败时Pod 会卡在ContainerCreating。参数说明kubectl get pv看STATUS是否为Available或Bound。df -h过滤挂载路径确认存储后端是否真的挂上了。如果用的是 local PV节点亲和性必须匹配否则 Pod 调度不到对应节点。6. 避坑与排查那些让我加班到凌晨的 k8s 故障这一章记录的是实际运维中反复踩到的坑每条都按「现象 → 原因 → 解决」写方便对照排查。6.1 节点磁盘满了导致 Pod 被驱逐现象节点上 Pod 突然大量 Evictedkubectl get nodes显示节点DiskPressure为True。原因kubelet 默认在磁盘使用率超过 85% 时触发驱逐镜像层、容器日志、emptyDir 是主要占用来源。解决先清理无用镜像crictl rmi --prune再限制容器日志大小。在 kubelet 配置里加containerLogMaxSize: 50Mi和containerLogMaxFiles: 3然后重启 kubelet。长期方案是把/var/lib/containerd和/var/log/pods挂到独立盘。6.2 Service 的 Endpoint 为空但 Pod 运行正常现象kubectl get endpoints显示noneService 访问不通但 Pod 本身 Running。原因Service 的selector和 Pod 标签不匹配或者 Pod 的readinessProbe没通过。解决用kubectl get pod --show-labels对比 Service 的 selector。如果是 readinessProbe 失败kubectl describe pod看 Events 里的Readiness probe failed调整探针的initialDelaySeconds和timeoutSeconds。6.3 多 master 环境下 apiserver 负载不均现象三台 master 中有一台 apiserver 连接数明显偏高其他两台空闲。原因kubeconfig 里只配了一个 apiserver 地址或者负载均衡器没做健康检查。解决kubeconfig 的server字段配 VIP 或负载均衡地址不要写单台 master IP。用kubectl config view确认当前上下文。负载均衡器要对 6443 端口做 TCP 健康检查剔除异常节点。6.4 Pod 时间与节点时间不一致现象Pod 内日志时间戳和节点时间差几分钟证书校验失败。原因容器默认继承节点时间节点 NTP 同步异常会导致时间漂移。解决在节点上确认chronyd或ntpd正常运行timedatectl查看NTP synchronized是否为yes。Pod 内如果需要独立时间挂载/etc/localtime并确保节点时间正确。6.5 删除 namespace 后一直 Terminating现象kubectl delete namespace后 namespace 卡在 Terminating 几十分钟不消失。原因namespace 下还有资源无法删除比如 APIService、CRD 的 finalizer 没清理。解决先kubectl get all -n namespace看残留资源手动删除。如果是 finalizer 阻塞用kubectl edit去掉metadata.finalizers字段。实在不行用kubectl replace --raw调 apiserver 接口强制删除但这是最后手段。7. 用 kubectl 插件和事件流把排查速度提上来排查 k8s 故障命令敲得快不快直接决定恢复时间。我自己的习惯是装几个 kubectl 插件再把事件流盯住很多问题在爆发前就能看到苗头。7.1 必装的三个 kubectl 插件kubectl-neat用来清理 YAML 里的冗余字段describe输出太长时用它过滤。kubectl-tree按 ownerReferences 展示资源层级排查 Deployment → ReplicaSet → Pod 关系时一目了然。kubectl-watch可以实时监听资源变化比反复敲get高效。# 用 krew 安装插件 kubectl krew install neat tree watch # 查看资源树 kubectl tree deployment deployment-name -n namespace # 实时监听 Pod 变化 kubectl watch pods -n namespace逻辑说明krew是 kubectl 的插件管理器安装后插件命令直接以kubectl plugin形式调用。tree展示的是资源之间的从属关系Pod 异常时能快速定位到上层控制器。watch适合观察滚动更新过程中的 Pod 状态变化。参数说明krew需要单独安装安装脚本从官方仓库获取。tree对 CRD 支持有限只对内置资源效果好。watch默认输出变化事件加-o wide可以看到更多字段。7.2 用事件流做故障预警k8s 的 Event 资源记录了集群里所有重要变化用kubectl get events -w可以实时盯住。我一般会开一个终端专门跑这个命令过滤出Warning级别的事件。# 实时查看所有命名空间的 Warning 事件 kubectl get events --all-namespaces --field-selector typeWarning -w # 按时间排序查看最近事件 kubectl get events --all-namespaces --sort-by.lastTimestamp | tail -30 # 查看特定资源的关联事件 kubectl get events -n namespace --field-selector involvedObject.namepod-name逻辑说明--field-selector typeWarning只显示警告事件过滤掉大量 Normal 事件。--sort-by.lastTimestamp按时间排序方便看最近发生了什么。involvedObject.name精确关联到某个 Pod 或节点排查单个资源时非常有用。参数说明-w是 watch 模式会持续输出新事件按 CtrlC 退出。--all-namespaces在事件多的时候输出量大建议配合grep过滤关键词。7.3 一个我常用的排查习惯每次遇到故障我会先开三个终端一个跑kubectl get events -w一个跑kubectl get pods -w第三个用来敲具体排查命令。这样在敲命令的同时能实时看到集群状态变化很多时候命令还没敲完事件流里已经给出了答案。这个习惯帮我省了很多时间。有一次一个 Deployment 滚动更新卡住事件流里直接显示FailedCreate和exceeded quota我连describe都没敲就定位到了 ResourceQuota 限制。希望这个思路能帮到你。本文还有配套的精品资源点击获取