系统学习 AI 学习路径:从 GPU 运行时到推理、微调与 RAG 评估

版本信息最后核对:2026-10-25

ray-scheduling

Ray 任务、Actor 与 GPU 一直 Pending 的定位:分清 Kubernetes 调度和 Ray 调度两层。

现象

Ray 任务或 Actor 提交后一直没有开始,Dashboard 和 ray status 显示资源需求等待满足;Ray Serve 副本长期停在启动中;或者客户端能连上 head,却始终拿不到执行结果。在 Kubernetes 上,KubeRay 创建的 head 或 worker Pod 也可能一直 Pending、CrashLoopBackOff,或者 Pod 已经是 Running,但 ray status 里根本看不到对应节点。

这类问题至少有两层调度:Kubernetes 调度器决定 Ray 的 Pod 放到哪台节点,Ray 调度器再把 task、actor 和 Serve 副本分配到已加入集群的 Ray 节点。Kubernetes 层成功只说明 Pod 被放置并启动,不代表 raylet 已加入 Ray 集群,也不代表 GPU 被正确登记成 Ray 逻辑资源。

因此“Pending”必须带上下文:是 K8s Pod Pending,还是 Ray task 没被调度;是节点上没有资源,还是资源被别的 Actor 占着;是卡型标签不匹配,还是 head 与 worker 根本没有连通。

快速判断

先同时看两层状态,再决定往哪边查:

kubectl get raycluster,rayjob,rayservice -A
kubectl get pods -l ray.io/cluster=<cluster> -n <ns> -o wide
kubectl describe pod <pod> -n <ns>
kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -30
kubectl exec -it <head-pod> -n <ns> -c <head-container> -- ray status

如果 Pod 处于 Pending,先读 kubectl describe pod 的 Events,问题通常在 Kubernetes 调度器:资源不足、污点、亲和性、卷未绑定或 GPU 资源不存在。如果 Pod 全部 Running 但 Ray 层仍等待,查看 ray status 的资源总量、已用量和 Pending Demands;这时问题在 Ray 的逻辑资源、任务声明或 head/worker 连接。

检查节点 GPU 与标签:

kubectl get nodes -o wide
kubectl get nodes -L nvidia.com/gpu.product,nvidia.com/gpu.count
kubectl get node <node> -o custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\\.com/gpu
kubectl describe node <node> | sed -n '/Allocated resources/,/Events/p'

观察点:节点是否有 nvidia.com/gpu 可分配资源,标签值是否与工作负载的 nodeSelector 或 GPU 类型选择一致,节点上已有 Pod 的 requests 是否把剩余容量切碎。nvidia.com/gpu.product 的实际值以集群为准,不要凭型号缩写猜标签。

确认 head 与 worker 是否真的连上:

kubectl get svc <cluster>-head-svc -n <ns>
kubectl get endpoints <cluster>-head-svc -n <ns>
kubectl logs <head-pod> -n <ns> -c <head-container> --tail=200
kubectl logs <worker-pod> -n <ns> -c <worker-container> --tail=200

再看 Ray 客户端、head 和 worker 的版本。版本不一致、head Service 解析失败、网络策略阻断或 worker 用错地址时,K8s Pod 可以是 Running,但不会出现在 ray status 的可用节点里。

