尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Kubernetes节点NotReady根因:CNI配置未初始化详解
1. 问题现场还原K8S节点卡在 NotReadyCNI 配置未初始化的典型症状刚接手一个某高校实验室搭建的轻量级教学集群三台物理机部署了 Kubernetes v1.13.12这个版本虽已归档但在教学环境和老旧硬件上仍有大量存量使用控制平面跑在 master 节点两个 worker 节点分别命名为 node-01 和 node-02。集群初始化用的是 kubeadm流程走完后kubectl get nodes的输出却让人皱眉NAME STATUS ROLES AGE VERSION master Ready master 15m v1.13.12 node-01 NotReady none 12m v1.13.12 node-02 NotReady none 10m v1.13.12Status 列清一色的 NotReady但奇怪的是kubectl describe node node-01的 Events 区域一片空白没有报错、没有 Warning连 Pod 调度失败的提示都没有——这比报错更棘手。进一步查日志journalctl -u kubelet -n 100 --no-pager | grep -i cni\|network瞬间锁定了关键线索kubelet[12345]: E0521 14:22:37.102198 12345 cni.go:203] Unable to update cni config: No networks found in /etc/cni/net.d/ kubelet[12345]: E0521 14:22:37.102215 12345 kubelet.go:2192] Container runtime network not ready: NetworkReadyfalse reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized核心错误就这一句“cni config uninitialized”。它不是说 CNI 插件没装也不是说插件崩溃了而是 kubelet 根本没在约定路径下找到任何有效的网络配置文件。这就像你给快递员留了地址但他翻遍整个通讯录都找不到你家门牌号——不是快递员不干活是根本没拿到“投递单”。v1.13 是一个承上启下的关键版本它正式将 CNI 作为默认网络插件接口但尚未引入 v1.15 中的--cni-bin-dir和--cni-conf-dir这类显式参数这些参数在 v1.13 中还只是实验性标志。因此kubelet 对 CNI 的依赖是硬编码的“信仰”它只认/opt/cni/bin/下的二进制和/etc/cni/net.d/下的.conf或.conflist文件。只要这个目录空着或者里面只有个空文件、格式错误的 JSONkubelet 就会坚定地认为“网络插件未就绪”并把节点状态钉死在 NotReady。我试过直接重启 kubelet也试过kubeadm reset kubeadm init重来结果一样。因为问题不在 kubelet 本身也不在 kubeadm 初始化逻辑里而在于一个被绝大多数新手忽略的“前置动作”CNI 插件的二进制和配置从来就不是 kubeadm 自动安装的。它只负责生成证书、启动组件、写入/etc/kubernetes/manifests/但/etc/cni/net.d/这个目录从始至终都是空的。这就像盖楼时地基kubelet和框架apiserver都搭好了但水电图纸CNI 配置和施工队CNI 二进制压根没进场。提示v1.13 的 kubelet 日志非常“诚实”它不会报“calico 启动失败”或“flannel 无法连接 etcd”它只会冰冷地告诉你“cni config uninitialized”。这意味着排查路径必须从最底层的文件系统开始而不是一头扎进某个具体 CNI 插件的日志里。2. 根因深挖为什么 /etc/cni/net.d/ 目录会是空的三个被掩盖的真相这个问题看似简单实则背后藏着三个相互交织、但常被文档一笔带过的真相。很多教程只说“装个 flannel 就行”却从不解释“装”的具体动作到底是什么导致读者在 v1.13 这种老版本上反复踩坑。2.1 真相一kubeadm 不负责分发 CNI 二进制只负责“调用”这是最根本的认知偏差。翻阅 kubeadm v1.13 的官方源码cmd/kubeadm/app/phases/bootstraptoken/node/join.go你会发现它在 join 流程中对 CNI 的处理仅限于检查/etc/cni/net.d/是否存在并验证其中是否有合法配置。它完全不包含下载、解压、校验或安装任何 CNI 插件二进制的逻辑。它的设计哲学是“职责分离”kubeadm 只管集群编排网络是独立插件的事。所以当你执行kubeadm join ...时kubelet 启动后立刻去/etc/cni/net.d/找配置而这个目录在全新节点上就是个空壳。此时 kubelet 的行为是每隔几秒轮询一次该目录一旦发现有合法配置就加载并上报 Ready如果一直为空就持续报错并维持 NotReady。这个机制本身没有问题问题在于用户误以为“kubeadm init/join 集群开箱即用”。2.2 真相二CNI 配置文件不是“自动生成”的而是需要手动或脚本注入CNI 规范要求每个网络插件提供一个 JSON 格式的配置文件如10-flannel.conflist里面定义了插件类型type: flannel、后端模式backend: vxlan、etcd 地址、网段等关键参数。这个文件不能由 kubelet 生成它必须由运维人员根据集群实际拓扑比如 master 的 IP、etcd 的监听地址、期望的 Pod 网段精确编写然后放到/etc/cni/net.d/下。以 Flannel 为例其官方提供的kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml这个 YAML本质是一个 DaemonSet它会在每个节点上启动一个 flanneld 容器并通过 volumeMount 将宿主机的/run/flannel/和/etc/cni/net.d/挂载进去。flanneld 容器启动后会读取自己的配置计算出本节点的子网然后主动创建/etc/cni/net.d/10-flannel.conflist这个文件。也就是说“配置文件的诞生”是 flanneld 这个进程的行为不是 kubeadm 或 kubelet 的行为。但在 v1.13 上这个过程有个致命前提flanneld 容器必须能成功启动。而它启动的前提是宿主机上必须有/opt/cni/bin/目录下的flannel、bridge、host-local等二进制。如果这个目录不存在flanneld 容器会因找不到host-local插件而 crashloop进而永远无法生成配置文件——形成一个死循环。2.3 真相三/opt/cni/bin/ 目录的缺失是“CNI 二进制分发”环节的彻底缺席这才是整个链条上最常被跳过的环节。CNI 插件不是单一程序而是一套工具集。flannel二进制负责管理网络但它需要bridge来创建网桥需要host-local来分配 IP需要loopback来配置本地回环。这些二进制必须全部放在/opt/cni/bin/下且具有可执行权限chmod xkubelet 才能在调用时找到它们。v1.13 的安装文档如 kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/在“Installing a pod network add-on”章节通常只给出一句“Choose a CNI provider and install it.” 然后附上 Calico/Flannel 的 YAML 链接。它默认你已经知道或者应该自己去 CNI 项目官网下载cni-plugins的 tarball 并手动解压。这个“手动”步骤在自动化脚本盛行的今天成了最大的知识断层。我曾看到某次线上故障复盘报告里写道“工程师执行了 kubeadm join节点 NotReady他花了 3 小时排查 etcd、apiserver、证书最后发现/opt/cni/bin/目录下只有 2 个文件而标准 CNI 插件包要求至少 12 个。” 这不是能力问题是信息不对称造成的必然结果。注意/opt/cni/bin/和/etc/cni/net.d/是 kubelet 的“刚需”。前者是“工具箱”后者是“施工图纸”。缺一不可。任何试图绕过这两个目录、用其他路径替代的方案比如修改 kubelet 启动参数在 v1.13 上都是高风险操作因为该版本的 kubelet 编译时硬编码了这些路径。3. 实操四步法从零开始让 NotReady 节点真正 Ready基于上述根因分析解决 “cni config uninitialized” 的唯一正解就是按顺序补全这四个物理存在的环节。这不是一个命令就能搞定的魔法而是一套必须亲手完成的“基础设施交付”。3.1 第一步确认并安装 CNI 二进制到 /opt/cni/bin/这是整个链条的起点。必须确保/opt/cni/bin/目录存在且里面包含了所有必需的插件二进制。首先检查现状ls -l /opt/cni/bin/ # 如果返回 No such file or directory说明目录不存在 # 如果目录存在但内容为空或极少说明二进制缺失然后下载并安装标准 CNI 插件包。v1.13 兼容的最新稳定版是cni-plugins-amd64-v0.7.5.tgz注意v0.8.x 及以上版本对 Go 版本有更高要求可能与 v1.13 的 kubelet 不兼容# 创建目录如果不存在 sudo mkdir -p /opt/cni/bin # 下载国内用户建议用镜像源如清华 TUNA curl -L https://github.com/containernetworking/plugins/releases/download/v0.7.5/cni-plugins-amd64-v0.7.5.tgz -o cni-plugins.tgz # 解压到 /opt/cni/bin/ sudo tar -C /opt/cni/bin/ -xzf cni-plugins.tgz # 验证安装应看到至少 bridge, host-local, loopback, flannel 等 ls -l /opt/cni/bin/ | wc -l # 正常应为 12 或 13 个文件为什么选 v0.7.5因为它是最后一个明确支持 Go 1.10v1.13 kubelet 的构建依赖的 CNI 插件版本。v0.8.0 开始要求 Go 1.12强行安装会导致exec format error或undefined symbol错误。这是一个典型的“版本对齐”陷阱很多教程不提导致用户下载最新版反而失败。3.2 第二步选择并部署 CNI 插件以 Flannel 为例CNI 二进制装好了但光有工具没有图纸还是没法开工。现在要部署一个具体的网络插件让它来生成那张“图纸”。Flannel 是 v1.13 最成熟、最轻量的选择。它的部署分为两步准备配置、应用 DaemonSet。准备 Flannel 配置Flannel 的官方 YAML (kube-flannel.yml) 默认使用host-gw模式这要求所有节点在同一个二层网络。对于跨网段或云环境必须改为vxlan模式。编辑 YAML 文件找到net-conf.json部分修改为{ Network: 10.244.0.0/16, Backend: { Type: vxlan } }同时确保image字段指向一个稳定的、与 v1.13 兼容的 Flannel 镜像例如quay.io/coreos/flannel:v0.11.0-amd64v0.11.0 是最后一个支持 v1.13 的 Flannel 主版本。应用 DaemonSet# 应用修改后的 YAML kubectl apply -f kube-flannel.yml # 检查 DaemonSet 状态 kubectl get ds -n kube-system # NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE # kube-flannel-ds-amd64 3 3 3 3 3 none 2m # 检查 flanneld Pod 是否 Running kubectl get pods -n kube-system -l appflannel # NAME READY STATUS RESTARTS AGE # kube-flannel-ds-amd64-abcde 1/1 Running 0 90s关键观察点在flanneldPod 进入 Running 状态后立刻登录到对应节点检查/etc/cni/net.d/# 在 node-01 上执行 ls -l /etc/cni/net.d/ # 应该能看到类似10-flannel.conflist cat /etc/cni/net.d/10-flannel.conflist | jq . # 查看内容是否合法如果这个文件存在且 JSON 格式正确说明 Flannel 已成功“画好图纸”。如果文件不存在说明flanneldPod 虽然 Running但内部逻辑失败需kubectl logs -n kube-system flannel-pod-name查看具体原因常见于 etcd 地址配置错误。3.3 第三步强制触发 kubelet 的 CNI 配置重载即使/etc/cni/net.d/下有了正确的配置文件kubelet 也不会立刻感知。它有一个内部的缓存和轮询机制默认轮询间隔是 3 秒但有时会因状态机卡住而延迟。最可靠的方法是重启 kubelet 服务强制它从头加载所有配置# 在 NotReady 的节点上执行如 node-01 sudo systemctl restart kubelet # 立即检查状态 sudo journalctl -u kubelet -n 50 --no-pager | grep -i cni\|ready # 应该看到类似I0521 14:45:22.123456 12345 kubelet_node_status.go:294] Setting node condition for node node-01 to true (NetworkReady)为什么不是systemctl reload kubelet因为reload只会重新读取 systemd 的 service 文件不会重启 kubelet 进程本身也就不会触发 CNI 配置的完整初始化流程。restart是唯一能保证“从零开始”的操作。3.4 第四步验证与收尾从 NotReady 到 Ready 的完整证据链重启 kubelet 后不要只看kubectl get nodes要建立一条完整的、可追溯的证据链证明问题已被根治。证据链一节点状态kubectl get nodes # NAME STATUS ROLES AGE VERSION # master Ready master 45m v1.13.12 # node-01 Ready none 42m v1.13.12 # node-02 Ready none 40m v1.13.12证据链二CNI 配置文件# 在 node-01 上 ls -l /etc/cni/net.d/ # -rw-r--r-- 1 root root 422 May 21 14:45 10-flannel.conflist cat /etc/cni/net.d/10-flannel.conflist | jq .name, .plugins[0].type, .plugins[0].backend.Type # cbr0 # flannel # vxlan证据链三CNI 二进制可用性# 在 node-01 上 /opt/cni/bin/bridge version # bridge version 0.7.5 /opt/cni/bin/host-local version # host-local version 0.7.5证据链四网络连通性# 在 master 上创建一个测试 Pod kubectl run test-pod --imagebusybox:1.28 -- sleep 3600 kubectl get pods -o wide # NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES # test-pod 1/1 Running 0 30s 10.244.1.2 node-01 none none # 从 master ping 这个 Pod 的 IP ping -c 3 10.244.1.2 # PING 10.244.1.2 (10.244.1.2) 56(84) bytes of data. # 64 bytes from 10.244.1.2: icmp_seq1 ttl63 time0.823 ms当这四条证据链全部闭合才意味着 “cni config uninitialized” 这个错误被真正、彻底地解决了。它不是一个状态的切换而是一整套基础设施的交付完成。4. 高阶避坑指南那些让问题“复发”的隐藏雷区解决了初次 NotReady不代表万事大吉。在后续的集群维护中有三个极易被忽视的“复发点”它们会让节点在毫无征兆的情况下再次陷入 NotReady。4.1 雷区一/etc/cni/net.d/ 目录被意外清空或覆盖这是最“冤枉”的复发。某次一位同事为了“清理旧配置”在节点上执行了rm -rf /etc/cni/net.d/*然后kubectl delete -f kube-flannel.yml删除了 DaemonSet。他以为删干净了再kubectl apply就能重来。结果新起的 flanneld Pod 日志里全是open /etc/cni/net.d/10-flannel.conflist: permission denied。原因在于kube-flannel.yml中的 volumeMount 将/etc/cni/net.d/挂载为hostPath但它的type字段默认是DirectoryOrCreate。当目录被rm -rf清空后hostPath会尝试重建它但重建的目录权限是root:root 755而 flanneld 容器是以uid0root运行的理论上没问题。但问题出在 SELinux 上——如果节点开启了 SELinuxhostPath挂载的目录会被打上system_u:object_r:container_file_t:s0标签而 flanneld 容器内的进程默认没有权限写入这个标签的目录。解决方案永远不要手动rm -rf /etc/cni/net.d/。如果要重装先kubectl delete -f kube-flannel.yml等待所有 flanneld Pod 彻底终止kubectl get pods -n kube-system -l appflannel返回空然后再sudo rm -rf /etc/cni/net.d/*。最后确保kube-flannel.yml中的hostPath配置显式指定了type: DirectoryOrCreate并在securityContext中添加privileged: true虽然不推荐生产环境但在 v1.13 的调试阶段是最快捷的绕过方式。4.2 雷区二CNI 二进制版本与 kubelet 版本的 ABI 不兼容v1.13 的 kubelet 是用 Go 1.11.13 编译的。如果你不小心安装了 v1.0.1 的 CNI 插件它要求 Go 1.16那么当 kubelet 尝试exec调用/opt/cni/bin/bridge时会收到fork/exec /opt/cni/bin/bridge: no such file or directory的错误。注意这个错误信息极具误导性——它不是说文件不存在而是说动态链接器ld-linux-x86-64.so.2找不到因为新版本二进制链接了旧版本系统没有的 glibc 符号。如何快速诊断在节点上直接执行/opt/cni/bin/bridge version # 如果报错 No such file or directory但 ls -l 显示文件存在基本可以断定是 ABI 不兼容。 # 进一步验证file /opt/cni/bin/bridge 查看其链接的 libc 版本ldd /opt/cni/bin/bridge 查看依赖。解决方案严格遵循“版本矩阵”。v1.13 对应 CNI 插件 v0.7.5对应 Flannel v0.11.0。任何偏离这个组合的操作都必须经过充分的e2e测试而不是凭经验猜测。4.3 雷区三kubelet 启动参数中的--network-plugincni被错误覆盖这是一个极其隐蔽的配置冲突。某些定制化的 kubelet service 文件比如通过 Ansible 或 Puppet 生成的可能会在ExecStart行后面追加--network-pluginkubenet或--network-plugin空值。这会直接覆盖 kubeadm 生成的默认参数。如何排查在 NotReady 节点上ps aux | grep kubelet | grep -o network-plugin[^ ]* # 如果输出是 network-pluginkubenet 或 network-plugin那就找到了元凶。解决方案永远不要直接修改/etc/systemd/system/kubelet.service.d/10-kubeadm.conf。如果需要自定义参数应该创建一个新的 drop-in 文件如/etc/systemd/system/kubelet.service.d/20-custom.conf并在其中只写你需要覆盖的参数确保--network-plugincni这一行始终存在且未被覆盖。修改后务必执行sudo systemctl daemon-reload sudo systemctl restart kubelet。提示在生产环境中我习惯在集群初始化完成后立即在所有节点上运行一个简单的健康检查脚本它会自动验证/opt/cni/bin/的完整性、/etc/cni/net.d/的存在性、以及ps aux | grep kubelet中的关键参数。这个脚本被集成到 CI/CD 流水线中每次节点变更都会触发将这类“人为失误”扼杀在摇篮里。5. 经验沉淀从 v1.13 的“古老”问题中提炼出的通用法则虽然我们讨论的是 v1.13 这个特定版本但解决 “cni config uninitialized” 的过程实际上提炼出了适用于所有 Kubernetes 版本的、关于网络插件部署的通用法则。这些法则是我过去十年在数十个不同规模、不同版本的集群中反复验证、摔打出来的。5.1 法则一CNI 是“声明式交付”而非“命令式安装”很多人把 CNI 插件当成一个软件包用apt install或yum install就完事了。这是巨大的误解。CNI 的本质是一套契约kubelet 声明“我需要网络”CNI 插件声明“我能提供网络”而/etc/cni/net.d/下的配置文件就是双方签署的“合同文本”。这个“合同”必须由插件自身如 flanneld或一个独立的初始化脚本如 calicoctl来签署kubelet 只是合同的执行方和监督者。因此任何脱离“配置文件生成”这个核心环节的所谓“安装”都是无效的。这也是为什么kubectl apply -f calico.yaml能成功而curl -L https://.../calicoctl | bash却常常失败——前者包含了完整的 DaemonSet 和 ConfigMap能自动生成配置后者只是一个 CLI 工具它不负责部署网络。5.2 法则二节点就绪状态Ready是“多条件与门”而非“单点开关”kubectl get nodes输出的 Ready 状态是 kubelet 内部多个健康检查项的综合结果。其中NetworkReady只是NodeCondition数组中的一个元素。其他还有PIDReady、DiskPressure、MemoryPressure等。cni config uninitialized导致的是NetworkReadyFalse但这并不影响PIDReadyTrue或DiskPressureFalse。所以当你看到一个节点是 NotReady第一反应不应该是“网络坏了”而应该是“打开 kubelet 的 NodeCondition 详情看是哪个条件不满足”。kubectl describe node node-name的输出中Conditions:部分会清晰列出所有条件的状态和最后更新时间。这比盲目重启 kubelet 或重装插件效率高出数倍。5.3 法则三日志是唯一的真相而journalctl -u kubelet是你的第一现场在容器化时代我们习惯了kubectl logs。但对于 kubelet 这个“容器的容器”它的日志不在 Kubernetes API 里而在宿主机的 systemd journal 里。journalctl -u kubelet是诊断一切 kubelet 相关问题的黄金入口。它记录了从启动、证书加载、CNI 初始化、到 Pod 同步的每一个细节。我给自己定了一条铁律只要节点 NotReady第一件事就是journalctl -u kubelet -n 200 --no-pager | grep -E (cni|network|error|failed)。这条命令能在 5 秒内把问题的根源从茫茫日志海中精准捞出来。它比任何 GUI 控制台、比任何第三方监控工具都更直接、更真实。最后再分享一个小技巧在集群初始化脚本的末尾加上一行echo CNI check: $(ls -l /etc/cni/net.d/ 2/dev/null | wc -l) files /var/log/cluster-init.log。这样每次集群部署完成后你都能在日志里一眼看到 CNI 配置是否就位。这个看似微不足道的“快照”在后续的故障复盘中往往能成为决定性的证据。
RELATED

