版本信息最后核对:2026-10-25
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 的可用节点里。
nodeSelector、node affinity、Pod affinity 或 topology spread 与节点标签冲突;节点带有未容忍的污点;节点被 cordon;可用区或实例类型不符合要求。num_gpus=1,集群资源里却没有 GPU 可分配。容器里能看到卡,不等于 Ray 运行时已经把它计入逻辑资源。nvidia.com/gpu.product 等标签选节点,Ray 侧却按另一套自定义资源调度,或者两者只配置了一层。Pod 被放到了 A 型号节点,任务却要求 B 型号资源,就会一直等待。maxReplicas 上限,或请求的 worker 组无法创建;Kubernetes cluster-autoscaler 无法为带特殊标签、污点或本地卷的 Pod 找到合适节点。逻辑上“还能扩”不等于实际能扩。给问题分层。记录 K8s Pod 的状态、重启次数和 Events,再记录 ray status 的节点数、资源总量和 Pending Demands。Pod Pending 与 task Pending 同时存在时先修 K8s;Pod 正常而 Ray 无节点或资源不足时查 Ray。
检查 K8s 调度约束。展开 Pod 的 Events,逐项核对 nodeSelector、affinity、tolerations 和资源 requests。Allocated resources 要看已分配 requests,不要被实时 CPU 使用率误导。若事件显示 GPU 资源不足,再查节点 allocatable 和 device plugin。
核对 GPU 标签与资源名。列出 GPU 节点及其型号、卡数标签,确认 nodeSelector 使用的键值真实存在;MIG 或共享 GPU 场景要确认资源名与插件配置一致。不要用不存在的标签“兜底”选节点。
检查 Ray 资源清单。在 head 上运行 ray status,确认节点数是否等于 K8s 中已就绪的 worker 数,GPU 总数是否与分配一致。若任务等待自定义资源,确认该资源已在 Ray 节点启动时注册;若已有 Actor 占用,比较申请量与剩余量。
检查任务声明。查看 @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 申请了多少资源、是否还有存活实例占着资源。
检查 head/worker 连接链路。确认 head Pod Ready、head Service 有 Endpoints、worker 使用的地址是稳定 Service 名而不是会变化的 Pod IP。查看 head 与 worker 日志中的连接、版本和认证错误;在集群内用镜像已有的网络工具验证名称解析和端口可达性,不要只从本机测试。实际端口以启动参数和清单为准。
检查自动伸缩。若开启了 Ray autoscaler,看 head 的 autoscaler 日志和 worker 组上限;若依赖 Kubernetes 节点级扩容,确认 cluster-autoscaler 能识别 Pod 的标签、污点、资源和拓扑要求。自动伸缩只对“扩容后确实能调度”的 Pod 有效,配置错误时要先修模板,而不是无限等待。
检查应用层状态。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 资源等待”分别写进值班手册,排障时就不会在上层现象里绕圈。
ray status 与任务提交合上页面,先回答,再展开对照。
ray status 里没有它,常见原因有哪些?nvidia.com/gpu.product 等节点标签配合 nodeSelector 或亲和性,把 Ray worker Pod 放到 A100 池;Ray 层需要把卡型注册成自定义资源,并在 task 或 Actor 上同时申请 GPU 与该资源。只做一层不够:Pod 可能被放到目标节点,但 Ray 调度器仍不知道卡型,或者 Ray 有卡型标记却没有 K8s 节点匹配。
小程序版
微信扫码,随时查命令