Kubernetes节点失联深度排查:从“找不到Master”到集群恢复的实战指南

凌晨三点,告警平台的短信把手机屏幕点亮,一行刺眼的红色文字:“K8s节点失联,Pod调度失败”。你从床上弹起来,打开电脑,登录集群,kubectl get nodes 命令返回的结果里,那个本该承载着核心服务的节点状态变成了 NotReady,更糟糕的是,kubectl logs 命令直接报错:The connection to the server 192.168.2.129:6443 was refused。紧接着,查看节点上的 kubelet 日志,你看到了那个熟悉又令人头疼的错误:kubelet.go:2461] “Error getting node“ err=“node \“k8s-master\“ not found“

这不是第一次了。在复杂的生产环境中,Kubernetes 节点与 Master 失联是运维人员最不愿遇到却又无法完全避免的故障之一。它不像某个 Pod 崩溃那样影响局部,而是直接切断了一个计算节点与集群大脑的连接,导致其上的所有工作负载陷入“孤岛”状态,无法被管理、无法被调度、也无法正常提供服务。对于依赖 Kubernetes 稳定性的企业来说,这种故障意味着服务中断的风险和潜在的业务损失。

面对这样的场景,慌乱地重启服务往往不是最佳选择。一个系统化的、基于故障树分析(FTA)的排查思路,才是快速定位问题、恢复集群健康的关键。本文将从一个资深 SRE 的视角出发,为你构建一套从表象到根源、从网络到配置的完整排查体系。我们不仅会解决眼前的“找不到 Master”问题,更会深入理解其背后的机制,让你在下一次告警响起时,能够胸有成竹。

1. 第一响应:建立诊断基线并快速验证网络层

当节点失联告警触发时,首要任务是保持冷静,并迅速建立一个清晰的诊断基线。盲目操作可能会掩盖问题线索,甚至导致故障扩散。

第一步,确认故障范围。 是单个节点失联,还是多个节点同时出现问题?使用一个健康的、能够连接 API Server 的节点(通常是另一个 Master 节点或运维跳板机)执行以下命令:

# 查看所有节点状态,确认故障节点
kubectl get nodes -o wide

# 查看故障节点上的 Pod 状态(从集群视角)
kubectl get pods -o wide --all-namespaces | grep <故障节点IP或主机名>

# 描述故障节点,获取更多事件信息
kubectl describe node <故障节点名>

如果 kubectl 命令本身报连接 API Server 失败,那说明问题可能出在控制平面(所有 Master 节点)或网络层面,需要优先排查 API Server 服务本身。如果只有特定节点失联,则聚焦于该节点与 Master 之间的通信链路。

紧接着,我们必须直击最可能的“元凶”:网络连通性。 Kubernetes 集群的神经中枢是 API Server(默认端口 6443),kubelet 需要持续地与它保持心跳(Node Lease)并汇报状态。网络层面的阻断是导致“找不到 Master”的最常见原因。

你需要登录到失联的节点上,执行一系列网络诊断命令:

# 1. 检查到 Master API Server 端口的连通性
# 假设你的 Master 节点 IP 是 192.168.2.129
ping 192.168.2.129
telnet 192.168.2.129 6443
# 或者使用更现代的 nc (netcat)
nc -zv 192.168.2.129 6443

# 2. 检查本地端口监听情况,确认 kubelet 是否在监听自己的端口(如10250)
sudo netstat -tlnp | grep kubelet

# 3. 检查本地防火墙规则(以 firewalld 为例)
sudo firewall-cmd --list-all
# 或者检查 iptables
sudo iptables -L -n | grep -E '(6443|10250)'

# 4. 检查路由表,确保到 Master 网络的路由正确
ip route get 192.168.2.129

# 5. 检查网络接口和 DNS 解析(如果 Master 使用主机名)
cat /etc/hosts
nslookup kubernetes.default.svc.cluster.local

注意:在云环境或使用了网络插件(如 Calico、Flannel)的集群中,还需要检查 CNI 插件是否正常。一个快速的检查方法是看 kube-system 命名空间下网络相关的 Pod 是否运行正常,以及节点上的 CNI 配置文件(如 /etc/cni/net.d/)是否存在且有效。

