部署 Ray Serve 应用程序#
先决条件#
本指南专注于 Ray Serve 多应用 API,该 API 自 Ray 版本 2.4.0 起可用。本指南主要关注 KubeRay v1.1.1 和 Ray 2.9.0 的行为。
Ray 2.4.0 或更新版本。
KubeRay 0.6.0, KubeRay nightly, 或更新版本。
什么是 RayService?#
一个 RayService 管理两个组件:
RayCluster:管理 Kubernetes 集群中的资源。
Ray Serve 应用: 管理用户的应用。
RayService 提供了什么?#
Kubernetes 原生支持 Ray 集群和 Ray Serve 应用: 在使用 Kubernetes 配置定义 Ray 集群及其 Ray Serve 应用后,您可以使用
kubectl
来创建集群及其应用。Ray Serve 应用程序的就地更新: 用户可以在 RayService CR 配置中更新 Ray Serve 配置,并使用
kubectl apply
来更新应用程序。更多详情请参见 步骤 7。Ray 集群的零停机升级: 用户可以在 RayService CR 配置中更新 Ray 集群配置,并使用
kubectl apply
来更新集群。RayService 会临时创建一个待处理的集群并等待其准备就绪,然后将流量切换到新集群并终止旧集群。更多详情请参见 步骤 8。高可用服务: 详情请参阅 RayService 高可用性。
示例:使用 RayService 提供两个简单的 Ray Serve 应用程序#
步骤 1:使用 Kind 创建一个 Kubernetes 集群#
kind create cluster --image=kindest/node:v1.26.0
步骤 2:安装 KubeRay 操作员#
按照 这个文档 从 Helm 仓库开始。请注意,此示例中的 YAML 文件使用 serveConfigV2
来指定多应用程序 Serve 配置,此功能从 KubeRay v0.6.0 开始可用。
步骤 3:安装 RayService#
kubectl apply -f https://raw.githubusercontent.com/ray-project/kuberay/v1.1.1/ray-operator/config/samples/ray-service.sample.yaml
首先,查看嵌入在 RayService YAML 中的 Ray Serve 配置
serveConfigV2
。注意两个高级应用程序:一个水果摊应用程序和一个计算器应用程序。注意一些关于水果摊应用程序的细节:水果摊应用程序包含在 test_dag 仓库的
fruit.py
文件中的deployment_graph
变量中,因此配置中的import_path
指向此变量,以告知 Serve 从何处导入应用程序。水果应用程序托管在路由前缀
/fruit
下,这意味着以/fruit
前缀开头的 HTTP 请求会被发送到水果摊应用程序。工作目录指向 test_dag 仓库,该仓库在运行时下载,RayService 在此目录中启动您的应用程序。更多详情请参见 运行时环境。
有关配置 Ray Serve 部署的更多详细信息,请参阅 Ray Serve 文档。
同样地,计算器应用从同一仓库的
conditional_dag.py
文件中导入,并且它托管在路径前缀/calc
下。
serveConfigV2: | applications: - name: fruit_app import_path: fruit.deployment_graph route_prefix: /fruit runtime_env: working_dir: "https://github.com/ray-project/test_dag/archive/78b4a5da38796123d9f9ffff59bab2792a043e95.zip" deployments: ... - name: math_app import_path: conditional_dag.serve_dag route_prefix: /calc runtime_env: working_dir: "https://github.com/ray-project/test_dag/archive/78b4a5da38796123d9f9ffff59bab2792a043e95.zip" deployments: ...
步骤 4:验证 Kubernetes 集群状态#
# Step 4.1: List all RayService custom resources in the `default` namespace.
kubectl get rayservice
# [Example output]
# NAME AGE
# rayservice-sample 2m42s
# Step 4.2: List all RayCluster custom resources in the `default` namespace.
kubectl get raycluster
# [Example output]
# NAME DESIRED WORKERS AVAILABLE WORKERS STATUS AGE
# rayservice-sample-raycluster-6mj28 1 1 ready 2m27s
# Step 4.3: List all Ray Pods in the `default` namespace.
kubectl get pods -l=ray.io/is-ray-node=yes
# [Example output]
# ervice-sample-raycluster-6mj28-worker-small-group-kg4v5 1/1 Running 0 3m52s
# rayservice-sample-raycluster-6mj28-head-x77h4 1/1 Running 0 3m52s
# Step 4.4: List services in the `default` namespace.
kubectl get services
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# ...
# rayservice-sample-head-svc ClusterIP 10.96.34.90 <none> 10001/TCP,8265/TCP,52365/TCP,6379/TCP,8080/TCP,8000/TCP 4m58s
# rayservice-sample-raycluster-6mj28-head-svc ClusterIP 10.96.171.184 <none> 10001/TCP,8265/TCP,52365/TCP,6379/TCP,8080/TCP,8000/TCP 6m21s
# rayservice-sample-serve-svc ClusterIP 10.96.161.84 <none> 8000/TCP 4m58s
KubeRay 根据 RayService YAML 中定义的 spec.rayClusterConfig
为 RayService 自定义资源创建一个 RayCluster。接下来,一旦 head Pod 运行并准备就绪,KubeRay 会向 head 的 dashboard 端口提交请求,以创建在 spec.serveConfigV2
中定义的 Ray Serve 应用程序。
当 Ray Serve 应用程序健康且准备就绪时,KubeRay 为 RayService 自定义资源创建一个头部服务和一个 serve 服务(例如,在步骤 4.4 中的 rayservice-sample-head-svc
和 rayservice-sample-serve-svc
)。用户可以通过 RayService 管理的头部服务(即 rayservice-sample-head-svc
)和 RayCluster 管理的头部服务(即 rayservice-sample-raycluster-6mj28-head-svc
)访问头部 Pod。然而,在零停机升级期间,会创建一个新的 RayCluster,并为新的 RayCluster 创建一个新的头部服务。如果你不使用 rayservice-sample-head-svc
,你需要更新入口配置以指向新的头部服务。但是,如果你使用 rayservice-sample-head-svc
,KubeRay 会自动更新选择器以指向新的头部 Pod,从而无需更新入口配置。
注意:默认端口及其定义。
端口 |
定义 |
---|---|
6379 |
Ray GCS |
8265 |
Ray 仪表盘 |
10001 |
Ray 客户端 |
8000 |
Ray Serve |
52365 |
Ray 仪表盘代理 |
步骤 5:验证 Serve 应用程序的状态#
# Step 5.1: Check the status of the RayService.
kubectl describe rayservices rayservice-sample
# Status:
# Active Service Status:
# Application Statuses:
# fruit_app:
# Health Last Update Time: 2024-03-01T21:53:33Z
# Serve Deployment Statuses:
# Fruit Market:
# Health Last Update Time: 2024-03-01T21:53:33Z
# Status: HEALTHY
# ...
# Status: RUNNING
# math_app:
# Health Last Update Time: 2024-03-01T21:53:33Z
# Serve Deployment Statuses:
# Adder:
# Health Last Update Time: 2024-03-01T21:53:33Z
# Status: HEALTHY
# ...
# Status: RUNNING
# Step 5.2: Check the Serve applications in the Ray dashboard.
# (1) Forward the dashboard port to localhost.
# (2) Check the Serve page in the Ray dashboard at http://localhost:8265/#/serve.
kubectl port-forward svc/rayservice-sample-head-svc 8265:8265
有关 RayService 可观测性的更多详细信息,请参阅 rayservice-troubleshooting.md。以下是 Ray 仪表板中 Serve 页面的截图示例。
步骤6:通过Kubernetes服务向Serve应用程序发送请求#
# Step 6.1: Run a curl Pod.
# If you already have a curl Pod, you can use `kubectl exec -it <curl-pod> -- sh` to access the Pod.
kubectl run curl --image=radial/busyboxplus:curl -i --tty
# Step 6.2: Send a request to the fruit stand app.
curl -X POST -H 'Content-Type: application/json' rayservice-sample-serve-svc:8000/fruit/ -d '["MANGO", 2]'
# [Expected output]: 6
# Step 6.3: Send a request to the calculator app.
curl -X POST -H 'Content-Type: application/json' rayservice-sample-serve-svc:8000/calc/ -d '["MUL", 3]'
# [Expected output]: "15 pizzas please!"
rayservice-sample-serve-svc
在所有拥有 Ray Serve 副本的工作者之间进行流量路由。
步骤 7:Ray Serve 应用程序的就地更新#
你可以通过修改 RayService 配置文件中的 serveConfigV2
来更新应用程序的配置。使用 kubectl apply
重新应用修改后的配置会将新配置应用到现有的 RayCluster,而不是创建一个新的 RayCluster。
将水果摊应用中芒果的价格从 3
更新为 4
,在 ray-service.sample.yaml 文件中进行此更改。此更改将重新配置现有的 MangoStand 部署,未来的请求将使用更新后的芒果价格。
# Step 7.1: Update the price of mangos from 3 to 4.
# [ray-service.sample.yaml]
# - name: MangoStand
# num_replicas: 1
# max_replicas_per_node: 1
# user_config:
# price: 4
# Step 7.2: Apply the updated RayService config.
kubectl apply -f ray-service.sample.yaml
# Step 7.3: Check the status of the RayService.
kubectl describe rayservices rayservice-sample
# [Example output]
# Serve Deployment Statuses:
# - healthLastUpdateTime: "2023-07-11T23:50:13Z"
# lastUpdateTime: "2023-07-11T23:50:13Z"
# name: MangoStand
# status: UPDATING
# Step 7.4: Send a request to the fruit stand app again after the Serve deployment status changes from UPDATING to HEALTHY.
# (Execute the command in the curl Pod from Step 6)
curl -X POST -H 'Content-Type: application/json' rayservice-sample-serve-svc:8000/fruit/ -d '["MANGO", 2]'
# [Expected output]: 8
步骤 8:Ray 集群的无停机升级#
在第7步中,修改 serveConfigV2
不会触发Ray集群的零停机升级。相反,它会将新配置重新应用到现有的RayCluster。然而,如果你在RayService YAML文件中修改 spec.rayClusterConfig
,它会触发Ray集群的零停机升级。RayService会暂时创建一个新的RayCluster并等待其准备就绪,然后通过更新由RayService管理的头服务的选择器(即 rayservice-sample-head-svc
)将流量切换到新的RayCluster,并终止旧的RayCluster。
在零停机升级过程中,RayService 会临时创建一个新的 RayCluster 并等待其准备就绪。一旦新的 RayCluster 准备就绪,RayService 会更新由 RayService 管理的头部服务的选择器(即 rayservice-sample-head-svc
),使其指向新的 RayCluster 以切换流量到新的 RayCluster。最后,旧的 RayCluster 将被终止。
某些异常不会触发零停机升级。只有 Ray 自动缩放器管理的字段,replicas
和 scaleStrategy.workersToDelete
,不会触发零停机升级。当你更新这些字段时,KubeRay 不会将更新从 RayService 传播到 RayCluster 自定义资源,因此什么也不会发生。
# Step 8.1: Update `spec.rayClusterConfig.workerGroupSpecs[0].replicas` in the RayService YAML file from 1 to 2.
# This field is an exception that doesn't trigger a zero-downtime upgrade, and KubeRay doesn't update the
# RayCluster as a result. Therefore, no changes occur.
kubectl apply -f ray-service.sample.yaml
# Step 8.2: Check RayService CR
kubectl describe rayservices rayservice-sample
# Worker Group Specs:
# ...
# Replicas: 2
# Step 8.3: Check RayCluster CR. The update doesn't propagate to the RayCluster CR.
kubectl describe rayclusters $YOUR_RAY_CLUSTER
# Worker Group Specs:
# ...
# Replicas: 1
# Step 8.4: Update `spec.rayClusterConfig.rayVersion` to `2.100.0`.
# This field determines the Autoscaler sidecar image, and triggers a zero downtime upgrade.
kubectl apply -f ray-service.sample.yaml
# Step 8.5: List all RayCluster custom resources in the `default` namespace.
# Note that the new RayCluster is created based on the updated RayService config to have 2 workers.
kubectl get raycluster
# NAME DESIRED WORKERS AVAILABLE WORKERS STATUS AGE
# rayservice-sample-raycluster-6mj28 1 1 ready 142m
# rayservice-sample-raycluster-sjj67 2 2 ready 44s
# Step 8.6: Wait for the old RayCluster terminate.
# Step 8.7: Submit a request to the fruit stand app via the same serve service.
curl -X POST -H 'Content-Type: application/json' rayservice-sample-serve-svc:8000/fruit/ -d '["MANGO", 2]'
# [Expected output]: 8
步骤 9:为什么 1 个工作 Pod 未就绪?#
新的 RayCluster 有 2 个工作 Pod,但只有 1 个工作 Pod 是准备好的。
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# curl 1/1 Running 0 27m
# ervice-sample-raycluster-ktf7n-worker-small-group-9rb96 0/1 Running 0 12m
# ervice-sample-raycluster-ktf7n-worker-small-group-qdjhs 1/1 Running 0 12m
# kuberay-operator-68f5866848-xx2bp 1/1 Running 0 108m
# rayservice-sample-raycluster-ktf7n-head-bnwcn 1/1 Running 0 12m
从 Ray 2.8 开始,没有 Ray Serve 副本的 Ray 工作 Pod 将不会有 Proxy 角色。从 KubeRay v1.1.0 开始,KubeRay 为每个工作 Pod 的 Ray 容器添加了一个就绪探针,以检查工作 Pod 是否具有 Proxy 角色。如果工作 Pod 缺少 Proxy 角色,就绪探针将失败,使工作 Pod 处于未就绪状态,因此,它不会接收任何流量。
kubectl describe pod ervice-sample-raycluster-ktf7n-worker-small-group-9rb96
# Readiness: exec [bash -c ... && wget -T 2 -q -O- http://localhost:8000/-/healthz | grep success] ...
# ......
# Events:
# Type Reason Age From Message
# ---- ------ ---- ---- -------
# ......
# Warning Unhealthy 35s (x100 over 8m15s) kubelet Readiness probe failed: success
为 Pod 创建一个 Ray Serve 副本以使其准备就绪。在 RayService YAML 文件中将 num_replicas
的值从 2 更新为 3,以为水果摊应用程序创建一个新的 Ray Serve 副本。此外,由于 max_replicas_per_node
为 1,新的 Ray Serve 副本必须分配给未准备好的工作 Pod。
kubectl apply -f ray-service.sample.yaml
# Update `num_replicas` from 2 to 3.
# [ray-service.sample.yaml]
# - name: MangoStand
# num_replicas: 3
# max_replicas_per_node: 1
# user_config:
# price: 4
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# curl 1/1 Running 0 43m
# ervice-sample-raycluster-ktf7n-worker-small-group-9rb96 1/1 Running 0 28m
# ervice-sample-raycluster-ktf7n-worker-small-group-qdjhs 1/1 Running 0 28m
# kuberay-operator-68f5866848-xx2bp 1/1 Running 0 123m
# rayservice-sample-raycluster-ktf7n-head-bnwcn 1/1 Running 0 28m
步骤 10:清理 Kubernetes 集群#
# Delete the RayService.
kubectl delete -f ray-service.sample.yaml
# Uninstall the KubeRay operator.
helm uninstall kuberay-operator
# Delete the curl Pod.
kubectl delete pod curl
下一步#
有关 RayService 高可用的更多详情,请参见 RayService 高可用性。
如果在使用过程中遇到任何问题,请参阅 RayService 故障排除指南。
更多 RayService 示例请参见 示例。MobileNet 示例 是一个很好的起点,因为它不需要 GPU 并且在本地机器上运行简单。