RayCluster 配置#

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

介绍#

在 Kubernetes 上部署 Ray 遵循 operator 模式。关键角色是

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

  • 一个 自定义控制器,即 KubeRay 操作员,它管理 Ray pod 以匹配 RayCluster 的规范。

要部署一个 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
              # Keep this preStop hook in each Ray container config.
              lifecycle:
                preStop:
                  exec:
                    command: ["/bin/sh","-c","ray stop"]
              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 具有相同的 Ray 版本。如果您使用的是 nightly 或开发版的 Ray 镜像,可以将 rayVersion 设置为 Ray 的最新发布版本。

Pod 配置:headGroupSpec 和 workerGroupSpecs#

在高层次上,RayCluster 是一组 Kubernetes pod,类似于 Kubernetes 的 Deployment 或 StatefulSet。与 Kubernetes 内置组件一样,关键的配置部分是

  • Pod 规范

  • 规模信息(期望的Pod数量)

Deployment 和 RayCluster 之间的关键区别在于,RayCluster 是专门用于运行 Ray 应用程序的。一个 Ray 集群由

  • 一个 主节点 ,负责为 Ray 集群托管全局控制进程。主节点也可以运行 Ray 任务和角色。

  • 任意数量的 工作节点,它们运行 Ray 任务和角色。工作节点以相同配置的 工作组 形式出现。对于每个工作组,我们必须指定 副本,即我们希望该组拥有的节点数量。

头节点的配置在 headGroupSpec 下指定,而工作节点的配置在 workerGroupSpecs 下指定。可能存在多个工作组,每个组都有自己的配置。workerGroupSpecreplicas 字段指定了该组在集群中保持的工作节点数量。每个 workerGroupSpec 还有可选的 minReplicasmaxReplicas 字段;如果你想启用 自动扩展,这些字段很重要。

Pod 模板#

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

容器#

Ray pod 模板至少指定一个容器,即运行 Ray 进程的容器。Ray pod 模板还可以指定额外的边车容器,用于诸如 日志处理 等目的。然而,KubeRay 操作符假设容器列表中的第一个容器是主 Ray 容器。因此,确保在主 Ray 容器之后指定任何边车容器。换句话说,Ray 容器应该是 containers 列表中的第一个

资源#

为每个组规范指定容器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。这些值将用作 Ray 头节点或工作组中 Ray pod 的逻辑资源容量。请注意,CPU 数量在传递给 Ray 之前会向上取整为最接近的整数。通告给 Ray 的资源容量可以在 Ray 启动参数 中被覆盖。

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

nodeSelector 和 tolerations#

您可以通过设置 pod 规范的 nodeSelectortolerations 字段来控制工作组 Ray pod 的调度。具体来说,这些字段决定了 pod 可以在哪些 Kubernetes 节点上调度。有关 Pod-to-Node 分配的更多信息,请参阅 Kubernetes 文档

图像#

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

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

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

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

metadata.name 和 metadata.generateName#

KubeRay 操作员将忽略用户设置的 metadata.namemetadata.generateName 的值。KubeRay 操作员将自动生成一个 generateName 以避免名称冲突。更多详情请参见 KubeRay issue #587

Ray 启动参数#

每个组规范中的 rayStartParams 字段是一个字符串到字符串的映射,表示传递给 Ray 容器的 ray start 入口点的参数。有关参数的完整列表,请参阅 ray start 的文档。我们特别注意以下参数:

仪表盘-主机#

在大多数使用场景中,此字段应设置为“0.0.0.0”以用于 Ray 头节点。这是为了将 Ray 仪表板暴露在 Ray 集群外部所必需的。(未来的版本可能会默认设置此参数。)

num-cpus#

这个可选字段告诉 Ray 调度器和自动缩放器 Ray pod 有多少个 CPU 可用。CPU 数量可以从组规范的 pod template 中指定的 Kubernetes 资源限制中自动检测。然而,有时覆盖这个自动检测的值是有用的。例如,为 Ray 头 pod 设置 num-cpus:"0" 将防止 Ray 工作负载在头节点上调度具有非零 CPU 要求的任务。请注意,所有 Ray 启动参数的值,包括 num-cpus,必须作为 字符串 提供。

num-gpus#

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

内存#

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

资源#

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

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

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

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

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

服务和网络#

Ray 头部服务。#

KubeRay 操作符会自动配置一个 Kubernetes 服务,该服务暴露 Ray 头节点 Pod 的多个服务的默认端口,包括

  • Ray 客户端(默认端口 10001)

  • Ray 仪表盘(默认端口 8265)

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

  • Ray Serve (默认端口 8000)

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

配置的 Kubernetes 服务的名称是 RayCluster 的名称 metadata.name,后面加上后缀 head-svc。对于本页提供的示例 CR,头部服务的名称将是 raycluster-example-head-svc。Kubernetes 网络 (kube-dns) 允许我们使用名称 raycluster-example-head-svc 来访问 Ray 头部的服务。例如,Ray 客户端服务器可以通过同一 Kubernetes 命名空间中的 pod 访问。

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

Ray Client 服务器可以通过另一个命名空间中的 pod 访问。

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

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

ServiceType, Ingresses#

Ray Client 和其他服务可以通过端口转发或入口暴露在 Kubernetes 集群外部。访问 Ray head 服务的最简单方式是使用端口转发。

在集群外部暴露头部的服务可能需要使用类型为 LoadBalancer 或 NodePort 的服务。将 headGroupSpec.serviceType 设置为适合您应用的类型。

您可能希望设置一个入口以将 Ray 头节点的服务暴露到集群外部。详情请参阅 [KubeRay 文档][IngressDoc]。

指定非默认端口。#

如果你想覆盖 Ray 头服务暴露的端口,可以通过在 headGroupSpec 下指定 Ray 头容器的 ports 列表来实现。以下是 Ray 头服务的非默认端口列表的示例。

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

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

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

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

Pod 和容器生命周期:preStopHook#

建议每个 Ray 容器的配置都包含以下阻塞块:

lifecycle:
  preStop:
    exec:
      command: ["/bin/sh","-c","ray stop"]

为了确保优雅终止,在 Ray pod 终止之前会执行 ray stop