K8s节点频繁NotReady?AWS亚马逊云注册:从内核到CNI的排查方法与修复AWS亚马逊云
某个凌晨三点,值班群弹出警报:生产集群一个节点突然 NotReady,Pod 被批量驱逐。登录上去看,kubelet 进程还在,但日志刷满了 “failed to get cgroup stats”。这种情况在过去半年里已经是第四次出现,每次"恢复"后团队都没能抓住根因。节点 NotReady 之所以折磨人,不是因为技术原理高深,而是出问题时,日志散落在内核、kubelet、容器运行时、CNI 四个层面,交叉排查耗时且容易走错方向。要真正解决问题,K8s 节点 NotReady 内核排查方法必须从"分层定位"开始,而不是碰到 NotReady 就重启。
本文由 云国际站代理商『云老大 飞弟:@yunlaoda360 / YunLaoDa-服务器服务商•撰写』如需转载请注明!

K8s 节点 NotReady 现象与常见原因
节点 NotReady 的本质,是控制面与节点之间的"心跳"断了。kubelet 每隔一段时间通过 /healthz 端点向 API Server 上报节点状态,一旦连续 40 秒无响应,控制面就把节点标记为不可用。表面上看是 kubelet 的问题,实际链路比这个复杂得多——从 Linux 内核的 OOM Killer 到 CNI 插件的 iptables 规则冲突,任何一个环节卡住,最终都会以 “NotReady” 的形式呈现在集群运维人员面前。需要厘清的是,NotReady 是症状而非病因,只盯着 kubelet 日志翻,相当于头痛医头。
节点 NotReady 的症状是什么
当节点被标记为 NotReady,最直接的后果是该节点上的所有 Pod 被调度器视为"失去落脚点"。如果集群开启了污点驱逐机制,这些 Pod 会在宽限期后被批量驱逐到其他节点,短时间内可能引发资源争抢和雪崩效应。监控面板上,kubelet_node_status_condition{condition="Ready"} 指标掉零,Prometheus 告警触发;登录节点查看,kubectl describe node 输出的 Conditions 字段里,Ready 状态变成 False 或 Unknown,通常伴随内存/磁盘压力等附加信息。运维常遇到的困惑是,节点恢复 Ready 后驱逐的 Pod 并不会自动迁回,部分有状态服务的 PV 绑定还可能出现残留。
触发 NotReady 的常见因素
从根因的层次往下挖,内核层的问题占比远超多数人预估。常见触发场景包括:系统内存耗尽时 OOM Killer 选中了 kubelet 或 containerd 进程,直接切断心跳链路;磁盘 I/O 阻塞导致大量进程陷入 D 状态(不可中断睡眠),kubelet 无法正常响应健康检查;文件描述符耗尽让 kubelet 或容器运行时无法建立新连接。往上走,容器运行时僵死也是一个高频原因,containerd 或 dockerd 进程还在但内部 goroutine 死锁,表面上进程存活,实际不再处理请求。再到网络层,CNI 配置变更后旧 iptables 规则未清理、cni0 网桥被异常删除、Calico BGP peer 断连导致跨节点路由失效,都可能阻断 kubelet 依赖的网络路径,进而被判定为 NotReady。