如果 telnet 6443 失败,而 ping 通,那么问题很可能出在防火墙、安全组或 API Server 服务本身。这时,你需要去 Master 节点检查 API Server 的 Pod 或服务状态,以及 Master 节点上的防火墙规则是否放行了 6443 端口来自工作节点的流量。

为了更清晰地对比网络排查要点,可以参考下表:

排查层面检查命令/位置正常表现异常可能原因
节点间连通性ping <Master-IP>, nc -zv <Master-IP> 6443能 ping 通,端口可连接物理网络中断、交换机故障、云安全组未放行
本地防火墙firewall-cmd --list-all, iptables -L -n有允许 6443 端口的规则防火墙规则丢失或错误,阻止了出站/入站连接
K8s 服务发现nslookup kubernetes.default.svc.cluster.local解析出 ClusterIPCoreDNS 故障,/etc/resolv.conf 配置错误
CNI 网络插件kubectl get pod -n kube-system | grep -E '(calico|flannel|cilium)'所有 Pod 为 Running网络插件 Pod 崩溃,CNI 二进制文件缺失或配置错误
主机路由ip route get <Master-IP>显示明确且可达的路由路由表混乱,多网卡配置冲突

完成这一轮快速网络检查,你可能已经解决了因安全组变更、防火墙误操作或临时网络抖动导致的问题。如果网络层一切正常,那么我们需要向更深处挖掘。

2. 深入服务层:剖析 kubelet 与 API Server 的运行状态

当网络通路确认无误后,注意力就该转移到服务本身。kubelet 是节点上的“节点代理”,它负责与 Master 通信,也是报错信息的直接来源。而 Master 端的 API Server 则是它唯一的对话对象。

首先,在问题节点上对 kubelet 进行一个全面的健康检查:

# 1. 检查 kubelet 服务状态
sudo systemctl status kubelet -l
# 重点查看 Active 状态是否是 `active (running)`,以及下面是否有明显的错误日志。

# 2. 如果状态异常,查看更详细的日志
sudo journalctl -xeu kubelet --since "1 hour ago"
# 使用 `-f` 参数可以实时跟踪日志输出。

# 3. 检查 kubelet 的关键配置文件
sudo cat /etc/kubernetes/kubelet.conf
# 关注 `server` 字段,是否正确指向了 API Server 的地址(可能是负载均衡器地址)。
sudo cat /var/lib/kubelet/config.yaml
# 检查 kubelet 的认证、证书等配置。

# 4. 检查 kubelet 进程是否存活,以及资源占用
ps aux | grep kubelet
top -p $(pgrep kubelet)

journalctl 输出的日志中,除了“node not found”,还需要关注以下几类关键信息:

  • 证书相关错误:如 x509: certificate has expired or is not yet valid, failed to load TLS config。这直接指向认证问题。
  • 连接拒绝:如 connection refused, dial tcp ... i/o timeout。这通常意味着对端 API Server 没有在监听端口,或者中间有网络策略阻断。
  • 权限不足:如 ... is forbidden: User \"system:node:...\" cannot ...。这表示该节点的身份(由证书标识)没有足够的 RBAC 权限。

其次,我们需要验证 Master 端 API Server 的健康状况。 如果可能,登录到一个健康的 Master 节点进行操作:

# 1. 检查 API Server 的静态 Pod 或服务状态(取决于安装方式)
# 如果是 kubeadm 安装,API Server 通常以静态 Pod 运行在 Master 节点上
sudo crictl pods | grep kube-apiserver
# 或者使用 docker(如果使用 docker 作为运行时)
sudo docker ps | grep kube-apiserver

# 2. 检查 API Server 的服务监听端口
sudo netstat -tlnp | grep 6443

# 3. 直接调用 API Server 的健康端点
curl -k https://localhost:6443/healthz
# 应该返回 `ok`。也可以从其他节点用 ClusterIP 测试:
curl -k https://<API-Server-ClusterIP>:6443/healthz

如果 API Server 本身不健康,那么所有节点都会出现问题,故障范围会更大。此时需要检查 Master 节点的系统资源(CPU、内存、磁盘)、etcd 集群状态等。

一个常见的、容易被忽略的故障点是:kubelet 与 API Server 之间的证书认证失败。Kubernetes 集群内部通信高度依赖 TLS 双向认证。kubelet 持有的客户端证书可能已经过期,或者 API Server 的服务器证书不被 kubelet 信任。