常见原因

  1. Kubernetes 资源不足:CPU、内存或 GPU 的 requests 超过节点可分配量,或者集群总量够但碎片化严重,没有单节点能容纳整个 Pod。GPU 是扩展资源,不能超卖,监控里的低利用率不代表调度器还有可分配额度。
  2. 节点约束不匹配:nodeSelector、node affinity、Pod affinity 或 topology spread 与节点标签冲突;节点带有未容忍的污点;节点被 cordon;可用区或实例类型不符合要求。
  3. GPU 没有暴露或没有申请:节点上 device plugin 未正常上报资源,Pod 资源名写错,MIG 环境的资源名与实际配置不一致。Kubernetes 没有分配 GPU,Ray 进程自然也看不到卡。
  4. Ray 节点没有登记 GPU:启动 Ray 节点时未正确识别或声明 GPU,任务虽然写了 num_gpus=1,集群资源里却没有 GPU 可分配。容器里能看到卡,不等于 Ray 运行时已经把它计入逻辑资源。
  5. 逻辑资源不足或被占用:已有长期运行的 Actor 占住 GPU 或自定义资源;新任务申请的数量超过单节点剩余量;任务要求的自定义卡型标签根本没有注册到任何节点。
  6. 请求本身无法满足:单个任务要求多张 GPU,但每个节点只有更少的卡,又没有合适的 placement group 或跨节点方案;严格打包的 placement group 等待一个永远凑不齐的整块资源。
  7. GPU 类型选择链路断裂:K8s 用 nvidia.com/gpu.product 等标签选节点,Ray 侧却按另一套自定义资源调度,或者两者只配置了一层。Pod 被放到了 A 型号节点,任务却要求 B 型号资源,就会一直等待。
  8. head/worker 连接失败:head 尚未就绪,worker 连错 Service、Pod IP 或端口;head Service 没有端点;DNS、NetworkPolicy 或防火墙阻断;客户端与集群版本不一致;worker 进程启动后反复重连。
  9. 自动伸缩没有兑现资源:Ray autoscaler 到达 maxReplicas 上限,或请求的 worker 组无法创建;Kubernetes cluster-autoscaler 无法为带特殊标签、污点或本地卷的 Pod 找到合适节点。逻辑上“还能扩”不等于实际能扩。
  10. 把应用错误当成调度问题:Serve 应用本身导入失败、初始化抛错或健康检查不通过时,副本会停在启动阶段,资源等待只是表象。要看 Pod 日志和 Serve 状态,而不只看 Pending。

排查步骤

  1. 给问题分层。记录 K8s Pod 的状态、重启次数和 Events,再记录 ray status 的节点数、资源总量和 Pending Demands。Pod Pending 与 task Pending 同时存在时先修 K8s;Pod 正常而 Ray 无节点或资源不足时查 Ray。

  2. 检查 K8s 调度约束。展开 Pod 的 Events,逐项核对 nodeSelector、affinity、tolerations 和资源 requests。Allocated resources 要看已分配 requests,不要被实时 CPU 使用率误导。若事件显示 GPU 资源不足,再查节点 allocatable 和 device plugin。

  3. 核对 GPU 标签与资源名。列出 GPU 节点及其型号、卡数标签,确认 nodeSelector 使用的键值真实存在;MIG 或共享 GPU 场景要确认资源名与插件配置一致。不要用不存在的标签“兜底”选节点。

  4. 检查 Ray 资源清单。在 head 上运行 ray status,确认节点数是否等于 K8s 中已就绪的 worker 数,GPU 总数是否与分配一致。若任务等待自定义资源,确认该资源已在 Ray 节点启动时注册;若已有 Actor 占用,比较申请量与剩余量。

  5. 检查任务声明。查看 @ray.remote 的 num_gpus、num_cpus 和 resources。需要卡型时,同时申请 GPU 和表示卡型的自定义资源;标记的数量决定能有多少任务并发占用该标记。不要在 Ray 资源不足时只改 K8s 标签,反之亦然。

import os
import torch
import ray

ray.init(address="auto")

@ray.remote(num_gpus=1)
def probe():
    return {
        "node": ray.get_runtime_context().get_node_id(),
        "cuda_visible": os.environ.get("CUDA_VISIBLE_DEVICES"),
        "cuda_count": torch.cuda.device_count(),
    }

print(ray.get(probe.remote()))

