从NotReady到Ready:一次完整的Kubernetes节点故障深度排查与Calico网络修复实战
最近在帮一个朋友调试他的K8S集群时,遇到了一个经典的节点NotReady问题。他刚搭建好的Master节点,kubectl get nodes一直显示NotReady状态,CoreDNS也卡在Pending,整个集群基本处于瘫痪状态。这种问题对于刚接触K8S的运维来说确实头疼,但如果你掌握了系统化的排查思路,其实就像解谜一样,每一步都有迹可循。今天我就把这次完整的排查过程整理出来,不仅告诉你如何解决这个具体问题,更重要的是分享一套通用的故障排查方法论,让你下次遇到类似问题时能快速定位。
节点NotReady的原因多种多样,可能是网络插件配置问题、系统资源不足、证书过期,或者是组件版本不兼容。这次我们遇到的是Calico网络插件与K8S版本匹配问题,但排查过程涉及了日志分析、污点处理、CNI配置检查等多个层面,非常具有代表性。无论你是负责生产环境稳定的SRE,还是正在学习K8S的开发者,这套排查思路都能帮你节省大量时间。
1. 建立系统化的故障排查思维框架
遇到节点NotReady,很多人的第一反应是重启kubelet或者整个节点。这有时能解决问题,但更多时候只是暂时掩盖了问题,甚至可能让情况变得更复杂。我建议采用分层排查的方法,从最外层现象逐步深入到内核原因。
1.1 理解节点状态机制
Kubernetes节点状态不是简单的是否在线,而是一个综合健康检查的结果。当kubelet启动后,它会定期向API Server报告节点状态,包括:
- NodeReady:节点是否准备好接收Pod
- MemoryPressure:内存是否不足
- DiskPressure:磁盘空间是否不足
- PIDPressure:进程ID是否耗尽
- NetworkUnavailable:节点网络配置是否正确
这些条件状态会直接影响调度器的决策。NotReady状态通常意味着一个或多个条件不满足,我们的任务就是找出具体是哪个条件出了问题。
1.2 分层排查策略
我习惯将排查分为四个层次:
- 表象层:通过
kubectl命令查看节点、Pod状态 - 组件层:检查kubelet、容器运行时、网络插件等核心组件
- 配置层:验证配置文件、证书、网络配置
- 系统层:检查内核参数、系统资源、防火墙规则
每一层都有对应的检查工具和方法,下面我们逐层深入。
2. 从表象到本质:初步诊断与信息收集
当接到节点NotReady的告警时,不要急于修改配置,先收集足够的信息。信息越全面,判断就越准确。
2.1 基础状态检查
首先获取节点的详细状态信息:
# 查看所有节点状态 kubectl get nodes -o wide # 获取特定节点的详细描述 kubectl describe node <node-name> # 查看节点上的事件 kubectl get events --field-selector involvedObject.kind=Node,involvedObject.name=<node-name>kubectl describe node的输出特别重要,它会显示:
- Conditions:节点的各种条件状态
- Addresses:节点的IP地址和主机名
- System Info:操作系统、内核版本、容器运行时信息
- Allocatable/Allocated Resources:资源分配情况
- Taints:节点污点设置
在我朋友的案例中,describe命令显示节点有一个node.kubernetes.io/not-ready:NoSchedule的污点。这是K8S自动添加的,当节点长时间处于NotReady状态时,调度器会避免将新Pod调度到该节点。
注意:污点是K8S的调度机制,不是问题的原因而是结果。直接移除污点可能让Pod调度到不健康的节点上,应该先解决根本问题。
2.2 核心组件Pod状态分析
接下来检查kube-system命名空间下的核心组件:
# 查看所有系统Pod kubectl get pods -n kube-system -o wide # 检查特定Pod的日志 kubectl logs -n kube-system <pod-name> # 获取Pod的详细描述和事件 kubectl describe pod -n kube-system <pod-name>这里我们发现CoreDNS Pod处于Pending状态,描述信息显示"0/1 nodes are available: 1 node(s) had taint {node.kubernetes.io/not-ready: }"。这证实了节点污点影响了Pod调度,但我们需要知道为什么节点会被标记为NotReady。
2.3 节点资源与系统检查
有时候NotReady是资源不足导致的,检查节点资源使用情况:
# 查看节点资源使用 kubectl top node # 或者使用原生命令 ssh <node-ip> "free -h && df -h && uptime"同时检查一些关键的系统参数:
# 检查swap是否开启(K8S要求关闭swap) cat /proc/swaps # 检查关键内核参数 sysctl net.bridge.bridge-nf-call-iptables sysctl net.ipv4.ip_forward资源检查通常能快速排除内存、磁盘等问题,如果这些都没问题,我们就需要深入组件层了。
3. 深入组件层:kubelet与容器运行时诊断
kubelet是节点上的核心代理,负责与API Server通信并管理容器。当节点NotReady时,kubelet通常是第一个需要检查的组件。
3.1 kubelet日志分析
kubelet的日志包含了丰富的诊断信息。在大多数Linux发行版上,你可以通过systemd的journalctl查看:
# 实时查看kubelet日志 journalctl -f -u kubelet # 查看特定时间段的日志 journalctl -u kubelet --since "2 hours ago" # 查看包含错误级别的日志 journalctl -u kubelet -p err在我遇到的案例中,日志里反复出现这样的错误:
W0430 21:11:05.628506 962 cni.go:239] Unable to update cni config: no networks found in /etc/cni/net.d E0430 21:11:06.818698 962 kubelet.go:2103] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized这两条信息非常关键:
- CNI配置未找到:kubelet在
/etc/cni/net.d目录下找不到网络配置 - 网络插件未就绪:容器运行时报告网络插件未初始化
这直接指向了CNI(Container Network Interface)网络插件的问题。
3.2 kubelet服务状态检查
除了查看日志,还要检查kubelet服务的运行状态:
# 检查kubelet服务状态 systemctl status kubelet -l # 查看kubelet的启动参数 ps aux | grep kubelet重点关注kubelet的启动参数,特别是--network-plugin参数。在K8S 1.24+版本中,默认使用cni作为网络插件,但需要确保CNI配置文件存在且正确。
3.3 容器运行时检查
K8S支持多种容器运行时(Docker、containerd、CRI-O等),需要确保运行时正常工作:
# 检查Docker服务状态(如果使用Docker) systemctl status docker # 测试Docker是否能运行容器 docker run --rm hello-world # 检查containerd状态 systemctl status containerd如果容器运行时有问题,kubelet就无法创建或管理容器,节点自然无法变为Ready状态。
4. CNI网络插件深度排查:以Calico为例
CNI网络插件是K8S网络的核心,也是节点NotReady的常见原因。Calico作为最流行的网络插件之一,其配置相对复杂,容易出问题。
4.1 CNI基础目录结构检查
首先验证CNI的基本目录结构是否存在:
# 检查CNI二进制文件目录 ls -la /opt/cni/bin/ # 检查CNI配置文件目录 ls -la /etc/cni/net.d/正常的Calico安装应该包含:
/opt/cni/bin/目录下的关键二进制文件:
| 文件 | 作用 | 是否必需 |
|---|---|---|
| calico | Calico CNI插件主程序 | 是 |
| calico-ipam | Calico IP地址管理插件 | 是 |
| bridge | 标准bridge插件 | 可选 |
| flannel | Flannel插件(如果同时使用) | 可选 |
| portmap | 端口映射插件 | 推荐 |
| tuning | 网络调优插件 | 推荐 |
/etc/cni/net.d/目录下的配置文件:
# 查看CNI配置文件 cat /etc/cni/net.d/10-calico.conflist一个基本的Calico CNI配置应该类似这样:
{ "name": "k8s-pod-network", "cniVersion": "0.3.1", "plugins": [ { "type": "calico", "log_level": "info", "datastore_type": "kubernetes", "nodename": "__KUBERNETES_NODE_NAME__", "mtu": __CNI_MTU__, "ipam": { "type": "calico-ipam" }, "policy": { "type": "k8s" }, "kubernetes": { "kubeconfig": "__KUBECONFIG_FILEPATH__" } }, { "type": "portmap", "snat": true, "capabilities": {"portMappings": true} }, { "type": "bandwidth", "capabilities": {"bandwidth": true} } ] }如果这些文件不存在或格式错误,CNI插件就无法正常工作。
4.2 Calico组件状态检查
Calico由多个组件构成,需要确保所有组件都正常运行:
# 查看所有Calico相关的Pod kubectl get pods -n calico-system kubectl get pods -n calico-apiserver kubectl get pods -n tigera-operator关键组件及其作用:
| 组件 | 命名空间 | 作用 | 预期状态 |
|---|---|---|---|
| calico-node | calico-system | 节点上的网络代理 | Running |
| calico-kube-controllers | calico-system | 策略控制器 | Running |
| calico-typha | calico-system | 数据平面扩展(大规模集群) | Running(可选) |
| tigera-operator | tigera-operator | Calico Operator | Running |
| calico-apiserver | calico-apiserver | API服务器(企业版) | Running(可选) |
如果任何关键组件不是Running状态,需要查看其日志:
# 查看calico-node日志 kubectl logs -n calico-system daemonset/calico-node # 查看calico-kube-controllers日志 kubectl logs -n calico-system deployment/calico-kube-controllers4.3 常见Calico问题与解决方案
根据我的经验,Calico相关的问题主要集中在以下几个方面:
1. 镜像拉取失败
Calico组件需要从特定镜像仓库拉取镜像,如果网络不通或镜像版本不对,Pod会处于ImagePullBackOff状态:
# 查看Pod的事件 kubectl describe pod -n calico-system <pod-name> # 手动测试镜像拉取 docker pull calico/node:v3.27.32. IP池配置问题
Calico需要正确的IP池配置,否则Pod无法获取IP地址:
# 查看IP池配置 kubectl get ippools -o yaml # 检查IP池是否启用 kubectl get ippools -o jsonpath='{.items[*].spec.disabled}'3. 网络策略冲突
过于严格的网络策略可能阻止Calico组件间的通信:
# 临时禁用所有网络策略进行测试 kubectl delete networkpolicies --all-namespaces --all4. 内核模块缺失
Calico需要一些内核模块支持,特别是IP转发和网桥过滤:
# 检查必需的内核模块 lsmod | grep -E 'ip_tables|iptable_filter|iptable_nat|iptable_mangle' lsmod | grep -E 'br_netfilter|bridge|nf_conntrack'如果缺少这些模块,需要加载它们:
# 临时加载模块 modprobe ip_tables modprobe iptable_filter modprobe iptable_nat modprobe br_netfilter # 永久生效(添加到/etc/modules-load.d/k8s.conf) echo -e "ip_tables\niptable_filter\niptable_nat\nbr_netfilter" >> /etc/modules-load.d/k8s.conf5. 版本兼容性:被忽视的关键问题
在我朋友的案例中,最根本的问题是版本兼容性。他使用的是K8S 1.29.4,但安装的Calico版本与这个K8S版本不兼容。这是很多人在升级K8S后容易忽略的问题。
5.1 K8S与Calico版本兼容性矩阵
Calico官方维护着详细的版本兼容性表格,以下是一些常见版本的兼容情况:
| Calico版本 | 支持的K8S版本 | 关键特性 | 备注 |
|---|---|---|---|
| v3.27.x | 1.25-1.29 | 支持eBPF数据平面 | 生产推荐 |
| v3.26.x | 1.24-1.28 | 稳定性优化 | 长期支持 |
| v3.25.x | 1.23-1.27 | 性能改进 | 逐步淘汰 |
| v3.24.x | 1.22-1.26 | 基础功能 | 仅维护 |
重要提示:始终参考Calico官方文档获取最新的兼容性信息。不要凭记忆或过时的博客文章选择版本。
5.2 版本不兼容的典型症状
当Calico与K8S版本不匹配时,可能会出现:
CRD(Custom Resource Definition)创建失败
# 查看CRD安装状态 kubectl get crd | grep calicoAPI版本错误
error: unable to recognize "calico.yaml": no matches for kind "Installation" in version "operator.tigera.io/v1"组件启动失败但无明确错误Pod处于CrashLoopBackOff状态,但日志没有明显错误。
5.3 安全升级与降级策略
如果发现版本不兼容,需要重新安装合适版本的Calico。以下是安全操作步骤:
步骤1:备份当前配置
# 备份Calico自定义资源 kubectl get installation.operator.tigera.io default -o yaml > calico-installation-backup.yaml kubectl get ippools -o yaml > calico-ippools-backup.yaml # 备份网络策略 kubectl get networkpolicies --all-namespaces -o yaml > all-networkpolicies-backup.yaml步骤2:卸载当前Calico
# 如果使用Operator方式安装 kubectl delete -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/tigera-operator.yaml # 如果使用manifest方式安装 kubectl delete -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/calico.yaml # 清理残留资源 kubectl delete crd $(kubectl get crd | grep calico | awk '{print $1}') kubectl delete crd $(kubectl get crd | grep tigera | awk '{print $1}')步骤3:安装兼容版本
# 对于K8S 1.28.2,我推荐Calico 3.27.3 kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/tigera-operator.yaml kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/custom-resources.yaml步骤4:验证安装
# 等待所有Pod就绪 watch kubectl get pods -n calico-system # 检查节点网络状态 kubectl get nodes -o wide # 测试网络连通性 kubectl run test-pod --image=busybox --rm -it --restart=Never -- ping -c 4 8.8.8.85.4 预防版本问题的实践建议
- 在测试环境先验证:任何版本升级都先在测试集群验证
- 阅读发布说明:关注版本间的破坏性变更
- 使用版本管理工具:如Helm,可以方便地回滚
- 监控组件健康:设置对Calico组件的监控告警
6. 高级排查技巧与工具
当基础排查无法解决问题时,需要一些更高级的工具和技巧。
6.1 使用kubectl-debug进行节点诊断
kubectl-debug是一个强大的诊断工具,可以在不登录节点的情况下运行诊断命令:
# 安装kubectl-debug curl -Lo kubectl-debug.tar.gz https://github.com/aylei/kubectl-debug/releases/download/v0.1.1/kubectl-debug_0.1.1_linux_amd64.tar.gz tar -zxvf kubectl-debug.tar.gz sudo mv kubectl-debug /usr/local/bin/ # 在问题节点上运行诊断容器 kubectl debug node/<node-name> --image=nicolaka/netshoot -- bash进入诊断容器后,你可以:
# 检查CNI配置 cat /host/etc/cni/net.d/* # 检查网络接口 ip addr show ip route show # 测试网络连通性 ping <其他节点IP> nc -zv <API-Server-IP> 64436.2 使用calicoctl进行Calico特定诊断
calicoctl是Calico的专用管理工具,提供了丰富的诊断功能:
# 安装calicoctl curl -L https://github.com/projectcalico/calico/releases/download/v3.27.3/calicoctl-linux-amd64 -o calicoctl chmod +x calicoctl sudo mv calicoctl /usr/local/bin/ # 检查Calico节点状态 calicoctl node status # 查看BGP对等状态 calicoctl get bgppeer # 检查IP池使用情况 calicoctl ipam show --show-blocks6.3 网络策略与连通性测试
有时候问题不是Calico本身,而是网络策略阻止了必要的通信:
# 创建测试Pod进行网络诊断 kubectl run network-test --image=nicolaka/netshoot --restart=Never -- sleep 3600 # 进入测试Pod kubectl exec -it network-test -- bash # 在Pod内测试各种连通性 # 测试到API Server的连通性 curl -k https://kubernetes.default.svc.cluster.local:443 # 测试到其他Pod的连通性 ping <其他PodIP> # 测试到服务的连通性 nslookup kubernetes.default.svc.cluster.local6.4 性能问题诊断
如果节点在重负载下变为NotReady,可能是性能问题:
# 检查系统资源使用 kubectl top node kubectl top pod -n calico-system # 检查Calico的CPU/内存限制 kubectl describe daemonset calico-node -n calico-system | grep -A5 Resources # 调整Calico资源限制(如果需要) kubectl patch daemonset calico-node -n calico-system --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources", "value": {"limits": {"cpu": "500m", "memory": "512Mi"}, "requests": {"cpu": "250m", "memory": "256Mi"}}}]'7. 预防措施与最佳实践
解决问题很重要,但预防问题更重要。以下是我总结的一些最佳实践:
7.1 监控与告警配置
建立完善的监控体系,在问题发生前发现迹象:
# Prometheus监控Calico的示例规则 groups: - name: calico.rules rules: - alert: CalicoNodeDown expr: absent(up{job="calico-node"} == 1) for: 5m labels: severity: critical annotations: summary: "Calico node {{ $labels.instance }} is down" - alert: BGPSessionDown expr: calico_bgp_session_state != 1 for: 2m labels: severity: warning annotations: summary: "BGP session {{ $labels.peer_ip }} is down on {{ $labels.instance }}"7.2 定期健康检查
建立定期检查脚本,自动化常见问题检测:
#!/bin/bash # k8s-node-health-check.sh # 检查节点状态 echo "=== 节点状态检查 ===" kubectl get nodes -o wide # 检查核心Pod echo -e "\n=== 核心Pod状态检查 ===" kubectl get pods -n kube-system -o wide | grep -v Running # 检查Calico组件 echo -e "\n=== Calico组件状态检查 ===" kubectl get pods -n calico-system -o wide | grep -v Running # 检查CNI配置 echo -e "\n=== CNI配置检查 ===" for node in $(kubectl get nodes -o name | cut -d/ -f2); do echo "检查节点 $node 的CNI配置..." kubectl debug node/$node --image=busybox -- cat /host/etc/cni/net.d/10-calico.conflist 2>/dev/null || echo "CNI配置不存在" done # 检查网络连通性 echo -e "\n=== 网络连通性测试 ===" kubectl run net-test --image=busybox --rm -it --restart=Never -- ping -c 3 8.8.8.87.3 文档与知识库
建立内部知识库,记录常见问题和解决方案:
| 问题现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
| 节点NotReady,CNI配置缺失 | Calico未正确安装 | 1. 检查/etc/cni/net.d目录 2. 检查Calico Pod状态 3. 查看kubelet日志 | 重新安装兼容版本的Calico |
| Calico-node Pod不断重启 | 资源不足或镜像拉取失败 | 1. 查看Pod日志 2. 检查节点资源 3. 检查镜像仓库可达性 | 调整资源限制或配置镜像仓库 |
| Pod无法跨节点通信 | 网络策略或路由问题 | 1. 检查网络策略 2. 检查Calico BGP状态 3. 验证防火墙规则 | 调整网络策略或修复BGP配置 |
7.4 灾难恢复计划
为最坏情况做好准备:
备份关键配置
# 备份Calico配置 kubectl get installation.operator.tigera.io -o yaml > calico-backup.yaml kubectl get ippools -o yaml > ippools-backup.yaml # 备份网络策略 kubectl get networkpolicies --all-namespaces -o yaml > networkpolicies-backup.yaml准备快速恢复脚本
# restore-calico.sh kubectl apply -f calico-backup.yaml kubectl apply -f ippools-backup.yaml kubectl apply -f networkpolicies-backup.yaml建立回滚流程
- 记录每次变更
- 测试回滚步骤
- 定期演练恢复过程
节点NotReady问题虽然令人头疼,但通过系统化的排查方法,大多数问题都能快速定位和解决。关键是要保持冷静,按照从外到内、从简单到复杂的顺序逐步排查。记住,日志是你的好朋友,kubectl describe和kubectl logs能提供80%的线索。版本兼容性这种看似简单的问题,往往是最容易被忽视的陷阱,特别是在快速发展的K8S生态中。定期更新你的知识库,测试环境先行,生产环境谨慎,这样才能在享受容器化便利的同时,保持系统的稳定可靠。