相关推荐

短视频配音工具哪个好用

短视频配音工具哪个好用

说明本文基于公开产品体验与多方使用反馈整理,不含任何商业合作,仅作为选型参考。文中产品均按公开信息描述,具体功能以各平台官方页面为准。结论先看短视频配音工具没有哪个绝对最好,选型的核心就是场景匹配。已经在用剪映做视频…

📅 2026/10/12 3:52:36
微信自动回复突然全停了?五个检查点从外到内

微信自动回复突然全停了?五个检查点从外到内

早上打开后台的那一刻就知道不对劲:一整夜的咨询,一条自动回复都没有,买家的消息静静躺在列表里。自动回复「全停」和「偶发漏答」是两种问题——偶发漏答多半是词表的事,全停基本是链路断了。排查顺序很重要:从外到内…

📅 2026/10/12 3:52:36
影刀RPA数字员工入门:从零搭建自动化流程实战手册

影刀RPA数字员工入门:从零搭建自动化流程实战手册

影刀RPA这两年在大众视野里出镜率越来越高,"数字员工"这个概念听起来也够玄乎。说白了,就是你电脑里的那些重复劳动——每天登录后台下载报表、把A系统数据搬到B系统、给几十个客户回邮件——都可以让一个按你指令行事的软件机器人来干。你可以…

