KubeRay 自动伸缩#

本指南介绍了如何在 Kubernetes 上配置 Ray 自动伸缩器。Ray 自动伸缩器是一个 Ray 集群进程,它根据资源需求自动向上和向下伸缩集群。自动伸缩器通过根据任务、执行器或放置组所需的资源来调整集群中的节点(Ray Pod)数量来实现这一点。

自动伸缩器利用逻辑资源请求(在 @ray.remote 中指示,并在 ray status 中显示),而不是物理机利用率来伸缩。如果您启动一个执行器、任务或放置组,并且资源不足,自动伸缩器会将请求排队。它会调整节点数量以满足队列需求,并随着时间的推移移除没有任务、执行器或对象的空闲节点。

何时使用自动伸缩?

自动伸缩可以降低工作负载成本,但会增加节点启动的开销,并且配置起来可能很棘手。如果您是 Ray 的新手,建议从非自动伸缩集群开始。

Ray 自动伸缩 V2 alpha (KubeRay) (@ray 2.10.0)

使用 Ray 2.10,Ray 自动伸缩 V2 alpha 可与 KubeRay 一起使用。它在可观测性和稳定性方面有所改进。有关更多详细信息,请参阅 本节

概述#

下图说明了 Ray 自动伸缩器与 KubeRay Operator 的集成。虽然为了清晰起见,将其描绘成一个独立的实体,但在实际实现中,Ray 自动伸缩器实际上是 Ray Head Pod 中的一个 sidecar 容器。

../../../_images/AutoscalerOperator.svg

KubeRay 中的 3 个自动伸缩级别

  • Ray 执行器/任务:一些 Ray 库,如 Ray Serve,可以根据传入的请求量自动调整 Serve 副本的数量(即 Ray 执行器)。

  • Ray 节点:Ray 自动伸缩器根据 Ray 执行器/任务的资源需求自动调整 Ray 节点(即 Ray Pod)的数量。

  • Kubernetes 节点:如果 Kubernetes 集群没有足够的资源来支持 Ray 自动伸缩器创建的新 Ray Pod,Kubernetes 自动伸缩器可以配置一个新的 Kubernetes 节点。您必须自己配置 Kubernetes 自动伸缩器。

  • 自动伸缩器通过以下事件顺序来伸缩集群

    1. 用户提交 Ray 工作负载。

    2. Ray Head 容器聚合工作负载的资源需求,并将其传达给 Ray 自动伸缩器 sidecar。

    3. 自动伸缩器决定添加一个 Ray Worker Pod 来满足工作负载的资源需求。

    4. 自动伸缩器通过增加 RayCluster CR 的 replicas 字段来请求一个额外的 Worker Pod。

    5. KubeRay Operator 创建一个 Ray Worker Pod 以匹配新的 replicas 规范。

    6. Ray 调度器将用户的工作负载放置在新创建的 Worker Pod 上。

  • 自动伸缩器还会通过删除空闲的 Worker Pod 来缩减集群。如果它找到一个空闲的 Worker Pod,它会减少 RayCluster CR 的 replicas 字段中的计数,并将识别出的 Pod 添加到 CR 的 workersToDelete 字段中。然后,KubeRay Operator 删除 workersToDelete 字段中的 Pod。

快速入门#

步骤 1:使用 Kind 创建 Kubernetes 集群#

kind create cluster --image=kindest/node:v1.26.0

步骤 2:安装 KubeRay operator#

按照 本指南 通过 Helm 仓库安装最新的稳定版 KubeRay Operator。

步骤 3:创建启用了自动伸缩的 RayCluster 自定义资源#

kubectl apply -f https://raw.githubusercontent.com/ray-project/kuberay/v1.5.1/ray-operator/config/samples/ray-cluster.autoscaler.yaml

步骤 4:验证 Kubernetes 集群状态#

# Step 4.1: List all Ray Pods in the `default` namespace.
kubectl get pods -l=ray.io/is-ray-node=yes

# [Example output]
# NAME                         READY   STATUS    RESTARTS   AGE
# raycluster-autoscaler-head   2/2     Running   0          107s

# Step 4.2: Check the ConfigMap in the `default` namespace.
kubectl get configmaps