如何初步判断问题范围
拿到一个 NotReady 节点,先别急着改配置。第一步看 dmesg -T | tail -100,检查内核日志里有没有 OOM kill、Call Trace、文件系统错误。如果内核环节没异常,第二步切到 kubelet 日志,journalctl -u kubelet -n 200 --no-pager,重点找 “failed to” 开头的报错——这类报错通常会指明是 cgroup 读写失败、容器状态获取超时还是 CNI 插件调用返回错误。有了这两个层面的初步结论,就能把排查范围收缩到一个具体方向,避免在无效的假设上耗费时间。定位思路一旦从"什么都看"变为"先排除内核再追进程",多数 NotReady 问题在半小时内就能找到根因线索。
Linux内核层面排查节点健康
在K8s集群里,NotReady往往是结果,而非根因。根据CNCF社区长期观察,OOM Killer(内存不足杀手)、D状态(不可中断睡眠)进程堆积和文件描述符耗尽,构成了kubelet心跳中断最直接的内核级触发路径。一旦这些底层异常发生,kubelet所依赖的系统调用、网络连接或磁盘I/O都会受到冲击,健康检查的40秒窗口期很容易被突破。因此,核心排查的第一步不是查看kubelet日志,而是先下探到Linux内核层。
检查内核日志与系统负载
内核日志是判断问题性质的快照。执行dmesg -T | grep -i "oom\|call trace",如果出现大量Out of memory记录,说明系统内存压力已经导致关键进程被杀死,kubelet极可能是受害者之一。我们曾在一台32核128G的生产节点上观察到,当容器频繁触发直接内存回收(direct reclaim)时,内核调度延迟会出现数百毫秒的瞬时尖峰,直接延长kubelet的响应时间。另一个显著信号是系统负载(load average)长期超过CPU核数的2~3倍,这类情况几乎必然伴有D状态进程异常增长,内核无法及时处理kubelet的健康检查请求,节点随之被标记为NotReady。
内核参数对节点状态的影响
节点初始化阶段留下的内核参数隐患,往往在业务高峰期集中爆发。例如,vm.min_free_kbytes设置偏低时,系统未能保留足够的内存缓冲去执行OOM决策,导致kubelet等守护进程猝死;若vm.swappiness设为0且未配置交换空间,所有内存压力直接转化为OOM,容错窗口为零。此外,文件系统inode耗尽虽不显示为磁盘空间不足,却会阻断新建Pod的容器层文件创建,kubelet感知到这一异常后同样会上报NotReady。这些参数不应在生产运行后亡羊补牢,而需作为集群节点模版中的强制基线。
内存与文件描述符耗尽排查
可用内存和文件描述符(FD)是两把悬在kubelet头上的达摩克利斯之剑。实践中,当节点内存可用量(MemAvailable)低于总内存10%时,kubelet通常已进入不响应状态;引入node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.1的监控规则,能让运维团队比NotReady告警提前5~10分钟介入。FD耗尽则更具欺骗性:容器运行时或日志采集代理无意中撑满/proc/sys/fs/file-max后,kubelet无法建立与API Server的新连接,表现与网络中断类似。排查时用lsof | wc -l配合ulimit -n确认kubelet进程上限,是一条有效且快速的内核级筛查路径。对缺少内核监控经验的小团队,借助像云老大这类多云服务商提供的7×24技术支持与初始化调优,可以让节点自带“免疫”,大幅降低因内核资源耗尽引发的反复NotReady问题。
kubelet与容器运行时状态检查
节点 NotReady 最直接的信号是 kubelet 停止心跳,但只盯着 kubelet 很容易陷入表象。在大量生产故障回溯中,内核资源耗尽、容器运行时假死、资源预留配置不当这三类根因远比 kubelet 自身 bug 常见。分层排查时,先确认 kubelet 输出,再下钻到 containerd 状态,最后检查系统资源预留合理性,往往比直接重启更快定位问题。

