☰
K8s 故障排查实战:Pod 异常、网络不通与节点 NotReady 处置手册
2026/9/29 13:28:35 网站建设 项目流程

简介:这份文档面向 Kubernetes 运维工程师、云计算从业者及正在准备相关认证的技术人员,系统梳理了 k8s 集群在实际生产环境中常见的故障类型与排查思路。内容围绕连接异常、通信异常、节点内部异常和应用异常四大场景展开,涵盖 pod 状态异常、资源配置错误、网络插件故障、存储卷挂载失败等典型问题,并给出对应的诊断命令与处理流程。资源包为 1 个 docx 文档,约 11.58MB,以图文笔记形式组织,便于按章节查阅与对照实操。目前已有 2877 人学习下载,说明其在运维排障领域具有较高的参考价值。读者可从中获得从 pod 调度失败到节点重置、从镜像拉取异常到存储插件安装的完整排错路径,适合作为日常运维速查手册或故障复盘的学习材料。

1. 从一次 Pod 集体 ContainerCreating 说起:这份 k8s 故障笔记到底能救什么场

凌晨两点被电话叫醒,打开监控一看,某个 node 节点上所有新建的 Pod 全卡在 ContainerCreating,业务发布直接停摆。这种场景做过 k8s 运维的人多少都遇到过——不是集群挂了,是某个环节悄悄出了问题,而排查入口散落在 kubectl、kubelet 日志、网络插件、存储挂载好几个地方。这份《k8s 常见故障处理总结》笔记,把连接异常、通信异常、节点异常、应用异常四条线拆开,每个故障状态配了诊断命令和处置步骤,适合正在管生产集群、需要快速定位问题的运维和 DevOps。它不是官方文档的复述,更像一份从实战里攒出来的排查手册,Pod 状态、网络插件、节点 NotReady 这些高频坑都有对应章节。下面按「资源能解决什么 → 怎么照着做 → 哪些地方容易翻车」的顺序拆一遍。

2. Pod 状态异常排查:从 ContainerCreating 到 CrashLoopBackOff 的完整处置链

k8s 里 Pod 的状态是最直接的信号灯,但同一个状态背后可能是完全不同的原因。这一章把笔记里提到的几种典型状态拆开,每种给出诊断路径和处置动作。

2.1 ContainerCreating 卡住:先分清是节点问题还是存储问题

ContainerCreating 表示 Pod 已经被调度到节点,但容器还没真正起来。笔记里列了两大类原因:节点本身故障,或者存储卷没挂上。

诊断的第一步是确认 Pod 落在哪个节点,然后看详细信息:

# 查看 Pod 在哪个节点,以及基本状态 kubectl get pod -o wide | grep <pod名> # 查看 Pod 详细信息,Events 部分会直接告诉你卡在哪一步 kubectl describe pod <pod名> # 如果 Pod 里有多个容器,指定容器名看日志 kubectl logs <pod名> -c <容器名> # 到 Pod 所在节点上检查 kubelet 服务状态 systemctl status kubelet # 查看 kubelet 日志,定位节点侧的具体报错 tail -f /var/log/kubelet.log

kubectl describe pod的 Events 区域是关键——如果看到FailedMount或Unable to attach or mount volumes,基本可以锁定存储问题;如果看到FailedCreatePodSandBox,则更可能是网络插件或容器运行时的问题。

判断节点是否故障有个简单办法:在同一个节点上创建一个测试 Pod,看能不能正常跑起来。如果测试 Pod 也卡在 ContainerCreating,说明节点有问题;如果测试 Pod 正常,那问题出在原 Pod 本身。

节点确认不可用时,需要重置后重新加入集群。笔记里给的步骤比较完整,我按顺序整理一下:

# 1. 先把该节点上的 Pod 驱逐到其他节点 kubectl drain <node名> --ignore-daemonsets --delete-emptydir-data # 2. 在故障节点上重置 k8s 配置(这一步会清空节点上的集群数据,务必先驱逐 Pod) kubeadm reset # 3. 停止相关服务 systemctl stop kubelet systemctl stop docker # 4. 清理残留文件 rm -rf /var/lib/kubelet/* rm -rf /etc/cni/ # 5. 清理网络接口(flannel 环境需要) ifconfig cni0 down ifconfig flannel.1 down ip link delete cni0 ip link delete flannel.1 # 6. 重启服务 systemctl start docker systemctl start kubelet # 7. 重新加入集群 kubeadm join <master地址>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>

