news 2026/9/21 15:23:32

KubeRay RayService 故障排查指南:从 Operator 日志到 Serve 应用状态的完整排障路径

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
KubeRay RayService 故障排查指南:从 Operator 日志到 Serve 应用状态的完整排障路径
  • 人工智能
  • 分布式训练
  • 强化学习
  • 任务调度
  • 模型推理服务

【免费下载链接】ray

Ray 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
点击查看免费下载

RayService 是 KubeRay 为 Ray Serve 提供的 Kubernetes 自定义资源(CRD):创建 RayService 时,KubeRay Operator 会先创建对应的 RayCluster,待集群就绪后再向 dashboard agent 提交请求,将serveConfigV2中声明的 Ray Serve 应用部署上去。因此,排障链条横跨「控制面(KubeRay Operator)— 数据面(Ray Serve 脚本与配置)」两层,一旦问题出在数据面,定位难度会明显上升。本文以 doc/source/cluster/kubernetes/troubleshooting/rayservice-troubleshooting.md 为骨架,系统讲解 5 类可观测手段和 11 类高频故障的定位与修复方法,读完即可建立一套从日志到状态、从配置到网络的完整排障流程。

排障前需要理解:RayService 的部署链路

在动手排障前,先明确 RayService 的正常工作链路(详见 RayService 部署指南):

  1. KubeRay Operator 根据spec.rayClusterConfig创建 RayCluster;
  2. head Pod 启动并就绪后,Operator 向 head 的 dashboard agent 端口(52365)发送PUT /api/serve/applications/请求,创建spec.serveConfigV2声明的 Serve 应用;
  3. Operator 周期性通过GET /api/serve/applications/拉取应用状态,写入 RayService CR 的status中。

理解这条链路后,你会发现几乎所有故障都可以归入三类:配置错误(Serve 脚本、serveConfigV2import_path、依赖缺失)、请求链路故障(网络策略、dashboard agent 未就绪/未运行)、资源与状态机问题(资源不足触发重启循环、升级冲突、Initializing 卡死)。下文先讲通用观测手段,再逐一拆解具体故障。

观测手段:五类定位入口

手段 1:查看 KubeRay Operator 日志

Operator 日志是定位控制面行为的第一手资料,尤其适合确认「Operator 是否发起了请求、因为什么原因重启了集群」:

kubectl logs $KUBERAY_OPERATOR_POD -n $YOUR_NAMESPACE | tee operator-log

将输出重定向到operator-log文件后,即可在文件内检索errorRestart RayCluster等关键词。Operator 日志中的典型信息包括:Serve 应用创建请求的失败原因、触发 RayCluster 重启的具体理由(例如 Issue 8 中的serviceUnhealthySecondThreshold超时),以及AvailableWorkerReplicasDesiredWorkerReplicas的对比。

手段 2:检查 RayService CR 的状态与事件

RayService 的statusevents记录了 Operator 视角下的应用状态:

kubectl describe rayservice $RAYSERVICE_NAME -n $YOUR_NAMESPACE

重点关注:

  • Status.ActiveServiceStatus.ApplicationStatuses:每个 Serve 应用及其部署的健康状态(RUNNING/HEALTHY/UPDATING);
  • Conditions:例如Ready=True表示 Serve 端点数量大于 0,可对外提供服务;UpgradeInProgress用于标识是否存在待切换的新集群;
  • Events:Operator 发出的事件(如 Issue 11 的超时 Warning 事件)。

手段 3:检查 head 与 worker Pod 内的 Serve 日志

Serve 的系统级日志(ServeController、HTTP Proxy)、访问日志和用户日志都落盘在 Pod 内:

kubectl exec -it $RAY_POD -n $YOUR_NAMESPACE -- bash # 检查 /tmp/ray/session_latest/logs/serve/ 下的日志

/tmp/ray/session_latest/logs/serve/目录是 Ray Serve 的标准日志落盘位置,相关说明见 Ray Serve 日志 与 Ray 日志配置。此外,head Pod 上的/tmp/ray/session_latest/logs/还包含dashboard.logdashboard_agent.log,分别对应 dashboard 与 dashboard agent 进程的运行状况,是排查 Issue 5、Issue 7 的关键。