观察返回的 node id 是否属于预期 GPU 节点,CUDA_VISIBLE_DEVICES 是否非空,cuda_count 是否符合申请量。如果 probe 也一直 Pending,先修资源声明;如果 probe 能跑但业务 Actor Pending,检查业务 Actor 申请了多少资源、是否还有存活实例占着资源。

  1. 检查 head/worker 连接链路。确认 head Pod Ready、head Service 有 Endpoints、worker 使用的地址是稳定 Service 名而不是会变化的 Pod IP。查看 head 与 worker 日志中的连接、版本和认证错误;在集群内用镜像已有的网络工具验证名称解析和端口可达性,不要只从本机测试。实际端口以启动参数和清单为准。

  2. 检查自动伸缩。若开启了 Ray autoscaler,看 head 的 autoscaler 日志和 worker 组上限;若依赖 Kubernetes 节点级扩容,确认 cluster-autoscaler 能识别 Pod 的标签、污点、资源和拓扑要求。自动伸缩只对“扩容后确实能调度”的 Pod 有效,配置错误时要先修模板,而不是无限等待。

  3. 检查应用层状态。Ray Serve 副本长时间未就绪时查看 Serve 状态、Deployment 日志和 Pod 日志,确认是资源等待、镜像拉取、依赖安装、权重加载还是应用导入报错。只有资源等待才按调度修复;初始化错误要回到应用与镜像。

修复与验证

修复后验证的顺序是:K8s Pod 被调度并 Ready;Ray ray status 出现预期数量的节点和 GPU;一个申请单卡的最小 task 能运行并返回正确 node id;卡型约束能把任务放到目标节点;Ray Serve 副本就绪并通过健康检查。还要在故障恢复后确认没有遗留 Actor 占着资源,扩容与缩容都能回到稳定状态。

预防

同时监控两层:K8s 的 Pending Pod、节点 allocatable、device plugin 状态,以及 Ray 的节点数、GPU 总量、Pending Demands 和 Actor 占用。告警不能只看 Pod Running,因为 Ray 层仍可能没有可用资源。把 GPU 型号标签、资源名和 Ray 自定义资源的对应关系写成统一约定,并通过准入校验或部署前检查阻止拼写不一致。

为 GPU 池设置清晰的最小、最大容量,定期用最小 probe 验证调度链路;升级 KubeRay、Ray、驱动或 device plugin 后重新验证。head 节点按长期基础设施对待,固定版本和资源,避免和大量业务负载抢资源。把“K8s 调度失败”和“Ray 资源等待”分别写进值班手册,排障时就不会在上层现象里绕圈。

相关命令

参考链接

读完自测

合上页面,先回答,再展开对照。

  1. K8s Pod 已经 Running,但 Ray task 一直 Pending,应该先查哪一层?和 Pod 本身 Pending 有什么不同?
  2. 要让任务只落在 A100 节点上,Kubernetes 层和 Ray 层分别需要做什么?
  3. worker Pod 是 Running,但 ray status 里没有它,常见原因有哪些?
答案
  1. Pod Running 说明 Kubernetes 已经完成放置,问题更可能在 Ray 层:Ray 节点是否已加入、逻辑资源是否声明、GPU 或自定义资源是否被其他 Actor 占用、任务申请量是否超过单节点容量。Pod 本身 Pending 则是 Kubernetes 调度还没成功,应先看 Events、资源 requests、节点标签、污点和卷。两层要分别确认,不能因为 Pod Running 就认定 Ray 可用。
  2. Kubernetes 层用 nvidia.com/gpu.product 等节点标签配合 nodeSelector 或亲和性,把 Ray worker Pod 放到 A100 池;Ray 层需要把卡型注册成自定义资源,并在 task 或 Actor 上同时申请 GPU 与该资源。只做一层不够:Pod 可能被放到目标节点,但 Ray 调度器仍不知道卡型,或者 Ray 有卡型标记却没有 K8s 节点匹配。
  3. 先看 head/worker 日志和 head Service 的 Endpoints,再核对 worker 使用的地址、端口、网络策略、防火墙和 Ray 版本。还要确认 worker 进程是否真正启动、raylet 是否连上 head,以及 K8s 中的 worker 数是否与 Ray 节点数一致。Pod Running 只代表容器进程存在,不代表 Ray 注册成功。
微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令