排查集群自动扩缩器未缩容问题


本页面介绍如何诊断和解决导致集群自动扩缩器无法对 Google Kubernetes Engine (GKE) 节点缩容的问题。

本页面适用于希望解决应用或服务出现的意外或负面情况的应用开发者,以及希望防止产品和服务交付中断的平台管理员和运维人员。

了解集群自动扩缩器何时会对节点缩容

在继续执行问题排查步骤之前,了解集群自动扩缩器何时会尝试对节点缩容可能会有所帮助。集群自动扩缩器之所以没有缩容,有可能是因为它没有这个需要。

集群自动扩缩器通过计算“利用率因子”来确定节点是否未充分利用且符合缩容条件。利用率因子的计算方式是节点上的 Pod 请求的 vCPU 和内存除以节点上的可分配 vCPU 和内存。

集群自动扩缩器每 10 秒检查一次节点的利用率因子,以查看其是否低于所需阈值。如果您使用的是 balanced 自动扩缩配置文件,则利用率因子阈值为 0.5。如果您使用的是 optimize-utilization 配置文件,则利用率因子则各有不同。如果利用率因子低于 vCPU 和内存的所需阈值,集群自动扩缩器会认为节点未充分利用。

如果节点未充分利用,集群自动扩缩器会将节点标记为待移除,并在接下来的 10 分钟内监控节点,以确保利用率因子保持在所需阈值以下。如果节点在 10 分钟后仍未充分利用,集群自动扩缩器应移除该节点。

示例:利用率计算

您有一个已启用集群自动扩缩器的集群,并且您使用的是 balanced 自动扩缩配置文件。此集群上的节点已配置了 e2-standard-4 机器类型,该机器类型提供 4 个 vCPU 和 16 GB 内存。此节点上的 Pod 请求 0.5 个 vCPU 和 10 GB 内存,因此集群自动扩缩器会计算以下利用率因子:

  • vCPU 利用率因子:0.5 个 vCPU/4 个 vCPU = 0.125
  • 内存利用率因子:10 GB/16 GB = 0.625

在此场景中,集群自动扩缩器不会认为此节点未充分利用,因为内存利用率因子 (0.625) 超过阈值 0.5。即使 vCPU 利用率较低,较高的内存用量也会阻止缩容,以确保 Pod 的工作负载仍有足够的资源可用。

检查问题是否是由限制引起的

如果您发现集群保持低利用率的时间超过了 10 分钟,且没有缩容,请确保该问题不是由集群自动扩缩器的限制之一引起的。

查看错误

如果问题不是由限制导致的,您通常可以通过查看错误消息来诊断问题的原因:

在通知中查看错误

如果您发现的问题发生在 72 小时以内,请在 Google Cloud 控制台中查看有关错误的通知。这些通知可提供有关集群自动扩缩器未缩容的原因的宝贵分析洞见,并提供有关如何解决错误以及如何查看相关日志以进行进一步调查的建议。

如需在 Google Cloud 控制台中查看通知,请完成以下步骤:

  1. 在 Google Cloud 控制台中,转到 Kubernetes 集群页面。

    转到 KUBERNETES 集群

  2. 查看通知列。以下通知与缩容问题相关:

    • Can't scale down nodes
    • Scale down blocked by pod
  3. 点击相关通知即可看到一个窗格,其中包含导致问题的原因的详细信息以及解决问题的建议措施。

  4. 可选:如需查看此事件的日志,请点击日志。此操作会带您前往 Logs Explorer,其中预先填充了查询,以帮助您进一步调查扩缩事件。如需详细了解缩容事件的工作情况,请参阅查看集群自动扩缩器事件

如果您在通知中查看建议后仍遇到问题,请参阅错误消息表格以获取更多帮助。

在事件中查看错误

如果您发现的问题发生在 72 小时之前,请在 Cloud Logging 中查看事件。如果发生错误,系统通常会在事件中记录相应错误。

如需在 Google Cloud 控制台中查看集群自动扩缩器日志,请完成以下步骤:

  1. 在 Google Cloud 控制台中,转到 Kubernetes 集群页面。

    转到 KUBERNETES 集群

  2. 选择要调查的集群的名称,以查看其集群详情页面。

  3. 集群详情页面上,点击日志标签页。

  4. 日志标签页上,点击自动扩缩器日志标签页以查看日志。

  5. 可选:如需应用更高级的过滤条件来缩小结果范围,请点击页面右侧带有箭头的按钮以在 Logs Explorer 中查看日志。

