运行 RayJob

在启用了 Kueue 的环境里运行 RayJobs

本页面展示了如何利用 Kueue 的调度和服务管理能力来运行 KubeRay 的 RayJob。

本指南适用于批处理用户 ,他们基本了解 Kueue。 更多信息,请参见 Kueue 概览。

开始之前

  1. 请确保你使用 Kueue v0.6.0 版本或更高版本,以及 KubeRay v1.1.0 或更高版本。

  2. 请参见管理集群配额了解初始 Kueue 设置的详细信息。

  3. 请参见 KubeRay 安装文档了解 KubeRay 的安装和配置详情。

RayJob 定义

当运行 RayJobs时,请考虑以下方面:

a. 队列选择

目标本地队列应在 RayJob 配置的 metadata.labels 部分指定。

metadata:
  labels:
    kueue.x-k8s.io/queue-name: user-queue

b. 配置资源需求

工作负载的资源需求可以在 spec.rayClusterSpec 中配置。

spec:
  rayClusterSpec:
    headGroupSpec:
      template:
        spec:
          containers:
            - resources:
                requests:
                  cpu: "1"
    workerGroupSpecs:
      - template:
          spec:
            containers:
              - resources:
                  requests:
                    cpu: "1"

c. Suspend 控制

Kueue 控制 RayJob 的 spec.suspend 字段。当 RayJob 被 Kueue 接纳时,Kueue 会通过将 spec.suspend 设置为 false 来取消暂停,无论其之前的值是什么。

d. 限制

  • 一个 Kueue 管理的 RayJob 不能使用现有的 RayCluster。
  • RayCluster 应在作业执行结束后删除,spec.ShutdownAfterJobFinishes 应为 true。
  • 因为一个 Kueue 工作负载最多可以有 8 个 PodSet,spec.rayClusterSpec.workerGroupSpecs 的最大数量为 7。

