内容提要
本文是 Volcano 源码阅读系列第六篇,讲解批调度基础架构,核心问题是 KubeRay 已创建 Pod 但集群资源不足时如何分配。Volcano 用 PodGroup 表达成组关系,Gang 插件检查最低运行门槛,Queue 组织共享资源。核心组件包括 Scheduler、Controller Manager 和 Admission Webhook。原生 Volcano Job 先准入再创建 Pod,而 KubeRay 路径会先创建未调度的 Ray Pod。
延伸解读
成组调度的适用边界
文章强调,Gang 调度并非所有 Ray 程序的必需。Ray task 可以按可用资源逐步执行,部分应用也允许弹性成员数。是否需要成组调度,取决于应用对最低资源量和成员数量的要求。配置过高的门槛会延长等待,甚至让原本可以逐步运行的程序始终无法启动。因此,在启用 Volcano 前,应明确作业是否真的需要所有成员同时就绪。
两条提交路径的差异
原生 Volcano Job 先经过准入再创建 Pod,可减少资源不足时产生大量等待 Pod。而 KubeRay 路径中,RayJob Controller 会先创建 PodGroup 和 RayCluster,RayCluster Controller 随后创建 head、worker Pod,不会复用 Volcano Job Controller 的等待逻辑。因此,接入 Volcano 后仍可能看到已创建但未调度的 Ray Pod,这是两条路径在对象创建顺序上的本质区别。
对象分层与排查思路
文章提醒,Volcano 中多个概念容易混淆:KubeRay 工作队列是待协调对象标识,Volcano Queue 是持久化资源策略对象,Scheduler 内部优先队列是内存结构。排查 Pod 未运行时,应依次检查 PodGroup 是否创建、成员 Pod 是否存在、Queue 是否允许调度、Pod 是否已绑定 Node。绑定后仍停滞,则需查镜像、存储、网络等,而非仅调整队列权重。
Q&A
Volcano 为什么需要把多个 Pod 放在一起调度?
因为像 Ray 这样的分布式计算作业,通常需要多个成员 Pod 同时运行才能有效计算。如果调度器逐个分配 Pod,可能出现两个作业各占一部分资源、但都因缺少成员而无法推进的情况。Volcano 通过 PodGroup 表达成组关系,并由 Gang 插件检查一组 Pod 的最低运行门槛,确保作业整体满足条件后再分配资源。
Volcano 中的 PodGroup 和 Queue 分别是什么?
PodGroup 是命名空间级资源,用于描述哪些 Pod 作为一组参与调度,以及组的最低要求,如 minMember(最低成员数)和 minResources(最低资源量)。Queue 是集群级资源,用于组织共享资源的工作负载,具有权重、资源上限、保障资源和状态等信息,可被多个命名空间中的 PodGroup 引用。
Volcano 的核心组件有哪些?它们分别运行在哪里?
Volcano 有三个主要常驻服务角色:Scheduler、Controller Manager 和 Admission Webhook。Scheduler 观察 Pod、PodGroup、Queue、Node,维护调度缓存并执行资源分配与绑定;Controller Manager 运行 Job、Queue、PodGroup 等控制器,维护对象生命周期;Admission Webhook 处理 API Server 转发的匹配请求,校验或补全资源字段。它们可以分别部署,也可以有多个副本。
Volcano Job 和 KubeRay 提交路径在创建 Pod 的流程上有什么不同?
Volcano Job 原生路径先经过准入再创建 Pod:Job Controller 维护 PodGroup,Scheduler 通过 enqueue 推进准入,Controller 再据此创建 Pod,这样可以减少资源不足时生成大量等待中的 Pod。KubeRay 路径则不同:RayJob Controller 创建 PodGroup 和 RayCluster,RayCluster Controller 随后维护 head、worker Pod,不会复用 Volcano Job Controller 的等待准入流程,因此接入 Volcano 后仍可能看到已创建但未调度的 Ray Pod。
Volcano 中一次 Pod 提交后,调度器是如何处理的?
Pod 出现后,Scheduler 根据 schedulerName 处理属于自己的待调度 Pod,再通过 annotation 找到 PodGroup。Queue 策略和 Gang 约束参与资源分配;选好节点后,通过 Kubernetes API 把 Pod 与该 Node 的关系确定下来,这一步称为绑定(Bind)。绑定完成只说明确定了运行位置,节点上的 kubelet 还要观察结果、调用容器运行时启动容器。
如果 demo-job 的 Pod 一直没有运行,应该按什么顺序排查?
可以先检查对象在哪一步停下:PodGroup 是否创建,成员 Pod 是否存在,Queue 是否允许进入调度,Pod 是否已经绑定 Node。如果绑定后还停在启动阶段,就应继续查镜像、存储、网络和容器,而不能只调队列权重。