Autoscaler v2#

本文档解释了开源 autoscaler v2 在 Ray 2.48 中的工作原理,并概述了其高级职责和实现细节。

概述#

Autoscaler 负责根据任务、Actor 和放置组的资源需求来调整集群大小。为此,它遵循一个结构化的流程:评估工作节点组配置,定期将集群状态与用户约束进行协调,应用装箱策略来满足待处理的工作负载需求,并通过实例管理器与云实例提供商进行交互。以下各节将详细介绍这些组件。

工作节点组配置#

工作节点组(也称为节点类型)定义了 Ray autoscaler 缩放的节点集合。每个工作节点组代表一类具有相同资源配置的逻辑节点,例如 CPU、内存、GPU 或自定义资源。

Autoscaler 根据工作负载需求的变化,动态调整集群大小,在每个组内添加或删除节点。换句话说,它根据指定的缩放规则和资源要求,通过修改每个工作节点组的节点数量来缩放集群。

工作节点组可以按以下方式配置

  • 如果您正在使用 ray up 集群启动器,则集群 YAML 文件中的 available_node_types 字段。

  • 如果您正在使用 KubeRay,则 RayCluster CRD 中的 workerGroupSpecs 字段。

配置指定了工作节点组中每个节点的逻辑资源,以及每个组应存在的最小和最大节点数。

注意

尽管 autoscaler 会满足待处理的资源需求并释放空闲节点,但它并不执行 Ray 任务、Actor 或放置组的实际调度。调度由 Ray 内部处理。Autoscaler 会定期自行模拟调度决策以确定要启动或停止的节点。有关详细信息,请参阅下一节。

定期协调#

Autoscaler 的入口点是 monitor.py,它启动一个 GCS 客户端并运行协调循环。

使用 ray up 集群启动器时,此过程由 start_head_processes 函数在 head 节点上启动。在 KubeRay 下运行时,它会作为 Head Pod 中的 单独的 autoscaler 容器 运行。

警告

对于集群启动器,如果 autoscaler 进程崩溃,则不会进行自动缩放。而在 KubeRay 的情况下,如果 autoscaler 容器因崩溃而终止,Kubernetes 会根据默认的容器重启策略重新启动它。

该进程使用 Reconciler 定期 协调 以下信息的快照

  1. 最新的待处理需求(从 get_cluster_resource_state GCS RPC 查询):待处理的 Ray 任务、Actor 和放置组。

  2. 最新的用户集群约束(从 get_cluster_resource_state GCS RPC 查询):最小集群大小(如果通过 ray.autoscaler.sdk.request_resources 调用指定)。

  3. 最新的 Ray 节点信息(从 get_cluster_resource_state GCS RPC 查询):集群中每个 Ray 节点的总资源和当前可用资源。还包括每个 Ray 节点的状​​态(ALIVE 或 DEAD)和其他信息,例如空闲时长。更多详情请参阅附录。

  4. 最新的云实例从云实例提供商的实现查询):由云实例提供商实现管理的实例列表。

  5. 最新的工作节点组配置(从集群 YAML 文件或 RayCluster CRD 查询)。

以上信息在每个协调循环开始时检索。Reconciler 使用这些信息来构建其内部状态,并通过观察执行“被动”实例生命周期转换。这是 同步阶段

同步阶段后,Reconciler 按顺序使用 ResourceDemandScheduler 执行 以下步骤

  1. 强制执行配置约束,包括每个工作节点组的最小/最大节点数。

  2. 强制执行用户集群约束(如果通过 ray.autoscaler.sdk.request_resources 调用指定)。

  3. 将待处理的需求拟合到集群快照中的可用资源。这是前面提到的模拟。

  4. 将任何剩余的需求(上一步剩余)拟合到工作节点组配置中,以确定要启动的节点。

  5. 根据每个节点的 idle_duration_ms(从 GCS 查询)和每个组配置的空闲超时时间,终止空闲实例(上一步 1-4 步所需的节点不被视为空闲)。

  6. 通过 Reconciler._update_instance_manager 将累积的缩放决策(步骤 1-5)发送到实例管理器。

  7. 短暂休眠(默认为 5 秒),然后返回同步阶段。