手段 4:通过 Ray Dashboard 观察 Serve 应用

将 head Pod 的 dashboard 端口转发到本地后,可在浏览器中直接查看 Serve 页面:

kubectl port-forward $RAY_POD -n $YOUR_NAMESPACE 8265:8265 # 浏览器访问 $YOUR_IP:8265,进入 Serve 页面

Serve 页面会展示 Controller status、Proxy status、Application status 三个关键健康标签,以及每个应用/部署的RUNNINGHEALTHY状态、副本数与最后部署时间,适合快速建立全局视图。更详细的 Dashboard 观测说明见 Serve 视图文档。也可以直接转发 RayService 管理的 head 服务来固定访问入口:kubectl port-forward svc/rayservice-sample-head-svc 8265:8265

手段 5:使用 Ray State CLI 检查 Actor 状态

在 head Pod 内使用 Ray State CLI 可以绕过 Kubernetes 层直接查看 Serve 各组件(ServeController、ServeReplica、ProxyActor)的运行情况:

# 登录 head Pod export HEAD_POD=$(kubectl get pods --selector=ray.io/node-type=head -o custom-columns=POD:metadata.name --no-headers) kubectl exec -it $HEAD_POD -- ray summary actors # [示例输出] # ======== Actors Summary: 2023-07-11 17:58:24.625032 ======== # Stats: # ------------------------------------ # total_actors: 14 # # Table (group by class): # ------------------------------------ # CLASS_NAME STATE_COUNTS # 0 ServeController ALIVE: 1 # 1 ServeReplica:fruit_app_OrangeStand ALIVE: 1 # 2 ProxyActor ALIVE: 3 # 4 ServeReplica:math_app_Multiplier ALIVE: 1 # 5 ServeReplica:math_app_create_order ALIVE: 1 # 7 ServeReplica:fruit_app_FruitMarket ALIVE: 1 # 8 ServeReplica:math_app_Adder ALIVE: 1 # 9 ServeReplica:math_app_Router ALIVE: 1 # 10 ServeReplica:fruit_app_MangoStand ALIVE: 1 # 11 ServeReplica:fruit_app_PearStand ALIVE: 1

ray summary actors的输出直接反映 Serve 控制面组件的存活情况:如果某个ServeReplica缺失或处于非 ALIVE 状态,说明对应部署的副本未能正常拉起,可以据此顺藤摸瓜去查 Pod 日志或资源状态。Ray State CLI 的完整用法见 Ray State CLI 参考。

常见问题详解

Issue 1:Ray Serve 脚本本身有误

serveConfigV2中的应用逻辑(部署图、路由、请求处理函数)属于数据面,KubeRay 与 Ray Serve 都不会在提交阶段发现脚本层面的语法或逻辑错误,错误通常要等到应用状态变为UNHEALTHY或副本反复启动失败时才暴露。

建议:在把脚本部署到 RayService 之前,先在本机或独立 RayCluster 中按 Serve 开发工作流 完成本地验证,确认import_path指向的应用对象能够成功构建。只有本地验证通过后再进入 KubeRay 部署,才能把变量隔离在数据面之外。

Issue 2:serveConfigV2配置格式错误

RayService CR 将serveConfigV2声明为 YAML 多行字符串,目的是获得书写灵活性,但代价是没有严格的类型检查——字段名拼写、命名风格错误都不会在提交时被拒绝,而是延迟到运行时才暴露。

排障要点:

  • 参照 Ray Serve API 文档 中多应用 APIPUT "/api/serve/applications/"的 schema 核对字段;
  • 命名风格:与serveConfig不同,serveConfigV2遵循 snake_case。例如单应用配置中的numReplicas,在多应用配置中必须写作num_replicas。类似的还有maxReplicasPerNodemax_replicas_per_nodeuserConfiguser_config

从源码看,多应用配置的结构定义在 python/ray/serve/schema.py 中:ServeApplicationSchema(L783)描述单个应用的nameimport_pathroute_prefixruntime_envdeployments等字段,ServeDeploySchema(L1087)则通过applications: List[ServeApplicationSchema](L1129)组织多应用列表。排查配置时可以对照这两个类的字段定义逐项核对。

