RayCluster 配置#

本指南涵盖了在 Kubernetes 上配置 Ray 集群的关键方面。

引言#

在 Kubernetes 上部署 Ray 遵循 Operator 模式。关键参与者包括

  • 一个 自定义资源,称为 RayCluster,描述 Ray 集群的期望状态。

  • 一个 自定义控制器,即 KubeRay Operator,它管理 Ray Pod 以匹配 RayCluster 的 spec。

要部署 Ray 集群,需要创建一个 RayCluster 自定义资源 (CR)

kubectl apply -f raycluster.yaml

本指南涵盖了 RayCluster CR 配置的主要特性。

作为参考,这里是一个简明的 RayCluster CR 的 yaml 格式示例。

apiVersion: ray.io/v1alpha1
kind: RayCluster
metadata:
  name: raycluster-complete
spec:
  rayVersion: "2.3.0"
  enableInTreeAutoscaling: true
  autoscalerOptions:
     ...
  headGroupSpec:
    serviceType: ClusterIP # Options are ClusterIP, NodePort, and LoadBalancer
    rayStartParams:
      dashboard-host: "0.0.0.0"
      ...
    template: # Pod template
        metadata: # Pod metadata
        spec: # Pod spec
            containers:
            - name: ray-head
              image: rayproject/ray-ml:2.3.0
              resources:
                limits:
                  cpu: 14
                  memory: 54Gi
                requests:
                  cpu: 14
                  memory: 54Gi
              ports: # Optional service port overrides
              - containerPort: 6379
                name: gcs
              - containerPort: 8265
                name: dashboard
              - containerPort: 10001
                name: client
              - containerPort: 8000
                name: serve
                ...
  workerGroupSpecs:
  - groupName: small-group
    replicas: 1
    minReplicas: 1
    maxReplicas: 5
    rayStartParams:
        ...
    template: # Pod template
      spec:
        ...
  # Another workerGroup
  - groupName: medium-group
    ...
  # Yet another workerGroup, with access to special hardware perhaps.
  - groupName: gpu-group
    ...

本指南的其余部分将讨论 RayCluster CR 的配置字段。另请参阅关于使用 KubeRay 配置 Ray 自动扩缩容 的指南。

Ray 版本#

字段 rayVersion 指定了 Ray 集群中使用的 Ray 版本。rayVersion 用于填充某些配置字段的默认值。RayCluster CR 中指定的 Ray 容器镜像应与 CR 的 rayVersion 版本相同。如果您正在使用 nightly 或 development 的 Ray 镜像,可以将 rayVersion 设置为 Ray 的最新发布版本。

Pod 配置:headGroupSpec 和 workerGroupSpecs#

从宏观上看,RayCluster 是 Kubernetes Pod 的集合,类似于 Kubernetes Deployment 或 StatefulSet。与 Kubernetes 内置资源一样,关键配置包括

  • Pod 规范

  • 规模信息(期望多少个 Pod)

Deployment 和 RayCluster 之间的主要区别在于,RayCluster 专门用于运行 Ray 应用。Ray 集群由以下部分组成

  • 一个 head pod,它托管 Ray 集群的全局控制进程。head pod 也可以运行 Ray 任务和 Actors。

  • 任意数量的 worker pods,运行 Ray 任务和 Actors。Workers 组织成配置相同的 worker groups。对于每个 worker group,必须指定 replicas,即该组所需的 Pod 数量。

head pod 的配置在 headGroupSpec 下指定,而 worker pods 的配置在 workerGroupSpecs 下指定。可能有多个 worker groups,每个 group 都有自己的配置。replicas 字段指定了该 group 在集群中要保留的 worker pods 数量。每个 workerGroupSpec 也包含可选的 minReplicasmaxReplicas 字段;如果您想启用自动扩缩容,这些字段很重要。

Pod 模板#

headGroupSpecworkerGroupSpec 的大部分配置在 template 字段中。template 是一个 Kubernetes Pod 模板,它决定了 group 中 pods 的配置。以下是一些需要注意的 pod template 的子字段

containers#

