排查 GKE 中处于 NotReady 状态的节点

Google Kubernetes Engine (GKE) 中的 NotReady 状态表示节点的 kubelet 未正确向控制平面报告。由于 Kubernetes 不会在 NotReady 节点上调度新 Pod,因此该问题可能会降低应用容量并导致停机。

使用本文档来区分预期的 NotReady 状态和实际问题,诊断根本原因,并找到常见问题的解决方案,例如资源耗尽、网络问题和容器运行时故障。

此信息适用于负责集群稳定性的平台管理员和运维人员,以及希望了解与基础设施相关的应用行为的应用开发者。如需详细了解我们在 Google Cloud 内容中提及的常见角色和示例任务,请参阅常见的 GKE 用户角色和任务。

准备工作

  • 如需获得执行本文档中的任务所需的权限,请让您的管理员为您授予 Google Cloud 项目的以下 IAM 角色:

    如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。

    您也可以通过自定义角色或其他预定义角色来获取所需的权限。

  • 配置 kubectl 命令行工具以与您的 GKE 集群通信:

    gcloud container clusters get-credentials CLUSTER_NAME \
        --location LOCATION \
        --project PROJECT_ID
    

    替换以下内容:

    • CLUSTER_NAME:您的集群的名称。
    • LOCATION:集群的 Compute Engine 区域或可用区(例如 us-central1 或 us-central1-a)。
    • PROJECT_ID:您的 Google Cloud 项目 ID。

检查节点的状态和条件