kubeadm reset这一步要特别小心,执行完节点上所有集群相关配置都会被清掉。我一般会先kubectl drain把 Pod 赶走,确认业务已经迁移到其他节点再操作。

如果是存储问题,先看 PV 绑定情况:

# 查看 PV 和 PVC 的绑定状态 kubectl get pv kubectl get pvc -n <命名空间> # 如果后端是 Ceph,检查节点上是否装了 ceph-common yum -y install ceph-common

Ceph 环境里ceph-common没装是个高频坑,Pod 会一直卡在 ContainerCreating,describe 里能看到 mount 相关的报错。装完之后删掉 Pod 让它重建,一般就能恢复。NFS 的话检查服务端是否正常、能不能手动挂载。

2.2 Pending 不调度:节点资源和污点要同时看

Pending 和 ContainerCreating 的区别在于:Pending 是 Pod 还没找到合适的节点,压根没被调度。排查要分两个层面——节点侧和 Pod 侧。

节点侧先看资源:

# 查看节点资源使用情况 free -m # 内存 top # CPU df -h # 磁盘 # 查看节点是否有污点 kubectl describe node <node名> | grep Taints

如果节点有污点(Taints),而 Pod 的 YAML 里没有对应的容忍度(Tolerations),调度就会失败。污点的输出格式类似Taints: node.kubernetes.io/not-ready:NoSchedule,后面跟的NoSchedule表示不允许调度。

Pod 侧则要看调度约束:

# 查看 Pod 的调度相关配置 kubectl get pod <pod名> -o yaml | grep -A5 nodeSelector kubectl get pod <pod名> -o yaml | grep -A5 affinity

如果 YAML 里指定了nodeName或nodeSelector,但目标节点不存在或标签不匹配,Pod 就会一直 Pending。资源请求过大也会导致调度失败——比如 Pod 要求 8 核 CPU,但所有节点剩余资源都不够。

2.3 ImagePullBackOff 和 CrashLoopBackOff:镜像和容器退出码

ImagePullBackOff 的原因比较集中:镜像拉取超时、镜像名写错、私有仓库认证失败、Dockerfile 打出来的镜像本身有问题。排查时先 describe 看 Events:

kubectl describe pod <pod名>

Events 里会显示具体的拉取错误,比如Failed to pull image "xxx": rpc error: code = NotFound说明镜像不存在,unauthorized说明认证有问题。手动拉一下镜像可以快速验证:

# 在 Pod 所在节点上手动拉取镜像 docker pull <镜像地址>

私有仓库需要在 YAML 里配置imagePullSecrets,这个 Secret 要用kubectl create secret docker-registry创建,且必须和 Pod 在同一个 namespace。

CrashLoopBackOff 是容器起来了又退出,反复重启。这个状态有偶发性,可能上一秒还是 Running,下一秒就变了。排查顺序是先看日志再看资源限制:

# 查看容器日志,看应用报了什么错 kubectl logs <pod名> -c <容器名> --previous # 查看 Pod 详细信息,关注 Last State 和 Limits kubectl describe pod <pod名>

--previous参数很关键,因为容器已经退出了,不加这个参数可能看不到日志。如果日志里没有明显错误,检查 YAML 里的resources.limits——内存限制设得太低,容器一启动就被 OOM Kill,也会表现为 CrashLoopBackOff。

2.4 Error 和 Terminating:依赖缺失与节点失联

Error 状态通常和依赖资源有关:ConfigMap、Secret、PV、StorageClass 不存在,或者 RBAC 权限没配好。排查时 describe 和 logs 一起上:

kubectl describe pod <pod名> kubectl logs <pod名> -c <容器名> journalctl -u kubelet -f tail -f /var/log/messages

Terminating 状态比较特殊,从 k8s 1.5 开始,节点失联后 Pod 不会被自动删除,而是标记为 Terminating 或 Unknown。如果确认节点已经不可用,可以强制删除:

# 强制删除,grace-period=0 表示立即删除 kubectl delete pod <pod名> --grace-period=0 --force