检查证书有效期的快速方法:

# 在问题节点上,检查 kubelet 客户端证书
sudo openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
# 输出中的 `notAfter` 就是过期时间。

# 检查 kubelet 服务账户令牌(如果使用)
sudo cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d '.' -f2 | base64 -d | jq .exp
# 需要安装 jq 工具。

# 在 Master 节点上,检查 API Server 的证书
sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates

证书过期是导致节点突然失联的一个经典原因,尤其是在那些运行了很长时间而忘记设置证书自动更新的集群中。如果发现证书过期,需要使用 kubeadm certs renew 等工具进行更新,并重启相关组件。

3. 解密配置与认证:RBAC、Bootstrap 与证书陷阱

如果网络通畅、服务运行,但 kubelet 依然无法注册或报告“node not found”,那么问题很可能出在更深层的配置和认证授权环节。这里的水更深,也更能体现排查工作的专业性。

首先,审视 RBAC(基于角色的访问控制)。 kubelet 在 API Server 眼中是一个名为 system:node:<nodeName> 的用户。它需要一系列权限来更新自己的节点状态、创建 Lease 对象等。这些权限通常由 Node 授权模式和 system:node 相关的 ClusterRoleBinding 赋予。

你可以通过以下方式检查节点的 RBAC 权限是否被意外修改或删除:

# 从一个健康节点,检查 ClusterRoleBinding
kubectl get clusterrolebinding -o json | jq -r '.items[] | select(.subjects[]?.name=="system:nodes") | .metadata.name'

# 查看具体的 ClusterRole 权限
kubectl describe clusterrole system:node
# 关注是否有对 `nodes`, `leases.coordination.k8s.io` 资源的 get, update, patch 等动词权限。

我曾经遇到过一个案例,运维人员为了“安全收紧”,修改了全局的 ClusterRole,无意中移除了 system:nodes 组对 leases 资源的访问权限,导致所有节点在 Lease 续期时失败,日志中就会出现 cannot get resource "leases" in API group "coordination.k8s.io" 的 forbidden 错误。

其次,关注 kubelet 的启动和引导(Bootstrap)过程。 对于使用 kubeadm 加入的节点,或者启用了 TLS Bootstrapping 的集群,kubelet 最初会使用一个低权限的 bootstrap-kubeconfig 文件启动,然后向 API Server 申请证书签名请求(CSR),批准后才会获得正式的身份凭证。

这个流程中的任何一环出错,都会导致节点无法获得合法身份。排查点包括:

  • Bootstrap Token 是否过期或失效kubeadm token list
  • CSR 是否被批准kubectl get csr。一个健康的、已加入节点的 CSR 状态应为 Approved,Issued
  • kubelet 的 kubeconfig 文件是否正确生成:比较 /etc/kubernetes/kubelet.conf/etc/kubernetes/bootstrap-kubelet.conf。前者应该包含有效的客户端证书。

这里有一个真实的配置陷阱:节点主机名或 IP 地址变更。Kubernetes 节点身份与它的主机名强绑定。如果你克隆了一台虚拟机作为新节点,或者修改了节点的主机名,但未清理旧的证书和配置,就会导致身份混淆。kubelet 试图以新主机名注册,但 API Server 发现已有同名节点,或者证书中的 Subject Alternative Name (SAN) 不匹配,从而拒绝请求。日志中可能会出现 node \"xxx\" cannot modify node \"yyy\" 这类令人困惑的错误。

处理方法是彻底清理旧节点信息并重新加入:

# 在 Master 上驱逐并删除旧节点(谨慎操作,会触发 Pod 迁移)
kubectl drain <old-node-name> --ignore-daemonsets --delete-local-data
kubectl delete node <old-node-name>

# 在问题节点上,清理 kubelet 工作目录和证书
sudo kubeadm reset -f
sudo rm -rf /etc/kubernetes/ /var/lib/kubelet/ /var/lib/etcd/(如果是 etcd 成员)

# 重新使用 kubeadm join 命令加入集群

4. 高级诊断与根因分析:日志、追踪与集群状态审计

当常规手段都未能定位问题时,就需要动用更高级的诊断工具和技术,进行根因分析(RCA)。这就像刑侦破案,需要从各个角落寻找蛛丝马迹。