Issue 3:Ray 镜像缺少应用所需依赖

Serve 应用(尤其是模型推理类应用)往往需要镜像之外的 Python 依赖,两种解决途径:

  • 自建 Ray 镜像:把依赖固化进镜像(如在 Dockerfile 中pip install),适用于依赖稳定、需要频繁部署的场景;
  • 通过runtime_env指定:在serveConfigV2中声明依赖,由 Ray 在运行时安装,灵活但会增加首次部署时间。

例如 MobileNet 示例中,starlette.requests.form()需要python-multipart,而官方镜像rayproject/ray:x.y.z并未预装该包,因此必须在 runtime_env 中补充声明:

serveConfigV2: | applications: - name: mobilenet import_path: mobilenet.mobilenet:app runtime_env: working_dir: "https://github.com/ray-project/serve_config_examples/archive/b393e77bbd6aba0881e3d94c05f968f05a387b96.zip" pip: ["python-multipart==0.0.6"]

依赖缺失的典型症状是副本反复CrashLoopBackOff,应用状态长时间停留在UPDATINGUNHEALTHY,此时结合手段 3 查看 replica 日志中的ModuleNotFoundError即可确认。

Issue 4:import_path写法错误

import_path的格式为<模块路径>:<应用变量名>,其语义可以拆解为三段。以 MobileNet 示例为例:

serveConfigV2: | applications: - name: mobilenet import_path: mobilenet.mobilenet:app runtime_env: working_dir: "https://github.com/ray-project/serve_config_examples/archive/b393e77bbd6aba0881e3d94c05f968f05a387b96.zip" pip: ["python-multipart==0.0.6"]

其中mobilenet.mobilenet:app的含义是:

  • 第一个mobilenetworking_dir中应用的根目录名;
  • 第二个mobilenet:该目录下的 Python 文件名(即mobilenet/mobilenet.py);
  • app:Python 文件中代表 Ray Serve 应用的变量名(通常通过app = FruitStandDeployment.bind(...)deployment_graph构建)。

任何一段对不上(目录名、文件名、变量名),都会导致应用在导入阶段失败。排障时建议先在本地按 Issue 1 的方式验证import_path可导入,再提交到 RayService。import_path字段的完整格式说明见 ServeApplicationSchema.import_path 的文档条目。

Issue 5:创建/更新 Serve 应用失败

KubeRay Operator 在 head Pod 就绪后立即提交PUT /api/serve/applications/请求,但dashboard、dashboard agent 与 GCS 需要在 head Pod 就绪后再花几秒完成启动,因此请求在最初几次失败是正常现象。

错误信息 1:connect: connection refused
Put "http://${HEAD_SVC_FQDN}:52365/api/serve/applications/": dial tcp $HEAD_IP:52365: connect: connection refused

处理步骤:

  1. 等待约 1 分钟再观察——这通常是组件启动时序导致的暂时性失败;
  2. 若超过 1 分钟仍持续失败,则可能是 dashboard 或 dashboard agent 未能正常启动,进入 head Pod 检查/tmp/ray/session_latest/logs/下的dashboard.logdashboard_agent.log
错误信息 2:i/o timeout
Put "http://${HEAD_SVC_FQDN}:52365/api/serve/applications/": dial tcp $HEAD_IP:52365: i/o timeout"

i/o timeoutconnection refused不同,说明端口并未直接拒绝连接,而是数据包没有收到响应,最常见的原因是 Kubernetes NetworkPolicy 阻断了 Pod 与 dashboard agent 端口(52365)之间的流量。此时应检查集群中的 NetworkPolicy 规则,确认 Operator → head Pod 的52365端口通路。

Issue 6:runtime_env相关故障

serveConfigV2中可以为应用指定运行时环境(working_dirpip依赖等),常见故障有两类:

  • working_dir指向私有 AWS S3 桶,但 Pod 没有访问该桶的权限(缺少 IAM 角色、访问密钥或 ServiceAccount 注解);
  • NetworkPolicy 阻断了 Pod 与runtime_env中外部 URL 之间的流量,导致working_dir下载失败或 pip 安装超时。