如需详细了解缩容事件的工作情况,请参阅查看集群自动扩缩器事件。如需了解如何使用 Cloud Logging 的示例,请参阅以下问题排查示例

示例:排查 72 小时以前的问题

以下示例展示了如何调查和解决集群未缩容的问题。

场景

一周前,您查看了 GKE Enterprise 信息中心,发现您的集群仅利用了 10% 的 CPU 和内存。尽管利用率低,但集群自动扩缩器未按预期删除该节点。现在,当您查看信息中心时,问题似乎已解决,但您决定弄清楚情况,以便避免该问题再次发生。

调查

  1. 由于问题发生在 72 小时之前,因此您使用 Cloud Logging 而不是通过查看通知消息来调查问题。
  2. 在 Cloud Logging 中,您可以找到集群自动扩缩器事件的日志记录详细信息,如在事件中查看错误中所述。
  3. 您需搜索 scaleDown 事件,这些事件在 nodesToBeRemoved 字段中包含你要调查的集群所拥有的节点。您可以过滤日志条目,包括按特定 JSON 字段值进行过滤。如需了解详情,请参阅高级日志查询
  4. 您未找到任何 scaleDown 事件。不过,如果您确实找到了 scaleDown 事件,可以搜索包含关联 eventIdeventResult 事件。然后,您可以在 errorMsg 字段中搜索错误。
  5. 您决定继续调查,搜索 noScaleDown 事件,这些事件在节点字段中包含您要调查的节点。

    您发现了一个 noScaleDown 事件,其中包含节点不缩容的原因。消息 ID 为 "no.scale.down.node.pod.not.backed.by.controller" 且有一个参数:"test-single-pod"

解决方法

查阅错误消息表,发现此消息指出 Pod 之所以阻止缩容是因为控制器不支持缩容。您发现一种解决方案是向 Pod 添加 "cluster-autoscaler.kubernetes.io/safe-to-evict": "true" 注解。您调查了 test-single-pod,发现一位同事添加了注解,并且在应用注解后,集群自动扩缩器正确地缩容了集群。您决定在安全的情况下将注解添加到所有其他 Pod 以避免该问题再次发生。

解决缩容错误

确定错误后,请使用以下各表来帮助了解导致错误的原因以及如何解决该错误。

ScaleDown 错误

您可以在相应 eventResult 事件的 resultInfo.results[].errorMsg 字段中找到针对 scaleDown 事件的错误事件消息。

事件消息 详细信息 参数 应对措施
"scale.down.error.failed.to.mark.to.be.deleted" 节点无法标记为待删除。 故障节点名称。 此消息应该是暂时性的。 如果它持续存在,请与 Cloud Customer Care 团队联系以进行进一步调查。
"scale.down.error.failed.to.evict.pods" 由于无法将某些 Pod 从节点中逐出,因此集群自动扩缩器无法缩容。 故障节点名称。 查看 Pod 的 PodDisruptionBudget,并确保规则在可接受时允许逐出应用副本。如需了解详情,请参阅 Kubernetes 文档中的为应用指定中断预算
"scale.down.error.failed.to.delete.node.min.size.reached" 集群自动扩缩器无法缩容,原因是集群已达到规模下限,导致无法删除节点。 故障节点名称。 查看为节点池自动扩缩设置的最小值,并根据需要调整设置。如需了解详情,请参阅错误:集群中的节点数已达到规模下限

noScaleDown 事件的原因

当系统阻止集群自动扩缩器删除节点时,会定期发出 noScaleDown 事件。noScaleDown 事件是尽力而为事件,无法涵盖所有可能的情况。

NoScaleDown 顶级原因

noScaleDown 事件的顶级原因消息会显示在 noDecisionStatus.noScaleDown.reason 字段中。该消息包含集群自动扩缩器无法对集群缩容的顶级原因。

事件消息 详细信息 应对措施
"no.scale.down.in.backoff" 由于缩容处于退避期(暂时被阻止),因此集群自动扩缩器无法缩容。

此消息应该是暂时性的,并且可能会在最近发生扩容事件时出现。

如果此消息持续存在,请与 Cloud Customer Care 联系以进行进一步调查。

"no.scale.down.in.progress"

由于之前的缩容仍在进行中,因此集群自动扩缩器无法进行缩容。

此消息应该是暂时性的,因为 Pod 最终会被移除。如果此消息频繁出现,请查看 Pod 阻止缩容的终止宽限期。为了加快解决速度,您还可以删除不再需要的 Pod。