Ray Pod 模板至少指定一个容器,即运行 Ray 进程的容器。Ray Pod 模板还可以指定额外的 sidecar 容器,例如用于 日志处理。但是,KubeRay Operator 假定 containers 列表中的第一个容器是主要的 Ray 容器。因此,务必在主要 Ray 容器之后指定任何 sidecar 容器。换句话说,Ray 容器应该是 containers 列表中的第一个

resources#

为每个 group spec 指定容器的 CPU 和内存请求和限制是很重要的。对于 GPU 工作负载,您可能还希望指定 GPU 限制。例如,如果使用 Nvidia GPU 设备插件并希望指定一个可访问 2 个 GPU 的 Pod,请设置 nvidia.com/gpu:2。有关 GPU 支持的更多详细信息,请参阅本指南

最理想的做法是调整每个 Ray Pod 的大小,使其占据其被调度的整个 Kubernetes 节点。换句话说,最好在每个 Kubernetes 节点上运行一个大型 Ray Pod。总的来说,使用少量大型 Ray Pod 比使用许多小型 Pod 更有效率。较少大型 Ray Pod 的模式具有以下优势

  • 更有效地利用每个 Ray Pod 的共享内存对象存储

  • 减少 Ray Pod 之间的通信开销

  • 减少每个 Pod 的 Ray 控制结构(例如 Raylets)的冗余

Ray 容器配置中指定的 CPU、GPU 和内存限制将自动通告给 Ray。这些值将用作 head group 或 worker group 中 Ray Pod 的逻辑资源容量。请注意,CPU 数量在传输给 Ray 之前将四舍五入到最近的整数。通告给 Ray 的资源容量可以在Ray 启动参数中被覆盖。

另一方面,CPU、GPU 和内存请求将被 Ray 忽略。因此,最好在可能的情况下将资源请求设置为等于资源限制

nodeSelector 和 tolerations#

您可以通过设置 worker groups 的 Ray Pod 的 nodeSelectortolerations 字段来控制其调度。具体来说,这些字段决定了 Pod 可以被调度到哪些 Kubernetes 节点上。有关 Pod 到节点分配的更多信息,请参阅Kubernetes 文档

image#

RayCluster CR 中指定的 Ray 容器镜像应与 CR 的 spec.rayVersion 版本相同。如果您正在使用 nightly 或 development 的 Ray 镜像,可以在 spec.rayVersion 下指定 Ray 的最新发布版本。

对于 Apple M1 或 M2 MacBook,请参阅为 Apple M1 或 M2 MacBook 使用基于 ARM 的 Docker 镜像 以指定正确的镜像。

您必须在可能运行特定 Ray 任务或 Actor 的每个 Ray 节点上安装其代码依赖项。实现此配置的最简单方法是为 Ray head 和所有 worker groups 使用相同的 Ray 镜像。无论如何,请务必确保 CR 中的所有 Ray 镜像都具有相同的 Ray 版本和 Python 版本。要将自定义代码依赖项分发到集群中,可以使用官方 Ray 镜像作为基础构建自定义容器镜像。请参阅本指南 了解更多关于官方 Ray 镜像的信息。对于面向迭代和开发的动态依赖项管理,您还可以使用Runtime Environments

对于 kuberay-operator 1.1.0 及更高版本,Ray 容器镜像必须安装有 wget

metadata.name 和 metadata.generateName#

KubeRay Operator 将忽略用户设置的 metadata.namemetadata.generateName 的值。KubeRay Operator 将自动生成一个 generateName 以避免名称冲突。有关更多详细信息,请参阅KubeRay issue #587

Ray 启动参数#

rayStartParams 字段是每个 group spec 中用于 Ray 容器 ray start entrypoint 的字符串-字符串映射。有关参数的完整列表,请参阅ray start 的文档。我们特别指出以下参数

dashboard-host#

在大多数使用场景中,Ray head pod 的此字段应设置为“0.0.0.0”。这是将 Ray dashboard 暴露到 Ray 集群外部所必需的。(未来版本可能会默认设置此参数。)

num-cpus#