如需确认节点是否处于 NotReady 状态并帮助您诊断根本原因,请按照以下步骤检查节点的状况、事件、日志和资源指标:

  1. 查看节点的状态。如需获取有助于诊断的其他详细信息(例如 IP 地址和内核版本),请使用 -o wide 标志:

    kubectl get nodes -o wide
    

    输出类似于以下内容:

    NAME                                STATUS     ROLES    AGE   VERSION               INTERNAL-IP  EXTERNAL-IP  OS-IMAGE                             KERNEL-VERSION   CONTAINER-RUNTIME
    gke-cluster-pool-1-node-abc1        Ready      <none>   94d   v1.32.3-gke.1785003   10.128.0.1   1.2.3.4      Container-Optimized OS from Google   6.6.72+          containerd://1.7.24
    gke-cluster-pool-1-node-def2        Ready      <none>   94d   v1.32.3-gke.1785003   10.128.0.2   5.6.7.8      Container-Optimized OS from Google   6.6.72+          containerd://1.7.24
    gke-cluster-pool-1-node-ghi3        NotReady   <none>   94d   v1.32.3-gke.1785003   10.128.0.3   9.10.11.12   Container-Optimized OS from Google   6.6.72+          containerd://1.7.24
    

    在输出中,查找 STATUS 列中值为 NotReady 的节点,并记下其名称。

  2. 查看有关具有 NotReady 状态的特定节点的更多信息,包括其状况和任何最近的 Kubernetes 事件:

    kubectl describe node NODE_NAME
    

    将 NODE_NAME 替换为具有 NotReady 状态的节点的名称。

    在输出中,重点关注 Conditions 部分,了解节点的健康状况;关注 Events 部分,了解近期问题的历史记录。例如:

    Name:                   gke-cluster-pool-1-node-ghi3
    ...
    Conditions:
    Type                          Status    LastHeartbeatTime                 LastTransitionTime                Reason                   Message
    ----                          ------    -----------------                 ------------------                ------                   -------
    NetworkUnavailable            False     Wed, 01 Oct 2025 10:29:19 +0100   Wed, 01 Oct 2025 10:29:19 +0100   RouteCreated             RouteController created a route
    MemoryPressure                Unknown   Wed, 01 Oct 2025 10:31:06 +0100   Wed, 01 Oct 2025 10:31:51 +0100   NodeStatusUnknown        Kubelet stopped posting node status.
    DiskPressure                  Unknown   Wed, 01 Oct 2025 10:31:06 +0100   Wed, 01 Oct 2025 10:31:51 +0100   NodeStatusUnknown        Kubelet stopped posting node status.
    PIDPressure                   False     Wed, 01 Oct 2025 10:31:06 +0100   Wed, 01 Oct 2025 10:29:00 +0100   KubeletHasSufficientPID  kubelet has sufficient PID available
    Ready                         Unknown   Wed, 01 Oct 2025 10:31:06 +0100   Wed, 01 Oct 2025 10:31:51 +0100   NodeStatusUnknown        Kubelet stopped posting node status.
    Events:
    Type     Reason                   Age                  From                                   Message
    ----     ------                   ----                 ----                                   -------
    Normal   Starting                 32m                  kubelet, gke-cluster-pool-1-node-ghi3  Starting kubelet.
    Warning  PLEGIsNotHealthy         5m1s (x15 over 29m)  kubelet, gke-cluster-pool-1-node-ghi3  PLEG is not healthy: pleg was last seen active 5m1.123456789s ago; threshold is 3m0s
    Normal   NodeHasSufficientMemory  5m1s (x16 over 31m)  kubelet, gke-cluster-pool-1-node-ghi3  Node gke-cluster-pool-1-node-ghi3 status is now: NodeHasSufficientMemory
    

    在 Conditions 部分中,任何否定条件的状态为 True,或 Ready 条件的状态为 Unknown,都表示存在问题。请密切关注这些条件的 Reason 和 Message 字段,因为它们说明了问题的原因。

    以下是每种条件类型的含义:

    • KernelDeadlock:如果节点的操作系统内核检测到死锁(一种可能导致节点冻结的严重错误),则为 True。
    • FrequentUnregisterNetDevice:如果节点经常取消注册网络设备,则为 True,这可能是驱动程序或硬件问题的迹象。
    • NetworkUnavailable:如果节点的网络未正确配置,则为 True。
    • OutOfDisk:如果可用磁盘空间完全耗尽,则为 True。此条件比 DiskPressure 更严重。
    • MemoryPressure:如果节点内存不足,则为 True。
    • DiskPressure:如果节点上的磁盘空间不足,则为 True。
    • PIDPressure:如果节点遇到进程 ID (PID) 耗尽问题,则为 True。
    • Ready:表示节点是否健康状况良好且已准备好接受 Pod。
      • 如果节点健康状况良好,则为 True。
      • 如果节点健康状况不佳且不接受 Pod,则为 False。
      • 如果节点控制器在宽限期内(默认值为 50 秒)未收到来自节点的消息,且节点状态为“未知”,则为 Unknown。

    接下来,检查 Events 部分,其中按时间顺序记录了有关节点的操作和观察结果。此时间轴对于了解节点变为 NotReady 之前立即发生的情况至关重要。查找有助于找出原因的特定消息,例如逐出警告(表示资源压力)、健康检查失败或节点生命周期事件(例如因维修而封锁)。

  3. 如需详细了解节点为何处于 NotReady 状态,请查看节点及其组件的日志。

    1. 检查 kubelet 日志中是否存在 NotReady 状态。

      kubelet 是向控制平面报告节点状态的主要智能体,因此其日志最有可能包含 NotReady 字面消息。这些日志是诊断 Pod 生命周期事件、资源压力情况(例如 MemoryPressure 或 DiskPressure)以及节点与 Kubernetes 控制平面的连接问题的权威来源。

    2. 在 Google Cloud 控制台中,前往 Logs Explorer 页面:

      转到日志浏览器

    3. 在查询窗格中,输入以下查询:

      resource.type="k8s_node"
      resource.labels.node_name="NODE_NAME"
      resource.labels.cluster_name="CLUSTER_NAME"
      resource.labels.location="LOCATION"
      log_id("kubelet")
      textPayload=~"(?i)NotReady"
      

      替换以下内容:

      • NODE_NAME:您要调查的节点的名称。
      • CLUSTER_NAME:您的集群的名称。
      • LOCATION:集群的 Compute Engine 区域或可用区(例如 us-central1 或 us-central1-a)。
    4. 点击运行查询,然后查看结果。

    5. 如果 kubelet 日志未揭示根本原因,请检查 container-runtime 和 node-problem-detector 日志。这些组件可能不会直接记录 NotReady 状态,但通常会记录导致问题的底层问题(例如运行时失败或内核崩溃)。

    6. 在 Logs Explorer 查询窗格中,输入以下查询:

      resource.type="k8s_node"
      resource.labels.node_name="NODE_NAME"
      resource.labels.cluster_name="CLUSTER_NAME"
      resource.labels.location="LOCATION"
      log_id("COMPONENT_NAME")
      

      将 COMPONENT_NAME 替换为以下某个值:

      • container-runtime:运行时 (containerd),负责完整的容器生命周期,包括拉取映像和管理容器执行。查看 container-runtime 日志对于排查与容器实例化、运行时服务错误或由运行时配置导致的问题相关的故障至关重要。
      • node-problem-detector:一种实用程序,可主动监控各种节点级问题并向控制平面报告。其日志对于识别可能导致节点不稳定(例如内核死锁、文件系统损坏或硬件故障)的底层系统性问题至关重要,而这些问题可能无法被其他 Kubernetes 组件捕获。
    7. 点击运行查询,然后查看结果。

  4. 使用 Metrics Explorer 查找节点变为 NotReady 时附近的资源耗尽情况:

    1. 在 Google Cloud 控制台中,前往 Metrics Explorer 页面:

      进入 Metrics Explorer

    2. 在 Metrics Explorer 中,检查节点的底层 Compute Engine 实例是否存在资源耗尽问题。重点关注与 CPU、内存和磁盘 I/O 指标相关的指标。例如:

      • GKE 节点指标:从带有 kubernetes.io/node/ 前缀的指标开始,例如 kubernetes.io/node/cpu/allocatable_utilization 或 kubernetes.io/node/memory/allocatable_utilization。这些指标显示了您的 Pod 使用了节点多少可用资源。可用量不包括 Kubernetes 为系统开销预留的资源。
      • 访客操作系统指标:如需从节点操作系统内部查看,请使用以 compute.googleapis.com/guest/ 为前缀的指标,例如 compute.googleapis.com/guest/cpu/usage 或 compute.googleapis.com/guest/memory/bytes_used。
      • Hypervisor 指标:如需从 Hypervisor 级别查看虚拟机的性能,请使用以 compute.googleapis.com/instance/ 为前缀的指标,例如 compute.googleapis.com/instance/cpu/utilization 或磁盘 I/O 指标(如 compute.googleapis.com/instance/disk/read_bytes_count)。

      对于访客操作系统和 Hypervisor 指标,您需要按底层 Compute Engine 实例名称(而非 Kubernetes 节点名称)进行过滤。您可以运行 kubectl describe node NODE_NAME 命令,并在输出中查找 ProviderID 字段,以找到节点的实例名称。实例名称是该值的最后一部分。例如:

      ...
      Spec:
      ProviderID: gce://my-gcp-project-123/us-central1-a/gke-my-cluster-default-pool-1234abcd-5678
      ...
      

      在此示例中,实例名称为 gke-my-cluster-default-pool-1234abcd-5678。

按症状确定原因

如果您已发现特定症状(例如日志消息、节点状况或集群事件),请使用下表查找问题排查建议:

类别 症状或日志消息 可能原因 问题排查步骤
节点条件 NetworkUnavailable: True 节点到控制平面的连接问题或容器网络接口 (CNI) 插件故障。 网络连接问题排查
MemoryPressure: True 节点内存不足。 排查节点资源短缺问题
DiskPressure: True 节点磁盘空间不足。 排查节点资源短缺问题
PIDPressure: True 节点可用的进程 ID 不足。 排查节点资源短缺问题
事件和日志消息 PLEG is not healthy Kubelet 因 CPU/IO 过高或 Pod 过多而过载。 解决 PLEG 问题
Out of memory: Kill process
sys oom event
节点内存已完全耗尽。 解决系统级 OOM 事件
leases.coordination.k8s.io...is forbidden kube-node-lease 命名空间卡在终止状态。 解决 kube-node-lease 命名空间的问题
Container runtime not ready
runtime is down
引用 /run/containerd/containerd.sock 或 docker.sock 时出错
Containerd 或 Docker 服务失败或配置错误。 解决容器运行时问题
Pod 卡在 Terminating 状态
Kubelet 日志显示 DeadlineExceeded(针对终止容器)
containerd 日志显示重复的 Kill container 消息
进程卡在不可中断的磁盘休眠状态(D 状态),通常与 I/O 相关。 解决卡在 D 状态的进程
集群级症状 在 DaemonSet 发布后,多个节点出现故障。 DaemonSet 正在干扰节点操作。 解决由第三方 DaemonSet 引起的问题
审核日志中出现 compute.instances.preempted。 Spot 虚拟机被抢占,这是预期行为。 确认节点抢占
kube-system Pod 卡在 Pending 状态。 准入 Webhook 正在阻止关键组件。 解决由准入 Webhook 引起的问题
exceeded quota: gcp-critical-pods 配置错误的配额正在阻止系统 Pod。 解决因资源配额而导致的问题

检查预期的 NotReady 事件

NotReady 状态并不总是表示存在问题。在计划的操作(例如节点池升级)期间,或者如果您使用某些类型的虚拟机,这可能是预期行为。

确认节点生命周期操作

症状:

在某些生命周期事件期间,节点会暂时显示 NotReady 状态。

原因:

在几个常见的生命周期事件期间,节点的状态会暂时变为 NotReady。每当创建或重新创建节点时,都会出现此行为,例如在以下场景中:

  • 节点池升级:在升级期间,每个节点都会被排空并替换。在完成初始化并加入集群之前,升级的新节点的状态为 NotReady。
  • 节点自动修复:当 GKE 替换出现故障的节点时,替换节点在预配期间会保持 NotReady 状态。
  • 集群自动扩缩器扩容:添加新节点后,这些节点会先处于 NotReady 状态,只有在完全预配并加入集群后才会变为 Ready 状态。
  • 手动更改实例模板:当您应用模板更改时,GKE 会重新创建节点。新节点在启动阶段处于 NotReady 状态。

解决方法:

节点应仅在短时间内处于 NotReady 状态。如果此状态持续超过 10 分钟,请调查其他原因。

确认节点抢占

如果您的节点在 Spot 虚拟机或抢占式虚拟机上运行,Compute Engine 可能会突然终止该节点以回收资源。对于这些类型的短期虚拟机,这是预期行为,而不是错误。

症状:

如果您观察到以下症状,则节点的 NotReady 状态很可能是由预期的 Spot 虚拟机抢占造成的:

  • 节点在被集群自动扩缩器删除并重新创建之前意外进入 NotReady 状态。
  • Cloud Audit Logs 会显示底层虚拟机实例的 compute.instances.preempted 事件。

原因:

节点在 Spot 虚拟机或抢占式虚拟机实例上运行,而 Compute Engine 为其他任务回收了这些计算资源。Spot 虚拟机可以随时中断,但通常会提前 30 秒发出终止通知。

解决方法:

仅将 Spot 虚拟机或抢占式虚拟机用于容错、无状态或批量工作负载,这些工作负载旨在妥善处理频繁的终止情况。对于无法容忍突然中断的生产或有状态工作负载,请使用标准按需虚拟机来预配节点池。

排查节点资源短缺问题

节点通常会因缺少 CPU、内存或磁盘空间等基本资源而变为 NotReady。如果节点没有足够的这些资源,关键组件将无法正常运行,从而导致应用不稳定和节点无响应。以下部分介绍了这些短缺情况可能以哪些不同的方式出现,从一般压力情况到更严重的系统级事件。

解决节点资源压力问题

当节点缺少足够的 CPU、内存、磁盘空间或进程 ID (PID) 来运行工作负载时,就会发生资源耗尽。此问题会导致出现 NotReady 状态。

症状:

如果您观察到以下节点状况和日志,则资源耗尽可能是导致节点处于 NotReady 状态的原因:

  • 在 kubectl describe node 命令的输出中,您会看到 True 状态,表示存在 OutOfDisk、MemoryPressure、DiskPressure 或 PIDPressure 等情况。
  • kubelet 日志可能包含内存不足 (OOM) 事件,表明系统调用了 OOM Killer。

原因:

节点上的工作负载集体要求的资源超出节点可以提供的资源。

解决方法:

对于 Standard 集群,请尝试以下解决方案:

对于 Autopilot 集群,您无法直接控制节点机器类型或启动磁盘大小。系统会根据您的 Pod 请求自动管理节点容量。确保工作负载资源请求在 Autopilot 限制范围内,并准确反映应用的需求。 持续存在的资源问题可能表明需要优化 Pod 请求,或者在极少数情况下,可能表明存在平台问题,需要 Cloud Customer Care 提供帮助。

