Kubernetes 节点 NotReady 排障:从 kubelet 到 CNI - 夜莺博客

Kubernetes 节点 NotReady 排障:从 kubelet 到 CNI

节点变成 NotReady 时,最常见的错误做法是直接重启 kubelet 或重启节点——如果根因是磁盘满、CNI 插件异常或证书过期,重启只会让问题下次以更严重的形式出现。正确的做法是按层排查:先看节点上报告的条件(Condition),再看 kubelet 日志,然后依次验证容器运行时、CNI 网络和系统资源。本文给出这条排查链的每一条命令和判断标准。

第一层:看清节点在抱怨什么

kubectl get nodes -o wide
kubectl describe node <node-name>
kubectl get node <node-name> -o jsonpath='{range .status.conditions[*]}{.type}={.status} {.reason}{"\n"}{end}'

Node 的 conditions 直接告诉你根因方向:

Condition 含义 下一步
Ready=False kubelet 心跳超时或自身不健康 看 kubelet 服务与日志
MemoryPressure=True 节点内存不足,开始驱逐 Pod 查 kubelet eviction 记录与内存占用大户
DiskPressure=True 镜像或日志目录接近阈值 清理镜像、日志、容器可写层
PIDPressure=True 进程数接近上限 查泄漏进程与 kernel.pid_max
NetworkUnavailable=True CNI 未就绪 检查 CNI DaemonSet 与路由

第二层:kubelet 本身

systemctl status kubelet
systemctl restart kubelet          # 仅在确认配置问题后
journalctl -u kubelet -n 200 --no-pager | grep -Ei "error|failed|expired|cni|runtime"

三类高频日志:

  • 证书过期x509: certificate has expired。kubelet 客户端证书与 API Server 的 CA 都有有效期,过期后节点直接 NotReady,且不会自动轮换(需要 kubelet 配置了证书轮换,或手工用 kubeadm 重新签发)。
  • 运行时连接失败failed to get container runtime status,说明 containerd/CRI-O 挂了或 socket 路径变了。
  • CNI 报错network plugin is not ready / failed to set up sandbox,指向下一层。

第三层:容器运行时

systemctl status containerd
crictl info
crictl ps -a | head
crictl logs <container-id>
crictl rmi --prune

运行时重启后常见的连锁问题:旧的 Pod sandbox 状态残留、镜像层损坏、磁盘被镜像占满。先清理再观察,而不是直接删节点。

第四层:CNI 与网络

kubectl -n kube-system get pods -o wide | grep -Ei "calico|cilium|flannel|kube-proxy"
kubectl -n kube-system logs ds/calico-node --tail=100
ip -br addr                      # 看 CNI 接口(如 cali*、cni0、veth*)是否存在
ip route | grep -E "10\.244|172\.16"   # Pod CIDR 路由是否在
conntrack -C                     # 连接跟踪表是否接近上限

Pod CIDR 路由缺失、CNI DaemonSet 在某节点 CrashLoopBackOff、conntrack 表满,这三件事都会让"节点 Ready 但 Pod 全部不通"。注意区分:节点 NotReady 属于 kubelet/节点层,Pod 不通属于网络层,两者现象不同但经常同时出现。

第五层:系统资源

df -h / /var/lib/containerd /var/log
du -sh /var/log/pods 2>/dev/null
free -m
sysctl kernel.pid_max
systemctl status systemd-journald

磁盘是最常见的根因。容器镜像层、Pod 日志、以及 /var/lib/containerd 下的孤儿快照都会持续增长。清理顺序:先 crictl rmi --prune,再清 Pod 日志,最后才考虑调整 kubelet 的 eviction-hard 阈值——放宽阈值只是把问题往后推。

