第 6 篇|Volcano 架构:从 Pod 到作业

本文最后更新于:2 天前

前言

第一至第五篇讨论了 KubeRay 怎样创建和维护 Ray 集群。从这一篇开始,先把 KubeRay 放到一边:当一批 Pod 已经成为待调度对象,Volcano 怎样把它们组织成作业、表达最低成员要求,并在作业之间分配资源?第九篇再把这条调度路径接回 RayJob 生命周期。

这次用两个各需三个成员的 Volcano Job 说明成组调度的需求,再沿其中一个作业展开对象和组件。它们分别叫 demo-job 和 demo-job-b,是独立的 Volcano 示例。第八篇另设 CPU 数量分析两个 Queue 的竞争,第九篇再回到 KubeRay 创建的资源。

成组调度的需求

假设提交了两个 Volcano Job:demo-job 和 demo-job-b。每个作业需要三个成员 Pod,并且三个成员到齐后才能有效计算。为了先看清成组分配,把每个 Pod 简化为一个等大的资源槽。

机器一共能放下四个这样的 Pod。如果两个作业各拿到两个槽,资源已经用满,双方却都还在等第三个成员。先让一组拿到三个槽,就有机会完成一次计算,释放资源后再安排另一组。

两个作业都需要三个成员。四个资源槽被各占两个时,双方都可能等待;先满足一组则能推进计算。
两个作业都需要三个成员。四个资源槽被各占两个时,双方都可能等待;先满足一组则能推进计算。 查看原图

若只让三个 Pod 各自寻找节点,调度器无法知道它们需要同时获得资源。Volcano 用 PodGroup 这个资源对象保存整组要求:最低资源量用于判断是否接纳这组需求,最低成员数用于判断试分配的结果是否足够。前一个检查属于 enqueue 准入阶段,后一个由 Gang 插件参与,在提交分配结果前完成。下面先认识承载这些要求的对象,第七篇再展开检查过程。

本例把最低成员数设为三。实际工作负载应按能够开始有效计算的最小规模配置这个门槛。

实际调度时,资源要同时记录 CPU、内存、GPU 等多个数量,下文称为资源向量。某台节点空闲 CPU 很多,也可能没有 GPU;集群总量够,也可能分散在几台放不下单个 Pod 的节点上。因此,调度器既要检查整组需求,也要为每个 Pod 找到合适的节点。

工作负载与调度对象

读 Volcano 时经常遇到 Job。先把 Kubernetes Job、Volcano Job 和调度器内部的 JobInfo 分开。表中的 Volcano task 指一类 Pod 的模板和副本要求;进入调度器后,一个 TaskInfo 通常对应一个具体 Pod。

对象 保存什么 由谁使用
Kubernetes Job,batch/v1 运行到完成的 Pod 模板、重试等要求 Kubernetes Job Controller
Volcano Job,batch.volcano.sh/v1alpha1 多类 task 的 Pod 模板、副本数、生命周期策略等 Volcano Job Controller

Volcano Job 是 Volcano 自己的工作负载入口。它描述一组 task,Job Controller 据此维护 PodGroup 和成员 Pod。

PodGroup 是命名空间级资源,描述哪些 Pod 作为一组参与调度,以及这组工作负载的最低要求。成员模板保存在 Volcano Job 中,PodGroup 只保存调度所需的信息。下面是 scheduling/v1beta1/types.go 中几个字段的节选,省略了注释和其他字段:

1
2
3
4
MinMember int32 `json:"minMember,omitempty" protobuf:"bytes,1,opt,name=minMember"`
Queue string `json:"queue,omitempty" protobuf:"bytes,2,opt,name=queue"`
PriorityClassName string `json:"priorityClassName,omitempty" protobuf:"bytes,3,opt,name=priorityClassName"`
MinResources *v1.ResourceList `json:"minResources,omitempty" protobuf:"bytes,4,opt,name=minResources"`

minMember 表达最低成员数,minResources 表达最低资源量。queue 指定这组 Pod 属于哪个 Queue,priorityClassName 引用 Kubernetes PriorityClass。PriorityClass 是把一个名称映射为数值优先级的集群级对象,供调度器比较先后次序。字段怎样参与判断取决于启用的插件,第七、八篇会沿调用位置展开。

Queue 是集群级资源,用来组织共享资源的工作负载。与按 namespace 区分的 PodGroup 不同,Queue 在整个 Kubernetes 集群里按名称识别,一个队列可以被多个命名空间中的 PodGroup 引用。它有权重、资源上限、保障资源和状态等信息;资源策略插件读取这些配置,决定哪些作业可以进入调度、哪个队列应先获得资源。

后文会检查 Queue 是否处于 Open,这是允许接纳新作业的开放状态。它说明队列可接受工作,至于资源是否足够,还要继续计算。

Volcano Job、PodGroup、Queue 与成员 Pod 的关系。Job Controller 维护工作负载对象,Scheduler 读取组需求和队列策略。
Volcano Job、PodGroup、Queue 与成员 Pod 的关系。Job Controller 维护工作负载对象,Scheduler 读取组需求和队列策略。 查看原图

