刚部署完Kubernetes集群的新手经常会遇到一个经典问题:执行完kubeadm init后,满怀期待地输入kubectl get pods -n kube-system,却发现CoreDNS的状态一直显示Pending。更糟的是,用kubectl get nodes查看节点状态,Master和Worker节点都显示NotReady。这种场景就像你买了一台新电脑,开机后却发现连最基本的文件管理器都打不开。
我最近在帮朋友搭建测试环境时就遇到了完全一样的情况。当时我们按照官方文档一步步操作,初始化过程看似顺利,直到检查系统组件时才发现异常。具体现象表现为:
kubectl describe pod查看事件,会发现类似"0/1 nodes available: 1 node(s) had taint"的警告这种状况通常发生在安装Flannel网络插件之后。很多人以为执行了kubectl apply -f kube-flannel.yml就万事大吉,实际上底层还缺少关键组件。就像组装电脑时插上了显卡却忘了装驱动,硬件识别了但无法正常工作。
当发现问题时,我首先会查看CoreDNS Pod的详细状态。这个操作相当于电脑蓝屏时查看错误日志:
bash复制kubectl describe pod coredns-7ff77c879f-xxxxx -n kube-system
在Events部分,通常会看到两类关键信息:
这些信息说明Pod调度本身没有问题,问题出在节点网络准备阶段。就像快递送到了你家门口,却发现门锁坏了无法签收。
接下来需要诊断节点为什么处于NotReady状态:
bash复制kubectl describe nodes <node-name>
在输出的Conditions部分,网络相关的错误通常会显示:
code复制NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
这个错误非常典型,它告诉我们Kubelet无法初始化CNI网络插件。就像路由器通电了但WAN口没有插网线,设备本身是好的,但网络功能无法启用。
为了获取更详细的信息,我们需要直接查看Kubelet的日志:
bash复制journalctl -u kubelet -f
在日志中搜索"CNI"或"network plugin"关键词,很可能会发现类似这样的错误:
code复制failed to find plugin "flannel" in path /opt/cni/bin
这就是问题的核心所在——系统找不到Flannel插件所需的CNI二进制文件。就像你安装了一个软件,系统却提示缺少某个DLL文件。
要理解这个问题,我们需要先了解CNI(Container Network Interface)插件的工作机制。CNI就像Kubernetes网络的驱动程序,负责为Pod配置网络接口和IP地址。Flannel作为网络方案,需要两个组件协同工作:
很多人在部署时只安装了第一个组件,却忽略了第二个。就像只安装了显卡驱动软件,却忘了下载实际的驱动文件。
在标准的kubeadm初始化过程中,CNI插件目录/opt/cni/bin/是空的。虽然Flannel的DaemonSet会部署网络策略组件,但它不会自动下载所需的CNI插件二进制文件。这是因为:
这就解释了为什么我们明明部署了kube-flannel.yml,网络仍然无法正常工作。就像给电脑装了操作系统,但没有安装各种硬件驱动,很多功能还是无法使用。
根据我的经验,对于Kubernetes 1.18+版本,CNI插件v0.8.6是一个稳定选择。我们可以直接从官方GitHub仓库下载:
bash复制wget https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz
下载完成后,先创建目标目录(如果不存在):
bash复制mkdir -p /opt/cni/bin
接下来解压下载的文件到临时目录:
bash复制mkdir -p /tmp/cni-plugins
tar -zxvf cni-plugins-linux-amd64-v0.8.6.tgz -C /tmp/cni-plugins
然后复制flannel插件到CNI目录:
bash复制cp /tmp/cni-plugins/flannel /opt/cni/bin/
同时建议复制其他可能用到的插件:
bash复制cp /tmp/cni-plugins/{bridge,host-local,portmap} /opt/cni/bin/
完成文件复制后,需要重启相关服务:
bash复制systemctl restart kubelet
等待几分钟后,再次检查节点和Pod状态:
bash复制kubectl get nodes
kubectl get pods -n kube-system
正常情况下,节点应该会转为Ready状态,CoreDNS Pod也会自动启动。如果仍有问题,可以尝试删除CoreDNS Pod让它重新创建:
bash复制kubectl delete pod -n kube-system -l k8s-app=kube-dns
为了避免每次手动安装,可以编写一个简单的安装脚本:
bash复制#!/bin/bash
CNI_VERSION="v0.8.6"
mkdir -p /opt/cni/bin
curl -L "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-amd64-${CNI_VERSION}.tgz" | tar -C /opt/cni/bin -xz
将这个脚本作为集群初始化的一部分,可以彻底避免CNI插件缺失的问题。
不同版本的Kubernetes对CNI插件有不同要求。以下是一个兼容性参考表:
| Kubernetes版本 | 推荐CNI插件版本 |
|---|---|
| 1.16-1.17 | v0.7.5 |
| 1.18-1.20 | v0.8.6 |
| 1.21+ | v0.9.1 |
在有多节点的集群中,需要在所有节点上重复相同的安装步骤。可以使用Ansible等工具批量执行:
bash复制ansible all -m copy -a "src=/tmp/cni-plugins/flannel dest=/opt/cni/bin/flannel mode=0755"
记得在每个节点上重启kubelet服务使更改生效。
当Kubelet需要为Pod配置网络时,它会按照以下步骤工作:
如果其中任何一步失败,就会导致我们看到的NetworkPluginNotReady错误。
Flannel实际上由两部分组成:
这种设计使得Flannel可以专注于网络策略管理,而将底层的网络配置交给标准的CNI接口实现。就像建筑设计师负责画蓝图,而建筑工人负责按图施工。
有时候即使文件存在,也可能因为权限问题导致无法执行:
bash复制chmod 755 /opt/cni/bin/flannel
在某些启用了SELinux的系统上,可能需要调整安全上下文:
bash复制chcon -R -t container_file_t /opt/cni/bin/
如果安装了其他网络插件(如Calico),可能会与Flannel产生冲突。需要确保集群中只启用一种网络方案:
bash复制kubectl get daemonsets -n kube-system
发现冲突时,应该先彻底清理一个网络方案,再安装另一个。
这次排障经历让我深刻理解了Kubernetes网络架构的分层设计。很多看似复杂的问题,往往都是基础组件缺失或配置不当导致的。对于集群网络问题,我现在的排查思路是:
这种自顶向下的排查方法,可以快速定位大多数网络问题。记住,在Kubernetes中,网络是Pod能够正常运行的基础,而CNI插件就是这个基础的关键组成部分。就像一栋大楼的地基,虽然看不见,但决定了整个建筑的稳定性。