NoScaleDown 节点级原因

noScaleDown 事件的节点级原因消息会显示在 noDecisionStatus.noScaleDown.nodes[].reason field 中。该消息包含集群自动扩缩器无法移除特定节点的原因。

事件消息 详细信息 参数 应对措施
"no.scale.down.node.scale.down.disabled.annotation" 集群自动扩缩器无法从节点池中移除节点,因为节点已添加了 cluster-autoscaler.kubernetes.io/scale-down-disabled: true 注解。 不适用 集群自动扩缩器会跳过带有此注解的节点,而不会考虑其利用率,并且无论节点的利用率因子如何,都会记录此消息。如果您希望集群自动扩缩器缩容这些节点,请移除注解。
"no.scale.down.node.node.group.min.size.reached"

当节点组规模超过规模下限时,集群自动扩缩器无法进行缩容。

出现这种情况是因为移除节点会违反节点自动预配设置中定义的集群级最低资源限制。

不适用 查看为节点池自动扩缩设置的最小值。如果您希望集群自动扩缩器缩容此节点,请调整最小值
"no.scale.down.node.minimal.resource.limits.exceeded"

由于缩容会违反集群范围的最低资源限制,因此集群自动扩缩器无法对节点进行缩容。

这些是针对节点自动预配设置的资源限制。

不适用 查看内存和 vCPU 的限制,如果您希望集群自动扩缩器对此节点缩容,请提高限制
"no.scale.down.node.no.place.to.move.pods" 由于没有位置可供移动 Pod,因此集群自动扩缩器无法缩容。 不适用 如果您希望重新调度 Pod,请查看未充分利用的节点上的 Pod 的调度要求,以确定它们是否可以移动到集群中的其他节点。如需了解详情,请参阅错误:没有位置可供移动 Pod
"no.scale.down.node.pod.not.backed.by.controller"

Pod 由于不受控制器支持而阻止了缩容。

具体而言,由于 Pod 缺少已识别的控制器,集群自动扩缩器无法对未充分利用的节点缩容。 允许的控制器包括 ReplicationController、DaemonSet、Job、StatefulSet 或 ReplicaSet。

执行阻止操作的 Pod 的名称。 为 Pod 设置注解 "cluster-autoscaler.kubernetes.io/safe-to-evict": "true" 或定义可接受的控制器。
"no.scale.down.node.pod.has.local.storage" Pod 由于具有本地存储空间而阻止纵向缩容。 执行阻止操作的 Pod 的名称。 如果 Pod 的本地存储空间中的数据不重要,请为 Pod 设置注解 "cluster-autoscaler.kubernetes.io/safe-to-evict": "true"。 只有使用低于 1.22 版本的集群才会出现此错误。
"no.scale.down.node.pod.not.safe.to.evict.annotation" 节点上的 Pod 具有 safe-to-evict=false 注解。 执行阻止操作的 Pod 的名称。 如果可以安全地逐出 Pod,请修改 Pod 的清单并将注解更新为 "cluster-autoscaler.kubernetes.io/safe-to-evict": "true"
"no.scale.down.node.pod.kube.system.unmovable" Pod 阻止缩容,因为它是 kube-system 命名空间中不含 PodDisruptionBudget 的非 DaemonSet、非镜像 Pod。 执行阻止操作的 Pod 的名称。

默认情况下,集群自动扩缩器不会移除 kube-system 命名空间中的 Pod。

如需解决此问题,请为 kube-system Pod 添加 PodDisruptionBudget,或者结合使用节点池污点和容忍来分隔 kube-system Pod 与应用 Pod。如需了解详情,请参阅错误:kube-system Pod 不可移动

"no.scale.down.node.pod.not.enough.pdb" Pod 没有足够的 PodDisruptionBudget,因此会阻止缩容。 执行阻止操作的 Pod 的名称。 查看 Pod 的 PodDisruptionBudget,并考虑放宽其限制。如需了解详情,请参阅错误:PodDisruptionBudget 不足
"no.scale.down.node.pod.controller.not.found" Pod 会阻止缩容,因为找不到它的控制器(例如 Deployment 或 ReplicaSet)。 不适用 如需确定执行了哪些操作使得 Pod 的控制器在移除后 Pod 仍保持运行,请查看日志。如需解决此问题,请手动删除 Pod。
"no.scale.down.node.pod.unexpected.error" Pod 由于意外错误而阻止了缩容。 不适用 此错误的根本原因不明。 与 Cloud Customer Care 团队联系以进行进一步调查。