深入挖掘日志。我们之前用了 journalctl 查看 kubelet 日志,但可能还不够。调整日志级别可以获取更详细的信息:

# 临时调整 kubelet 日志级别为更详细的 v=4 或 v=5
# 编辑 kubelet 的 systemd 配置文件,通常在 /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
# 在 `ExecStart=` 行的最后添加 `--v=4`
sudo sed -i 's/$/ --v=4/' /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# 查看重启后的详细日志
sudo journalctl -xeu kubelet --since "now" | grep -E "(node.*not found|forbidden|error.*getting.*node)"
# 事后记得将日志级别调回,避免磁盘被日志塞满。

--v=4 或更高级别的日志中,你可以看到 kubelet 与 API Server 每一次通信的细节,包括发送的请求 URL、收到的响应状态码。这对于诊断权限问题、资源路径问题非常有帮助。

利用 Kubernetes 自身的调试工具。 kubectl 提供了一些强大的调试命令:

# 1. 检查集群事件,看是否有与节点相关的事件
kubectl get events --all-namespaces --sort-by='.lastTimestamp' | grep -i node

# 2. 检查节点控制器(Node Controller)和节点生命周期管理器的状态(需要访问 Master 组件日志)
# 节点控制器是 kube-controller-manager 的一部分
sudo journalctl -u kube-controller-manager | grep -i node

# 3. 使用 `kubectl describe node` 查看节点的完整状态和条件(Conditions)
kubectl describe node <node-name>
# 关注 `Conditions` 部分,如 `Ready`, `MemoryPressure`, `DiskPressure`, `PIDPressure`, `NetworkUnavailable` 等。
# 这些条件可以告诉你节点不健康的具体原因(如磁盘已满)。

进行集群状态审计。 有时候,问题不是出在失联的节点上,而是出在集群的全局状态上。例如:

  • etcd 集群健康度:etcd 是 Kubernetes 的大脑数据库。如果 etcd 出现 leader 选举、网络分区或磁盘性能问题,API Server 的读写操作就会异常,间接导致节点状态更新失败。检查 etcd 集群状态:
    # 在 etcd 容器或进程所在节点执行
    ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
      --cacert=/etc/kubernetes/pki/etcd/ca.crt \
      --cert=/etc/kubernetes/pki/etcd/server.crt \
      --key=/etc/kubernetes/pki/etcd/server.key endpoint health
    
  • API Server 的资源压力:检查 Master 节点的 CPU、内存、磁盘 I/O。API Server 过载会导致请求响应变慢甚至超时,kubelet 的心跳可能因此超时,导致节点被标记为 NotReady
  • 网络策略(NetworkPolicy)或服务网格(Service Mesh)的干扰:如果集群中部署了复杂的网络策略或 Istio 等服务网格,它们可能会在不知情的情况下拦截或修改 kubelet 与 API Server 之间的流量。检查相关的网络策略和 sidecar 注入配置。

最后,建立一个预防性的监控和告警体系,远比事后排查更重要。你应该监控以下指标,并在出现异常趋势时提前告警:

  • 节点 Ready 状态的变化
  • kubelet 心跳间隔的异常(node_heartbeat_interval_seconds
  • API Server 的请求延迟和错误率
  • 证书的过期时间(使用 kube-cert-manager 或类似工具监控)
  • 系统组件的日志错误率(通过 Loki、Elasticsearch 等日志聚合工具)

回到我们开头那个凌晨三点的故障。通过上述系统化的排查,我们最终发现原因是:一个自动化安全扫描工具临时修改了节点的 iptables 规则,意外丢弃了通往 API Server 负载均衡器 IP 的 TCP 包。在恢复了 iptables 规则并重启 kubelet 后,节点在几分钟内重新加入了集群,状态恢复为 Ready

处理 Kubernetes 节点失联问题,没有一成不变的银弹。它考验的是你对 Kubernetes 架构的深刻理解、对运维流程的熟练掌握,以及面对压力时清晰的排查思路。将这套故障树分析法内化为你的肌肉记忆,当下一次屏幕再次亮起红色告警时,你就能从容应对,快速让集群恢复绿色。记住,最好的故障处理,是让故障根本不要发生。因此,在集群平稳运行时,定期进行上述环节的“消防演练”和健康检查,同样至关重要。

更多推荐