此可选字段告诉 Ray 调度器和自动伸缩器 Ray Pod 可用的 CPU 数量。CPU 数量可以从组规范 (group spec) 的 Pod template 中指定的 Kubernetes 资源限制自动检测。然而,有时覆盖此自动检测的值很有用。例如,为 Ray Head Pod 设置 num-cpus:"0" 将阻止具有非零 CPU 需求的 Ray 工作负载在该 Head Pod 上调度。请注意,所有 Ray 启动参数(包括 num-cpus)的值都必须以字符串形式提供。

num-gpus#

此字段指定 Ray 容器可用的 GPU 数量。在未来的 KubeRay 版本中,GPU 数量将从 Ray 容器资源限制中自动检测。请注意,所有 Ray 启动参数(包括 num-gpus)的值都必须以字符串形式提供。

memory#

Ray 可用的内存从 Kubernetes 资源限制中自动检测。如果需要,您可以通过在 rayStartParams.memory 下设置所需的内存值(以字节为单位)来覆盖此自动检测的值。请注意,所有 Ray 启动参数(包括 memory)的值都必须以字符串形式提供。

resources#

此字段可用于为 Ray Pod 指定自定义资源容量。这些资源容量将向 Ray 调度器和 Ray 自动伸缩器进行通告。例如,以下注解将标记一个 Ray Pod 具有 1 个单位的 Custom1 容量和 5 个单位的 Custom2 容量。

rayStartParams:
    resources: '"{\"Custom1\": 1, \"Custom2\": 5}"'

然后,您可以使用类似 @ray.remote(resources={"Custom2": 1}) 的注解来注解任务和 Actor。Ray 调度器和自动伸缩器将采取适当的行动来调度此类任务。

请注意用于表达资源字符串的格式。特别是,请注意反斜杠作为字符串中的实际字符存在。如果您以编程方式指定 RayCluster,您可能需要转义反斜杠,以确保它们被处理为字符串的一部分。

字段 rayStartParams.resources 应仅用于自定义资源。键 CPUGPUmemory 是禁止的。如果您需要为这些资源字段指定覆盖值,请使用 Ray 启动参数 num-cpusnum-gpusmemory

服务与网络#

Ray Head 服务。#

KubeRay operator 会自动配置一个 Kubernetes Service,暴露 Ray Head Pod 几个服务的默认端口,包括:

  • Ray Client(默认端口 10001)

  • Ray Dashboard(默认端口 8265)

  • Ray GCS 服务器(默认端口 6379)

  • Ray Serve(默认端口 8000)

  • Ray Prometheus 指标(默认端口 8080)

配置的 Kubernetes Service 的名称是 RayCluster 的名称 metadata.name 加上后缀head-svc。对于本页给出的示例 CR(自定义资源实例),Head 服务的名称将是raycluster-example-head-svc。然后,Kubernetes 网络(kube-dns)允许我们使用名称raycluster-example-head-svc来访问 Ray Head 的服务。例如,可以在同一 Kubernetes 命名空间中的 Pod 中使用以下方式访问 Ray Client 服务器:

ray.init("ray://raycluster-example-head-svc:10001")

在另一个命名空间中的 Pod 中,可以使用以下方式访问 Ray Client 服务器:

ray.init("ray://raycluster-example-head-svc.default.svc.cluster.local:10001")

(这假设 Ray 集群部署在默认的 Kubernetes 命名空间中。如果 Ray 集群部署在非默认命名空间中,请使用该命名空间替换 default。)

指定非默认端口。#

如果您希望覆盖 Ray Head 服务暴露的端口,可以在 headGroupSpec 下指定 Ray Head 容器的 ports 列表。这是一个 Ray Head 服务非默认端口列表的示例。

ports:
- containerPort: 6380
  name: gcs
- containerPort: 8266
  name: dashboard
- containerPort: 10002
  name: client

如果指定了 Head 容器的 ports 列表,Ray Head 服务将精确地暴露列表中的端口。在上面的示例中,Head 服务将仅暴露三个端口;特别是不会暴露 Ray Serve 的端口。

为了让 Ray Head 实际使用端口列表中指定的非默认端口,您还必须指定相关的 rayStartParams。对于上面的示例,

rayStartParams:
  port: "6380"
  dashboard-port: "8266"
  ray-client-server-port: "10002"
  ...