这两类问题的现象相似:副本长时间处于启动中,日志中出现下载/连接相关错误。排查顺序建议为:先看副本日志中的具体异常(权限 403 还是连接超时),再对应检查云凭证配置或网络策略。

Issue 7:无法获取 Serve 应用状态

Operator 在成功提交PUT请求后,会持续通过GET /api/serve/applications/拉取状态:

Get "http://${HEAD_SVC_FQDN}:52365/api/serve/applications/": dial tcp $HEAD_IP:52365: connect: connection refused"

与 Issue 5 不同——PUT成功说明 dashboard agent 当时是就绪的,因此GET失败属于异常状态(组件随后可能发生了崩溃)。最典型的原因是head Pod 上的 dashboard agent 进程未运行。可以按以下步骤复现与验证:

# Step 1: 登录 head Pod kubectl exec -it $HEAD_POD -n $YOUR_NAMESPACE -- bash # Step 2: 找到 dashboard agent 进程的 PID ps aux # [示例输出] # ray 156 ... 0:03 ray::DashboardAgent -- # Step 3: 手动杀掉 dashboard agent 进程 kill 156 # Step 4: 查看 dashboard agent 日志确认退出原因 cat /tmp/ray/session_latest/logs/dashboard_agent.log # [示例输出] # 2023-07-10 11:24:31,962 INFO web_log.py:206 -- 10.244.0.5 [10/Jul/2023:18:24:31 +0000] "GET /api/serve/applications/ HTTP/1.1" 200 13940 "-" "Go-http-client/1.1" # ... # 2023-07-10 11:24:38,590 WARNING agent.py:531 -- Exiting with SIGTERM immediately... # Step 5: 在新终端查看 KubeRay Operator 日志,确认 GET 请求开始失败 kubectl logs $KUBERAY_OPERATOR_POD -n $YOUR_NAMESPACE | tee operator-log # [示例输出] # Get "http://rayservice-sample-raycluster-rqlsl-head-svc.default.svc.cluster.local:52365/api/serve/applications/": dial tcp 10.96.7.154:52365: connect: connection refused

这个实验表明:一旦 dashboard agent 退出,Operator 的GET请求就会持续收到connection refused。生产中遇到此类报错,应重点检查dashboard_agent.log中的崩溃原因,而不是怀疑 NetworkPolicy。

Issue 8:资源不足导致 RayCluster 重启循环(KubeRay v0.6.1 及更早版本)

注意:KubeRay Operator 目前没有针对「Kubernetes 集群资源耗尽」的明确处理方案,因此确保集群有充足资源容纳 Serve 应用是首选策略。

当 Serve 应用状态在超过serviceUnhealthySecondThreshold秒后仍未变为RUNNING时,Operator 会将该 RayCluster 标记为不健康,并着手准备新 RayCluster。若集群资源确实不足,新集群同样无法部署应用,于是形成重启循环

可通过如下实验复现该场景:

  • 一个只有 8 个 CPU 的节点;
  • RayCluster:1 个 head Pod(4 个物理 CPU,但rayStartParamsnum-cpus=0,防止 Serve 副本调度到 head 上)+ 1 个默认 1 CPU 的 worker Pod;
  • serveConfigV2声明 5 个 Serve 部署,每个部署 1 个副本、需要 1 CPU。