但删 node 节点要慎重——先确认节点真的不能用了,而且上面的 Pod 已经迁移走。如果节点恢复了,kubelet 会重新和 apiserver 通信,Pod 可能自己就恢复了。

3. 网络通信故障:Pod 跨主机不通、DNS 解析超时、网段冲突的排查路径

网络故障在 k8s 里是最难排查的一类,因为涉及宿主机网络、网络插件、DNS 多个层面。这一章按通信类型拆开讲。

3.1 网络插件选型:Calico 和 Flannel 的边界在哪

笔记里提到三种常用插件:Calico、Flannel、Canal。选型时核心看两点——是否需要网络策略、性能要求。

Calico 既能提供 IP 又能配置网络策略,可以指定哪些 Pod 允许哪些网段访问。Flannel 只提供 IP,不支持网络策略,但支持多种后端模式:VXLAN(默认)、DirectRouting、host-gw。Canal 是 Calico 和 Flannel 的结合,既有 IP 分配又有网络策略。

性能方面,Flannel 的 DirectRouting 模式和 Calico 差不多,VXLAN 模式因为多了一层封装会略低。如果集群不需要网络策略,Flannel 够用;如果需要精细控制 Pod 之间的访问,Calico 更合适。

# 查看网络插件 Pod 状态 kubectl get pod -n kube-system | grep -E "calico|flannel" # 查看网络插件日志 kubectl logs -n kube-system <插件Pod名>

网络插件 Pod 本身如果不是 Running,Pod 之间的通信肯定有问题。先确保插件正常,再排查其他。

3.2 Pod 跨主机不通:先查网段冲突

跨主机通信中断的典型现象是:同一节点上的 Pod 能互通,跨节点就报No route to host。笔记里给了一个容易被忽略的原因——Pod 网段和宿主机网段重合。

排查时先看网络插件日志,如果发现 Pod CIDR 和物理机网段有重叠,就需要修改 Pod 网段:

# 导出当前 IPPool 配置 kubectl get ippool default-ipv4-ippool -o yaml > default-ipv4-ippool.yaml # 编辑 YAML,修改 cidr 为不冲突的网段 vim default-ipv4-ippool.yaml

修改时注意spec.cidr字段,比如原来可能是192.168.0.0/16,和宿主机网段冲突,改成100.64.0.0/10这类不冲突的地址段。改完后:

# 删除旧 IPPool kubectl delete -f default-ipv4-ippool.yaml # 应用新 IPPool kubectl apply -f default-ipv4-ippool.yaml # 依次删除网络插件 Pod,让它们重新读取配置 kubectl delete pod -n kube-system <calico或flannel的Pod名>

这个操作会影响集群网络,建议在维护窗口做。删除 IPPool 后新建的 Pod 会分配新网段的 IP,旧 Pod 需要重建才能生效。

3.3 DNS 解析超时:CoreDNS 和 resolv.conf 的联动

CoreDNS 反复重启、报Loop detected是高频问题。根因在宿主机/etc/resolv.conf里如果有127.0.0.1或127.0.0.53这样的本地回环地址,CoreDNS 会陷入死循环。

解决步骤:

# 1. 修改节点宿主机 resolv.conf,把 nameserver 改成可用的 DNS vim /etc/resolv.conf # 改成类似:nameserver 114.114.114.114 # 2. 把 CoreDNS 副本数改为 0,停掉现有 Pod kubectl edit deployment coredns -n kube-system # 修改 replicas: 0 # 3. 再把副本数改回 2 或原值,触发重建 kubectl edit deployment coredns -n kube-system # 修改 replicas: 2

重建后的 CoreDNS Pod 会重新读取宿主机的 resolv.conf。这里有个细节:网络插件 Pod 和 CoreDNS Pod 会挂载宿主机的 resolv.conf,但普通应用 Pod 不会——普通 Pod 的 resolv.conf 里 nameserver 是 CoreDNS 的 ClusterIP(通常是 10.96.0.10)。

验证方法:

# 查看网络插件 Pod 的 DNS 配置 kubectl exec -it <calico-node-pod> -n kube-system -- cat /etc/resolv.conf # 查看普通应用 Pod 的 DNS 配置 kubectl exec -it <应用Pod名> -n <命名空间> -- cat /etc/resolv.conf | grep nameserver