进行进一步调查

以下部分提供了有关如何使用 Logs Explorer 和 gcpdiag 进一步了解错误的指导。

在 Logs Explorer 中调查错误

如果您想进一步调查错误消息,可以查看特定于错误的日志:

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

    前往 Logs Explorer

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

    resource.type="k8s_cluster"
    log_id("container.googleapis.com/cluster-autoscaler-visibility")
    jsonPayload.resultInfo.results.errorMsg.messageId="ERROR_MESSAGE"
    

    ERROR_MESSAGE 替换为您要调查的消息。例如 scale.down.error.failed.to.delete.node.min.size.reached

  3. 点击运行查询

使用 gcpdiag 调试某些错误

gcpdiag 是一种在 Google Cloud 技术工程师的支持下创建的开源工具。它不是由官方提供支持的 Google Cloud 产品。

如果您遇到以下某个错误消息,可以使用 gcpdiag 来帮助排查问题:

  • scale.down.error.failed.to.evict.pods
  • no.scale.down.node.node.group.min.size.reached

如需查看所有 gcpdiag 工具标志的列表和说明,请参阅 gcpdiag 使用说明

解决复杂的缩容错误

以下部分提供了相关指导,用于解决缓解措施涉及多个步骤的错误,以及没有相关集群自动扩缩器事件消息的错误。

错误:集群中的节点数已达到规模下限

如果您看到以下错误,则表示集群自动扩缩器无法删除节点,因为集群中的节点数已达到规模下限:

通知

由于已达到集群自动扩缩器的最小资源限制,系统阻止了对利用率不足的节点进行缩容。

事件

"scale.down.error.failed.to.delete.node.min.size.reached"

如需解决此问题,请查看并更新自动扩缩的最小限制:

  1. 在 Google Cloud 控制台中,前往 Kubernetes 集群页面:

    前往 KUBERNETES 集群

  2. 点击通知或 Cloud Logging 中标识的集群的名称。

  3. 集群详情页面上,前往节点标签页。

  4. 查看节点数列中的值,并将其与自动扩缩列中列出的节点数下限进行比较。例如,如果您看到自动扩缩列中列出了 4 - 6 个节点,并且节点池中的节点数为 4,则节点池数量已等于规模下限,因此集群自动扩缩器无法进一步对节点缩容。

  5. 如果配置正确且节点数的值等于为自动扩缩定义的最小值,则集群自动扩缩器会按预期运行。如果节点数下限过高,无法满足您的需求,请降低规模下限,以便节点可以缩容。

错误:没有位置可供移动 Pod

当集群自动扩缩器尝试对节点缩容但无法完成时,会发生以下错误,因为该节点上的 Pod 无法移动到其他节点:

通知

利用率不足的节点被阻止缩容,因为它存在无法移至集群中的其他节点的 Pod。

事件

"no.scale.down.node.no.place.to.move.pods"

如果您不希望重新调度此 Pod,则应该会出现此消息,并且无需进行任何更改。如果您确实希望重新调度 Pod,请查看 Pod 清单的 pod.spec block 中的以下定义:

  • NodeAffinity:查看未充分利用的节点上的 Pod 的调度要求。您可以通过检查 Pod 清单并查找任何 NodeAffinity 或 NodeSelector 规则来查看这些要求。如果 Pod 定义了 nodeSelector,并且集群中没有其他节点(来自其他节点池)与此选择器匹配,则集群自动扩缩器无法将 Pod 移至其他节点,这反过来会阻止其移除任何未充分利用的节点。
  • maxPodConstraint:如果 maxPodConstraint 配置为默认值 110 以外的任何其他数字,请确认这是有意的更改。降低此值会增加出现问题的可能性。如果集群中的所有其他节点都已达到 maxPodConstraint 中定义的值,则集群自动扩缩器无法将 Pod 重新调度到其他节点,从而无法为新 Pod 留出空间进行调度。通过增加 maxPodConstraint 值,可以在节点上调度更多 Pod,并且集群自动扩缩器将有足够的空间来重新调度 Pod 和对未充分利用的节点进行缩容。定义 maxPodConstraint 时,请注意每个节点上大约有 10 个系统 Pod。
  • hostPort:为 Pod 指定 hostPort 意味着该节点上只能运行一个 Pod。这可能会使集群自动扩缩器难以减少节点数量,因为如果节点的端口已在使用,Pod 可能无法移动到其他节点。这是预期行为。