这组三成员例子的 Queue 命名为 ray-batch。图文用 ray-demo-job-pg 示意 demo-job 对应的 PodGroup;实际创建时,Volcano Job Controller 将 Job 名与 UID(对象的唯一标识)组合成组名。Pod 的 scheduling.k8s.io/group-name annotation 填写实际组名,让 Scheduler 把三个成员归入同一组。owner reference 表达对象的拥有关系,用于生命周期管理;PodGroup 负责描述调度要求,成员 Pod 仍由工作负载 Controller 创建和删除。

Volcano Scheduler 观察 PodGroup 和成员 Pod 后,在内存中构造 JobInfo。后文代码里的 job 多数指这份调度模型。

组件分工与提交路径

核心组件的进程边界

把基本部署拆开看,有三个主要的常驻服务角色:Scheduler、Controller Manager 和 Admission Webhook。Webhook 是供 API Server 调用的 HTTP 回调服务;这里的 Admission 指资源写入前的准入处理,可以校验请求或补全字段。这三个角色可以分别部署,也可以有多个副本。这里数的是职责与进程角色,实际 Pod 数量由安装配置决定。

角色 核心功能 源码入口
Scheduler 观察 Pod、PodGroup、Queue、Node;维护调度缓存;执行资源分配与绑定 cmd/scheduler、pkg/scheduler
Controller Manager 运行 Job、Queue、PodGroup 等控制器,维护对应对象的生命周期和关联资源 cmd/controller-manager、pkg/controllers
Admission Webhook 处理 API Server 转发的匹配请求,校验或补全资源字段 cmd/webhook-manager、pkg/webhooks

客户端工具用于提交、查询和管理资源,运行时按需调用。cmd 目录中还包括拓扑、分片等扩展组件,各组件按所需功能部署。

Controller Manager 内部的多个控制器也不对应多个独立进程。cmd/controller-manager/app/server.go 的 startControllers 遍历已注册且启用的控制器,初始化后启动各自的运行循环。关键动作是:

1
go c.Run(ctx.Done())

它们可以在同一进程里共享客户端和 informer。informer 持续从 API Server 接收资源变化,维护本地缓存,再通知感兴趣的控制器。

基本部署中的进程边界。控制器和调度器通过 Kubernetes API 观察与更新对象;Admission 是 API Server 对匹配请求发起的同步调用。图中没有展开主备副本。
基本部署中的进程边界。控制器和调度器通过 Kubernetes API 观察与更新对象;Admission 是 API Server 对匹配请求发起的同步调用。图中没有展开主备副本。 查看原图

Scheduler 的 Action 和 Plugin 则运行在 Scheduler 进程内。Action 组织调度阶段,Plugin 注册排序、过滤、资源准入和就绪判断等函数。它们通常通过 Go 函数调用协作,不是每个插件起一个远程服务。

这里有两处都使用“准入”这个词。Admission Webhook 在资源写入前校验 API 请求;Scheduler 的 enqueue 阶段读取已经保存的 PodGroup,检查这组工作负载能否进入调度。

从 Volcano Job 到 Pod 运行

用户提交 Volcano Job 后,Job Controller 先维护对应的 PodGroup。源码 job_controller_actions.go 会观察 PodGroup 的 phase;组仍处于空状态或 Pending 时,Controller 等待 Scheduler 通过 enqueue 推进准入,之后再进入正常的 task 同步创建流程。

这个先准入、后创建成员的过程,可以减少原生作业在资源不足时生成大量等待中的 Pod。Controller 写出组需求,Scheduler 更新调度状态,Controller 再从后续观察中继续创建成员。

Volcano Job 从声明、准入、成员创建、调度绑定到容器启动的主要阶段。
Volcano Job 从声明、准入、成员创建、调度绑定到容器启动的主要阶段。 查看原图

成员 Pod 出现后,spec.schedulerName 指定由哪个调度器处理它,本例填写 volcano。Scheduler 通过 annotation 找到 PodGroup,结合 Queue 策略、Gang 门槛和节点条件选择 Node,再通过 Kubernetes API 提交绑定。节点上的 kubelet 观察绑定结果,拉取镜像并启动容器。

小结:工作负载管理与调度的分工

把本篇出现的几个近似名称放在一起,可以看到它们分属不同位置。

代码或文章中的词 这里实际指什么
Volcano Queue 持久化的资源策略对象,例如集群级的 ray-batch
Scheduler 内部优先队列 一轮调度中,按照回调比较结果排列 Queue、JobInfo 或 TaskInfo 的内存结构
Volcano 调度器里的 task 通常对应一个 Pod;Volcano Job 的 task 模板还可以展开多个 Pod 副本
Controller 插件与 Scheduler 插件 前者扩展工作负载管理,后者参与调度决策,调用位置不同

Volcano Job 表达工作负载,Job Controller 维护 PodGroup 和成员 Pod,PodGroup 表达组需求,Queue 表达资源策略,Scheduler 决定 Pod 的运行位置。绑定以后,镜像准备、存储挂载与容器启动由节点侧继续处理。

下一篇从 Scheduler 的 runOnce 开始,跟踪 ray-demo-job-pg 怎样在一轮调度的工作视图(Session)里试分配,以及为什么 Gang 判断通过以后仍然需要处理绑定失败。

参考资料


第 6 篇|Volcano 架构:从 Pod 到作业
https://tanxinyu.work/kuberay-volcano-06-volcano-architecture/
作者
谭新宇
发布于
2026年9月15日
许可协议