警告

如果发生任何错误,例如云实例提供商的错误或同步阶段的超时,则当前协调将被中止,循环将跳转到步骤 7 等待下一次协调。

注意

步骤 1-5 中的所有缩放决策纯粹在内存中累积。在步骤 6 之前,不与云实例提供商进行任何交互。

装箱和工作节点组选择#

Autoscaler 应用以下评分逻辑来评估每个现有节点。它选择得分最高的节点并为其分配一部分可行需求。它还对每个工作节点组应用相同的评分逻辑,并选择得分最高的节点来启动新实例。

评分 基于四个值的元组

  1. 节点是否是 GPU 节点以及可行请求是否需要 GPU

    • 0 如果节点是 GPU 节点且请求 **不** 需要 GPU。

    • 1 如果节点不是 GPU 节点或请求需要 GPU。

  2. 可行请求使用的资源类型的数量。

  3. 可行请求使用的所有资源类型的最小 利用率

  4. 可行请求使用的所有资源类型的平均 利用率

注意

可行请求使用的利用率计算为总资源与可用资源之差除以总资源。

换句话说

  • Autoscaler 除非必要,否则会避免启动 GPU 节点。

  • 它优先选择最大化利用率并最小化未用资源的节点。

示例

  • 任务需要 **2 个 GPU**。

  • 提供两种节点类型

    • A:[GPU:6]

    • B:[GPU:2,TPU:1]

应选择节点类型 **A**,因为节点 B 会留下未使用的 TPU(TPU 利用率为 0%),使其在第三个评分标准方面不太受欢迎。

此过程将重复进行,直到所有可行待处理需求都装箱完毕或达到最大集群大小。

实例管理器和云实例提供商#

云实例提供商 是一个抽象接口,它定义了在云中管理实例的操作。

实例管理器 是跟踪实例生命周期并驱动调用云实例提供商的事件订阅者的组件。

如上一节所述,autoscaler 在内存中累积缩放决策(步骤 1-5),并通过实例管理器与云实例提供商进行协调。

缩放决策表示为一系列 InstanceUpdateEvent 记录。例如:

  • 用于启动新实例:- instance_id:Instance Manager 用于跟踪的随机生成 ID。- instance_type:要启动的实例类型。- new_instance_statusQUEUED

  • 用于终止实例:- instance_id:要停止的实例 ID。- new_instance_statusTERMINATINGRAY_STOP_REQUESTED

这些更新事件被传递给实例管理器,实例管理器会转换实例状态。

实例的正常转换流程是

  • (non-existent) -> QUEUED:当 Reconciler 决定启动新实例时,它会创建一个具有 QUEUED InstanceUpdateEvent 的实例。

  • QUEUED -> REQUESTED:Reconciler 在每次协调迭代中,在考虑 max_concurrent_launchesupscaling_speed 后,会从队列中选择一个实例进行转换。

  • REQUESTED -> ALLOCATED:一旦 Reconciler 从云实例提供商检测到实例已被分配,它就会将实例转换为 ALLOCATED

  • ALLOCATED -> RAY_INSTALLING:如果云实例提供商不是 KubeRayProvider,当实例被分配时,Reconciler 会将实例转换为 RAY_INSTALLING

  • RAY_INSTALLING -> RAY_RUNNING:一旦 Reconciler 从 GCS 检测到 Ray 已在实例上启动,它就会将实例转换为 RAY_RUNNING

  • RAY_RUNNING -> RAY_STOP_REQUESTED:如果实例空闲时间超过配置的超时时间,Reconciler 会将实例转换为 RAY_STOP_REQUESTED 以开始排空 Ray 进程。

  • RAY_STOP_REQUESTED -> RAY_STOPPING:一旦 Reconciler 从 GCS 检测到 Ray 进程正在排空,它就会将实例转换为 RAY_STOPPING

  • RAY_STOPPING -> RAY_STOPPED:一旦 Reconciler 从 GCS 检测到 Ray 进程已停止,它就会将实例转换为 RAY_STOPPED

  • RAY_STOPPED -> TERMINATING:Reconciler 将实例从 RAY_STOPPED 转换为 TERMINATING

  • TERMINATING -> TERMINATED:一旦 Reconciler 检测到实例已被云实例提供商终止,它就会将实例转换为 TERMINATED

