资讯动态

K8s节点异常诊断与修复实战:从NotReady到自动化运维

发布时间:2026/8/13 11:24:44 来源:尧图企业网站定制
1. 项目概述当K8s节点“生病”了怎么办在K8s集群的日常运维中节点异常是每个管理员迟早都会面对的“必修课”。想象一下你正悠闲地喝着咖啡监控大盘上突然亮起一片刺眼的红色告警某个工作节点Node失联了或者它的状态变成了NotReady。紧接着你可能会发现一批Pod被标记为Terminating或Unknown服务开始出现间歇性中断。这不仅仅是某个虚拟机重启那么简单它背后牵扯到Pod的驱逐策略、工作负载的重新调度、存储卷的挂载状态以及整个集群的稳定性。处理节点异常远不止是登录机器看看日志它是一套需要清晰思路、标准流程和丰富经验的综合应对策略。今天我就结合自己踩过的坑和总结的经验来系统性地拆解一下K8s节点异常处理的方方面面从快速诊断到根因排查再到预防优化给你一套可以直接“抄作业”的实战手册。2. 节点异常的核心表现与快速诊断当节点出现问题时它会在K8s API中表现出特定的状态我们的第一步就是快速、准确地识别这些信号。2.1 识别关键异常状态首先打开你的终端使用kubectl get nodes命令。一个健康的节点状态应该是Ready。常见的异常状态包括NotReady: 这是最常见的异常状态。意味着Kubelet节点上的代理无法向控制平面Control Plane报告其状态或者节点上的核心组件运行不正常。这通常是硬件故障、系统负载过高、网络分区或Kubelet进程崩溃导致的。Unknown: API Server在一段时间内由--node-monitor-grace-period参数控制默认40秒完全无法从该节点收到任何状态更新。这比NotReady更严重通常指向节点与控制平面之间的网络完全中断或者节点本身已经宕机。特定条件的异常: 使用kubectl describe node node-name查看详细情况。你需要关注Conditions部分例如MemoryPressure/DiskPressure/PIDPressure: 内存、磁盘或进程ID资源压力。这会导致K8s调度器避免向该节点调度新Pod并可能开始驱逐现有Pod。NetworkUnavailable: 节点网络配置有问题。KubeletReady: 如果这个条件是False那就是Kubelet自身出了问题。注意NotReady和Unknown状态都会触发K8s的“节点生命周期控制器”行动但行为略有不同。对于Unknown节点在经过一个更长的容忍期--pod-eviction-timeout默认5分钟后控制平面会认为该节点已不可恢复并开始强制驱逐其上的Pod。而对于NotReady驱逐行为可能更早或依据其他条件触发。2.2 五分钟快速诊断清单遇到节点异常不要慌按以下清单快速过一遍能解决大部分表面问题检查节点资源登录到问题节点如果还能登录运行top或htop查看CPU、内存使用率。运行df -h检查根目录和关键挂载点如/var/lib/kubelet的磁盘使用率。95%的磁盘使用率就可能触发DiskPressure。检查Kubelet服务systemctl status kubelet。看看服务是否在运行有没有崩溃重启的记录journalctl -u kubelet --since “1 hour ago”。一个常见的坑是证书过期导致Kubelet无法启动。检查容器运行时如果是Dockersystemctl status docker如果是Containerdsystemctl status containerd。运行时挂了Kubelet自然无法工作。检查网络连通性从节点上ping一下API Server的Service IP通常是kubernetes.default.svc.cluster.local对应的IP和控制平面节点的IP。同时检查cni0、flannel.1、calico*等CNI接口是否存在且状态正常。查看节点事件kubectl describe node node-name输出的Events部分包含了非常宝贵的线索比如“NodeControllerEviction”开始驱逐Pod或者“KubeletHasSufficientPID”等。3. 深入排查常见根因分析与实操快速诊断可能能解决一些简单问题但更多时候我们需要深入挖掘。下面针对几种典型场景展开详细的排查步骤。3.1 场景一资源压力导致的节点异常这是生产环境中最频繁的一类问题。K8s本身有资源压力处理机制但我们需要理解其逻辑并主动干预。内存压力MemoryPressure: 当节点可用内存低于一个阈值由--eviction-hard或--eviction-soft参数定义例如memory.available100Mi时节点会报告MemoryPressure。Kubelet会按照Pod的优先级和服务质量QoS从低到高开始驱逐Pod。BestEffort Pod最先被牺牲。排查步骤:kubectl top node和kubectl top pod -n namespace --use-protocol-buffers查看资源使用情况。注意top命令依赖Metrics Server确保它已部署且正常。登录节点使用ps aux --sort-%mem找出宿主机上占用内存最多的进程。有时不是容器的问题而是某个宿主机进程内存泄漏。检查Pod的内存限制Limit和请求Request。如果Pod实际使用量持续超过其Limit它会被OOM Killer杀掉。查看kubectl describe pod中是否有OOMKilled的事件。实操心得不要只依赖Pod的Limit。务必为关键Pod设置合理的内存Request这能帮助调度器做出更优决策。同时考虑启用HPA水平Pod自动扩缩容基于内存使用率来自动调整副本数。磁盘压力DiskPressure: 通常由镜像、日志或容器可写层占满磁盘引起。Kubelet会清理未使用的镜像和退出的容器但如果清理速度跟不上增长节点就会异常。排查步骤:df -h重点查看/var/lib/docker(Docker运行时) 或/var/lib/containerd(Containerd运行时) 以及/var/log目录。使用docker system df(Docker) 或crictl images/crictl ps -a(Containerd) 查看镜像和容器占用的空间。清理策略可以手动删除无用镜像和退出状态的容器。但治本之策是配置Kubelet的垃圾回收参数例如--image-gc-high-threshold默认85%和--image-gc-low-threshold默认80%并配置日志轮转如使用logrotate。踩坑记录有一次线上告警节点DiskPressure。排查发现是某个应用在/var/log下疯狂写日志且没配置轮转。临时解决后我们立即为所有Pod配置了日志输出到标准输出并由DaemonSet如Fluentd收集到中心日志系统彻底杜绝了此类问题。3.2 场景二Kubelet与组件故障节点状态上报依赖于Kubelet。如果Kubelet挂了节点就是“瞎子和聋子”。证书问题: Kubelet需要使用客户端证书与API Server进行双向TLS认证。证书通常一年有效。过期后Kubelet将无法连接API Server节点状态变为NotReady。排查与解决:在节点上检查Kubelet证书openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -text | grep -A 2 Validity。查看Not After日期。如果证书即将过期或已过期在控制平面节点上通常证书轮换是自动的。检查Kubelet配置--rotate-certificates是否为true。对于已过期的可以尝试重启Kubeletsystemctl restart kubelet它会自动向API Server申请新的证书如果配置了自动批准。如果自动轮换失败可能需要手动批准CSR证书签名请求kubectl get csr找到对应的请求然后kubectl certificate approve csr-name。配置错误或文件丢失:/var/lib/kubelet/config.yaml文件被误删或kubelet.conf损坏会导致Kubelet启动失败。排查与解决:从集群中其他正常节点或备份中恢复config.yaml。检查/etc/kubernetes/kubelet.conf它包含了连接API Server的认证信息。如果损坏可以从控制平面重新生成并下发。对于kubeadm搭建的集群可以在控制平面执行kubeadm init phase kubeconfig kubelet然后scp到对应节点。内核参数或系统依赖问题: 例如net.ipv4.ip_forward未设置为1或者bridge-nf-call-iptables未设置会导致网络插件如Calico、Flannel工作不正常进而影响Kubelet。排查步骤:sysctl net.ipv4.ip_forward确认值为1。sysctl net.bridge.bridge-nf-call-iptables确认值为1。确保conntrack、iptables或nftables等工具可用。3.3 场景三网络分区与CNI故障网络问题是最棘手的之一现象往往是节点状态时好时坏或者Pod之间网络不通。控制平面与节点网络中断: 节点无法访问API Server的6443端口。这可能是安全组规则、防火墙iptables/firewalld、网络设备或云服务商网络ACL的问题。排查步骤:从节点telnet api-server-ip 6443。如果不通逐层排查检查节点本地防火墙sudo iptables -L -n或sudo firewall-cmd --list-all。检查云服务商的安全组/网络ACL确保允许节点IP访问控制平面IP的6443端口以及控制平面节点之间相关端口如etcd的2379/2380。如果是混合云或复杂网络检查路由表是否正确。Pod网络CNI故障: 节点内部Pod无法跨节点通信或者无法访问Service。这通常是CNI插件Calico, Cilium, Flannel等的问题。排查步骤:kubectl get pods -n kube-system查看CNI插件的Pod是否全部Running。登录节点检查CNI的二进制文件通常在/opt/cni/bin和配置文件/etc/cni/net.d是否存在且正确。检查CNI插件创建的网桥如cni0和虚拟网卡。使用ip link show和ip addr show。一个非常实用的命令是kubectl run net-test --imagenicolaka/netshoot -it --rm -- /bin/bash启动一个网络诊断工具Pod在里面可以测试到其他Pod、Service和外部地址的网络连通性。4. 高级处理策略与自动化运维当手动排查清楚根因并修复后我们更需要一套自动化的预防和恢复机制。4.1 利用Node Affinity/Taint与Toleration预防这不是事后的处理而是事前的预防。通过污点Taint和容忍度Toleration你可以主动管理Pod的调度。给不稳定节点打上污点例如某个节点硬件较老你可以给它打上node.kubernetes.io/unstabletrue:NoSchedule。这样没有对应容忍度的Pod就不会被调度上去。关键Pod使用节点亲和性对于数据库等有状态应用使用nodeAffinity将其绑定到性能稳定、存储可靠的特定节点上避免被调度到问题节点。DaemonSet的容忍度像日志收集、网络插件这类DaemonSet必须容忍所有的污点包括node.kubernetes.io/unreachable和node.kubernetes.io/not-ready以确保它们在所有节点包括异常节点上都能运行这对于排查问题至关重要。4.2 配置合理的Pod中断预算PDBPod Disruption Budget (PDB) 用于保护应用在自愿中断如节点维护时至少有多少个副本可用。虽然它主要针对自愿中断但在节点异常导致Pod被驱逐时理解PDB有助于你评估影响范围。例如一个Deployment有3个副本你设置PDB为minAvailable: 2。那么当节点异常K8s尝试驱逐该Deployment的Pod时会确保任何时候至少有两个Pod在运行。如果第三个Pod所在的节点挂了PDB不会阻止这个非自愿中断但它能让你明确应用的高可用性设计是否达标。4.3 实现节点自动修复在云环境中你可以结合云提供商的健康检查与节点自动伸缩组Auto Scaling Group实现节点自愈。基本逻辑创建一个监控检查定期探测节点的健康状态不仅仅是K8s的Ready还包括系统负载、关键进程等。当检测到节点持续异常且无法自动恢复时标记该节点。通过调用云提供商API从ASG中移出该异常节点并终止实例。ASG会自动启动一个新的实例来替代。新的实例通过启动脚本自动加入K8s集群通常需要预装Kubelet、容器运行时并自动执行kubeadm join或类似操作。注意事项有状态应用这种“直接替换节点”的策略对有状态应用是灾难性的。确保有状态应用使用StatefulSet并配置了持久化存储Persistent Volume且存储与节点生命周期解耦。优雅驱逐在终止节点前最好能通过kubectl drain node-name --ignore-daemonsets --delete-emptydir-data命令优雅地驱逐节点上的Pod。这给了Pod一个体面的关闭时间并触发控制器如Deployment在其他节点上创建新的副本。防止雪崩设置速率限制避免短时间内大量节点同时被替换对控制平面和存储后端造成冲击。5. 构建可观测性监控与告警体系“没有度量就没有管理”。一套完善的监控告警体系能让你在用户感知之前发现问题。5.1 核心监控指标你需要监控以下四个层面的指标节点层面CPU/内存/磁盘/网络的使用率和饱和度。Kubelet和容器运行时的进程状态。node_status_condition如Ready、MemoryPressure的状态。Pod/容器层面容器的CPU、内存使用率相对于其Limit。容器的重启次数kube_pod_container_status_restarts_total频繁重启是重要信号。Pod的阶段Phase和状态Status。K8s组件层面API Server、Scheduler、Controller Manager的请求延迟和错误率。etcd的写入延迟、wal同步延迟和leader健康状况。应用业务层面服务的请求延迟、错误率和吞吐量。这能帮你判断节点异常是否真的影响了业务。5.2 告警规则配置示例使用Prometheus等监控系统时可以配置如下告警规则# 节点不可用告警 - alert: NodeNotReady expr: kube_node_status_condition{conditionReady, statustrue} 0 for: 5m # 持续5分钟才告警避免网络抖动误报 labels: severity: critical annotations: summary: 节点 {{ $labels.node }} 已超过5分钟未就绪 description: 节点 {{ $labels.node }} 状态为 NotReady可能影响其上运行的Pod。 # 节点资源压力告警 - alert: NodeMemoryPressure expr: kube_node_status_condition{conditionMemoryPressure, statustrue} 1 for: 2m labels: severity: warning annotations: summary: 节点 {{ $labels.node }} 内存压力 # Kubelet心跳丢失告警比NotReady更敏感 - alert: KubeletDown expr: absent(up{jobkubelet} 1) for: 1m labels: severity: critical annotations: summary: Kubelet {{ $labels.instance }} 心跳丢失5.3 日志收集与集中分析除了指标日志同样关键。确保通过DaemonSet如Fluentd或Filebeat将每个节点上/var/log/containers/下的容器日志以及Kubelet、容器运行时、内核的系统日志全部收集到Elasticsearch或Loki这样的中心化日志平台。当节点异常时你可以第一时间在日志平台检索该节点相关的所有日志无需登录服务器极大提升排查效率。6. 典型故障场景实战复盘最后分享两个我亲身经历的、比较复杂的故障排查案例希望能给你带来更直观的感受。6.1 案例一内核内存碎片导致节点间歇性NotReady现象集群中几个节点每隔几小时就会随机变成NotReady持续几分钟后自动恢复。kubectl describe node显示大量Failed to update node status的错误。排查过程初步检查CPU、内存、磁盘均无压力网络也正常。查看Kubelet日志 (journalctl -u kubelet)发现大量node not found和connection refused错误指向API Server。但API Server监控指标正常其他节点连接良好。登录问题节点在故障发生时尝试curl -k https://api-server:6443发现非常缓慢甚至超时。同时执行任何kubectl命令通过节点上的kubeconfig也很慢。使用ss -tnp发现存在大量TIME-WAIT状态的连接到API Server。怀疑是连接数或端口耗尽。检查sysctl net.ipv4.ip_local_port_range和net.ipv4.tcp_tw_reuse/tcp_tw_recycle注意tcp_tw_recycle在NAT环境下有问题不推荐启用。最终通过dmesg -T | grep -i “memory”发现内核日志中有“page allocation failure”错误。这表明是内核内存碎片化严重导致无法为新的网络连接分配内存。根本原因是节点上某个遗留的Java应用非容器化存在内存泄漏且长时间运行导致内核内存碎片。Kubelet需要频繁与API Server通信创建新连接时申请内存失败。解决方案短期重启有问题的宿主机进程并重启Kubelet (systemctl restart kubelet)。重启会释放所有内核内存。长期将非容器化的应用迁移到K8s集群内管理并为其设置合理的内存限制。同时调整内核参数vm.min_free_kbytes为一个更高的值需谨慎测试为内核保留更多空闲内存减少碎片化概率。6.2 案例二CoreDNS副本数不足引发的连锁反应现象部分节点上的Pod解析内部Service域名超时但解析外部域名如百度正常。同时这些节点上的Pod日志里出现大量“i/o timeout”错误且这些Pod恰好需要频繁调用其他Service。排查过程首先怀疑节点DNS配置。检查/etc/resolv.conf指向的是正确的CoreDNS Service IP通常是10.96.0.10。在问题Pod内执行nslookup kubernetes.default.svc.cluster.local发现超时。检查CoreDNS Podkubectl get pods -n kube-system -l k8s-appkube-dns。发现只有1个副本在运行且它被调度到了节点A上。查看节点A的状态是Ready的。但节点B、C上的Pod为什么解析失败检查CoreDNS Servicekubectl get svc kube-dns -n kube-system -o yaml。确认其clusterIP正确且Endpoints指向了唯一的那个CoreDNS Pod。在节点B上直接curl 10.96.0.10:53测试端口不通。问题来了Service是集群范围的理论上任何节点都应该能访问到clusterIP。检查Calico网络策略没有限制。检查kube-proxy日志正常。最终在节点B上执行iptables-save | grep 10.96.0.10发现相关的iptables规则缺失。重启节点B的kube-proxy Pod后规则恢复DNS解析正常。根本原因是kube-proxy在某些节点上异常未能正确同步Service的iptables/ipvs规则。而CoreDNS只有一个副本当它所在的节点A与节点B之间的kube-proxy出现问题时节点B上的Pod就无法通过Service IP访问到CoreDNS。解决方案与心得增加CoreDNS副本数至少部署2个副本并配置podAntiAffinity让它们分散在不同节点上。这样即使一个节点或一个Pod出问题DNS服务依然可用。命令示例kubectl scale deployment coredns -n kube-system --replicas2。监控kube-proxy将kube-proxy的容器日志纳入收集并监控其健康状态。考虑将kube-proxy的DaemonSet更新策略从RollingUpdate改为OnDelete并在维护窗口手动重启以减少对网络规则的瞬时影响。使用NodeLocal DNSCache在生产环境大规模集群中强烈建议部署NodeLocal DNSCache。它在每个节点上作为一个DaemonSet运行缓存DNS查询结果可以大幅减少对CoreDNS的依赖和跨节点查询即使CoreDNS短暂异常或网络有轻微波动本地缓存也能保障基础解析不中断。这起故障最终促使我们全面上线了NodeLocal DNSCache。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价