背景#
Ray 是一个统一的框架,支持分布式计算,因为单个服务器可能无法处理繁重的计算任务。在本文中,我们对 Ray 的两个重要特性进行了深入分析,即多租户和自动扩展。多租户允许将不同的资源(如 CPU 和内存)分配给不同的用户。另一方面,自动扩展根据流量和使用水平来增加或减少实例。
选择 Kubernetes 的原因#
虽然 Ray 提供了一个官方的自动扩展解决方案,根据资源请求扩展 Ray 集群,但我们的特定场景需要基于内存使用情况的自动扩展。因此,我们开发了两个潜在的解决方案。第一个解决方案涉及利用 Ray 资源监视器来监控每个任务和演员的资源使用情况。鉴于我们为每个用户分配了不同的配额,有必要累积每个任务和演员的资源使用情况。此外,我们必须建立一个机制,以确定何时根据内存使用情况触发自动扩展。第二个解决方案涉及利用 Kubernetes 水平 Pod 自动扩展器,根据内存使用情况自动调整副本数量。因此,我们在这里主要选择第二个解决方案。
构建 Ray 集群#
假设您已经设置了 Kubernetes 和 Harbor 环境,下一步是安装 Ray 集群。需要注意的是,您需要修改镜像以匹配您的特定配置。
# Ray 主节点服务,允许工作负载 Pod 发现主节点以进行双向通信。
# 更多上下文可以在 [端口配置文档](https://docs.ray.io/en/latest/ray-core/configure.html#ports-configurations) 中找到。
apiVersion: v1
kind: Service
metadata:
name: service-ray-cluster
labels:
app: ray-cluster-head
spec:
clusterIP: None
ports:
- name: client
protocol: TCP
port: 10001
targetPort: 10001
- name: dashboard
protocol: TCP
port: 8265
targetPort: 8265
- name: gcs-server
protocol: TCP
port: 6380
targetPort: 6380
selector:
app: ray-cluster-head
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-ray-head
labels:
app: ray-cluster-head
spec:
# 请勿更改此项 - Ray 当前仅支持每个集群一个主节点。
replicas: 1
selector:
matchLabels:
component: ray-head
type: ray
app: ray-cluster-head
template:
metadata:
labels:
component: ray-head
type: ray
app: ray-cluster-head
spec:
# 如果主节点崩溃,整个集群(包括所有工作节点)也会崩溃。
# 如果您希望 Kubernetes 在这种情况下启动一个新的主节点,请将此设置为 "Always",否则设置为 "Never"。
restartPolicy: Always
# 此卷为 Ray 分配共享内存,用于其 Plasma 对象存储。
# 如果您不提供此项,Ray 将回退到 /tmp,这会导致速度变慢,如果它不是共享内存卷。
# volumes:
# - name: dshm
# emptyDir:
# medium: Memory
containers:
- name: ray-head
image: rayproject/ray:2.3.0
imagePullPolicy: Always
command: [ "/bin/bash", "-c", "--" ]
# 如果 Redis 没有密码,请设置 --redis-password=''
args:
- "ray start --head --port=6380 --num-cpus=$MY_CPU_REQUEST --dashboard-host=0.0.0.0 --object-manager-port=8076 --node-manager-port=8077 --dashboard-agent-grpc-port=8078 --dashboard-agent-listen-port=52365 --redis-password='123456' --block"
ports:
- containerPort: 6380 # GCS 服务器
- containerPort: 10001 # Ray 客户端使用
- containerPort: 8265 # Ray 仪表板使用
# 此卷为 Ray 分配共享内存,用于其 Plasma 对象存储。
# 如果您不提供此项,Ray 将回退到 /tmp,这会导致速度变慢,如果它不是共享内存卷。
# volumeMounts:
# - mountPath: /dev/shm
# name: dshm
env:
# RAY_REDIS_ADDRESS 让 Ray 使用外部 Redis 进行容错
- name: RAY_REDIS_ADDRESS
value: redis:6379 # 外部 Redis 的 IP 地址,在此示例中为 "redis:6379"
# 这在 ray start 命令中使用,以便 Ray 可以生成正确数量的进程。
# 忽略此项可能会导致性能下降。
- name: MY_CPU_REQUEST
valueFrom:
resourceFieldRef:
resource: requests.cpu
resources:
limits:
cpu: "2"
memory: "5G"
requests:
# 对于生产用例,我们建议指定整数 CPU 请求和限制。
# 我们还建议将请求设置为 CPU 和内存的限制相等。
# 在此示例中,我们使用 500m CPU 请求来适应资源受限的本地 Kubernetes 测试环境,如 KinD 和 minikube。
cpu: "500m"
# Ray 主节点的休眠状态内存使用量约为 1Gb。我们不建议为 Ray 主 Pod 分配少于 2Gb 的内存。
# 对于生产用例,我们建议为每个 Ray 容器分配至少 8Gb 的内存。
memory: "2G"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-ray-worker
labels:
app: ray-cluster-worker
spec:
# 更改此项以扩展在 Ray 集群中启动的工作节点数量。
selector:
matchLabels:
component: ray-worker
type: ray
app: ray-cluster-worker
template:
metadata:
labels:
component: ray-worker
type: ray
app: ray-cluster-worker
spec:
restartPolicy: Always
# volumes:
# - name: dshm
# emptyDir:
# medium: Memory
containers:
- name: ray-worker
image: rayproject/ray:2.3.0
imagePullPolicy: Always
command: ["/bin/bash", "-c", "--"]
args:
- "ray start --num-cpus=$MY_CPU_REQUEST --address=service-ray-cluster:6380 --object-manager-port=8076 --node-manager-port=8077 --dashboard-agent-grpc-port=8078 --dashboard-agent-listen-port=52365 --block"
# 此卷为 Ray 分配共享内存,用于其 Plasma 对象存储。
# 如果您不提供此项,Ray 将回退到 /tmp,这会导致速度变慢,如果它不是共享内存卷。
# volumeMounts:
# - mountPath: /dev/shm
# name: dshm
env:
# 这在 ray start 命令中使用,以便 Ray 可以生成正确数量的进程。
# 忽略此项可能会导致性能下降。
- name: MY_CPU_REQUEST
valueFrom:
resourceFieldRef:
resource: requests.cpu
# 此配置中的资源请求和限制对于生产来说太小了!
# 使用几个大型 Ray Pod 比使用许多小型 Pod 更好。
# 对于生产,理想情况下每个 Ray Pod 的大小应占用其调度的整个 Kubernetes 节点。
resources:
limits:
cpu: "1"
memory: "1G"
# 对于生产用例,我们建议指定整数 CPU 请求和限制。
# 我们还建议将请求设置为 CPU 和内存的限制相等。
# 在此示例中,我们使用 500m CPU 请求来适应资源受限的本地 Kubernetes 测试环境,如 KinD 和 minikube。
requests:
cpu: "500m"
memory: "1G"
一旦您在服务器上创建了 ray-dev.yaml
文件,您可以继续安装 Ray 主节点和工作节点。如果您想在 ns
命名空间中安装 Ray 集群,可以通过执行以下命令来实现:
kubectl apply -f ray-dev.yaml -n ns
在服务器上创建 ray-dev.yaml
文件并安装 Ray 主节点和工作节点后,下一步是为工作节点启用水平 Pod 自动扩展(HPA)。
要为工作节点启用 HPA,您需要在服务器上创建另一个名为 ray-autoscaler.yaml
的文件。该文件将包含 Kubernetes HPA 对象的配置,该对象将根据 CPU 使用情况自动扩展工作 Pod 的数量。以下是一个示例 ray-autoscaler.yaml
文件:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: ray-autoscaler
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: deployment-ray-worker
minReplicas: 1
maxReplicas: 2
targetCPUUtilizationPercentage: 60
此 HPA 将根据 CPU 使用情况自动调整指定部署的副本数量,最小为 1 个副本,最大为 2 个副本,目标 CPU 使用率为 60%。
要在 ns
命名空间中应用此 HPA,请执行以下命令:
kubectl apply -f ray-autoscaler.yaml -n ns
监控和可观察性#
下一步是启用 集群监控。Ray 在主节点上自动生成一个名为 prom_metrics_service_discovery.json
的 Prometheus 服务发现文件,便于指标代理的服务发现。要使用默认的 Kubernetes Prometheus 为您的 Ray 集群启用监控,您需要将此文件映射出来并将其添加到 Prometheus 配置中。
我们可以更新 ray-dev.yaml
配置文件,以使用持久卷声明(PVC)来映射 /tmp/ray
目录(请参阅文档以获取更多详细信息)。这涉及在 YAML 文件中添加以下 volumes
和 volumeMounts
配置。最后,我们可以将此 PVC 与我们的 Prometheus 实例关联。
spec:
volumes:
- name: ray-prom
persistentVolumeClaim:
claimName: ray-prom-pvc
containers:
- name: ray-head
volumeMounts:
- name: ray-prom
mountPath: /tmp/ray