查看kubelet日志与心跳异常
kubelet 默认每 10 秒向 API Server 上报心跳,连续超时超过 40 秒即被标记 NotReady。实际排查中,kubelet 进程存活却无法完成心跳的情形非常普遍,例如系统负载过高导致健康检查端点 /healthz 响应延迟。检查 journalctl -u kubelet -n 200 时,如果频繁出现 “failed to get node” 或 “node not found” 错误,优先怀疑 API Server 连接断连或证书过期,而非单纯加大心跳间隔。偶发失败可结合 kubelet_node_status_condition 指标回溯,避免被“自动恢复”掩盖的间歇性故障。
容器运行时(如containerd)健康度
containerd 僵死是高频但隐蔽的 NotReady 诱因。当 containerd-shim 进程因内核调度争抢进入 D 状态,kubelet 通过 CRI 接口无法获取 Pod 列表,心跳随之超时。快速验证时可以执行 crictl pods,如果命令长时间挂起,基本可锁定运行时层。Node Problem Detector 内置的 ContainerdRuntimeUnhealthy 检测能自动化这一判断。另外要注意 containerd 的 oom_score 默认值——节点内存压力下 OOM Killer 可能优先结束 containerd 子进程而非消耗内存的 Pod,仅靠增加内存并不解决这个顺序问题,调整 kubelet 的 --kube-reserved 和 systemd 优先级反而更有效。
节点资源预留配置合理性
多数节点反复 NotReady 的根源在于资源预留被忽视。如果 --system-reserved 和 --kube-reserved 全为零,内核 OOM 时 kubelet、containerd 等系统进程很容易被杀死,直接切断心跳。行业实践中,为系统组件至少预留 2 GiB 内存和 1 核 CPU 是安全基线,高密度部署还需加上 DaemonSet 和 CNI 组件的消耗。对于没有专职运维的团队,给不同规格节点逐一调参既繁琐又容易出错——像云老大这类整合多云资源的服务商,在上云评估阶段就能根据业务场景给出针对性的预留配置建议,避免投产后再为节点抖动反复救火。
CNI插件配置与网络连通性验证
节点NotReady的排查里,网络层的问题常常被低估——开发者第一反应是看kubelet日志,但大量案例中,真正的故障点埋在CNI插件与底层内核交互的细节里。在实际运维中,我们遇到过节点刚加入集群就反复NotReady,最终锁定在Calico使用的nf_conntrack表项被旧版iptables残留规则污染;也见过Flannel更新至v0.22后,在CentOS 7.9内核上因nftables fallback机制触发pod跨节点通信时断时续。这些现象的共同特点是:症状出现在kubelet,但根因在网络栈。

