KubeRay 集成 Apache YuniKorn#

Apache YuniKorn 是一个轻量级的、通用的容器编排系统资源调度器。它能够在大规模、多租户和云原生环境中高效地为各种工作负载执行细粒度的资源共享。YuniKorn 为由无状态批处理工作负载和有状态服务组成的混合工作负载带来了统一的、跨平台的调度体验。

KubeRay 的 Apache YuniKorn 集成能够更高效地在多租户 Kubernetes 环境中调度 Ray Pod。

注意

此功能需要 KubeRay 版本 1.2.2 或更新版本,目前处于 Alpha 测试阶段。

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

在终端中运行以下命令

kind create cluster

步骤 2: 安装 Apache YuniKorn#

在启用 KubeRay 与 Apache YuniKorn 集成之前,您需要在 Kubernetes 集群中成功安装 Apache YuniKorn。请参阅 入门指南 以获取 Apache YuniKorn 的安装说明。

步骤 3: 安装支持 Apache YuniKorn 的 KubeRay 操作器#

使用 Helm 安装 KubeRay 操作器时,在命令行上传递 --set batchScheduler.name=yunikorn 标志

helm install kuberay-operator kuberay/kuberay-operator --version 1.5.1 --set batchScheduler.name=yunikorn

步骤 4: 使用 Apache YuniKorn 进行分组调度#

本示例演示了使用 Apache YuniKorn 和 KubeRay 对 RayCluster 自定义资源进行分组调度。从 KubeRay 1.5.1 开始,KubeRay 也支持对 RayJob 自定义资源进行分组调度。

首先,通过编辑 ConfigMap 创建一个具有 4 个 CPU 和 6Gi 内存容量的队列

运行 kubectl edit configmap -n yunikorn yunikorn-defaults

Helm 在安装 Apache YuniKorn Helm Chart 时会创建此 ConfigMap。

data 键下添加一个 queues.yaml 配置。 ConfigMap 应如下所示

apiVersion: v1
kind: ConfigMap
metadata:
  # Metadata for the ConfigMap, skip for brevity.
data:
  queues.yaml: |
    partitions:
      - name: default
        queues:
          - name: root
            queues:
              - name: test
                submitacl: "*"
                parent: false
                resources:
                  guaranteed:
                    memory: 6G
                    vcore: 4
                  max:
                    memory: 6G
                    vcore: 4

保存更改并退出编辑器。此配置将创建一个名为 root.test、具有 4 个 CPU 和 6Gi 内存容量的队列。

接下来,创建一个带有 1 个 CPU 和 2GiB 内存的 head 节点,以及两个每个具有 1 个 CPU 和 1GiB 内存的 worker 节点,总共 3 个 CPU 和 4GiB 内存的 RayCluster

# Path: kuberay/ray-operator/config/samples
# Configure the necessary labels on the RayCluster custom resource for Apache YuniKorn scheduler's gang scheduling:
# - `ray.io/gang-scheduling-enabled`: Set to `true` to enable gang scheduling.
# - `yunikorn.apache.org/app-id`: Set to a unique identifier for the application in Kubernetes, even across different namespaces.
# - `yunikorn.apache.org/queue`: Set to the name of one of the queues in Apache YuniKorn.
wget https://raw.githubusercontent.com/ray-project/kuberay/master/ray-operator/config/samples/ray-cluster.yunikorn-scheduler.yaml
kubectl apply -f ray-cluster.yunikorn-scheduler.yaml

检查 KubeRay 操作器创建的 RayCluster

$ kubectl describe raycluster test-yunikorn-0

Name:         test-yunikorn-0
Namespace:    default
Labels:       ray.io/gang-scheduling-enabled=true
              yunikorn.apache.org/app-id=test-yunikorn-0
              yunikorn.apache.org/queue=root.test