如果应用 Pod 访问集群内服务超时,先确认是否用了全域名。跨 namespace 访问需要service.namespace.svc.cluster.local这样的完整格式,短域名可能解析不到。

3.4 Pod 访问外网超时:桥接模式和 ip_forward

Pod 内部 ping 不通外网,常见原因是桥接模式没开。检查:

# 查看 bridge-nf-call-iptables 的值 cat /proc/sys/net/bridge/bridge-nf-call-iptables

如果不是 1,需要修改:

# 临时生效 echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables # 永久生效,写入 sysctl.conf echo "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.conf sysctl -p

另一个常见原因是 ip_forward 没开。用 tcpdump 抓包会看到大量重复 SYN 但没有 ACK:

# 在宿主机上抓包 tcpdump -i <网卡名> host <目标IP> and port <目标端口>

解决:

# 修改 sysctl.conf vim /etc/sysctl.conf # 添加或修改:net.ipv4.ip_forward=1 # 生效 sysctl -p

Pod 连接集群外数据库超时时,排查顺序是:先确认数据库地址配置正确,再进容器 telnet 测试端口,然后检查数据库是否拒绝了 Pod 网段的访问,最后看是否有慢查询或连接数打满。

4. 节点异常与调度失败:NotReady、污点、断电恢复的处置细节

节点是 k8s 的底座,节点异常会直接导致 Pod 无法调度或运行。这一章覆盖 NotReady、污点、断电恢复几个场景。

4.1 Node NotReady 的两种典型场景

刚装好的集群 Node NotReady,最常见原因是网络插件没装。这时候 CoreDNS 的 Pod 也会是 Pending,因为网络还没通。装上 Calico 或 Flannel 后节点会自动恢复。

运行一段时间后 NotReady,排查顺序是:

# 查看节点状态和资源 kubectl get node kubectl describe node <node名> # 在节点上检查资源 free -m df -h top # 检查 kubelet 服务 systemctl status kubelet

kubectl describe node的 Conditions 部分会显示具体原因,比如MemoryPressure、DiskPressure、PIDPressure。如果是 kubelet 服务挂了,重启即可:

systemctl restart kubelet

4.2 调度机制:预选、优选和亲和性

Pod 调度的过程分两步:预选(Predicates)过滤掉不满足条件的节点,优选(Priorities)给剩余节点打分。预选阶段会检查资源是否足够、端口是否冲突、亲和性是否满足等。

调度约束有几种方式:

方式说明适用场景
nodeName直接指定节点,跳过调度器极少使用,调试场景
nodeSelector根据节点标签选择简单标签匹配
nodeAffinity节点亲和性,支持硬策略和软策略复杂调度需求
podAffinityPod 亲和性,和指定 Pod 部署在同一拓扑域需要就近部署的服务
podAntiAffinityPod 反亲和性,不和指定 Pod 部署在同一拓扑域高可用分散部署

nodeAffinity 的操作符支持 In、NotIn、Exists、DoesNotExist、Gt、Lt。硬策略(required)必须满足,软策略(preferred)尽量满足但不保证。

# nodeAffinity 示例 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disktype operator: In values: - ssd preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: zone operator: In values: - zone-a

硬策略下如果所有节点都不满足标签要求,Pod 会一直 Pending。软策略则会尽量满足,满足不了就按其他策略调度。

4.3 节点断电恢复后 Pod 起不来:污点没自动清除

节点突然断电,恢复后 Pod 无法启动,报错提示节点有污点但没有容忍度。原因是节点宕机时 k8s 会自动打上不可调度污点,节点恢复后污点可能没自动消失。

排查:

# 查看节点污点 kubectl describe node <node名> | grep Taints

如果确认节点已经正常,可以手动删除污点:

kubectl taint node <node名> <污点key>-

注意命令末尾的减号,表示删除该污点。

另一个容易忽略的点是主机名。如果节点初始化后改了主机名,kubelet 会按最初注册的主机名连接集群,改不回来就注册不上。所以初始化时就要定好主机名,后面不要随意改。

4.4 节点资源不足导致的调度失败

节点资源不足时,Pod 会一直 Pending。检查节点已分配资源:

kubectl describe node <node名> | grep -A10 "Allocated resources"