# [Example output]
# NAME                  DATA   AGE
# ray-example           2      21s
# ...

RayCluster 包含一个 Head Pod 和零个 Worker Pod。Head Pod 包含两个容器:一个 Ray Head 容器和一个 Ray 自动伸缩器 sidecar 容器。此外,ray-cluster.autoscaler.yaml 包含一个名为 ray-example 的 ConfigMap,其中包含两个 Python 脚本:detached_actor.pyterminate_detached_actor.py

  • detached_actor.py 是一个创建需要 1 个 CPU 的已分离执行器的 Python 脚本。

    import ray
    import sys
    
    @ray.remote(num_cpus=1)
    class Actor:
      pass
    
    ray.init(namespace="default_namespace")
    Actor.options(name=sys.argv[1], lifetime="detached").remote()
    
  • terminate_detached_actor.py 是一个终止已分离执行器的 Python 脚本。

    import ray
    import sys
    
    ray.init(namespace="default_namespace")
    detached_actor = ray.get_actor(sys.argv[1])
    ray.kill(detached_actor)
    

步骤 5:通过创建已分离执行器触发 RayCluster 扩容#

# Step 5.1: Create a detached actor "actor1" which requires 1 CPU.
export HEAD_POD=$(kubectl get pods --selector=ray.io/node-type=head -o custom-columns=POD:metadata.name --no-headers)
kubectl exec -it $HEAD_POD -- python3 /home/ray/samples/detached_actor.py actor1

# Step 5.2: The Ray Autoscaler creates a new worker Pod.
kubectl get pods -l=ray.io/is-ray-node=yes

# [Example output]
# NAME                                             READY   STATUS    RESTARTS   AGE
# raycluster-autoscaler-head                       2/2     Running   0          xxm
# raycluster-autoscaler-small-group-worker-yyyyy   1/1     Running   0          xxm

# Step 5.3: Create a detached actor which requires 1 CPU.
kubectl exec -it $HEAD_POD -- python3 /home/ray/samples/detached_actor.py actor2
kubectl get pods -l=ray.io/is-ray-node=yes

# [Example output]
# NAME                                             READY   STATUS    RESTARTS   AGE
# raycluster-autoscaler-head                       2/2     Running   0          xxm
# raycluster-autoscaler-small-group-worker-yyyyy   1/1     Running   0          xxm
# raycluster-autoscaler-small-group-worker-zzzzz   1/1     Running   0          xxm

# Step 5.4: List all actors in the Ray cluster.
kubectl exec -it $HEAD_POD -- ray list actors


# ======= List: 2023-09-06 13:26:49.228594 ========
# Stats:
# ------------------------------
# Total: 2

# Table:
# ------------------------------
#     ACTOR_ID  CLASS_NAME    STATE    JOB_ID    NAME    ...
#  0  xxxxxxxx  Actor         ALIVE    02000000  actor1  ...
#  1  xxxxxxxx  Actor         ALIVE    03000000  actor2  ...

Ray 自动伸缩器为每个新创建的已分离执行器生成一个新的 Worker Pod。这是因为 Ray Head 中的 rayStartParams 字段指定了 num-cpus: "0",阻止 Ray 调度器将任何 Ray 执行器或任务调度到 Ray Head Pod。此外,每个 Ray Worker Pod 的容量为 1 个 CPU,因此自动伸缩器会创建一个新的 Worker Pod 来满足需要 1 个 CPU 的已分离执行器的资源需求。

  • 使用已分离执行器并非触发集群扩容的必要条件。常规执行器和任务也可以启动它。已分离执行器即使在作业驱动进程退出后仍然持久存在,这也是为什么当 detached_actor.py 进程退出时,自动伸缩器不会自动缩减集群,这使得本教程更加方便。

  • 在此 RayCluster 自定义资源中,从 Ray 自动伸缩器的角度来看,每个 Ray Worker Pod 仅拥有 1 个逻辑 CPU。因此,如果您创建一个具有 @ray.remote(num_cpus=2) 的已分离执行器,自动伸缩器不会启动创建新的 Worker Pod,因为现有 Pod 的容量仅限于 1 个 CPU。

  • (进阶) Ray 自动伸缩器还提供了一个 Python SDK,允许高级用户(如 Ray 维护者)直接从自动伸缩器请求资源。通常,大多数用户不需要使用 SDK。