# Step 1: 查看节点可用 CPU kubectl get nodes -o custom-columns=NODE:.metadata.name,ALLOCATABLE_CPU:.status.allocatable.cpu # [示例输出] # NODE ALLOCATABLE_CPU # kind-control-plane 8 # Step 2: 安装 KubeRay Operator # Step 3: 创建资源不足的 RayService kubectl apply -f ray-service.insufficient-resources.yaml # Step 4: 查看 RayService 状态,副本因资源不足而无法调度 kubectl describe rayservices.ray.io rayservice-sample -n $YOUR_NAMESPACE # [示例输出] # fruit_app_FruitMarket: # Health Last Update Time: 2023-07-11T02:10:02Z # Last Update Time: 2023-07-11T02:10:35Z # Message: Deployment "fruit_app_FruitMarket" has 1 replicas that have taken more than 30s to be scheduled. This may be caused by waiting for the cluster to auto-scale, or waiting for a runtime environment to install. Resources required for each replica: {"CPU": 1.0}, resources available: {}. # Status: UPDATING # Step 5: 超过 serviceUnhealthySecondThreshold(此处为 300s)后,Operator 创建新 RayCluster kubectl logs $KUBERAY_OPERATOR_POD -n $YOUR_NAMESPACE | tee operator-log # [示例输出] # 2023-07-11T02:14:58.109Z INFO controllers.RayService Restart RayCluster {"appName": "fruit_app", "restart reason": "The status of the serve application fruit_app has not been RUNNING for more than 300.000000 seconds. Hence, KubeRay operator labels the RayCluster unhealthy and will prepare a new RayCluster."} # ... # 2023-07-11T02:14:58.122Z INFO controllers.RayService Restart RayCluster {"ServiceName": "default/rayservice-sample", "AvailableWorkerReplicas": 1, "DesiredWorkerReplicas": 5, "restart reason": "The serve application is unhealthy, restarting the cluster. If the AvailableWorkerReplicas is not equal to DesiredWorkerReplicas, this may imply that the Autoscaler does not have enough resources to scale up the cluster. Hence, the serve application does not have enough resources to run. ..."}

日志中AvailableWorkerReplicasDesiredWorkerReplicas不一致是资源不足的关键信号:它说明 Autoscaler 无法获得足够资源把集群扩到期望规模。对应的修复方向是扩容 Kubernetes 节点、降低应用资源需求或减少副本数,而不是反复观察 Operator 自动重启。

Issue 9:从单应用 API 无缝升级到多应用 API

KubeRay v0.6.0 起通过serveConfigV2支持 Ray Serve API V2(多应用)。但Ray Serve 不允许同一集群中同时使用 API V1 与 API V2。若在现有使用serveConfig的 RayService 上原地替换为serveConfigV2,会遇到:

ray.serve.exceptions.RayServeException: You are trying to deploy a multi-application config, however a single-application config has been deployed to the current Serve instance already. Mixing single-app and multi-app is not allowed. Please either redeploy using the single-application config format `ServeApplicationSchema`, or shutdown and restart Serve to submit a multi-app config of format `ServeDeploySchema`. If you are using the REST API, you can submit a multi-app config to the the multi-app API endpoint `/api/serve/applications/`.

解决方案:将serveConfig替换为serveConfigV2,同时把rayVersion修改为一个无实际效果的值(Ray 2.0.0 及以后rayVersion不决定实际镜像,仅用于触发升级),例如2.100.0。这会触发新 RayCluster 的创建,从而以「新集群方式」完成 API 版本迁移,而不是原地更新。

如果按上述步骤操作后仍报错,且开启了 GCS fault tolerance,则问题可能出在ray.io/external-storage-namespace注解:新旧 RayCluster 若使用相同的存储命名空间,会共享旧集群的元数据。此时应移除该注解,让 KubeRay 为每个 RayCluster 自动生成唯一键。

Issue 10:启用 GCS fault tolerance 时的无停机升级问题

KubeRay 会为 head Pod 设置环境变量RAY_external_storage_namespace,其取值来自gcsFaultToleranceOptions.externalStorageNamespace,或更早的ray.io/external-storage-namespace注解。该值代表 Ray 集群元数据在 Redis 中的存储命名空间:head Pod 恢复时会用该值重连 Redis 以恢复集群数据。

风险在于:如果该值在 RayService 中固定,零停机升级时新 RayCluster 会访问与旧集群相同的 Redis 存储命名空间。由于 Redis 中已存在旧集群的元数据,Operator 可能误以为 Serve 应用已经就绪,从而过早地退役旧 RayCluster 并将流量切到新集群——而新集群此时可能还在初始化 Serve 应用,造成流量中断。