解决系统级 OOM 事件

当节点的总内存耗尽时,会发生系统级内存不足 (OOM) 事件,迫使 Linux 内核终止进程以释放资源。 此事件不同于容器级 OOM 事件,后者是指单个 Pod 超出其内存限制。

症状:

如果您发现以下症状,则节点不稳定可能是系统级 OOM 事件造成的:

  • 您在节点的串行控制台日志中看到消息 Out of memory: Kill process。
  • Kubelet 日志包含 oom_watcher 事件,表明 kubelet 已检测到系统级 OOM 事件。
  • 各种进程(包括可能至关重要的系统守护程序或工作负载 Pod,不一定是内存消耗最高的进程)意外终止。

原因:

节点的总体内存已用尽。此问题可能是由系统服务中的 bug、消耗过多内存的配置错误的工作负载,或对于所有正在运行的 Pod 的集体内存需求而言过小的节点所致。

解决方法:

如需解决系统级 OOM 事件,请诊断原因,然后减少内存需求或增加节点容量。如需了解详情,请参阅排查 OOM 事件问题。

解决 PLEG 问题

Pod 生命周期事件生成器 (PLEG) 是 kubelet 内的一个组件。它会定期检查节点上所有容器的状态,并将任何更改报告给 kubelet。

当 PLEG 遇到性能问题时,无法及时向 kubelet 提供更新,这可能会导致节点变得不稳定。

症状:

如果您观察到以下症状,则表示 PLEG 可能无法正常运行:

  • 相应节点的 kubelet 日志包含类似于 PLEG is not healthy 的消息。
  • 节点的状态在 Ready 和 NotReady 之间频繁变化。

原因:

PLEG 问题通常是由性能问题引起的,这些问题会导致 kubelet 无法及时从容器运行时接收更新。常见原因包括:

  • CPU 负载过高:节点的 CPU 已饱和,会导致 kubelet 和容器运行时无法获得所需的处理能力。
  • I/O 节流:节点的启动磁盘正在执行大量 I/O 操作,这可能会减慢所有与磁盘相关的任务。
  • Pod 过多:单个节点上的 Pod 过多可能会使 kubelet 和容器运行时不堪重负,导致资源争用。

解决方法:

对于 Standard 集群,请减轻节点资源压力:

  • 降低节点负载:通过缩减部署来降低节点的整体工作负载。 您还可以使用污点和容忍度、节点亲和性或 Pod 拓扑分布约束来影响调度,从而使 Pod 在集群中的其他节点上分布得更均匀。
  • 设置 CPU 限制:为防止任何单个工作负载消耗所有可用的 CPU 资源,请对 Pod 强制执行 CPU 限制。如需了解详情,请参阅 Kubernetes 文档中的 Pod 和容器的资源管理。
  • 增加节点容量:考虑使用具有更多 CPU 和内存的更大节点来处理工作负载。如需了解详情,请参阅通过更改节点机器属性进行纵向扩缩。
  • 提升磁盘性能:如果问题与 I/O 节流相关,请使用更大的启动磁盘或升级到 SSD 启动磁盘。此更改可以显著提升磁盘性能。如需了解详情,请参阅排查磁盘性能问题。

对于 Autopilot 集群,虽然您无法直接更改现有节点的大小或磁盘类型,但可以使用自定义 ComputeClass 来影响工作负载运行的硬件。 借助此功能,您可以在工作负载清单中指定要求,例如最低 CPU 和内存量或特定机器系列,以指导 Pod 的调度位置。

如果您不使用 ComputeClass,请调整工作负载部署(例如副本数量和资源请求或限制),并确保它们在 Autopilot 限制范围内。如果在优化工作负载后 PLEG 问题仍然存在,请与 Cloud Customer Care 联系。

解决卡在 D 状态的进程

卡在不可中断的磁盘休眠状态(D 状态)的进程可能会导致节点无响应。此问题会阻止 Pod 终止,并可能导致 containerd 等关键组件失败,从而导致 NotReady 状态。

症状:

  • Pod(尤其是使用 NFS 等网络存储的 Pod)长时间卡在 Terminating 状态。
  • 尝试停止容器时,Kubelet 日志显示 DeadlineExceeded 错误。
  • 节点的串行控制台日志可能会显示有关 hung tasks 或任务被阻止超过 120 秒的内核消息。

原因:

进程在等待 I/O 操作完成且无法中断时会进入 D 状态。常见原因包括:

  • 远程文件系统速度缓慢或无响应,例如配置错误或过载的 NFS 共享。
  • 节点本地磁盘上的磁盘性能严重下降或硬件 I/O 错误。

解决方法:

如需解决 D 状态进程的问题,请确定 I/O 源,然后选择以下选项之一来清除状态:

Standard 集群

  1. 找到卡住的进程并确定它在等待什么:

    1. 使用 SSH 连接到受影响的节点:

      gcloud compute ssh NODE_NAME \
          --zone ZONE \
          --project PROJECT_ID
      

      替换以下内容:

      • NODE_NAME:要连接到的节点的名称。
      • ZONE:节点的 Compute Engine 可用区。
      • PROJECT_ID:您的项目 ID。
    2. 查找处于 D 状态的任何进程:

      ps -eo state,pid,comm,wchan | grep '^D'
      

      输出类似于以下内容:

      D  12345  my-app      nfs_wait
      D  54321  data-writer io_schedule
      

      输出不会包含标题。各列依次表示:

      • 状态
      • 进程 ID (PID)
      • 命令
      • 等待渠道 (wchan)
    3. 查看 wchan 列以确定 I/O 源:

      • 如果 wchan 列包含 nfs 或 rpc 等字词,则表示相应进程正在等待 NFS 共享。
      • 如果 wchan 列包含 io_schedule、jbd2 或 ext4 等字词,则表示相应进程正在等待节点的本地启动磁盘。
    4. 如需详细了解进程正在等待的内核函数,请检查进程的内核调用堆栈:

      cat /proc/PID/stack
      

      将 PID 替换为您在上一步中找到的进程 ID。

  2. 重启节点。重启通常是清除卡在 D 状态的进程最有效方法。

    1. 排空节点。
    2. 删除底层虚拟机实例。 GKE 通常会创建一个新的虚拟机来替换它。
  3. 在解决当前问题后,调查底层存储系统,以防止问题再次发生。

    • 对于网络存储 (NFS) 问题:请使用存储空间服务的监控工具来检查是否存在高延迟时间、服务器端错误或 GKE 节点与 NFS 服务器之间的网络问题。

    • 对于本地磁盘问题:在 Cloud Monitoring 中查看 Compute Engine 实例的 compute.googleapis.com/instance/disk/throttled_read_ops_count 和 compute.googleapis.com/instance/disk/throttled_write_ops_count 指标,检查是否存在 I/O 节流。

Autopilot 集群

  1. 尝试确定阻塞的来源:

    在 Autopilot 集群中,无法直接通过 SSH 访问节点,也无法运行 ps 或 cat /proc 等命令。您必须依赖日志和指标。

    1. 检查节点日志:在 Cloud Logging 中,分析受影响节点的日志。按节点名称和问题的时间范围过滤。查找指示 I/O 错误、存储超时(例如,到磁盘或 NFS)或来自 CSI 驱动程序的内核消息。
    2. 检查工作负载日志:检查受影响节点上运行的 Pod 的日志。应用日志可能会显示与文件操作、数据库调用或网络存储访问相关的错误。
    3. 使用 Cloud Monitoring:虽然无法获取进程级详细信息,但可以检查节点级 I/O 问题。
  2. 触发节点替换以清除状态。

    您无法手动删除底层虚拟机。如需触发替换,请排空节点。此操作会封锁节点并逐出 Pod。

    GKE 会自动检测健康状况不佳的节点并启动修复流程,通常是通过替换底层虚拟机来实现。

    如果节点在排空后仍处于卡顿状态,且未自动替换,请与 Cloud Customer Care 联系。

  3. 在解决当前问题后,调查底层存储系统,以防止问题再次发生。

    • 对于本地磁盘问题:通过查看 compute.googleapis.com/instance/disk/throttled_read_ops_count 和 compute.googleapis.com/instance/disk/throttled_write_ops_count 指标,检查 Cloud Monitoring 中是否存在 I/O 节流。您可以针对节点池的底层实例组过滤这些指标,不过各个实例由 Google 管理。
    • 对于网络存储 (NFS) 问题:请使用存储空间服务的监控工具来检查是否存在高延迟时间、服务器端错误或 GKE 节点与 NFS 服务器之间的网络问题。在 Cloud Logging 中查看任何 CSI 驱动程序 Pod 的日志。

排查核心组件故障

在排除预期原因和资源短缺问题后,节点软件或核心 Kubernetes 机制可能是问题的原因。当容器运行时等关键组件发生故障时,可能会出现 NotReady 状态。当核心 Kubernetes 健康检查机制(例如节点租约系统)出现故障时,也可能会发生这种情况。

解决容器运行时问题

容器运行时(例如 containerd)出现问题可能会导致 kubelet 无法在节点上启动 Pod。

症状:

如果您在 kubelet 日志中看到以下消息,则容器运行时问题可能是节点处于 NotReady 状态的原因:

  • Container runtime not ready
  • Container runtime docker failed!
  • docker daemon exited
  • 连接到运行时套接字(例如 unix:///var/run/docker.sock 或 unix:///run/containerd/containerd.sock)时出错。

原因:

容器运行时无法正常运行、配置错误或卡在重启循环中。

解决方法:

如需解决容器运行时问题,请执行以下操作:

  1. 分析容器运行时日志:

    1. 在 Google Cloud 控制台中,前往 Logs Explorer 页面。

      转到日志浏览器

    2. 如需查看受影响节点上的所有容器运行时警告和错误日志,请在查询窗格中输入以下内容:

      resource.type="k8s_node"
      resource.labels.node_name="NODE_NAME"
      resource.labels.cluster_name="CLUSTER_NAME"
      resource.labels.location="LOCATION"
      log_id("container-runtime")
      severity>=WARNING
      

      替换以下内容:

      • NODE_NAME:您要调查的节点的名称。
      • CLUSTER_NAME:您的集群的名称。
      • LOCATION:集群的 Compute Engine 区域或可用区(例如 us-central1 或 us-central1-a)。
    3. 点击运行查询,然后查看输出中是否有指示运行时失败原因的特定错误消息。Cloud Logging 中 containerd 日志中的 failed to load TOML 等消息通常表示文件格式错误。

    4. 如需检查运行时是否卡在重启循环中,请运行搜索启动消息的查询。短时间内出现大量此类消息可确认频繁重启。

      resource.type="k8s_node"
      resource.labels.node_name="NODE_NAME"
      resource.labels.cluster_name="CLUSTER_NAME"
      resource.labels.location="LOCATION"
      log_id("container-runtime")
      ("starting containerd" OR "Containerd cri plugin version" OR "serving..."
      OR "loading plugin" OR "containerd successfully booted")
      

      频繁重启通常表明存在底层问题,例如配置文件损坏或资源压力,导致服务反复崩溃。

  2. 查看 containerd 配置是否有修改:不正确的设置可能会导致容器运行时失败。您可以通过节点系统配置文件或通过具有提权的工作负载所做的直接修改来更改配置。

    1. 确定节点池是否使用节点系统配置文件:

      gcloud container node-pools describe NODE_POOL_NAME \
          --cluster CLUSTER_NAME \
          --location LOCATION \
          --format="yaml(config.containerdConfig)"
      

      替换以下内容:

      • NODE_POOL_NAME:节点池的名称。
      • CLUSTER_NAME:您的集群的名称。
      • LOCATION:集群的 Compute Engine 区域或可用区。

      如果输出显示 containerdConfig 部分,则表示 GKE 正在管理这些自定义设置。如需修改或恢复设置,请按照在 GKE 节点中自定义 containerd 配置中的说明操作。

    2. 如果 GKE 管理的自定义设置未处于活跃状态,或者您怀疑存在其他更改,请查找可能直接修改节点文件系统的工作负载。查找具有提升权限 (securityContext.privileged: true) 或 hostPath 卷的 DaemonSet,这些卷会挂载敏感目录,例如 /etc。

      如需检查其配置,请以 YAML 格式列出所有 DaemonSet:

      kubectl get daemonsets --all-namespaces -o yaml
      

      查看输出,并检查任何可疑 DaemonSet 的日志。

    3. 对于 Standard 集群,请直接检查配置文件。 在 Autopilot 集群中,无法进行 SSH 访问和手动文件检查,因为 Google 会管理运行时配置。向 Google Cloud Customer Care 报告持续存在的运行时问题。

      如果您使用的是 Standard 集群,请检查该文件:

      1. 使用 SSH 连接到节点:

        gcloud compute ssh NODE_NAME \
            --zone ZONE \
            --project PROJECT_ID
        

        替换以下内容:

        • NODE_NAME:要连接到的节点的名称。
        • ZONE:节点的 Compute Engine 可用区。
        • PROJECT_ID:您的项目 ID。
      2. 显示 containerd 配置文件的内容:

        sudo cat /etc/containerd/config.toml
        
      3. 如需检查最近的修改,请列出文件详细信息:

        ls -l /etc/containerd/config.toml
        
    4. 将此文件的内容与您在上一步中运行的 gcloud node-pools describe 命令的 containerdConfig 输出进行比较。/etc/containerd/config.toml 中未出现在 gcloud 输出中的任何设置都是非受管更改。

    5. 如需更正任何配置错误,请移除未通过节点系统配置应用的任何更改。

  3. 排查常见的运行时问题:如需了解更多问题排查步骤,请参阅排查容器运行时问题。

解决 kube-node-lease 命名空间的问题

kube-node-lease 命名空间中的资源负责维护节点健康状况。此命名空间不应删除。尝试删除此命名空间会导致该命名空间卡在 Terminating 状态。当 kube-node-lease 命名空间卡在 Terminating 状态时,kubelet 无法续订健康检查租约。此问题会导致控制平面认为节点健康状况不佳,从而导致整个集群出现节点在 Ready 和 NotReady 状态之间交替的问题。

症状:

如果您观察到以下症状,则 kube-node-lease 命名空间存在问题可能是导致集群范围不稳定性的原因:

  • 每个节点上的 kubelet 日志都会显示类似于以下内容的持续性错误:

    leases.coordination.k8s.io NODE_NAME is forbidden: unable to create new content in namespace kube-node-lease because it is being terminated
    
  • 集群中的节点在 Ready 和 NotReady 状态之间反复交替。

原因:

管理节点检测信号的 kube-node-lease 命名空间异常卡在 Terminating 状态。此错误会阻止 Kubernetes API 服务器允许在命名空间内创建或修改对象。因此,kubelet 无法续订 Lease 对象,而这些对象对于向控制平面发出活跃信号至关重要。如果没有这些状态更新,控制平面就无法确认节点是否健康状况良好,从而导致节点的状态在 Ready 和 NotReady 之间交替变化。

kube-node-lease 命名空间本身可能卡在 Terminating 状态的根本原因包括:

  • 具有终结器的资源:虽然对于系统 kube-node-lease 命名空间(主要包含 Lease 对象)来说,这种情况不太常见,但其中的资源可能具有终结器。Kubernetes 终结器是键,用于指示控制器必须先执行清理任务,然后才能删除资源。如果负责移除终结器的控制器无法正常运行,则资源不会删除,并且命名空间删除进程会停止。
  • 健康状况不佳或无响应的聚合 API 服务:如果用于注册聚合 API 服务器的 APIService 对象与命名空间相关联并变得健康状况不佳,则命名空间终止可能会阻止。控制平面可能会等待聚合 API 服务器正常关闭或清理,但如果服务无响应,则不会发生这种情况。
  • 控制平面或控制器问题:在极少数情况下,Kubernetes 控制平面(尤其是命名空间控制器)内的 bug 或问题可能会阻止成功进行垃圾回收和删除命名空间。

解决方法:

请按照排查命名空间卡在终止状态的问题中的指导操作。

网络连接问题排查

网络问题可能会阻止节点与控制平面通信,或阻止 CNI 插件等关键组件正常运行,从而导致节点处于 NotReady 状态。

症状:

如果您观察到以下症状,则可能是网络问题导致节点处于 NotReady 状态:

  • NetworkNotReady 条件为 True。
  • 节点上的 Kubelet 日志显示类似于以下内容的错误:
    • connection timeout to the control plane IP address
    • network plugin not ready
    • CNI plugin not initialized
    • 尝试访问控制平面 IP 地址时,出现 connection refused 或 timeout 消息。
  • Pod(尤其在 kube-system 命名空间中)卡在 ContainerCreating 状态,并显示 NetworkPluginNotReady 等事件。

原因:

与网络相关的症状通常表示以下其中一个方面存在故障:

  • 连接问题:节点无法与 Kubernetes 控制平面建立稳定的网络连接。
  • CNI 插件故障:负责配置 Pod 网络的 CNI 插件未正常运行或未能初始化。
  • Webhook 问题:配置错误的准入 Webhook 可能会干扰 CNI 插件相关资源,导致无法正确配置网络。

解决方法:

如需解决网络问题,请执行以下操作:

  1. 解决暂时性 NetworkNotReady 状态:在新创建的节点上,看到短暂的 NetworkNotReady 事件是正常的。在 CNI 插件和其他组件初始化时,此状态应在一两分钟内解决。如果状态仍然存在,请继续执行以下步骤。

  2. 验证节点与控制平面之间的连接和防火墙规则:确保节点与控制平面之间的网络路径已打开且正常运行:

    1. 检查防火墙规则:确保您的 VPC 防火墙规则允许 GKE 节点与控制平面之间传输必要的流量。如需了解 GKE 在节点与控制平面通信方面所需的规则,请参阅自动创建的防火墙规则。
    2. 测试连接:使用 Network Intelligence Center 中的连接测试来验证节点内部 IP 地址与控制平面端点 IP 地址(位于端口 443 上)之间的网络路径。结果为 Not Reachable 通常有助于您找出阻止通信的防火墙规则或路由问题。
  3. 调查 CNI 插件状态和日志:如果节点网络未就绪,则可能是 CNI 插件存在问题。

    1. 检查 CNI Pod 状态:确定正在使用的 CNI 插件(例如 netd 或 calico-node),并检查其 Pod 在 kube-system 命名空间中的状态。您可以使用以下命令过滤特定节点:

      kubectl get pods \
          -n kube-system \
          -o wide \
          --field-selector spec.nodeName=NODE_NAME \
          | grep -E "netd|calico|anetd"
      
    2. 检查 CNI Pod 日志:如果 Pod 无法正常运行,请在 Cloud Logging 中检查其日志,以获取详细的错误消息。对于特定节点上的 netd Pod,请使用类似于以下内容的查询:

      resource.type="k8s_container"
      resource.labels.cluster_name="CLUSTER_NAME"
      resource.labels.location="LOCATION"
      resource.labels.namespace_name="kube-system"
      labels."k8s-pod/app"="netd"
      resource.labels.node_name="NODE_NAME"
      severity>=WARNING
      
    3. 解决特定的 CNI 错误:

      • 如果日志显示 Failed to allocate IP address,则可能是您的 Pod IP 地址范围已用尽。验证 Pod IP 地址利用率,并检查集群的 CIDR 范围。
      • 如果日志显示 NetworkPluginNotReady 或 cni plugin not initialized,请确认节点具有足够的 CPU 和内存资源。您还可以尝试通过删除 CNI Pod 来重启它,这样一来,DaemonSet 就会重新创建该 Pod。
      • 如果您使用 GKE Dataplane V2,且日志显示 Cilium API client timeout exceeded,请重启节点上的 anetd Pod。
    4. 检查是否存在准入 Webhook 干扰:出现故障的 Webhook 可能会阻止 CNI Pod 启动,导致节点处于 NetworkNotReady 状态。

    5. 检查 API 服务器日志:在 Cloud Logging 中查看 API 服务器日志,了解与 Webhook 调用相关的错误。如需确定 Webhook 是否阻止了 CNI 资源创建,请搜索类似 failed calling webhook 的消息。

      如果某个 Webhook 导致了问题,您可能需要找出有问题的 ValidatingWebhookConfiguration 或 MutatingWebhookConfiguration,并暂时将其停用,以便节点进入就绪状态。如需了解详情,请参阅解决由准入 Webhook 引起的问题。

排查集群配置错误

以下部分可帮助您审核一些可能会干扰正常节点操作的集群范围配置。

解决由准入 Webhook 引起的问题

配置错误、不可用或速度过慢的准入 Webhook 可能会阻止关键 API 请求,从而导致基本组件无法启动或节点无法加入集群。

症状:

如果您观察到以下症状,则可能是配置错误的或不可用的准入 Webhook 正在阻止必要的集群操作:

  • Pod(尤其在 kube-system 命名空间中,例如 CNI 或存储 Pod)卡在 Pending 或 Terminating 状态。
  • 新节点无法加入集群,经常会因超时而显示 NotReady 状态。

原因:

配置错误或无响应的准入 Webhook 可能会阻止基本的集群操作。

解决方法:

查看您的 Webhook 配置,确保它们具有弹性且范围适当。为防止中断,请将非关键 Webhook 的 failurePolicy 字段设置为 Ignore。对于关键 Webhook,请确保后端服务具有高可用性,并使用 namespaceSelector 将 kube-system 命名空间从 Webhook 监督中排除,以避免控制平面死锁。如需了解详情,请参阅确保使用 Webhook 时控制平面的稳定性。

解决因资源配额而导致的问题

kube-system 命名空间中计算错误的资源配额可能会阻止 GKE 创建关键系统 Pod。由于网络 (CNI) 和 DNS 等组件被阻止,此问题可能会阻止新节点成功加入集群。

症状:

  • kube-system 命名空间中的关键 Pod(例如 netd、konnectivity-agent 或 kube-dns)卡在 Pending 状态。
  • 集群日志或 kubectl describe pod 输出中的错误消息显示了 exceeded quota: gcp-critical-pods 等故障。

原因:

当 Kubernetes 资源配额控制器停止准确更新 ResourceQuota 对象中的使用数量时,就会出现此问题。常见原因是第三方准入 Webhook 出现故障,导致控制器更新受阻,使得配额用量看起来比实际高得多。

解决方法:

  1. 由于有问题的 Webhook 最有可能是根本原因,请按照解决因准入 Webhook 引起的问题部分中的指导,找出并修复可能会阻止系统组件的任何 Webhook。修复 Webhook 通常会自动解决配额问题。
  2. 验证配额的记录使用情况是否与实际运行的 Pod 数量不同步。此步骤用于确认 ResourceQuota 对象的数量是否不正确:

    1. 检查配额的报告使用情况:

      kubectl get resourcequota gcp-critical-pods -n kube-system -o yaml
      
    2. 检查 Pod 的实际数量:

      kubectl get pods -n kube-system --no-headers | wc -l
      
  3. 如果 ResourceQuota 中的已用数量看起来错误(例如,远高于实际 Pod 数量),请删除 gcp-critical-pods 对象。GKE 控制平面旨在自动重新创建此对象,并使用正确且协调一致的使用次数:

    kubectl delete resourcequota gcp-critical-pods -n kube-system
    
  4. 监控 kube-system 命名空间几分钟,确保对象已重新创建,且待处理的 Pod 开始调度。

解决由第三方 DaemonSet 引起的问题

新部署或更新的第三方 DaemonSet(通常用于安全性、监控或日志记录)有时会导致节点不稳定。如果 DaemonSet 干扰了节点的容器运行时或网络,消耗了过多的系统资源,或者进行了意外的系统修改,则可能会发生此问题。

症状:

如果您观察到以下症状,则最近部署或修改的第三方 DaemonSet 可能是节点故障的原因:

  • 在部署或更新 DaemonSet 后不久,多个节点(可能遍布整个集群)进入 NotReady 状态。
  • 受影响节点的 Kubelet 日志会报告如下错误:
    • container runtime is down
    • Failed to create pod sandbox
    • 连接到容器运行时套接字(例如 /run/containerd/containerd.sock)时出错。
  • Pod(包括系统 Pod 或 DaemonSet 自身的 Pod)卡在 PodInitializing 或 ContainerCreating 状态。
  • 应用的容器日志显示异常错误,例如 exec format error。
  • Node Problem Detector 可能会报告与运行时健康状况或资源压力相关的条件。

原因:

第三方 DaemonSet 可能会因以下原因而影响节点稳定性:

  • 消耗过多的 CPU、内存或磁盘 I/O,影响关键节点组件的性能。
  • 干扰容器运行时的运行。
  • 导致与节点的网络配置或容器网络接口 (CNI) 插件发生冲突。
  • 以意外方式更改系统配置或安全政策。

解决方法:

如需确定是否是 DaemonSet 导致了问题,请对其进行隔离和测试:

  1. 确定 DaemonSet:列出集群中运行的所有 DaemonSet:

    kubectl get daemonsets --all-namespaces
    

    请密切关注不属于默认 GKE 安装的 DaemonSet。

    您通常可以通过查看以下内容来识别这些 DaemonSet:

    • 命名空间:默认 GKE 组件通常在 kube-system 命名空间中运行。其他命名空间中的 DaemonSet 可能是第三方或自定义的。
    • 命名:默认 DaemonSet 通常具有 gke-metrics-agent、netd 或 calico-node 等名称。第三方智能体的名称通常会反映相应产品。
  2. 关联部署时间:检查 NotReady 节点的出现是否与特定第三方 DaemonSet 的部署或更新相一致。

  3. 在单个节点上进行测试:

    1. 选择一个受影响的节点。
    2. 封锁并排空节点。
    3. 暂时阻止 DaemonSet 在此节点上进行调度:
      • 应用临时节点标签,并在 DaemonSet 的清单中配置节点亲和性或反亲和性。
      • 删除该特定节点上的 DaemonSet Pod。
    4. 重启节点的虚拟机实例。
    5. 观察节点是否变为 Ready,并在 DaemonSet 未在其上运行时保持稳定。如果重新引入 DaemonSet 后问题再次出现,则很可能就是此因素造成的。
  4. 咨询供应商:如果您怀疑是第三方智能体导致了问题,请查看供应商的文档,了解已知的兼容性问题或在 GKE 上运行该智能体的最佳实践。如果您需要进一步支持,请与软件供应商联系。

验证节点是否已恢复

应用潜在解决方案后,按照以下步骤验证节点是否已成功恢复并稳定运行:

  1. 检查节点的状态:

    kubectl get nodes -o wide
    

    在输出中查找受影响的节点。Status 列现在应显示值 Ready。应用修复后,状态可能需要几分钟才能更新。如果状态仍显示 NotReady 或在各种状态之间循环,则表示问题尚未完全解决。

  2. 检查节点的 Conditions 部分:

    kubectl describe node NODE_NAME
    

    在 Conditions 部分中,验证以下值:

    • Ready 条件的状态为 True。
    • 之前状态为 True 的否定条件(例如 MemoryPressure 或 NetworkUnavailable)现在状态为 False。这些条件的 Reason 和 Message 字段应指明问题已解决。
  3. 测试 Pod 调度。如果节点之前无法运行工作负载,请检查是否正在其上调度新 Pod,以及现有 Pod 是否正常运行:

    kubectl get pods --all-namespaces -o wide --field-selector spec.nodeName=NODE_NAME
    

    节点上的 Pod 应具有 Running 或 Completed 状态。您不应看到 Pod 卡在 Pending 或其他错误状态。

后续步骤