步骤 6:通过终止已分离执行器触发 RayCluster 缩容#

# Step 6.1: Terminate the detached actor "actor1".
kubectl exec -it $HEAD_POD -- python3 /home/ray/samples/terminate_detached_actor.py actor1

# Step 6.2: A worker Pod will be deleted after `idleTimeoutSeconds` (default 60s) seconds.
kubectl get pods -l=ray.io/is-ray-node=yes

# [Example output]
# NAME                                             READY   STATUS    RESTARTS   AGE
# raycluster-autoscaler-head                       2/2     Running   0          xxm
# raycluster-autoscaler-small-group-worker-zzzzz   1/1     Running   0          xxm

# Step 6.3: Terminate the detached actor "actor2".
kubectl exec -it $HEAD_POD -- python3 /home/ray/samples/terminate_detached_actor.py actor2

# Step 6.4: A worker Pod will be deleted after `idleTimeoutSeconds` (default 60s) seconds.
kubectl get pods -l=ray.io/is-ray-node=yes

# [Example output]
# NAME                         READY   STATUS    RESTARTS   AGE
# raycluster-autoscaler-head   2/2     Running   0          xxm

步骤 7:Ray 自动伸缩器可观测性#

# Method 1: "ray status"
kubectl exec $HEAD_POD -it -c ray-head -- ray status

# [Example output]:
# ======== Autoscaler status: 2023-09-06 13:42:46.372683 ========
# Node status
# ---------------------------------------------------------------
# Healthy:
#  1 head-group
# Pending:
#  (no pending nodes)
# Recent failures:
#  (no failures)

# Resources
# ---------------------------------------------------------------
# Usage:
#  0B/1.86GiB memory
#  0B/514.69MiB object_store_memory

# Demands:
#  (no resource demands)

# Method 2: "kubectl logs"
kubectl logs $HEAD_POD -c autoscaler | tail -n 20

# [Example output]:
# 2023-09-06 13:43:22,029 INFO autoscaler.py:421 --
# ======== Autoscaler status: 2023-09-06 13:43:22.028870 ========
# Node status
# ---------------------------------------------------------------
# Healthy:
#  1 head-group
# Pending:
#  (no pending nodes)
# Recent failures:
#  (no failures)

# Resources
# ---------------------------------------------------------------
# Usage:
#  0B/1.86GiB memory
#  0B/514.69MiB object_store_memory

# Demands:
#  (no resource demands)
# 2023-09-06 13:43:22,029 INFO autoscaler.py:464 -- The autoscaler took 0.036 seconds to complete the update iteration.

步骤 8:清理 Kubernetes 集群#

# Delete RayCluster and ConfigMap
kubectl delete -f https://raw.githubusercontent.com/ray-project/kuberay/v1.5.1/ray-operator/config/samples/ray-cluster.autoscaler.yaml

# Uninstall the KubeRay operator
helm uninstall kuberay-operator

# Delete the kind cluster
kind delete cluster

KubeRay 自动伸缩配置#

快速入门示例中使用的 ray-cluster.autoscaler.yaml 包含有关配置选项的详细注释。建议将本节与 YAML 文件结合阅读。

1. 启用自动伸缩#

  • enableInTreeAutoscaling:通过设置 enableInTreeAutoscaling: true,KubeRay Operator 会自动为 Ray Head Pod 配置一个自动伸缩 sidecar 容器。

  • minReplicas / maxReplicas / replicas:设置 minReplicasmaxReplicas 字段以定义 workerGroupreplicas 的范围。通常,在部署自动伸缩集群时,您会将 replicasminReplicas 初始化为相同的值。之后,Ray 自动伸缩器在向集群添加或移除 Pod 时会调整 replicas 字段。

2. 扩容和缩容速度#

如果需要,您可以调节从集群添加或删除节点的 pace。对于具有大量短期任务的应用程序,考虑采用更保守的方法来调整扩容和缩容速度可能是有益的。