注意

如果节点在排空请求到达时不再空闲,则 RAY_STOP_REQUESTED 发送的排空请求可能会被拒绝。然后,实例将被转换回 RAY_RUNNING

您可以在 get_valid_transitions 方法中找到所有有效的转换。

一旦 Reconciler 触发转换,订阅者就会执行副作用,例如:

  • QUEUED -> REQUESTED:CloudInstanceUpdater 通过云实例提供商启动实例。

  • ALLOCATED -> RAY_INSTALLING:ThreadedRayInstaller 安装 Ray 进程。

  • RAY_RUNNING -> RAY_STOP_REQUESTED:RayStopper 在实例上停止 Ray 进程。

  • RAY_STOPPED -> TERMINATING:CloudInstanceUpdater 通过云实例提供商终止实例。

注意

这些转换会触发副作用,但副作用不会直接触发新的转换。相反,它们的结果是在同步阶段从外部状态观察到的;后续的转换将基于这些观察结果触发。

注意

autoscaler v2 中的云实例提供商实现必须实现:

  • 列出实例:返回提供商当前管理的实例集合。

  • 启动实例:给定请求的实例类型和标签创建新实例。

  • 终止实例:安全地移除由其 ID 标识的实例。

KubeRayProvider 就是这样一种云实例提供商实现。

NodeProviderAdapter 是一个适配器,可以包装 v1 节点提供商(例如 AWSNodeProvider)以充当云实例提供商。

附录#

如何 get_cluster_resource_state 聚合集群状态#

Autoscaler 通过 GCS 提供的 get_cluster_resource_state RPC(HandleGetClusterResourceState)检索集群快照,该 RPC 在 MakeClusterResourceStateInternal 中构建回复。在内部,GCS 通过组合每个节点的资源报告、待处理工作负载需求以及任何用户请求的集群约束来构建单个 ClusterResourceState 消息。

  • 数据源和所有权

    • GcsAutoscalerStateManager 维护一个 ResourcesData 的每个节点缓存,包括总量、可用量和按形状划分的负载。GCS 定期轮询每个活动的 raylet(GetResourceLoad)并更新此缓存(GcsServer::InitGcsResourceManagerUpdateResourceLoadAndUsage),然后使用它来构建快照。

    • GcsNodeInfo 提供静态且变化缓慢的节点元数据(节点 ID、实例 ID、节点类型名称、IP、标签、实例类型)以及死/活状态。

    • 放置组需求来自 放置组管理器

    • 用户集群约束来自 GCS 记录的 autoscaler SDK 请求。

  • 回复中组装的字段

    • node_states:对于每个节点,GCS 从 GcsNodeInfo 设置身份和元数据,并从缓存的 ResourcesData (GetNodeStates) 中提取资源和状态。死节点被标记为 DEAD 并省略资源详细信息。对于活动的节点,GCS 还包括 idle_duration_ms 和任何节点活动字符串。

    • pending_resource_requests:通过聚合集群中按形状划分的每个节点负载来计算(GetPendingResourceRequests)。对于每种资源形状,计数是尚未调度的不可行、积压和就绪请求的总和。

    • pending_gang_resource_requests:待处理或重新调度的放置组,表示为成组请求(GetPendingGangResourceRequests)。

    • cluster_resource_constraints:先前通过 ray.autoscaler.sdk.request_resources 请求的最小集群资源约束集(GetClusterResourceConstraints)。