Annotations:  <none>
API Version:  ray.io/v1
Kind:         RayCluster
Metadata:
  Creation Timestamp:  2024-09-29T09:52:30Z
  Generation:          1
  Resource Version:    951
  UID:                 cae1dbc9-5a67-4b43-b0d9-be595f21ab85
# Other fields are skipped for brevity

注意 RayCluster 上的标签: ray.io/gang-scheduling-enabled=trueyunikorn.apache.org/app-id=test-yunikorn-0yunikorn.apache.org/queue=root.test

注意

当您需要分组调度时,只需要 ray.io/gang-scheduling-enabled 标签。如果未设置此标签,YuniKorn 将在不强制分组调度的情况下调度 Ray 集群。

由于队列的容量为 4 个 CPU 和 6GiB 内存,此资源应该可以成功调度,没有任何问题。

$ kubectl get pods

NAME                                  READY   STATUS    RESTARTS   AGE
test-yunikorn-0-head-98fmp            1/1     Running   0          67s
test-yunikorn-0-worker-worker-42tgg   1/1     Running   0          67s
test-yunikorn-0-worker-worker-467mn   1/1     Running   0          67s

通过查看 Apache YuniKorn 仪表板 来验证调度。

kubectl port-forward svc/yunikorn-service 9889:9889 -n yunikorn

转到 https://:9889/#/applications 查看正在运行的应用。

Apache YuniKorn dashboard

接下来,添加另一个具有相同 head 和 worker 节点配置的 RayCluster,但名称不同

# Replace the name with `test-yunikorn-1`.
sed 's/test-yunikorn-0/test-yunikorn-1/' ray-cluster.yunikorn-scheduler.yaml | kubectl apply -f-

现在 test-yunikorn-1 的所有 Pod 都处于 Pending 状态

$ kubectl get pods

NAME                                      READY   STATUS    RESTARTS   AGE
test-yunikorn-0-head-98fmp                1/1     Running   0          4m22s
test-yunikorn-0-worker-worker-42tgg       1/1     Running   0          4m22s
test-yunikorn-0-worker-worker-467mn       1/1     Running   0          4m22s
test-yunikorn-1-head-xl2r5                0/1     Pending   0          71s
test-yunikorn-1-worker-worker-l6ttz       0/1     Pending   0          71s
test-yunikorn-1-worker-worker-vjsts       0/1     Pending   0          71s
tg-test-yunikorn-1-headgroup-vgzvoot0dh   0/1     Pending   0          69s
tg-test-yunikorn-1-worker-eyti2bn2jv      1/1     Running   0          69s
tg-test-yunikorn-1-worker-k8it0x6s73      0/1     Pending   0          69s

Apache YuniKorn 会创建带有 tg- 前缀的 Pod,用于分组调度目的。

转到 https://:9889/#/applications,可以看到 test-yunikorn-1 处于 Accepted 状态,但尚未运行

Apache YuniKorn dashboard

由于新集群所需的 CPU 和内存超出了队列允许的范围,即使其中一个 Pod 可以容纳剩余的 1 个 CPU 和 2GiB 内存,Apache YuniKorn 也不会放置集群的 Pod,直到有足够的空间容纳所有 Pod。如果未使用 Apache YuniKorn 以这种方式进行分组调度,KubeRay 将放置其中一个 Pod,并且仅部分分配集群。

删除第一个 RayCluster 以释放队列中的资源

kubectl delete raycluster test-yunikorn-0

现在第二个集群的所有 Pod 都变为 Running 状态,因为现在有足够的资源来调度整个 Pod 集合

再次检查 Pod,以查看第二个集群现在已启动并正在运行

$ kubectl get pods

NAME                                  READY   STATUS    RESTARTS   AGE
test-yunikorn-1-head-xl2r5            1/1     Running   0          3m34s
test-yunikorn-1-worker-worker-l6ttz   1/1     Running   0          3m34s
test-yunikorn-1-worker-worker-vjsts   1/1     Running   0          3m34s

清理资源

kubectl delete raycluster test-yunikorn-1