ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ray on Kubernetes 故障排查:为什么 RayService 中无 Serve Replica 的 Worker Pod 会处于未就绪状态

Ray on Kubernetes 故障排查:为什么 RayService 中无 Serve Replica 的 Worker Pod 会处于未就绪状态 Ray on Kubernetes 故障排查为什么 RayService 中无 Serve Replica 的 Worker Pod 会处于未就绪状态【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址: https://gitcode.com/gh_mirrors/ra/ray本指南针对 KubeRay 的 RayService API 下一种典型场景展开由于某个 Worker Pod 上没有运行 Ray Serve 副本replica该 Pod 的 Kubernetes 就绪探针readiness probe持续失败导致 Pod 长期处于0/1 Running的未就绪状态。读完本文你将理解 ProxyActor 与就绪探针之间的联动机制掌握通过serveConfigV2与rayStartParams控制副本调度的方法并能通过 Dashboard、kubectl describe、port-forward等工具完成验证与原地in-place更新。上图展示了 Step 4 场景下 Ray Dashboard 的 Cluster 页面名为rayservice-no-ray-serve-replica-raycluster-dnn28-s-worker-7712k与...-worker-46017的两个 Worker 节点中只有承载ray::ServeReplica.simple_app::BaseService与ray::ProxyActor的节点处于健康状态另一个节点因缺少 ProxyActor 而无法通过就绪探针。背景ProxyActor、Serve Replica 与就绪探针的关系在深入操作之前需要先厘清 Ray Serve 中的两个核心组件更完整的架构说明可参考 Ray Serve 架构文档中关于 Replica 与 ProxyActor 的高层视图Ray Serve Replica真正承载用户业务逻辑即deployments中定义的部署的进程负责处理具体请求。ProxyActor集群中的 HTTP 代理负责把进入的请求转发给对应的 Ray Serve Replica。任何一个没有运行 ProxyActor 的 Head 或 Worker Pod一旦接收到请求这些请求都会失败。KubeRay 正是利用这一特性来构建就绪语义从 Ray 2.8 开始没有承载任何 Ray Serve Replica 的 Worker Pod 不会启动 ProxyActor从 KubeRay v1.1.0 开始KubeRay 会给每个 Worker Pod 的 Ray 容器注入就绪探针专门检查该 Pod 是否存在 ProxyActor。当一个 Worker Pod 缺少 ProxyActor 时就绪探针失败Pod 被标记为未就绪流量也不会被调度到它上面——这正是本指南要复现和解释的现象。Ray Serve 的默认行为是只在运行着 Ray Serve Replica 的 Pod 上创建 ProxyActor。为了直观演示这一点下文使用 RayService 部署一个简单的 Serve 应用并故意让副本数量少于 Worker 数量观察多余的 Worker Pod 进入未就绪状态。Step 1用 Kind 创建 Kubernetes 集群本指南全程在本地 Kind 集群中完成。如果你已经有可用的 Kubernetes 集群可以跳过本步。kind create cluster --imagekindest/node:v1.26.0Step 2安装 KubeRay Operator按照 KubeRay Operator 安装文档 的指引通过 Helm 仓库安装最新稳定版 KubeRay Operator。核心命令如下Operator 被安装到独立的ray-system命名空间以隔离其 ServiceAccount 与业务 Podhelm repo add kuberay https://ray-project.github.io/kuberay-helm/ helm repo update kubectl create namespace ray-system helm install kuberay-operator kuberay/kuberay-operator --version 1.7.0 -n ray-system安装完成后可用kubectl get pods -n ray-system验证 Operator 已处于Running状态。Step 3部署一个 RayService从 KubeRay 仓库下载示例清单并应用curl -O https://raw.githubusercontent.com/ray-project/kuberay/master/ray-operator/config/samples/ray-service.no-ray-serve-replica.yaml kubectl apply -f ray-service.no-ray-serve-replica.yaml3.1 解读serveConfigV2控制副本数量与调度密度示例 RayService 内嵌的 Serve 多应用配置serveConfigV2是理解整个现象的关键。清单中名为simple_app的应用在deployments下只声明了一个部署BaseService其中两个参数直接决定了副本的调度结果num_replicas控制该部署用于处理请求的副本总数。这里初始化为1确保整个 Ray Serve 的副本总数为 1从而只会有一个 Worker Pod 承载 Replica。max_replicas_per_node控制单个 Pod 上允许运行的最大副本数。这里设为1保证一个 Pod 最多只有一个 Replica为后续一个 Worker 就绪、另一个 Worker 未就绪的局面创造条件。serveConfigV2: | applications: - name: simple_app import_path: ray-operator.config.samples.ray-serve.single_deployment_dag:DagNode route_prefix: /basic runtime_env: working_dir: https://github.com/ray-project/kuberay/archive/master.zip deployments: - name: BaseService num_replicas: 1 max_replicas_per_node: 1 ray_actor_options: num_cpus: 0.1关于serveConfigV2的字段语义、snake_case 命名规范例如numReplicas是单应用 APIserveConfig的写法而num_replicas才是serveConfigV2的写法以及import_path的格式可进一步参考 RayService 故障排查指南 中的相关 Issue 说明。3.2 解读rayClusterConfig.headGroupSpec让 Head Pod 不参与副本调度再看 RayService 中内嵌的 Head Pod 配置。rayClusterConfig.headGroupSpec通过向rayStartParams传入num-cpus: 0把 Head Pod 的可用 CPU 数声明为 0headGroupSpec: rayStartParams: num-cpus: 0 template: ...这样做的目的很明确Head Pod 没有任何可用 CPU 资源Ray 调度器便不会把 Ray Serve Replica 调度到 Head 上示例中 Replica 还要求num_cpus: 0.1。rayStartParams的完整参数清单可参考 KubeRay 的 rayStartParams 指南。由此Replica 只可能在 Worker Pod 之间分布为下面的1 个就绪 1 个未就绪现象奠定了基础。Step 4为什么有一个 Worker Pod 未就绪4.1 等待 RayService 就绪kubectl describe rayservices.ray.io rayservice-no-ray-serve-replica示例输出中可以看到 RayService 已经 ReadyConditions: Last Transition Time: 2025-03-18T14:14:43Z Message: Number of serve endpoints is greater than 0 Observed Generation: 1 Reason: NonZeroServeEndpoints Status: True Type: Ready Last Transition Time: 2025-03-18T14:12:03Z Message: Active Ray cluster exists and no pending Ray cluster Observed Generation: 1 Reason: NoPendingCluster Status: False Type: UpgradeInProgress4.2 列出 RayCluster 的 Podkubectl get pods -lray.io/is-ray-nodeyes示例输出NAME READY STATUS RESTARTS AGE rayservice-no-ray-serve-replica-raycluster-dnm28-head-9h2qt 1/1 Running 0 2m21s rayservice-no-ray-serve-replica-raycluster-dnm28-s-worker-46t7l 1/1 Running 0 2m21s rayservice-no-ray-serve-replica-raycluster-dnm28-s-worker-77rzk 0/1 Running 0 2m20s可以看到Head Pod 与其中一个 Workerworker-46t7l都是1/1 Running而另一个 Workerworker-77rzk处于0/1 Running——容器在运行但就绪探针尚未通过。4.3 检查未就绪 Worker Pod 的事件kubectl describe pods {YOUR_UNREADY_WORKER_POD_NAME}示例输出Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 3m4s default-scheduler Successfully assigned default/rayservice-no-ray-serve-replica-raycluster-dnm28-s-worker-77rzk to kind-control-plane Normal Pulled 3m3s kubelet Container image rayproject/ray:2.46.0 already present on machine Normal Created 3m3s kubelet Created container wait-gcs-ready Normal Started 3m3s kubelet Started container wait-gcs-ready Normal Pulled 2m57s kubelet Container image rayproject/ray:2.46.0 already present on machine Normal Created 2m57s kubelet Created container ray-worker Normal Started 2m57s kubelet Started container ray-worker Warning Unhealthy 78s (x19 over 2m43s) kubelet Readiness probe failed: success4.4 现象解释结合第 4.2 步的输出现象一目了然KubeRay 依据spec.serveConfigV2只创建一个 Ray Serve Replica并把该 Replica 调度到其中一个 Worker Pod 上承载 Replica 的 Worker Pod 上同时会启动 ProxyActor就绪探针通过Pod 被标记为 ready另一个没有 Replica 的 Worker Pod 从 Ray 2.8 起不会启动 ProxyActorKubeRay 的就绪探针自 v1.1.0 起对每个 Worker Pod 的 Ray 容器执行因检测不到 ProxyActor 而失败Pod 被标记为 unready且不会接收任何流量。简而言之Pod 未就绪并不是故障而是 KubeRay 主动的流量保护机制——确保只有具备 ProxyActor、真正能完成请求转发与处理的 Pod 才进入服务状态。Step 5通过 Ray Dashboard 验证 Serve 应用状态使用port-forward把 Head Pod 的 Dashboard 端口8265暴露到本地kubectl port-forward svc/rayservice-no-ray-serve-replica-head-svc 8265:8265随后在浏览器中访问http://localhost:8265并切换到 Serve / Cluster 页面。如本文开头截图所示在一个 Worker Pod 上运行着ray::ServeReplica::simple_app::BaseService与ray::ProxyActor对应 Pod 被标记为 ready在另一个 Worker Pod 上没有运行任何 Serve Replica 与 ProxyActor对应 Pod 被标记为 unready。关于 RayService 可观测性的更多手段Operator 日志、CR 状态、Pod 内/tmp/ray/session_latest/logs/serve/下的 Serve 日志、Dashboard、Ray State CLI 等请参考 RayService 故障排查指南。Step 6通过 Kubernetes Serve Service 发送请求rayservice-no-ray-serve-replica-serve-svc负责在承载 Ray Serve Replica 的 Worker 之间做流量路由。虽然存在一个未就绪的 Worker Pod但 Serve 仍能把流量导向就绪的 Worker因此应用可以正常对外服务。# Step 6.1运行一个 curl Pod。 # 如果已有 curl Pod可以用 kubectl exec -it curl-pod -- sh 直接进入。 kubectl run curl --imagecurlimages/curl:latest -i --tty -- sh # Step 6.2向 simple_app 发送请求。 curl -X POST -H Content-Type: application/json rayservice-no-ray-serve-replica-serve-svc:8000/basic # [Expected output]: hello worldStep 7对 Ray Serve 应用做原地In-place更新当 Serve 应用需要扩容时无需重建 RayCluster只需修改serveConfigV2中的副本数并重新apply。下面把BaseService的num_replicas从1改为2# Step 7.1把应用的 num_replicas 从 1 改为 2。 # [ray-service.no-ray-serve-replica.yaml] # deployments: # - name: BaseService # num_replicas: 2 # max_replicas_per_node: 1 # ray_actor_options: # num_cpus: 0.1 # Step 7.2应用更新后的 RayService 配置。 kubectl apply -f ray-service.no-ray-serve-replica.yaml # Step 7.3再次列出 RayCluster 的 Pod。 kubectl get pods -lray.io/is-ray-nodeyes示例输出NAME READY STATUS RESTARTS AGE rayservice-no-ray-serve-replica-raycluster-dnm28-head-9h2qt 1/1 Running 0 46m rayservice-no-ray-serve-replica-raycluster-dnm28-s-worker-46t7l 1/1 Running 0 46m rayservice-no-ray-serve-replica-raycluster-dnm28-s-worker-77rzk 1/1 Running 0 46m重新配置后KubeRay 会请求 Head Pod 创建额外的 Ray Serve Replica 以满足新的num_replicas。由于max_replicas_per_node为1新 Replica 会被调度到原本没有副本的那个 Worker Pod 上该 Pod 随之获得 ProxyActor就绪探针转为通过KubeRay 将其标记为 ready。最终三个 Pod 全部1/1 Running集群资源得到充分利用这正是副本调度密度 就绪探针组合的正确用法。Step 8清理集群资源实验结束后按以下顺序清理# 删除 RayService。 kubectl delete -f ray-service.no-ray-serve-replica.yaml # 卸载 KubeRay Operator。 helm uninstall kuberay-operator # 删除 curl Pod。 kubectl delete pod curl进一步阅读遇到其他 RayService 问题serveConfigV2写错、import_path错误、Serve 应用创建/更新失败、RuntimeEnv 权限、GCS 容错升级等参见 RayService 故障排查指南。需要更多 RayService 部署样例High Availability、增量升级、DeepSeek 推理、MobileNet 等参见 KubeRay 示例集。本文涉及的示例清单ray-service.no-ray-serve-replica.yaml位于 KubeRay 仓库的ray-operator/config/samples/目录示例中引用的 Serve 应用定义single_deployment_dag:DagNode可在同一仓库的ray-operator/config/samples/ray-serve/中找到。小结围绕一个 Worker Pod 为什么不就绪这一现象本文梳理了完整的排查链路从 Ray 2.8 起无 Replica 即无 ProxyActor 的 Serve 行为到 KubeRay v1.1.0 起基于 ProxyActor 的就绪探针设计再到通过num_replicas、max_replicas_per_node、rayStartParams精确控制副本调度最后通过 Dashboard 与 Serve Service 验证应用可用性并利用原地更新实现无损扩容。理解这一机制后未就绪的 Worker不再是需要恐慌的异常而是 KubeRay 保证 Serve 流量只进入健康代理的一种预期设计。【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址: https://gitcode.com/gh_mirrors/ra/ray创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进