利用 RayCluster CR 的 autoscalerOptions 字段来实现这一点。此字段包含以下子字段:

  • upscalingMode:此选项控制扩容过程的速率。有效值为:

    • Conservative:扩容受到速率限制;待处理的 Worker Pod 数量最多为连接到 Ray 集群的 Worker Pod 的数量。

    • Default:不限制扩容速率。

    • Aggressive:Default 的别名;不限制扩容速率。

  • idleTimeoutSeconds(默认 60 秒):这表示在缩减空闲 Worker Pod 之前等待的时间(以秒为单位)。当 Worker 节点没有任何活动任务、执行器或引用的对象(无论是存储在内存中还是溢出到磁盘)时,该节点被视为空闲。

3. 自动伸缩器 sidecar 容器#

autoscalerOptions 字段还提供了配置自动伸缩器容器的选项。通常,不需要指定这些选项。

  • resourcesautoscalerOptionsresources 子字段设置自动伸缩器 sidecar 容器的可选资源覆盖。这些覆盖应按照标准的 容器资源规格格式 进行指定。下面显示了默认值。

    resources:
      limits:
        cpu: "500m"
        memory: "512Mi"
      requests:
        cpu: "500m"
        memory: "512Mi"
    
  • image:此字段覆盖自动伸缩器容器的镜像。默认情况下,容器使用与 Ray 容器相同的 **镜像**。

  • imagePullPolicy:此字段覆盖自动伸缩器容器的镜像拉取策略。默认值为 IfNotPresent

  • envenvFrom:这些字段指定自动伸缩器容器的环境变量。这些字段应遵循 Kubernetes API 关于容器环境变量的格式。

4. 设置 rayStartParams 和 Ray 容器的资源限制#

从 Ray 2.41.0 开始,资源限制是可选的。

从 Ray 2.41.0 开始,Ray 自动伸缩器可以从 rayStartParams、资源限制或 Ray 容器的资源请求中读取资源规范。您必须指定至少一个这些字段。早期版本仅支持 rayStartParams 或资源限制,而不识别资源请求。

如果您使用的是带有 Ray 2.45.0 或更高版本的自动伸缩镜像,则 rayStartParams 是可选的。

从 KubeRay 1.4.0 开始,RayCluster CRD 的 rayStartParams 是可选的,但在早期版本中是必需的。如果您省略 rayStartParams 并想使用自动伸缩,自动伸缩镜像必须包含 Ray 2.45.0 或更高版本。

Ray 自动伸缩器会读取 RayCluster 自定义资源规范中的 rayStartParams 字段或 Ray 容器的资源限制,以确定 Ray Pod 的资源需求。关于 CPU 数量的信息对于 Ray 自动伸缩器伸缩集群至关重要。因此,没有这些信息,Ray 自动伸缩器将报告错误并无法启动。以下面的 ray-cluster.autoscaler.yaml 为例:

  • 如果用户在 rayStartParams 中设置了 num-cpus,Ray 自动伸缩器将能正常工作,而无需考虑容器上的资源限制。

  • 如果用户未设置 rayStartParams,则 Ray 容器必须具有指定的 CPU 资源限制。

headGroupSpec:
  rayStartParams:
    num-cpus: "0"
  template:
    spec:
      containers:
      - name: ray-head
        resources:
          # The Ray Autoscaler still functions if you comment out the `limits` field for the
          # head container, as users have already specified `num-cpus` in `rayStartParams`.
          limits:
            cpu: "1"
            memory: "2G"
          requests:
            cpu: "1"
            memory: "2G"
...
workerGroupSpecs:
- groupName: small-group
  template:
    spec:
      containers:
      - name: ray-worker
        resources:
          limits:
            # The Ray Autoscaler versions older than 2.41.0 will fail to start if the CPU resource limit for the worker
            # container is commented out because `rayStartParams` is empty.
            # The Ray Autoscaler starting from 2.41.0 will not fail but use the resource requests if the resource
            # limits are commented out and `rayStartParams` is empty.
            cpu: "1"
            memory: "1G"
          requests:
            cpu: "1"
            memory: "1G"

5. 自动伸缩器环境配置#

您可以使用 RayCluster 自定义资源 autoscalerOptions 下的 envenvFrom 字段中指定的环境变量来配置 Ray 自动伸缩器。这些变量可以对自动伸缩器内部行为进行精细控制。

