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 定期 协调 以下信息的快照
最新的待处理需求(从 get_cluster_resource_state GCS RPC 查询):待处理的 Ray 任务、Actor 和放置组。
最新的用户集群约束(从 get_cluster_resource_state GCS RPC 查询):最小集群大小(如果通过
ray.autoscaler.sdk.request_resources调用指定)。最新的 Ray 节点信息(从 get_cluster_resource_state GCS RPC 查询):集群中每个 Ray 节点的总资源和当前可用资源。还包括每个 Ray 节点的状态(ALIVE 或 DEAD)和其他信息,例如空闲时长。更多详情请参阅附录。
最新的云实例(从云实例提供商的实现查询):由云实例提供商实现管理的实例列表。
最新的工作节点组配置(从集群 YAML 文件或 RayCluster CRD 查询)。
以上信息在每个协调循环开始时检索。Reconciler 使用这些信息来构建其内部状态,并通过观察执行“被动”实例生命周期转换。这是 同步阶段。
同步阶段后,Reconciler 按顺序使用 ResourceDemandScheduler 执行 以下步骤
强制执行配置约束,包括每个工作节点组的最小/最大节点数。
强制执行用户集群约束(如果通过 ray.autoscaler.sdk.request_resources 调用指定)。
将待处理的需求拟合到集群快照中的可用资源。这是前面提到的模拟。
将任何剩余的需求(上一步剩余)拟合到工作节点组配置中,以确定要启动的节点。
根据每个节点的
idle_duration_ms(从 GCS 查询)和每个组配置的空闲超时时间,终止空闲实例(上一步 1-4 步所需的节点不被视为空闲)。通过 Reconciler._update_instance_manager 将累积的缩放决策(步骤 1-5)发送到实例管理器。
短暂休眠(默认为 5 秒),然后返回同步阶段。
警告
如果发生任何错误,例如云实例提供商的错误或同步阶段的超时,则当前协调将被中止,循环将跳转到步骤 7 等待下一次协调。
注意
步骤 1-5 中的所有缩放决策纯粹在内存中累积。在步骤 6 之前,不与云实例提供商进行任何交互。
装箱和工作节点组选择#
Autoscaler 应用以下评分逻辑来评估每个现有节点。它选择得分最高的节点并为其分配一部分可行需求。它还对每个工作节点组应用相同的评分逻辑,并选择得分最高的节点来启动新实例。
评分 基于四个值的元组
节点是否是 GPU 节点以及可行请求是否需要 GPU
0如果节点是 GPU 节点且请求 **不** 需要 GPU。1如果节点不是 GPU 节点或请求需要 GPU。
可行请求使用的资源类型的数量。
可行请求使用的所有资源类型的最小 利用率。
可行请求使用的所有资源类型的平均 利用率。
注意
可行请求使用的利用率计算为总资源与可用资源之差除以总资源。
换句话说
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_status:QUEUED。用于终止实例:-
instance_id:要停止的实例 ID。-new_instance_status:TERMINATING或RAY_STOP_REQUESTED。
这些更新事件被传递给实例管理器,实例管理器会转换实例状态。
实例的正常转换流程是
(non-existent) -> QUEUED:当 Reconciler 决定启动新实例时,它会创建一个具有QUEUEDInstanceUpdateEvent的实例。QUEUED -> REQUESTED:Reconciler 在每次协调迭代中,在考虑max_concurrent_launches和upscaling_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::InitGcsResourceManager,UpdateResourceLoadAndUsage),然后使用它来构建快照。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)。