Kubernetes节点NotReady深度排查:从文件系统异常到CNI插件故障链分析
当你发现Kubernetes集群中的节点突然集体罢工,状态全部显示为NotReady时,那种感觉就像走进了一个没有出口的迷宫。最近我在维护一个生产环境集群时就遇到了这种情况——四个节点同时罢工,表面报错是invalid capacity 0 on image filesystem,但深入排查后发现这仅仅是冰山一角。本文将带你完整还原这次故障排查的全过程,揭示Kubernetes组件间那些不为人知的依赖关系。
1. 故障现象与初步诊断
那天早上,监控系统突然报警,显示集群所有节点状态变为NotReady。使用kubectl get nodes命令确认后,输出如下:
NAME STATUS ROLES AGE VERSION sealos-k8s-node-01 NotReady control-plane,master 4m5s v1.22.0 sealos-k8s-node-02 NotReady <none> 2m39s v1.22.0 sealos-k8s-node-03 NotReady <none> 2m40s v1.22.0 sealos-k8s-node-04 NotReady control-plane,master 3m33s v1.22.0查看kubelet日志发现了两个看似不相关的错误:
journalctl -xe -u kubelet | grep -i error输出显示:
May 18 13:47:56 node-04 kubelet[5038]: E0518 13:47:56.386059 5038 kubelet.go:2332] "Container runtime network not ready" networkReady="NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized" May 18 13:47:56 node-04 kubelet[5038]: E0518 13:47:56.386123 5038 kubelet.go:1303] "Image garbage collection failed once. Stats initialization may not have completed yet" err="invalid capacity 0 on image filesystem"这两个错误中,invalid capacity 0 on image filesystem看起来像是存储问题,而cni plugin not initialized则明显是网络问题。它们之间是否存在关联?这成为了我们排查的第一个关键点。
2. 镜像文件系统容量异常分析
invalid capacity 0 on image filesystem这个错误通常意味着kubelet无法正确获取节点上容器镜像文件系统的容量信息。这会影响kubelet的垃圾回收功能,但理论上不应该直接导致节点NotReady。让我们深入分析可能的原因:
2.1 可能的原因排查
文件系统挂载问题:
- 检查
/var/lib/kubelet和/var/lib/containerd的挂载状态 - 使用
df -h查看相关挂载点的可用空间
- 检查
containerd服务异常:
- containerd负责管理容器镜像,如果它出现问题,kubelet无法获取镜像信息
- 检查containerd状态:
systemctl status containerd
磁盘inode耗尽:
- 使用
df -i检查inode使用情况 - 容器环境容易产生大量小文件,导致inode耗尽
- 使用
SELinux或AppArmor限制:
- 检查安全模块是否阻止了kubelet访问文件系统
- 查看
/var/log/audit/audit.log获取相关拒绝记录
2.2 诊断命令与预期输出
执行以下命令收集信息:
# 检查文件系统挂载 mount | grep -E 'kubelet|containerd' # 检查磁盘空间 df -h /var/lib/kubelet /var/lib/containerd # 检查containerd状态 containerd config dump | grep -A 5 "\[plugins.\"io.containerd.grpc.v1.cri\".image\]" # 检查inode使用 df -i /var在本次故障中,这些检查都没有发现明显异常,containerd服务也显示为运行状态。这提示我们需要更深入地分析kubelet与containerd的交互。
3. CNI插件初始化失败的根本原因
cni plugin not initialized这个错误表明Kubernetes的网络插件未能正确启动。CNI(Container Network Interface)插件负责为Pod配置网络,如果它失败,节点上的所有Pod都将无法正常工作,导致节点被标记为NotReady。
3.1 CNI插件依赖链分析
CNI插件的正常工作依赖于以下几个关键组件:
- 容器运行时(containerd/docker):提供基础的容器运行环境
- kubelet:协调容器生命周期管理
- 网络插件二进制和配置文件:如Calico、Flannel等的可执行文件和配置文件
- 网络命名空间:内核级别的网络隔离机制
这些组件之间的关系可以用下表表示:
| 组件 | 功能 | 依赖项 | 故障表现 |
|---|---|---|---|
| containerd | 容器运行时 | 内核cgroup支持 | 容器无法创建 |
| kubelet | 节点代理 | containerd API | Pod调度失败 |
| CNI插件 | 网络配置 | containerd socket | 网络不通 |
| kube-proxy | 服务代理 | CNI网络 | 服务无法访问 |
3.2 关键诊断步骤
检查CNI配置文件:
ls -l /etc/cni/net.d/ cat /etc/cni/net.d/10-calico.conflist验证CNI插件二进制文件:
ls -l /opt/cni/bin/检查kubelet网络配置:
ps -ef | grep kubelet | grep --color=auto network-plugin查看containerd日志:
journalctl -u containerd --since "1 hour ago" | grep -i cni
在本次故障中,所有这些检查都显示配置正确,但CNI插件仍然报告未初始化。这提示我们可能需要关注containerd的内部状态。
4. containerd重启的"神奇"效果
在多次排查无果后,尝试了一个简单的操作:重启containerd服务。令人惊讶的是,这个操作不仅解决了CNI插件初始化问题,连invalid capacity 0 on image filesystem错误也一并消失了。
systemctl restart containerd几分钟后,所有节点状态恢复正常:
NAME STATUS ROLES AGE VERSION sealos-k8s-node-01 Ready control-plane,master 50m v1.22.0 sealos-k8s-node-02 Ready <none> 49m v1.22.0 sealos-k8s-node-03 Ready <none> 49m v1.22.0 sealos-k8s-node-04 Ready control-plane,master 50m v1.22.04.1 为什么重启containerd能解决问题?
containerd作为容器运行时,维护着多个内部状态和连接:
- gRPC连接状态:kubelet通过gRPC与containerd通信,长时间运行可能出现连接问题
- 文件系统挂载缓存:containerd缓存了文件系统信息,可能导致容量报告不准确
- 插件管理状态:CNI插件通过containerd注册,状态异常会影响网络功能
当containerd运行时间过长或遇到某些内部错误时,这些状态可能会变得不一致。重启服务会重置所有这些状态,通常能解决一些难以定位的"幽灵"问题。
4.2 更优雅的解决方案
虽然重启containerd可以快速解决问题,但在生产环境中,我们可能需要更优雅的解决方案:
配置containerd自动恢复:
# 在/etc/containerd/config.toml中添加 [plugins."io.containerd.grpc.v1.cri"] enable_selinux = false sandbox_image = "registry.k8s.io/pause:3.6" [plugins."io.containerd.grpc.v1.cri".containerd] snapshotter = "overlayfs" disable_snapshot_annotations = true设置kubelet健康检查:
# 在kubelet配置中添加 --healthz-port=10248 --healthz-bind-address=0.0.0.0监控关键指标:
- containerd的goroutine数量
- gRPC调用延迟
- 文件系统操作错误计数
5. 系统性故障排查框架
基于这次经验,我总结了一个Kubernetes节点NotReady问题的系统性排查框架:
5.1 排查流程图
- 确认节点状态:
kubectl get nodes -o wide - 检查kubelet日志:
journalctl -u kubelet -n 100 -f - 验证容器运行时:
crictl ps和systemctl status containerd - 检查网络插件:
ip link show和kubectl get pods -n kube-system - 查看资源使用:
top和df -h - 检查内核日志:
dmesg -T | tail -n 50
5.2 常见错误模式对照表
| 错误现象 | 可能原因 | 验证方法 | 解决方案 |
|---|---|---|---|
| CNI插件未初始化 | containerd状态异常 | containerd日志 | 重启containerd |
| 镜像容量为0 | 文件系统挂载问题 | mount命令 | 重新挂载或重启 |
| 节点频繁NotReady | 内存压力 | free -m | 调整kubelet资源预留 |
| Pod网络不通 | 网络策略冲突 | calicoctl get networkpolicy | 调整网络策略 |
5.3 预防性维护建议
为了避免类似问题再次发生,可以考虑以下预防措施:
定期滚动重启节点组件:
# 使用kubectl drain安全排空节点 kubectl drain <node-name> --ignore-daemonsets systemctl restart kubelet containerd kubectl uncordon <node-name>配置资源监控和告警:
- 监控containerd的内存使用和goroutine数量
- 设置kubelet健康检查端点监控
保持版本兼容性:
- 确保kubelet、containerd和CNI插件版本兼容
- 参考Kubernetes官方发布的版本兼容性矩阵
那次故障后,我在所有集群节点上设置了containerd的定期健康检查,并调整了kubelet的资源预留参数。半年过去了,再没遇到过类似的全节点NotReady情况。有时候,最简单的解决方案背后隐藏着最复杂的系统交互,这就是Kubernetes运维的迷人之处——你永远在学习和发现新的问题解决模式。