例如,AUTOSCALER_UPDATE_INTERVAL_S 决定了自动伸缩器检查集群状态并决定是否扩容或缩容的频率。

有关完整示例,请参阅 ray-cluster.autoscaler.yamlray-cluster.autoscaler-v2.yaml

autoscalerOptions:
  env:
    - name: AUTOSCALER_UPDATE_INTERVAL_S
      value: "5"

下一步#

有关 Ray 自动伸缩器与 Kubernetes 自动伸缩器之间关系 的更多详细信息,请参阅 (进阶) 理解 Kubernetes 环境下的 Ray 自动伸缩器

自动伸缩器 V2 (KubeRay)#

先决条件#

  • KubeRay v1.4.0 和最新的 Ray 版本是自动伸缩器 V2 的首选配置。

Ray 2.10.0 的发布引入了与 KubeRay 集成的 Ray 自动伸缩器 V2 的 alpha 版本,在可观测性和稳定性方面带来了改进。

  1. 可观测性:自动伸缩器 V2 为每个 Ray Worker 的生命周期提供实例级别跟踪,从而更容易调试和理解自动伸缩器的行为。它还报告每个节点的空闲信息,包括节点为何空闲或活动的原因。

> ray status -v

======== Autoscaler status: 2024-03-08 21:06:21.023751 ========
GCS request time: 0.003238s

Node status
---------------------------------------------------------------
Active:
 1 node_40f427230584b2d9c9f113d8db51d10eaf914aa9bf61f81dc7fabc64
Idle:
 1 node_2d5fd3d4337ba5b5a8c3106c572492abb9a8de2dee9da7f6c24c1346
Pending:
 (no pending nodes)
Recent failures:
 (no failures)

Resources
---------------------------------------------------------------
Total Usage:
 1.0/64.0 CPU
 0B/72.63GiB memory
 0B/33.53GiB object_store_memory

Pending Demands:
 (no resource demands)

Node: 40f427230584b2d9c9f113d8db51d10eaf914aa9bf61f81dc7fabc64
 Usage:
  1.0/32.0 CPU
  0B/33.58GiB memory
  0B/16.79GiB object_store_memory
 # New in autoscaler V2: activity information
 Activity:
  Busy workers on node.
  Resource: CPU currently in use.

Node: 2d5fd3d4337ba5b5a8c3106c572492abb9a8de2dee9da7f6c24c1346
 # New in autoscaler V2: idle information
 Idle: 107356 ms
 Usage:
  0.0/32.0 CPU
  0B/39.05GiB memory
  0B/16.74GiB object_store_memory
 Activity:
  (no activity)
  1. 稳定性:自动伸缩器 V2 在空闲节点处理方面取得了显著改进。V1 自动伸缩器可能会停止在终止处理过程中变为活动的节点,从而可能导致任务或执行器失败。V2 使用 Ray 的优雅排水机制,可以安全地停止空闲节点而不会中断正在进行的工作。

ray-cluster.autoscaler-v2.yaml 是一个启用了自动伸缩器 V2 的 RayCluster 的示例 YAML 文件,可与最新的 KubeRay 版本配合使用。

如果您使用的是 KubeRay >= 1.4.0,请通过设置 RayCluster.spec.autoscalerOptions.version: v2 来启用 V2。

spec:
  enableInTreeAutoscaling: true
  # Set .spec.autoscalerOptions.version: v2
  autoscalerOptions:
    version: v2

如果您使用的是 KubeRay < 1.4.0,请通过在 Head 中设置 RAY_enable_autoscaler_v2 环境变量,并在 Head 和所有 Worker Group 上使用 restartPolicy: Never 来启用 V2。

spec:
  enableInTreeAutoscaling: true
  headGroupSpec:
    template:
      spec:
        containers:
        - name: ray-head
          image: rayproject/ray:2.X.Y
          env:
          # Set this environment variable
          - name: RAY_enable_autoscaler_v2
            value: "1"
        restartPolicy: Never # Prevent container restart to maintain Ray health.


  # Prevent Kubernetes from restarting Ray worker pod containers, enabling correct instance management by Ray.
  workerGroupSpecs:
  - replicas: 1
    template:
      spec:
        restartPolicy: Never
        ...