输出会显示 CPU 和内存的 Requests 和 Limits 占比。如果 Requests 已经接近 100%,新 Pod 就调度不上去。这时候要么扩容节点,要么调整 Pod 的资源请求。

5. 避坑与常见问题:那些排查手册不会写的细节

这一章记录几个实际排查中容易翻车的地方,每条按现象、原因、解决来写。

现象一:kubectl describe pod 看不到 Events

原因:Events 有保留时间,默认 1 小时,超过就被清理了。如果 Pod 是昨天创建的,今天再看可能已经没 Events 了。

解决:养成第一时间 describe 的习惯,或者用kubectl get events --sort-by='.lastTimestamp'查看最近事件。也可以部署事件持久化方案,把 Events 存到外部存储。

现象二:Pod 日志看不到,容器已经退出了

原因:容器退出后日志默认不保留,直接kubectl logs会提示容器不在运行。

解决:加--previous参数查看上一个容器的日志。如果还是看不到,检查容器的日志驱动配置,或者看节点上的容器日志文件。

现象三:改了 resolv.conf 但 CoreDNS 还是报 Loop

原因:CoreDNS 的 Pod 可能没有重建,还在用旧的配置。或者改了错误的节点——CoreDNS 可能调度在其他节点上。

解决:确认 CoreDNS Pod 所在的节点,改对应节点的 resolv.conf。改完后必须重建 CoreDNS Pod,让它重新读取配置。用kubectl get pod -n kube-system -o wide | grep coredns确认节点。

现象四:kubeadm reset 后节点加不回去

原因:token 过期了。kubeadm join 的 token 默认 24 小时有效。

解决:在 master 上重新生成 token:

kubeadm token create --print-join-command

这条命令会输出完整的 join 命令,直接复制执行即可。

现象五:Pod 网段和宿主机网段冲突,改了 IPPool 但新 Pod 还是不通

原因:旧 Pod 还在用旧网段的 IP,没有重建。IPPool 修改后只影响新建的 Pod。

解决:删除旧 Pod 让它们重建,或者滚动重启 Deployment。确认新 Pod 的 IP 在新网段内:

kubectl get pod -o wide | grep <pod名>

6. 健康检查与 Token 管理:两个容易被忽略的稳定性抓手

健康检查配不好,Pod 可能频繁重启或者流量打到还没准备好的容器上。Token 管理不当,节点加入集群会失败。这两个点平时不起眼,出问题的时候很要命。

6.1 探针配置:Liveness 和 Readiness 的区别与参数

k8s 有两种探针:LivenessProbe 决定是否重启容器,ReadinessProbe 决定是否把流量转发给容器。没有探针的话,k8s 只看 Pod 是否运行,容器里的应用是不是真的能提供服务它不知道。

三种探测方式:

方式说明成功条件
httpGet对容器 IP 和指定端口路径发 HTTP GET响应码 2xx
tcpSocket和容器指定端口建立 TCP 连接连接建立成功
exec在容器内执行命令退出码为 0

配置示例:

livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 15 periodSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5

initialDelaySeconds很关键——容器启动后要等多久才开始探测。设得太短,容器还没起来就探测,肯定失败,然后被重启,陷入死循环。我一般会设 15 到 30 秒,具体看应用启动速度。

failureThreshold是连续失败多少次才判定为失败。默认 3 次,配合periodSeconds算下来,大概 30 秒才会触发重启。这个时间窗口要和应用的实际恢复能力匹配。

6.2 Token 过期与 kubectl 权限问题

kubeadm join 的 token 默认 24 小时过期。节点加入失败时先检查 token:

# 查看现有 token kubeadm token list # 重新生成 join 命令 kubeadm token create --print-join-command

kubectl 执行异常、提示无权限连接集群,常见原因是 kubeconfig 文件不对或者证书过期。检查:

# 查看当前 context kubectl config current-context # 查看集群信息 kubectl cluster-info # 检查证书有效期 kubeadm certs check-expiration

证书过期需要续期:

kubeadm certs renew all

续期后需要重启 kubelet 和 apiserver 相关的静态 Pod。

从那以后我每次配探针都会先确认应用的启动时间,initialDelaySeconds至少设成启动时间的两倍,failureThreshold也适当放宽。Token 方面,我会在节点加入成功后记录 token 的过期时间,提前重新生成,避免节点扩容时卡住。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询