凌晨的告警里最让人心跳加速的一类,是节点状态变成 NotReady。Pod 开始被标记驱逐,服务副本数在掉,而节点本身还能 ping 通、SSH 也进得去,看不出哪里坏了。这种情况下最容易犯的错,是一头扎进某个可疑进程里排查半小时,最后发现根因是磁盘写满导致容器运行时无法创建新的容器层。要把恢复时间压进半小时,顺序比工具重要:先划定故障边界,再按日志、监控、资源三条线交叉验证,最后才动手恢复。
先用三分钟划定边界,再决定往哪查
拿到告警的第一件事不是登机器,而是看这次异常涉及几个节点、分布在哪些可用区、是否集中在同一批机型或同一批新上线的节点。单节点异常大概率是本机资源、磁盘或内核层面的问题;同机架、同可用区的多节点同时抖动,更可能是网络或底层虚拟化;全集群节点批量 NotReady,那就要怀疑控制面组件、证书或 API 访问链路,这时候登任何一台工作节点都是浪费时间。
紧接着看节点对象本身携带的信息。kubectl get nodes 给出状态和最后一次心跳时间,kubectl describe node 里的 Conditions 和事件记录往往已经把方向写出来了:MemoryPressure 指向内存耗尽,DiskPressure 指向磁盘或 inode 不足,PIDPressure 指向进程数失控,而所有 Condition 都是 Unknown 则说明 kubelet 根本没能上报状态,问题在 kubelet 自身或它与 API Server 之间的链路上。心跳中断时间点也很关键,把它和变更记录、发布窗口、监控曲线的拐点对齐,常常能省掉大量盲查。
日志:按关键词分层筛,而不是从头翻
节点日志的量足以让人迷失,所以要按层次筛。先看 kubelet 的日志,再看容器运行时,最后看内核。这个顺序对应的是"编排层是否知道自己出了问题—运行时是否还能干活—操作系统是否已经在自救"。
筛日志时用三类关键词,效率最高。第一类是状态类,比如失败、超时、拒绝连接、无法连接 API Server 这类词,它们直接告诉你链路断在哪一段。第二类是资源类,比如内存不足、磁盘空间不足、无可用 inode、进程数超限,一旦命中基本可以直接跳到资源维度做确认。第三类是生命周期类,比如镜像拉取失败、沙箱创建失败、探针失败、Pod 被驱逐,它们区分的是"节点整体挂了"还是"某些工作负载在拖累节点"。
内核日志值得单独留意。系统层面的 OOM 处理记录会明确写出被终止的进程,这比事后猜测内存曲线可靠得多;文件系统被置为只读、块设备 I/O 报错、网络设备复位这类记录,则说明问题已经下沉到硬件或虚拟化层,继续在应用层排查不会有结果。看日志时始终带着时间窗口意识:只看故障时刻前后的一段,并且注意最早出现的异常而不是最刷屏的那条——刷屏的通常是连锁反应,最早那条才是入口。
监控:先看形状,再看数值
监控在这个阶段的作用不是给出精确数值,而是回答两个问题:异常是突变还是缓慢累积,是本节点独有还是集群共性。突变型曲线通常对应一次具体事件,比如发布、配置变更、宿主机迁移或某个任务被触发;缓慢爬升型曲线更像泄漏、日志堆积或缓存无节制增长,这类问题重启能缓解,但不重启就会复发。
判断顺序上建议从"节点是否还在正常工作"这类信号入手:节点心跳与 kubelet 状态是否连续、可分配资源是否被打满、负载与可用 CPU 是否严重失衡、内存是否已经吃掉缓存并开始触发回收、磁盘使用率与 I/O 等待是否同时上扬、网络是否出现丢包或连接数异常。多个指标同时拐弯的时刻,就是根因发生的时刻。
有两类容易误判的情况需要提醒。一是负载高但业务正常,这往往是 I/O 等待而非 CPU 计算瓶颈,盯着 CPU 使用率会走偏。二是内存使用率看起来很高但没有真实压力,需要区分缓存与实际占用,只有回收行为和 OOM 记录才能确认压力真的存在。同样地,把同机型、同角色的其他节点拉出来做横向对比,比纠结单节点的绝对数值更快得出结论。
资源:CPU、内存、磁盘各有固定排查路径
磁盘建议放在最前面查,因为它最常见、影响面最大,而且排查成本最低。按下面的顺序推进基本不会漏:
- 确认各挂载点使用率,重点关注根分区、容器运行时数据目录和日志目录;
- 使用率不高就查 inode,大量小文件耗尽 inode 的表现和空间写满几乎一样;
- 定位占用来源,通常集中在容器日志、镜像层、临时卷和残留的构建缓存;
- 检查文件系统是否已被挂载为只读,只读状态下任何清理动作都需要先处理挂载问题;
- 清理或扩容后,确认 kubelet 的相关 Condition 是否随之恢复。
内存的排查核心是"谁在吃"和"有没有触发终止"。先从节点整体占用定位到具体容器或进程,再回到内核日志确认是否发生过 OOM 终止。如果被终止的是业务容器,那更可能是资源限制设置不合理或存在泄漏;如果被终止的是系统组件或 kubelet,说明节点预留资源不足,需要在配置层面调整,而不是简单重启了事。
CPU 方面,先看压力是持续还是尖刺。持续打满时要区分是业务真实流量、失控的循环,还是被压缩的调度配额导致的排队。尖刺型抖动往往和定时任务、批处理、日志切割或备份窗口重合,把时间点和这些周期性动作对齐通常一查就中。另外别忘了进程数和文件描述符这两个隐形限制,它们耗尽的表象是"什么都创建不了",但资源曲线看起来一切正常。
恢复动作的取舍
定位到方向后,恢复动作的选择决定了这次故障最终算三十分钟还是三小时。原则是先止损再修复:确认根因在本节点后,第一步是把节点标记为不可调度,避免新的 Pod 继续被调度到一个不健康的节点上;第二步再决定是否驱逐存量负载,这一步要提前确认有状态服务和本地存储的影响。
修复手段按侵入性从低到高:清理可回收资源、重启异常的业务容器、重启节点组件、重启节点、最后才是隔离下线并交给底层排查。每往上一级都会丢掉一部分现场,所以在重启之前务必先把日志片段、进程快照和监控截图留下来,否则复发时又要从零开始。恢复后不要立刻取消不可调度标记,先观察一段时间的心跳、Condition 和关键指标,确认稳定再放回调度池。
最后留一份收尾动作清单,故障恢复后逐项过一遍:
- 现场证据是否已归档,包括异常时间窗口的日志与指标;
- 根因是配置、容量还是硬件,是否可能在其他同类节点上复现;
- 是否需要补上缺失的告警项,尤其是磁盘 inode、进程数、文件描述符这类容易被忽略的维度;
- 资源限制、节点预留和日志轮转策略是否需要调整;
- 本次的判断路径是否值得沉淀成排查手册,让下一个值班的人不必重新推导。
节点故障的排查很少需要高深技巧,难的是在告警刷屏时仍然按顺序走完边界判断、日志筛查、指标对比和资源确认这四步。把顺序固化下来,三十分钟内定位并恢复就不再靠运气。
发布者:jacky,转转请注明出处:https://kubiyun.com/archives/4497
评论列表(9条)
磁盘写满导致 NotReady 太真实了,差点就瞎查进程。
@RadiantFog:确实,一开始都容易往进程方向想,后来发现是磁盘才反应过来。
监控那段说得好,先看形状再看数值,这个思路很实用
inode 这项告警我们真没加,明天就补上
@星云漂流:这项太容易漏了,空间还剩一半但 inode 满了,看着一切正常。顺手把日志目录也一起加上?
重启前先留现场证据,这一步经常被忽略
@浪迹者:没错,先留好节点状态、日志和监控时间点,重启只能恢复服务,不能替我们保留根因线索。
先看是单节点还是整批节点,能少走很多弯路
@数据库探险家:先划清故障范围,后面才能判断是节点自身、网络,还是控制面的问题,排查效率确实会高很多。