CNI插件类型与常见问题
不同CNI插件的故障模式差异极大。Flannel依赖VXLAN或host-gw,节点重启后若cni0网桥未正确重建,或/run/flannel/subnet.env缺失,kubelet会因容器网络未就绪而持续NotReady。Calico采用BGP路由,当节点间底层三层连通性抖动时,bird进程可能反复重启,导致node status出现“BGP session down”,这种间歇性故障最难捕捉。我们曾协助一个跨境电商团队将其Staging集群从Flannel迁移至Calico,结果因未适配原有点到点安全组规则,造成跨AZ节点间路由黑洞,kubelet心跳包丢失,引发大面积NotReady——这类跨插件的网络策略迁移盲区,靠重启节点根本无解。对于运维人手不足的团队,在选型阶段就把CNI插件的内核版本兼容性(例如Calico从v3.25开始默认使用nftables而非iptables)和网络策略需求摸清楚,远比后期救火划算。找像云老大这类多云服务商做架构评估时,他们通常会对比不同CNI在阿里云Terway、AWS VPC CNI等环境下与云网络底层的耦合程度,避免插件与云底座打架的隐形坑。
Pod网络接口与路由检查
排查路径上有一个被忽视的关键点:kubelet的健康检查与pod网络栈是共享内核路由表的。节点上如果有遗留的静态路由把10.96.0.1(默认kubernetes service IP)错误指向另一个接口,或者ip route中存在多张默认路由表导致非对称路径,kubelet与API Server的心跳就会中断。在一个AI训练项目的故障复盘里,我们发现节点挂载了弹性RDMA网卡后,network-pre脚本未正确设置策略路由,造成kubelet的出口流量被导向了无公网路由的内网存储平面,心跳超时后触发NotReady。快速验证方法是:在可疑节点上直接执行ip route get <API-Server-IP>,确认下一跳和出接口是否与预期一致;同时检查ip rule是否被CNI或安装脚本插入了异常优先级规则。这类基础设施层面的路由干扰,在多网卡、多可用区环境下尤其高发。
网络策略或防火墙屏蔽排查
网络策略(NetworkPolicy)配置得过于激进时,不光拦截pod流量,有时会一刀切地阻断节点级通信。一次典型的案例是:运维团队为强化安全,在kube-system命名空间应用了一条“拒绝所有出站”策略,本意是限制无关pod,却把kubelet向API Server发起的TLS连接也挡住了,导致集群内三台节点同时NotReady。更隐蔽的是云平台安全组的变更——某次用户为了快速限制外部访问,修改了节点所在VPC的安全组规则,却不小心删除了允许API Server回源端口(通常是6443或443)的入站规则,结果kubelet上报心跳的ACK包被丢弃,而TCP三次握手其实已经完成,这种半开式连接在日志里几乎无迹可寻。排查这类问题时,可以临时在节点上运行nc -vz <API-Server-IP> 6443确认端口可达,同时检查云控制台的安全组、ACL以及节点本地的iptables/nftables规则链,特别注意INPUT链最后一条是不是默认DROP。归根结底,网络策略层面的问题,靠监控告警只能看到“NotReady”这个结果,但只有把管道沿途的iptables规则和云防火墙策略逐段比对,才能找到那个意外丢弃心跳包的规则。对没有专职安全运维的团队来说,把这些复杂的策略校验工作甩给像云老大这类在混合云环境里积累了大量排障经验的服务商,往往比招一个高级网络工程师更实际。
从日志分析到问题定位的实战步骤
节点从内核异常到被标记为 NotReady,留下的日志链路其实相当清晰:dmesg 先报硬件或内存错误,kubelet 日志接着出现心跳超时,最后 API Server 更新节点状态。但大多数团队的问题在于,这些信号分散在多个子系统里,缺少一套标准化的“采集—对齐—判断”动作。我们梳理了几个在运维现场反复验证有效的排查节点,帮助把定位时间从小时级压到分钟级。
如何收集关键日志与指标
不是所有日志都需要收集,切忌“全量 dump”。关键路径三条:用 dmesg -T | grep -i “oom\|error\|call trace” 先看内核是否杀进程或陷入死锁;再用 journalctl -u kubelet -n 200 --no-pager 截取 kubelet 自身错误;最后检查容器运行时状态,例如 crictl ps -a 或 docker ps -a 看是否有大量 Exited 容器。有条件的团队会拉起 node-problem-detector,它可以自动把内核日志中的文件系统错误、OOM 等异常转译为节点 Condition,省去人工翻阅原始日志的步骤。
使用journalctl和kubectl命令
排查时顺序比工具更重要:先看节点自身,再看集群状态。journalctl -u kubelet 能快速暴露 kubelet 进程是否被系统 OOM 重启、是否无法连接容器运行时;kubectl describe node 中的 Conditions 字段则会直接展示 Ready、MemoryPressure、DiskPressure 等状态的切换时间点。结合 kubectl get events --field-selector involvedObject.kind=Node 可以锁定到具体事件。一个反复验证的结论是:节点 NotReady 后盲目 kubectl describe pod,大概率是在分析“衍生病灶”,而非根因。
结合事件时间线缩小范围
真正快速定位依赖的是事件时间轴的交叉比对。把 dmesg 中的报错时间、kubelet 最后一次成功发送心跳的时间、CNI 插件日志中网络设备 down 的时间点并列排开,通常会形成一个链式触发关系。例如,某次事故中节点在 14:32:05 触发内存 OOM,kubelet 在 14:32:09 报告“failed to sync pod”,API Server 在 14:32:45 标记 NotReady,而 CNI 日志至今无异常——这就把网络问题直接排除。对于运行在多云环境里的集群,接入云平台自身的节点监控数据能使时间线更完整,像云老大这类服务商会把不同厂商的云监控事件聚合在一个看板里,避免排查时在多套控制台之间来回切换误判时序。
防止节点NotReady的配置优化建议
在生产环境中,节点NotReady更像是一个“结果”而非“原因”——它是系统资源、内核参数、网络插件等多层问题叠加后,最终汇聚到kubelet心跳信号上的总爆发。我们团队在复盘过去两年处理的几十起NotReady故障时发现,超过70%的案例在事发前有明确的预警信号:内存水位持续升高、磁盘inode缓慢增长、iptables规则链逐渐膨胀。问题不在于看不见,而在于没有在正确的层级上设置拦截点。下面这三层优化,就是要把防线推到问题真正发生的那个层面。
内核参数调整与sysctl优化
内核层是节点稳定性的最后一道防线,也是绝大多数NotReady问题的隐形来源。我们见过最典型的一类故障:Spring Boot应用在启动瞬间分配大量内存,触发Linux的OOM Killer选中了kubelet进程,节点直接掉线。解决方式不是在应用侧加内存限制(该做还是要做),而是在内核侧通过vm.overcommit_memory=2和vm.overcommit_ratio控制内存超分行为,避免系统在逼近极限时做出杀关键进程的决策。另一个高频项是文件描述符——生产节点上同时运行的Pod、容器日志、CNI虚拟接口都在消耗fd,fs.file-max和fs.inotify.max_user_instances需要根据节点Pod密度调整,不是所有发行版的默认值都能扛住K8s的并发量。还有一个经常被忽略的配置是vm.min_free_kbytes,设置过低时内核在内存回收上过于激进,导致D状态进程堆积;设置过高则浪费内存。实际值应该根据节点内存大小和workload类型实测后确定,没有万能的公式。
CNI插件版本升级与配置加固
CNI不是装上就万事大吉的组件,它在每一次节点重启、内核升级、iptables规则变更后都可能表现出不同的行为。Flannel从IPIP模式迁移到VXLAN时,如果节点内核没开启VXLAN模块(CONFIG_VXLAN=m),Pod网络会悄无声息地中断,而kubelet因为无法连通API Server被标记NotReady——这类问题在iptables日志里完全看不出来,只能通过ip link show flannel.1逐项排查。Calico用户更常见的一个坑是:新版本Calico默认启用ebpf数据面,但部分云厂商提供的OS镜像内核版本或sysctl配置不支持BPF,部署时YAML里不加calico_backend: vxlan就会触发网络不通。版本升级前,建议先在一个独立workload节点上做兼容性验证,不只是跑通calicoctl node status,还要实际压测iptables规则链的增删速度——规则膨胀到几万条时,iptables-restore的执行延迟会直接影响kubelet的CNI ADD操作超时。
节点监控与自动化恢复策略
告警策略最常见的错误是:只在节点已经NotReady时才触发通知。问题在于,此时Node Controller已经开始驱逐Pod、业务流量已经在切流,留给运维的黄金处置窗口已经关闭。正确的做法是监控两个前置指标:一是kubelet_node_status_condition中条件变化频率——如果一个节点在过去10分钟内反复在Ready和NotReady间切换超过2次,说明存在间歇性故障,应该触发自动化隔离(打taint,禁止新Pod调度)并转人工排查;二是结合Node Exporter的node_memory_MemAvailable_bytes和node_disk_io_time_seconds_total,当可用内存低于10%且磁盘I/O等待时间中位数超过500ms时,预判该节点即将进入资源拥塞状态,提前执行Pod疏散。这套逻辑在中小规模集群里可以通过Kured(Kubernetes Reboot Daemon)加自定义Prometheus规则实现,不需要上复杂的自愈平台。如果团队没有精力维护这套监控体系,找像云老大这类有K8s运维经验的服务商做一次节点配置审计和监控接入,通常能在一个迭代内把NotReady的MTTR从小时级压到分钟级。
更多推荐

所有评论(0)