示例 {#examples} RayJob

在本例中,代码通过 ConfigMap 提供给 Ray 框架。

apiVersion: v1
kind: ConfigMap
metadata:
  name: ray-job-code-sample
data:
  sample_code.py: |
    import ray
    import os
    import requests

    ray.init()

    @ray.remote
    class Counter:
        def __init__(self):
            # Used to verify runtimeEnv
            self.name = os.getenv("counter_name")
            assert self.name == "test_counter"
            self.counter = 0

        def inc(self):
            self.counter += 1

        def get_counter(self):
            return "{} got {}".format(self.name, self.counter)

    counter = Counter.remote()

    for _ in range(5):
        ray.get(counter.inc.remote())
        print(ray.get(counter.get_counter.remote()))

    # Verify that the correct runtime env was used for the job.
    assert requests.__version__ == "2.26.0"

RayJob 如下所示:

apiVersion: ray.io/v1
kind: RayJob
metadata:
  name: rayjob-sample
  labels:
    kueue.x-k8s.io/queue-name: user-queue
spec:
  shutdownAfterJobFinishes: true
  entrypoint: python /home/ray/samples/sample_code.py
  runtimeEnvYAML: |
    pip:
      - requests==2.26.0
      - pendulum==2.1.2
    env_vars:
      counter_name: "test_counter"
  rayClusterSpec:
    rayVersion: '2.55.1'
    headGroupSpec:
      rayStartParams:
        dashboard-host: '0.0.0.0'
      template:
        spec:
          containers:
            - name: ray-head
              image: rayproject/ray:2.55.1
              ports:
                - containerPort: 6379
                  name: gcs-server
                - containerPort: 8265
                  name: dashboard
                - containerPort: 10001
                  name: client
              resources:
                limits:
                  cpu: "1"
                  memory: "5Gi"
                requests:
                  cpu: "1"
                  memory: "2Gi"
              volumeMounts:
                - mountPath: /home/ray/samples
                  name: code-sample
          volumes:
            - name: code-sample
              configMap:
                name: ray-job-code-sample
                items:
                  - key: sample_code.py
                    path: sample_code.py
    workerGroupSpecs:
      - replicas: 1
        minReplicas: 1
        maxReplicas: 5
        groupName: small-group
        rayStartParams: {}
        template:
          spec:
            containers:
              - name: ray-worker
                image: rayproject/ray:2.55.1
                lifecycle:
                  preStop:
                    exec:
                      command: [ "/bin/sh","-c","ray stop" ]
                resources:
                  limits:
                    cpu: "1"
                  requests:
                    cpu: "200m"

你可以使用以下命令运行此 RayJob:

# 创建代码 ConfigMap(一次)
kubectl apply -f ray-job-code-sample.yaml
# 创建 RayJob。你可以多次运行此命令,以观察作业的排队和准入。
kubectl create -f ray-job-sample.yaml

动态扩容(Autoscaling,a.k.a InTreeAutoscaling)

RayJob动态扩容 可以根据资源使用需求自动添加或者移除Ray worker pod 实例。

这个特性在如下及以后版本中支持: v0.15.2 和 v0.14.7.

如何在 RayJob 中启用动态扩容

  1. 打开特性开关 弹性工作负载(Elastic Workloads / Workload Slices)
ElasticJobsViaWorkloadSlices: true
  1. 在 RayJob 中添加 elastic-job 注释
  annotations:
    kueue.x-k8s.io/elastic-job: "true"
  1. 在 RayJob 中打开enableInTreeAutoscaling设置
spec:
  rayClusterSpec:
    enableInTreeAutoscaling: true

带有动态扩容的 RayJob 示例

在本例中,代码通过 ConfigMap 提供给 Ray 框架。

apiVersion: v1
kind: ConfigMap
metadata:
  name: ray-job-autoscaling-code-sample
data:
  sample_code.py: |
    import ray
    import os

    ray.init()
    
    @ray.remote
    def my_task(x, s):
        import time
        time.sleep(s)
        return x * x

    # run tasks in sequence to avoid triggering autoscaling in the beginning
    print([ray.get(my_task.remote(i, 1)) for i in range(10)])

    # run tasks in parallel to trigger autoscaling (scaling up)
    print(ray.get([my_task.remote(i, 10) for i in range(10)]))

    # run tasks in sequence to trigger scaling down
    print([ray.get(my_task.remote(i, 1)) for i in range(30)])

RayJob 如下所示:

apiVersion: ray.io/v1
kind: RayJob
metadata:
  name: rayjob-autoscaling-sample
  labels:
    kueue.x-k8s.io/queue-name: user-queue
  annotations:
    kueue.x-k8s.io/elastic-job: "true"
spec:
  shutdownAfterJobFinishes: true
  entrypoint: python /home/ray/samples/sample_code.py
  runtimeEnvYAML: |
    pip:
      - requests==2.26.0
      - pendulum==2.1.2
    env_vars:
      counter_name: "test_counter"
  rayClusterSpec:
    rayVersion: '2.55.1'
    autoscalerOptions:
      idleTimeoutSeconds: 30
      upscalingMode: Aggressive
    enableInTreeAutoscaling: true
    headGroupSpec:
      rayStartParams:
        dashboard-host: '0.0.0.0'
      template:
        spec:
          containers:
            - name: ray-head
              image: rayproject/ray:2.55.1
              ports:
                - containerPort: 6379
                  name: gcs-server
                - containerPort: 8265
                  name: dashboard
                - containerPort: 10001
                  name: client
              resources:
                limits:
                  cpu: "1"
                  memory: "5Gi"
                requests:
                  cpu: "1"
                  memory: "2Gi"
              volumeMounts:
                - mountPath: /home/ray/samples
                  name: code-sample
          volumes:
            - name: code-sample
              configMap:
                name: ray-job-autoscaling-code-sample
                items:
                  - key: sample_code.py
                    path: sample_code.py
    workerGroupSpecs:
      - replicas: 1
        minReplicas: 1
        maxReplicas: 5
        groupName: small-group
        rayStartParams: {}
        template:
          spec:
            containers:
              - name: ray-worker
                image: rayproject/ray:2.55.1
                lifecycle:
                  preStop:
                    exec:
                      command: [ "/bin/sh","-c","ray stop" ]
                resources:
                  limits:
                    cpu: "1"
                  requests:
                    cpu: "200m"

你可以使用以下命令运行此 RayJob:

# 创建代码 ConfigMap(一次)
kubectl apply -f ray-job-autoscaling-code-sample.yaml
# 创建 RayJob。你可以多次运行此命令,以观察作业的排队和准入。
kubectl create -f ray-job-autoscaling-sample.yaml