📅 2026/10/12 3:52:36
MORE NEWS

更多资讯

📰

CSS - 盒模型初识:内容 内边距 边框 外边距解析

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕CSS这个话题展开,希望能为你带来一些启发或实…

📰

具身智能创新原理(149):多模态语义对齐与跨模态策略融合机制研究

前沿技术探索:TVA智能体(简称TVA)TVA智能体(亦称“AI智能体视觉”)是依托Transformer架构与“因式智能体”理论构建的新型工业视觉系统,也是当前最具代表性的具身视觉技术之一。它有机融合深度强化学习&…

📰

深度强化学习在移动机器人路径规划中的工程落地

简介:本资源是一篇发表于《计算机工程与应用》的学术论文PDF,面向人工智能、机器人学及强化学习方向的研究生、科研人员与工程实践者,聚焦移动机器人在复杂未知环境下的自主路径规划难题。论文提出IDDDQN(改进型竞争式深度双Q网络…

📰

具身智能创新原理(143):元认知监控与策略自修正的融合机制研究

前沿技术探索:TVA智能体(简称TVA)TVA智能体(亦称“AI智能体视觉”)是依托Transformer架构与“因式智能体”理论构建的新型工业视觉系统,也是当前最具代表性的具身视觉技术之一。它有机融合深度强化学习&…

📰

企业级智能体如何做到可控:从螃蟹式防御到全链路审计的工程实践

1. 智能体进化的一记警钟:从AI玩具回归业务系统前几天跟一位金融行业的技术负责人聊智能体落地,他抛给我一个很真实的问题:“你们说的Agent我 demo 过,确实聪明,但我怎么确保它不会像那家社交平台的AI一样,…

📰

图像法矿石粒度分析:基于Matlab的粒径统计与系统实现

矿石粒度分析这几年在砂石骨料、选矿、破碎生产里的需求越来越大。以前大家熟悉的是人工筛分:取样、搬运、振动筛,一套下来没有一两个小时出不了结果,现场粉尘还大。用Matlab写矿石粒度分析系统软件,核心就是石料粒径特性统计——…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