错误:kube-system Pod 无法移动

当系统 Pod 阻止缩容时,会发生以下错误:

通知

Pod 阻止纵向缩容,因为它是 kube-system 命名空间中不含 PodDisruptionBudget 的非 DaemonSet、非镜像 Pod。

事件

"no.scale.down.node.pod.kube.system.unmovable"

kube-system 命名空间中的 Pod 会被视为系统 Pod。默认情况下,集群自动扩缩器不会移除 kube-system 命名空间中的 Pod。

如需解决此错误,请选择以下解决方案之一:

  • kube-system Pod 添加一个 PodDisruptionBudget。如需详细了解如何为 kube-system Pod 手动添加 PodDisruptionBudget,请参阅 Kubernetes 集群自动扩缩器常见问题解答

    创建 PodDisruptionBudget 可能会影响系统工作负载的可用性,从而导致集群停机。集群自动扩缩器会在缩容进程中在不同的工作器节点上重新调度这些系统工作负载。

  • 结合使用节点池污点和容忍来分隔 kube-system Pod 与应用 Pod。如需了解详情,请参阅 GKE 中的节点自动预配

验证节点是否具有 kube-system Pod

如果您不确定节点是否正在运行 kube-system Pod,并且想要进行验证,请完成以下步骤:

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

    进入日志浏览器

  2. 点击查询构建器

  3. 使用以下查询查找所有网络政策日志记录:

    - resource.labels.location="CLUSTER_LOCATION"
    resource.labels.cluster_name="CLUSTER_NAME"
    logName="projects/PROJECT_ID/logs/container.googleapis.com%2Fcluster-autoscaler-visibility"
    jsonPayload.noDecisionStatus.noScaleDown.nodes.node.mig.nodepool="NODE_POOL_NAME"
    

    替换以下内容:

    • CLUSTER_LOCATION:您的集群所在的区域。
    • CLUSTER_NAME:您的集群的名称。
    • PROJECT_ID:您的集群所属项目的 ID。
    • NODE_POOL_NAME:您的节点池的名称。

    如果有 kube-system Pod 正在节点池上运行,则输出内容包括:

    "no.scale.down.node.pod.kube.system.unmovable"
    

错误:PodDisruptionBudget 不足

当 PodDisruptionBudget 阻止缩容时,会发生以下错误:

通知

利用率不足的节点的缩容被阻止,因为在它上面运行的 Pod 没有足够的 Pod 中断预算来允许逐出 Pod。

事件

NoScaleDownNodePodNotEnoughPdb: "no.scale.down.node.pod.not.enough.pdb"

如需了解 PodDisruptionBudget 的限制是否过于严格,请查看其设置:

kubectl get pdb --all-namespaces

输出类似于以下内容:

NAMESPACE        NAME    MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
example-app-one  one_pdb       N/A             1                 1               12d
example-app-two  two_pdb       N/A             0                 0               12d

在此示例中,集群自动扩缩器不会逐出与 two_pdb 标签选择器匹配的任何 Pod。此 PodDisruptionBudget 中的 maxUnavailable: 0 设置规定,所有副本必须始终保持可用状态。此外,disruptionsAllowed: 0 禁止对这些 Pod 进行任何中断。因此,运行这些 Pod 的节点无法缩容,因为这样做会导致中断并违反 PodDisruptionBudget。

如果 PodDisruptionBudget 按您希望的方式运行,则无需执行任何其他操作。如果您想调整 PodDisruptionBudget,以便可以移动未充分利用的节点上的 Pod,请修改 PodDisruptionBudget 的清单。例如,如果您将 maxUnavailable 设置为 0,则可以将其更改为 1,以便集群自动扩缩器可以缩容。

问题:节点处于封锁状态且未被移除

如果 Google 服务账号没有编辑者角色而导致集群自动扩缩器无法缩减节点池规模,则会出现如下所示的错误:

Required 'compute.instanceGroups.update' permission for 'INSTANCE_GROUP_NAME'.

此问题的一个常见症状是,当集群自动扩缩器尝试缩减节点池规模时,节点不会更改状态。

如需解决此问题,请检查默认服务账号 (PROJECT_NUMBER@cloudservices.gserviceaccount.com) 是否在项目中具有编辑者角色 (roles/editor)。如果服务账号没有此角色,请添加。GKE 使用此服务账号来管理您的项目资源。如需了解如何执行此操作,请参阅 IAM 文档中的授予或撤消单个角色

后续步骤