推荐做法

  • 移除ray.io/external-storage-namespace注解:不设置时,KubeRay 自动使用每个 RayCluster CR 的 UID 作为RAY_external_storage_namespace值,新旧集群互不可见,天然隔离;
  • 或者为每个 RayCluster 手动设置唯一RAY_external_storage_namespace值。

关于默认命名空间如何同时支持 GCS fault tolerance 与零停机升级,见 GCS fault tolerance 与零停机升级。

Issue 11:RayService 卡在 Initializing——用初始化超时快速失败

当底层 Pod 被调度但无法启动(如ImagePullBackOffCrashLoopBackOff或其他容器启动错误)时,RayService 可能无限期停留在 Initializing 状态,持续占用集群资源,且根因更难定位。

机制

KubeRay 通过注解ray.io/initializing-timeout暴露可配置的初始化超时。超时触发后 Operator 的行为:

  • RayServiceReady条件被置为False,reason 为InitializingTimeout
  • RayService 进入终态(失败),此时修改 spec 不会触发重试,恢复需要删除并重新创建 RayService
  • RayService CR 上的集群名称被清空,触发底层 RayCluster 资源的清理(删除仍会遵循RayClusterDeletionDelaySeconds的延迟);
  • 发出一个记录超时与失败原因的Warning事件。
启用方式

只需在 RayService 的metadata中加注解,无需任何其他 CRD 变更。注解值支持 Go duration 字符串(如"30m""1h")或整数秒(如"1800"):

metadata: annotations: ray.io/initializing-timeout: "30m"
使用建议

超时值需要在「正常启动所需时间」与「快速失败以节省集群资源」之间权衡:设置过短可能误杀正常部署中的服务,设置过长则失去 fail-fast 的意义。结合手段 2 观察RayServiceReady条件与事件,即可判断是否触发了InitializingTimeout

排障流程速查

将上述方法与问题对应,可以形成一条快速路径:

症状首选观测手段对应 Issue
应用部署后状态异常手段 3(Pod 内 Serve 日志)1、3、6
提交配置后立刻报错手段 2(CR 状态)+ Issue 2 的字段核对2、4
创建/更新请求失败手段 1(Operator 日志)+ head Pod 内dashboard.log/dashboard_agent.log5、7
集群反复重启手段 1(Operator 日志中的restart reason8
升级后报错或流量中断手段 2(CR 状态与事件)9、10
服务卡在 Initializing手段 2(RayServiceReady条件与 Warning 事件)11

排障的顺序建议是:先看 Operator 日志确认控制面行为 → 再看 RayService CR 状态确认应用视图 → 最后进入 Pod 查 Serve 与 dashboard agent 日志确认进程级细节。数据面问题(Issue 1–4、6)尽量先在本地或独立 RayCluster 复现,把「脚本/配置错误」与「Kubernetes 环境问题」分离;控制面与基础设施问题(Issue 5、7、8、11)则紧抓 Operator 日志中的请求与重启记录。掌握这套方法后,RayService 的绝大多数故障都可以在十分钟内完成定性。

  • 人工智能
  • 分布式训练
  • 强化学习
  • 任务调度
  • 模型推理服务

【免费下载链接】ray

Ray 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
点击查看免费下载

相关推荐

上一篇:Eino许可证:Apache-2.0开源协议使用指南
下一篇:t5-small-qg-hl部署指南:云端与本地环境配置详解

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 14:46:46

Keil uVision5安装与STM32芯片包配置完整指南

1. 为什么STM32开发绕不开Keil uVision5这套工具链搞STM32开发的人&#xff0c;十有八九第一个接触的IDE就是Keil uVision5。这不是没有原因的——它把编辑器、编译器、调试器、芯片支持包管理全部塞进一个界面里&#xff0c;装完之后新建工程、选芯片型号、写代码、点下载&…

作者头像 李华
网站建设 2026/9/21 14:31:40

使用 MXNet Sparse Symbol 与 Module API 训练稀疏线性回归模型

使用 MXNet Sparse Symbol 与 Module API 训练稀疏线性回归模型 【免费下载链接】mxnet Lightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more 项…

作者头像 李华