快速恢复流程

  1. kubectl describe node 确认是哪种 condition 触发。
  2. 磁盘类问题:清理镜像与日志,确认 df 回落,等待 kubelet 自动恢复(通常几十秒)。
  3. 证书类问题:确认 kubelet 证书有效期,按集群方式重新签发并重启 kubelet。
  4. CNI 类问题:重启对应 DaemonSet 的 Pod,检查节点路由与接口是否重建。
  5. 运行时类问题:重启 containerd,确认 crictl info 正常后再重启 kubelet。
  6. 全部恢复正常后,kubectl get pods -A -o wide | grep <node> 确认原有 Pod 已重新调度或回到 Running。

预防措施

  • 对节点 condition、kubelet 存活状态、磁盘使用率设置监控告警,阈值设在驱逐阈值之前。
  • 统一容器日志轮转策略(containerLogMaxSize / containerLogMaxFiles),避免日志撑满磁盘。
  • 提前监控证书有效期,在到期前 30 天告警。
  • 定期巡检 CNI 与 kube-proxy 的 DaemonSet 是否在所有节点上都有 Running 实例。

常见根因与对应处置

根因 判据 处置
磁盘使用率超过驱逐阈值 DiskPressure=True,kubelet 日志中频繁出现 image GC 清理镜像与日志,检查是否有容器写满可写层
kubelet 证书过期 日志出现 x509 过期,kubectl get csr 无新请求 重新签发证书并启用自动轮换
容器运行时无响应 crictl info 超时,containerd 服务重启过 重启 containerd,确认 socket 与状态恢复
CNI 插件异常 Pod 卡在 ContainerCreating,CNI 日志报 sandbox 失败 重启 CNI Pod,检查节点路由与接口
连接跟踪表满 conntrack -C 接近 nf_conntrack_max,日志报 table full 调大上限与超时,排查异常连接来源
内存耗尽触发 OOM dmesg 中有 OOM killer 记录 定位超用 Pod,设置合理的 requests/limits
时间漂移导致心跳失败 节点时间与 API Server 相差过大 修复 NTP/chrony,确认时间同步后再观察

时间同步这一项经常被忽略:证书校验、令牌有效期和审计日志都与时钟相关,节点时间偏离几分钟就可能导致认证失败或控制面异常。排查任何"莫名的认证问题"之前,先确认 timedatectl 显示同步正常。

集群层面的排查动作

kubectl get nodes -o wide
kubectl get events -A --sort-by=.lastTimestamp | tail -30
kubectl -n kube-system get pods -o wide | grep -v Running
kubectl describe node <node-name> | sed -n '/Conditions:/,/Addresses:/p'
kubectl top nodes
kubectl top pods -A --sort-by=memory | head -20

三类事件值得优先关注:NodeNotReadyEvictedFailedScheduling。它们分别对应节点层、资源层和调度层的问题,串起来看能快速定位是单点故障还是集群性的容量问题。

预防与巡检清单

  • 节点条件、kubelet 存活、磁盘与内存使用率纳入监控,阈值设在驱逐阈值之前。
  • 容器日志轮转显式配置,避免日志撑满节点磁盘。
  • 证书到期时间纳入监控,提前 30 天告警。
  • 每月巡检一次 CNI 与 kube-proxy 的 DaemonSet 覆盖情况,确认所有节点都有 Running 实例。
  • 节点内核参数(pid_maxnf_conntrack_maxvm.max_map_count)统一基线化管理。
  • 把本文的排查命令整理成节点的应急手册,放在运维平台上随手可取的位置。

最后一点经验:NotReady 的处置目标不是"让节点变回 Ready",而是"让业务恢复并且知道为什么"。若根因是硬件或内核层面的不稳定,正确的做法是把节点标记为不可调度、驱逐负载,再离线诊断,而不是反复重启把它暂时拉回来。

相关文章

集群网络策略的常见起点见 Kubernetes NetworkPolicy 默认拒绝模式,CNI 选型对比见 Calico、Cilium、Flannel 对比,kube-proxy 转发模式(iptables/IPVS/nftables)的选择见 kube-proxy 三种模式对比

原文链接:https://kubernetes.io/docs/tasks/debug/debug-cluster/