K8s节点NotReady与Flannel Pod Terminating排查实战

发布时间:2026/10/21 12:14:46
K8s节点NotReady与Flannel Pod Terminating排查实战 如果你某天早上登录集群发现kubectl get nodes里 master 节点还是NotReady而kube-system下面那个kube-flannel-ds-amd64-xxxxx一直挂在Terminating状态别急着去删 Pod。这个组合很常见也挺典型的Flannel 作为 CNI 插件负责把节点间的 Pod 网络打通它一旦“半死不活”节点状态很快就会跟着异常。我经历过几次类似问题今天把这套排查过程、修复命令和踩坑经验完整复现一遍希望能帮你少走弯路。这套内容适合正在维护 Kubernetes 集群的运维、SRE也适合刚接触 K8s 网络组件的人对照着排查。不管是单节点还是多节点环境问题逻辑基本一致只是排查命令的侧重点略有不同。我的排查环境是 containerd 作为容器运行时网络插件用的 Flannel VXLAN 模式如果你那边是 Docker 运行时个别命令改成docker执行即可不影响整体思路。1. 故障现象与影响范围1.1 现场看到的样子先说现象。故障发生时机通常是节点重启过一次或者内核升级后重启了机器。经过一段时间的运行集群里某个节点特别是 master虽然进程看起来都在但状态就是不健康。执行kubectl get nodes$ kubectl get nodes NAME STATUS ROLES AGE VERSION master1 NotReady control-plane 127d v1.28.6 worker1 Ready none 127d v1.28.6同时查看kube-system下的 Flannel DaemonSet Pod$ kubectl get pods -n kube-system | grep flannel kube-flannel-ds-amd64-8f2n4 0/1 Terminating 0 8h kube-flannel-ds-amd64-m6k9s 1/1 Running 0 6h这就是最典型的状态组合老 Pod 卡在Terminating新 Pod 已经被 DaemonSet 重新拉起但节点状态依然是NotReady。关注的指标不只是这两个字段。0/1表示容器没有处于 Ready 状态Terminating表示删除流程已经卡住Pod 的宽限期grace period耗尽了也没被真正清理掉。你还会发现 master 节点的所有系统组件像kube-apiserver、etcd这些并不一定受影响它们继续在跑但 kubelet 向控制面上报的状态始终不健康这导致调度器默认不会把新工作负载调度到该节点。1.2 为什么 Flannel 出问题会让 master 节点置为 NotReadyKubernetes 节点状态是 kubelet 周期性上报的kubelet 判断节点是否 Ready 会同时看很多因素其中NetworkReady条件是很关键的一项。kubelet 会检查节点网络是否就绪而网络是否就绪依赖于 CNI 插件是否配置正常、Pod 网络是否能创建。Flannel 作为 CNI 插件承担的是在节点之间建立 VXLAN 隧道、维护路由表和flannel.1接口的责任。如果 Flannel Pod 异常退出或者 CNI 配置缺失kubelet 在创建 Pod 时调用 CNI 插件会失败网络不可用kubelet 就会给节点打上NotReady标记。所以表面上你看到的是 Flannel PodTerminating本质上是这个节点上的 Pod 网络已经断了节点自然被判定为不健康。还有一个容易忽略的细节Flannel DaemonSet 在节点上以 Pod 方式运行如果它本身启动依赖的某些条件不满足比如节点上没有分配 PodCIDR、内核 VXLAN 模块缺失、CNI 配置文件残留等它会一直 CrashLoopBackOff 或者反复重启导致节点网络始终无法恢复。2. 排障思路先理清因果链再动手2.1 从 Pod 状态和节点状态反推可能原因我习惯先不执行任何修复操作而是把可能的故障链路在脑子里过一遍。Flannel Pod 卡Terminating和节点NotReady之间存在多种原因指向常见的大致分成这几类第一类是容器运行时的问题。containerd 或者 dockerd 可能因为存储、内存、或并发问题导致容器删除流程卡住旧 Pod 的容器进程还残留着kubelet 删不动Pod 就挂在Terminating。这种场景下你会观察到kubelet日志中有反复尝试移除容器的报错。第二类是网络资源残留。Flannel 会在节点上创建flannel.1虚拟网卡、路由项、iptables 规则。如果节点异常重启后这些资源处于不一致状态新 Pod 无法创建新的flannel.1旧 Pod 因为持有这些网络资源而无法清理于是互相卡死。第三类是协调数据不一致。比如节点上的 PodCIDR 分配信息、CNI 配置文件里的 subnet 信息与集群实际配置不一致Flannel 启动后发现自己拿到的子网不对无法正常建立路由Pod 反复重启也恢复不了。所以排查的第一步不是删除而是搞清楚当前节点上到底哪里卡住了。先看事件、日志、网络配置再决定动手方向。2.2 先看数据Pod 事件和容器运行时状态我会按这样的顺序收集信息# 1. 看 Pod 当前状态和事件 kubectl describe pod -n kube-system kube-flannel-ds-amd64-8f2n4 # 2. 看新起的 Flannel Pod 日志 kubectl logs -n kube-system kube-flannel-ds-amd64-m6k9s -c kube-flannel --tail200 # 3. 看节点 kubelet 最近日志 journalctl -u kubelet --since 1 hour ago --no-pager如果容器还在运行但已经 Ready 了想进一步看它内部是否正常可以进入 Pod 执行命令。但这里有个注意事项Flannel 的容器镜像比较精简不一定带 shell有时需要依靠日志和nsenter查看主机网络命名空间。describe输出里值得留意的关键字包括FailedKillPod、NetworkNotReady、FailedCreatePodSandBox、CNI failed to set up network。这些字符串基本能定位到是容器运行时、CNI 还是网络配置的问题。2.3 明确排查顺序不要一上来就删 Pod这里必须多说一句看到Terminating就执行kubectl delete pod --force是我见过最多的盲目操作。强制删除一个卡住的 Pod 只能让 kubelet 放弃等待但 Pod 底层的容器如果还占着网络资源、CRI 里还有残留新 Pod 依然可能马上失败节点仍然NotReady。更严重的是强制删除会掩盖真实根因日志里唯一的线索被清掉了。正确的顺序是查看事件和日志判断是 Pod 清理失败还是 CNI 配置失败。检查节点网络接口和路由表确认flannel.1是否存在、是否处于正确状态。检查容器运行时状态确认是否还有旧容器没有被清理。确认 CNI 配置文件有哪些是否有残留或者冲突。全部确认完毕后再做清理和恢复。3. 实操定位一步步找到根因3.1 检查 Flannel Pod 状态与事件登录到 NotReady 节点先查看对应 Pod 的详细事件kubectl describe pod -n kube-system kube-flannel-ds-amd64-8f2n4关键的事件可能长这样Events: Warning FailedKillPod 6h kubelet error killing pod: failed to KillContainer for kube-flannel with KillContainerError: rpc error: code DeadlineExceeded desc context deadline exceededDeadlineExceeded表示 kubelet 在删除容器时调用的 CRI 接口超时了。这种情况通常是 containerd 在处理容器停止时 hang 住可能是 containerd 内部任务队列阻塞、存储驱动异常也可能是容器内进程处于 D 状态不可中断睡眠导致 kill 无效。再看新 Pod 的事件如果新的 Pod 反复 CreateContainer 失败事件里大概率会有 CNI 相关报错Warning FailedCreatePodSandBox 3m kubelet Failed to create pod sandbox: rpc error: code Unknown desc failed to setup network for sandbox ... network plugin call failed: cni plugin not foundcni plugin not found直接意味着节点上的 CNI 插件二进制缺失或者配置文件损坏。有时候你看到的是failed to find plugin flannel这是因为/opt/cni/bin里确实没有 flannel 的可执行文件。3.2 检查 Flannel 日志与 CNI 配置如果新 Pod 能跑起来但 node 还NotReady那问题多半不在容器运行本身而在网络配置。此时看 Flannel 日志kubectl logs -n kube-system kube-flannel-ds-amd64-m6k9s -c kube-flannel --tail200正常的日志尾部一般是不断输出Subnet added、Lease acquired这类信息并且包含当前的节点 IP 和 Pod 网段。如果日志里出现类似下面的内容就能找到线索E0603 12:00:01.123456 12345 main.go:456] Error registering network: failed to acquire lease: node master1 pod cidr not assignedpod cidr not assigned是在告诉你说该节点没有 PodCIDR。这个字段是 kube-controller-manager 在节点加入时分配的如果你的集群使用 Flannel 作为网络插件并且在 kube-controller-manager 启动参数里没有设置--allocate-node-cidrstrue和--cluster-cidr节点就不会拿到 PodCIDRFlannel 也就拿不到可用的网段。排查时还要看 CNI 配置文件ls -la /etc/cni/net.d/ cat /etc/cni/net.d/10-flannel.conflist我遇到过一种很典型的情况节点上同时存在多个 CNI 配置文件比如/etc/cni/net.d/10-flannel.conflist和/etc/cni/net.d/05-cilium.conf同时存在。CNI 会按字典序读取配置如果先读到的是其他插件的配置容器创建时就会调用错误插件Flannel 的实际网络配置一直不生效。3.3 检查节点网络接口与路由这一步最容易忽略但往往是问题所在。节点异常重启后flannel.1接口可能会处于 DOWN 状态或者完全存在但路由表已经乱了。查看接口状态ip addr show flannel.1期望的结果是接口存在且状态为UP并有对应的 VXLAN 隧道 IP。如果接口不存在说明 Flannel 还没有成功启动或者已经删除了自己的接口。再看路由表ip route | grep flannel.1正常的 Flannel VXLAN 模式路由表大约长这样10.244.0.0/24 dev flannel.1 proto kernel scope link src 10.244.0.1 10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink如果路由表里只剩部分条目或者对应flannel.1的路由指向了错误网关那说明租约信息不同步Flannel 无法正确建立节点间隧道。内核模块也需要确认。VXLAN 模式依赖vxlan模块检查方式lsmod | grep vxlan modinfo vxlan如果模块没有加载Flannel 启动时会报operation not permitted但这个问题在云主机上相对少见多为自建物理机或特定内核环境。3.4 检查容器运行时确认卡 Terminating 的根源这一节最接近“Terminating 为什么卡住”的答案。用crictl查看节点上的容器crictl ps -a | grep flannel你会看到类似输出CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID a1b2c3d4e5f6 rancher/flannel:v0.24.2 8h Running kube-flannel 1 ... f6e5d4c3b2a1 rancher/flannel:v0.24.2 8h Exited kube-flannel 0 ...注意两个容器共用同一个 Pod一个是新容器一个是旧容器。如果旧容器对应的 STATE 不是Exited而是Running说明 containerd 里容器进程还活着而 kubelet 已经放弃了等待。这种情况强制删除 Pod 意义不大真正要做的是去 containerd 里确认容器是否还在运行crictl inspect a1b2c3d4e5f6 | grep -A 20 state如果镜像层挂载无法卸载crictl 删除也会卡住这类问题通常和 containerd 的元数据损坏、磁盘空间不足有关系。检查一下节点磁盘df -h一个很常见但容易被忽略的事实如果/var/lib/containerd所在分区满了任何需要写元数据的容器操作都会卡在 IO 等待上表现为容器删除超时。4. 修复过程从清残留到恢复 Ready4.1 清理卡住的容器与网络资源确认是容器残留导致的问题后分步骤清理。先处理 containerd 里残留的容器crictl stop a1b2c3d4e5f6 crictl rm a1b2c3d4e5f6如果crictl stop也超时可以考虑直接查看容器进程发送 SIGKILLcrictl inspect a1b2c3d4e5f6 | grep pid kill -9 pid然后等待 containerd 回收容器。这种操作在紧急处理时是有效的但如果你发现 kill 之后容器仍然存在说明 containerd 的元数据已经不一致需要考虑重启 containerd。清理完容器后接下来是网络资源。如果旧的flannel.1接口状态异常删除后由 Flannel 重建ip link delete flannel.1删除接口会影响节点上所有使用该接口的 Pod 网络所以这一步建议在业务低峰期做并且要确保你已经知道这个节点上 Pod 会全部被重建。接着清理残留路由ip route flush dev flannel.1还要检查一下 CNI 配置目录是否需要清理其他插件残留ls -la /etc/cni/net.d/如果有其他插件的配置文件应该先备份再移除到其他目录确保 Flannel 的配置文件成为唯一可用的 CNI 配置。4.2 强制删除异常 Pod让 DaemonSet 重新调度清理完容器和网络资源后终于可以强制删除那个卡住的TerminatingPod 了kubectl delete pod -n kube-system kube-flannel-ds-amd64-8f2n4 --force --grace-period0这里解释一下为什么要加--force --grace-period0正常删除会等待 30 秒左右的宽限期让容器优雅退出但对于已经卡死、kubelet 删不掉的容器来说宽限期没有意义强制删除只是让 API Server 侧立刻清理对象记录。真正的容器清理工作我们刚才已经在节点上用crictl完成了所以这里只是做一次“对齐”。删除旧 Pod 后DaemonSet 会在该节点上重新创建新 Pod。这时候不要急着认为所有问题都解决了先观察新的 Pod 是否能通过 CNI 创建网络沙箱。如果新 Pod 能进入Running状态说明之前的容器残留和网络资源是根因如果新 Pod 也失败问题出在 CNI 配置或内核模块等更底层的原因。4.3 重启 kubelet 并验证集群状态清理完节点上的网络残留后建议重启一次 kubelet让它的 Pod 网络缓存清掉systemctl restart kubelet重启 kubelet 会让它重新执行所有 Pod 的创建流程。对于已经在跑的 Podkubelet 不会把它们全部杀掉只是重新校验状态对于网络异常期间被判定失败的 Pod这里会重新尝试创建。如果 containerd 在之前也表现得不太正常比如频繁超时建议一并重启systemctl restart containerd但注意containerd 是容器运行时的核心重启它会让节点上所有容器中断影响面远大于重启 kubelet。所以这个操作一定要谨慎如果只是个别容器卡删除不建议重启 containerd如果节点上大量容器都处于异常状态再考虑重启。等待一两分钟后验证kubectl get nodes kubectl get pods -n kube-system | grep flannel正常结果应该是NAME STATUS ROLES AGE VERSION master1 Ready control-plane 127d v1.28.6 worker1 Ready none 127d v1.28.6 kube-flannel-ds-amd64-m6k9s 1/1 Running 0 6h节点状态变为Ready后还需要验证一下 Pod 网络是否真的恢复正常随便在一个 Pod 里ping另一个节点的 Pod IP确认 VXLAN 隧道是通的。如果只是节点状态 Ready 但 Pod 网络不通Flannel 的租约信息可能还有问题需要进一步排查。5. 复盘真正导致 NotReady 的根因与防复发5.1 一次典型故障的完整复盘我在某项目X的测试集群里遇到过一模一样的情况过程记录在这里作为一个典型的复盘案例。那台 master 节点起初一切正常某天晚上因为它所在宿主机的操作系统进行了一次内核升级机器重启了。重启之后 kubelet 和 containerd 都自动起来了但 Flannel Pod 一直起不来。我登录节点看到状态API Server 里的旧 Pod 卡在Terminating新 Pod 不断CrashLoopBackOff。翻日志后发现关键报错是failed to create vxlan interface: file exists原因在于节点重启后内核里那个与flannel.1重名的接口实际是 VXLAN 虚拟网卡没有被清掉但 Flannel 的 meta 信息里并没有记录这个接口存在于是启动时尝试创建同名接口直接失败。而旧 Pod 之所以一直Terminating是因为 containerd 里对应的容器进程在重启时没有退出一直占着那个网络接口。我当时没有直接强制删 Pod而是先在节点上执行ip link delete flannel.1然后crictl stop/crictl rm清理掉旧容器最后等待 DaemonSet 把新 Pod 调度起来。整个过程不到两分钟节点状态恢复Ready。这个案例说明一个核心问题Flannel 的状态和节点上的实际网络状态必须保持一致而节点重启往往打破这种一致Flannel 又没有足够强的自愈能力。5.2 如何避免同类问题再次发生经验上讲有几类措施可以显著降低 Flannel Pod 卡 Terminating 的概率。第一是节点维护流程要规范化。重启节点前先做一次平滑操作把该节点上的负载驱逐到其他节点然后停止 kubelet再重启操作系统。不要直接重启让 kubelet 和 containerd 一起断掉那样 Flannel 的清理流程没有任何机会执行残留是必然的。第二是初始化集群时务必确认 CNI 配置的唯一性和完整性。装完 Flannel 后检查/etc/cni/net.d/目录是否只有一个编号-flannel.conflist文件检查kube-controller-manager是否开启了--allocate-node-cidrs检查节点annotations里的flannel.alpha.coreos.com/public-ip和backend-type是否正确。这些细节点决定了 Flannel 后续能不能稳定运行。第三是监控要覆盖这一类异常。不只是看kubectl get nodes更要用类似 Prometheus 的方式持续跟踪kube_pod_container_status_waiting_reason、kube_node_status_condition这些指标。Flannel 状态在变成 Terminating 之前通常有配置异常或模块缺失做前兆能早一点知道就早一点处理。6. 常见问题速查表这里整理了一份排查速查表可以直接对照使用。现象可能原因快速确认方式对应处理Flannel Pod 一直 Terminating容器运行时删除超时容器残留crictl ps -a查看容器状态crictl stopcrictl rm清理残留容器Flannel Pod 反复 CrashLoopBackOffCNI 配置冲突或缺失ls /etc/cni/net.d/查看日志清理多余 CNI 配置保留有效配置Flannel Pod 报 subnet 错误节点 PodCIDR 未分配查看kubectl get node node -o yaml的podCIDR字段检查 kube-controller-manager 参数新 Pod 报 cni plugin not foundCNI 插件二进制缺失ls /opt/cni/bin/重新安装插件二进制到所有节点节点 NotReady 但 Flannel 日志正常flannel.1 接口或路由残留异常ip addr show flannel.1、ip routeip link delete flannel.1后重启 kubelet强制删除 Pod 后新 Pod 也失败更深层网络配置问题查看新 Pod 的 Sandbox 事件清理 CNI 配置文件、重启 containerd多节点中只有一个节点异常该节点本地网络或镜像缓存问题df -h、journalctl -u containerd检查磁盘、修复 containerd使用这个表的时候有个原则每一项都要先“快速确认”再“对应处理”不要在没确认的情况下直接执行修复尤其是删除接口和重启 containerd这类操作。关于 Flannel 的配置文件还有一个容易踩的坑如果集群里使用其他模板工具管理 Flannel 的 ConfigMap改完 ConfigMap 后要记得把 DaemonSet 里的 Pod 滚动重启一次否则老 Pod 用的还是旧配置。这个操作本身是安全的因为 DaemonSet 会逐个重建 Pod避免节点网络中断时间过长。最后再分享一个小技巧在把一堆命令发给自己的浏览器终端前先在出问题的节点上把所有诊断命令的输出存一份到文件里。我习惯的做法是{ echo flannel pods kubectl get pods -n kube-system -o wide | grep flannel echo describe kubectl describe pod -n kube-system flannel-pod-name echo flannel logs kubectl logs -n kube-system flannel-pod-name -c kube-flannel --tail200 echo network interfaces ip addr show flannel.1 echo routes ip route echo cni config ls -la /etc/cni/net.d/ echo kubelet logs journalctl -u kubelet --since 30 min ago --no-pager | tail -100 } flannel-debug-$(date %Y%m%d-%H%M%S).log这个文件既能留作事后复盘用也是你如果向其他同事求助时的“病历本”。我踩过几次坑后发现很多人拿到诊断信息就能秒定位但如果你只给一句“节点 NotReady”没人能帮到你。这套流程下来kube-flannel-ds-amd64 的 Terminating 和 master 节点的 NotReady 基本都能解决。核心是不要慌不要先删按着日志、容器、接口、配置的顺序一层层排查最后你会发现大多数情况下问题都出在那些不起眼却特别致命的细节上。

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询