Volcano 调度器执行流Execution Flow深度解析从会话周期到任务分配的全链路调度机制【免费下载链接】volcanoA Cloud Native Batch System (Project under CNCF)项目地址: https://gitcode.com/GitHub_Trending/vol/volcano本文以 Volcano 官方设计文档 docs/design/execution-flow.md 为核心骨架结合调度器源码与单元测试系统讲解 Volcano 调度器在一个调度会话Session内完成工作负载 → 节点分配的整体执行流。你将掌握会话周期与集群快照的构建方式、队列—作业—任务三级分配循环、谓词筛选与节点评分、以及 Gang 调度语义下分配提交Commit与流水线Pipeline的触发时机并能在实际排障与调优时快速定位到对应代码路径。一、整体视图一次调度会话的工作流Volcano 调度器采用会话Session模型调度器在启动后周期性开启新会话每个会话基于**集群当前状态的本地副本快照**执行一轮调度决策。设计文档 docs/design/execution-flow.md 给出的核心流程如下Session 每 1 秒开启一次每个会话开始时会创建Queues队列、JobsMap作业映射、PendingTasks待调度任务和 Node List节点列表的本地副本遍历会话中的每个 Job若 Job 所属的 Queue 存在于本地队列副本中则将作业加入 JobsMapQueueID → JobList 的映射若队列不存在则给出警告并继续遍历本地队列中的每个 Queue弹出队列后先检查队列是否超用overused超用则跳过否则取出该队列的作业列表弹出作业若作业尚无本地 PendingTasks则收集其所有 Pending 任务的资源需求构建本地任务列表并登记到 PendingTasks若已有则依次为每个任务执行获取候选节点 → 评分排序 → 资源匹配的分配流程资源不足时进入 Pipeline检查作业是否已满足分配条件Gang 语义的 minAvailable满足则提交分配不满足则把作业放回队列继续下一轮循环直至所有队列都被处理完毕。这一流程在实际代码中由 pkg/scheduler/scheduler.go 的Run→runOnce循环驱动。下图是设计文档自带的完整执行流图二、会话周期每 1 秒开启一次调度循环设计文档指出Session Opens every 1 sec这在源码中对应调度器启动时注册的定时循环go wait.Until(pc.runOnce, pc.schedulePeriod, stopCh)见 pkg/scheduler/scheduler.goschedulePeriod的默认值为time.Second定义于 cmd/scheduler/app/options/options.go 中的defaultSchedulerPeriod time.Second可通过启动参数--schedule-period调整周期例如 Helm Chart 中通过custom.scheduler_schedule_period注入见 installer/helm/chart/volcano/templates/scheduler.yaml每次runOnce的执行时序pkg/scheduler/scheduler.go为framework.OpenSession开启会话 → 按配置顺序依次执行各 Actionenqueue / allocate / backfill / preempt / reclaim 等→framework.CloseSession关闭会话并刷新状态同时记录各阶段耗时指标。提示文档中每 1 秒是默认配置下的行为实际周期由--schedule-period决定调大周期可降低调度器 CPU 开销调小周期可加快对集群状态变化的响应。三、会话的本地副本从缓存快照到 Session 上下文设计文档强调每个会话都会创建 Queues、JobsMap、PendingTasks 与 Node List 的本地副本。这里的本地副本即 pkg/scheduler/framework/session.go 中的Session结构体其核心字段包括Queues map[api.QueueID]*api.QueueInfo—— 队列副本Jobs map[api.JobID]*api.JobInfo—— 作业副本即文档中的 JobsMapNodes map[string]*api.NodeInfo—— 节点列表副本任务则通过各 Job 的TaskStatusIndex[api.Pending]按状态索引组织见 pkg/scheduler/actions/allocate/allocate.go。快照的生成链路为OpenSession调用openSession(cache)其中执行cache.OnSessionOpen()pkg/scheduler/framework/framework.goOnSessionOpen触发SchedulerCache.Snapshot()从调度缓存中克隆出完整的集群视图ClusterInfo包含 Nodes、HyperNodes、Jobs、Queues、NamespaceInfo、CSINodesStatus 等pkg/scheduler/cache/cache.go快照中会过滤掉未就绪节点!value.Ready()的节点不进入副本以及队列不存在的作业The Queue ... does not exist, ignore it这正是文档第 3 步队列不存在则警告并跳过的缓存层实现随后OpenSession依次调用各插件Tier 中的 plugin的OnSessionOpen向 Session 注册QueueOrderFn、JobOrderFn、TaskOrderFn、PredicateFn等扩展点函数完成一次会话的上下文构建。从源码结构可以推断每个会话的决策都基于独立快照会话之间互不污染插件在每个会话开启时重新初始化——这是文档所述本地副本设计的根本目的保证单轮调度内部数据一致同时允许配置热加载后下一会话立即生效。四、调度动作Action与默认配置一个会话内要执行哪些动作由调度器配置的actions字段决定。默认配置定义在 pkg/scheduler/util.go 的DefaultSchedulerConfactions: enqueue, allocate, backfill tiers: - plugins: - name: priority - name: gang - name: conformance - plugins: - name: overcommit - name: drf - name: predicates - name: proportion - name: nodeorder配置结构体定义于 pkg/scheduler/conf/scheduler_conf.go包含四个部分字段含义actions本会话依次执行的调度动作enqueue、allocate、backfill、preempt、reclaim 等按逗号分隔tiers插件分层每层可含多个插件插件可提供排序、谓词、overused 等扩展点configurations针对特定 Action 的参数如 allocate 的enablePredicateErrCacheKeymetrics指标采集配置runOnce中会按配置顺序逐个执行 Actionpkg/scheduler/scheduler.go并记录每个 Action 的耗时指标。其中enqueue把处于 Pending 状态的 PodGroup 置为 InqueuePodGroupInqueue即入队pkg/scheduler/actions/enqueue/enqueue.goallocate本文核心负责把 Inqueue 作业的任务分配到节点backfill负责填充剩余零散空闲资源。值得注意的一个细节如果配置中没有 enqueue 动作allocate 会直接把 Pending 状态的 PodGroup 提升为 Inqueue 后再参与分配避免作业被阻塞pkg/scheduler/actions/allocate/allocate.go。五、队列维度的分配循环超用检查与优先级排序allocate 动作的入口是Executepkg/scheduler/actions/allocate/allocate.go其注释明确概括了五阶段模型与设计文档完全对应选取一个队列 Q使用ssn.QueueOrderFn从 Q 中选取作业 J使用ssn.JobOrderFn从 J 中选取任务 T使用ssn.TaskOrderFn用predicateFn过滤出 T 可以放置的节点用ssn.NodeOrderFn评出最优节点并分配给 T。buildAllocateContextpkg/scheduler/actions/allocate/allocate.go负责构建文档所述的JobsMap数据结构queuesRegular/queuesNominated按QueueOrderFn排序的队列优先队列jobsByQueue map[QueueID]*PriorityQueueQueueID → 作业优先队列的映射即文档中的 JobsMap若作业所属队列不存在则记录告警并跳过Skip adding Job ... because its queue is not found作业入队时按JobOrderFn排序从而保证高优先级作业先被弹出。在allocateResourcesForQueuespkg/scheduler/actions/allocate/allocate.go中每次弹出一个队列后第一步就是文档第 4.1.1 步的队列超用检查if ssn.Overused(queue) { klog.V(3).Infof(Queue %s is overused, ignore it., queue.Name) continue }Overused会依次调用各插件注册的overusedFns如 proportion、drf 等配额/公平份额插件判断队列是否超出其应得份额pkg/scheduler/framework/session_plugins.go。超用的队列在本轮被整体跳过这正是文档中Check if Queue is overused → If Yes then Continue的实现。另一个关键细节是一个队列处理完其作业后会被放回优先队列pkg/scheduler/actions/allocate/allocate.go源码注释解释了原因——确保队列优先级始终基于最新的资源分配情况重新计算从而让低份额队列在下一轮有机会获得资源。六、作业维度的处理PendingTasks 的构建文档第 4.1.2 步描述了若作业不存在本地 PendingTasks则构建任务列表。对应实现为organizeJobWorksheetpkg/scheduler/actions/allocate/allocate.go遍历作业的每个子任务SubJob的TaskStatusIndex[api.Pending]只收集 Pending 状态的任务跳过带**外部调度门控scheduling gate**的任务task.SchGated且非 Volcano 管理的门控跳过BestEffort 任务task.Resreq.IsEmpty()即无资源请求的任务——它们由 backfill 动作负责剩余任务按TaskOrderFn组成任务优先队列即文档中的 Local TasksList该任务列表被登记到jobWorksheet并在分配开始时作为该作业的待调度任务池。同时allocateResourcesForTasks中还有一道队列层面的资源把关ssn.Allocatable(queue, task)pkg/scheduler/actions/allocate/allocate.go即使队列整体未超用单个任务仍可能因队列剩余配额不足而被跳过实现了队列→任务的双层配额校验。七、任务分配谓词筛选、评分与 Pipeline文档第 4.1.2.2 步描述了任务级分配四部曲弹出任务 → 获取谓词节点 → 评分排序 → 资源匹配。对应实现位于allocateResourcesForTasks与allocateResourcesForTaskpkg/scheduler/actions/allocate/allocate.go。1. 预筛选PrePredicatessn.PrePredicateFn(task)先执行插件级预检查失败则记录 FitErrors若作业已满足最小成员数则中断否则跳过该任务继续L835-L846。2. 谓词节点获取Predicate优先尝试 Pod 的NominatedNodeName上一轮抢占留下的提名节点属于本迭代节点集合时才信任否则对全部候选节点执行PredicateNodesL854-L865。只有通过全部谓词资源、亲和性、端口冲突等的节点才会进入候选列表——对应文档Get the list of predicate nodes for the task。3. 评分排序PrioritizeprioritizeNodespkg/scheduler/actions/allocate/allocate.go把候选节点分为两个梯度第一梯度当前Idle空闲资源即可满足任务的节点优先考虑本分片shard内节点第二梯度需要靠FutureIdle()含 Releasing 释放资源才能满足的节点。第一梯度存在可分配节点时直接选用否则才对第二梯度节点用NodeOrderFn/BatchNodeOrderFn评分如 nodeorder 插件提供的最优/最差节点排序取最高分节点——对应文档Score the predicate nodes and sort it。4. 资源匹配与落位方式allocateResourcesForTaskL1009-L1039// 1. 空闲资源足够 - 直接分配绑定 if task.InitResreq.LessEqual(node.Idle, api.Zero) { stmt.Allocate(task, node) } // 2. 仅释放资源足够 - 进入 Pipeline流水线等待 if task.InitResreq.LessEqual(node.FutureIdle(), api.Zero) { stmt.Pipeline(task, node.Name, false) }这正是文档第 4.1.2.2.4 步若任务所需资源小于节点空闲资源则加入 Pipeline的完整语义Volcano 不仅能把任务绑定到当前空闲资源还能在节点上有正在释放evict/reclaim 中的资源时把任务流水线化待资源真正释放后再完成绑定从而提升资源利用率。八、Gang 语义下的分配提交JobReady 是唯一闸门设计文档反复强调Check if Job is ready to be allocated是则 push the Job提交分配否则把队列/作业放回列表。在 Volcano 中这一检查由 Gang 插件的JobReady扩展点驱动只有当作业满足minAvailable最小可用成员数要求时整个作业的分配才是有效的。allocate 动作对 Statement 的处理严格遵循该语义pkg/scheduler/actions/allocate/allocate.goif stmt ! nil ssn.JobReady(job) { // do not commit stmt when job is pipelined stmt.Commit() // 若 min available replicas剩余任务继续分配作业放回队列 if !jobWorksheet.Empty() { jobs.Push(job) } }三个关键行为只有JobReady为真时才stmt.Commit()Gang 语义下不会出现只绑定部分任务的中间状态要么整组满足最低成员数要么本轮不落任何绑定Pipelined 状态不提交作业处于流水线等待时不提交 Statement避免在资源未真正释放时提前生成绑定记录未就绪的作业放回队列queues.Push(queue)等待下一轮会话再次尝试——对应文档Continue till all the Job is ready。单元测试 pkg/scheduler/actions/allocate/allocate_test.go 中的TestAllocate用例直观验证了这一行为用例 prepredicate failed and tasks are not used up, continue on until min member meetminAvailable2master×1 worker×1的 PodGroup即使部分任务谓词失败只要最终能凑齐最小成员数ExpectBindsNum: 2两个任务均被绑定用例 masters min member can not be allocated, break from allocatingminAvailable2但只凑齐 1 个 master 任务时ExpectBindsNum: 0一个任务都不绑定——这正是 Gang 语义整组就绪才提交的测试级证据。九、贯穿全流程的关键配置与观测手段结合上述执行流以下是实际部署与排障中最常涉及的配置点配置/参数默认值作用参考位置--schedule-period1s两次调度会话的间隔cmd/scheduler/app/options/options.go--scheduler-namevolcano只处理.spec.schedulerName匹配的 Pod同上--default-queuedefault作业未指定队列时的默认队列同上actionsenqueue, allocate, backfill会话内执行的动作序列pkg/scheduler/util.gotiers两层插件priority/gang/conformance → overcommit/drf/predicates/proportion/nodeorder插件分层注册扩展点同上--scheduler-conf空用默认配置自定义调度器配置文件路径支持热加载pkg/scheduler/scheduler.go观测方面runOnce中每次会话都记录UpdateE2eDuration每个 Action 记录UpdateActionDuration每个插件的OnSessionOpen/OnSessionClose记录UpdatePluginDuration见 pkg/scheduler/framework/framework.go可通过 Prometheus 指标定位队列超用跳过谓词失败Pipeline 等待等环节的耗时瓶颈。十、总结从设计文档到源码的执行流全景设计文档步骤对应源码实现关键语义Session 每 1 秒开启wait.Until(pc.runOnce, pc.schedulePeriod, stopCh)pkg/scheduler/scheduler.go周期由--schedule-period控制创建本地副本SchedulerCache.Snapshot()克隆集群视图pkg/scheduler/cache/cache.go会话间数据隔离、配置热加载生效队列不存在则警告buildAllocateContext跳过并Warningfpkg/scheduler/actions/allocate/allocate.go缓存层已过滤双保险队列超用检查ssn.Overused(queue)pkg/scheduler/framework/session_plugins.goproportion/drf 等插件提供 overused 扩展点构建 PendingTasksorganizeJobWorksheet按TaskOrderFn收集 Pending 任务pkg/scheduler/actions/allocate/allocate.go跳过 BestEffort 与外部门控任务谓词 评分 排序PredicateNodesprioritizeNodes同上 L848-L1007空闲资源优先FutureIdle 兜底资源不足进入 Pipelinestmt.Pipeline(task, node, false)同上 L1027-L1037等待节点释放资源后再绑定作业就绪才提交ssn.JobReady(job)为真才stmt.Commit()同上 L366Gang 调度 minAvailable 的提交闸门Volcano 调度器正是通过会话快照 队列/作业/任务三级优先队列 谓词评分 Gang 就绪门控这套执行流在保证公平配额proportion/drf与 Gang 语义minAvailable的前提下把待调度 Pod 高效、可预期地分配到集群节点上。理解这一执行流是排查调度延迟、配额竞争与作业长期 Inqueue等问题的第一步。【免费下载链接】volcanoA Cloud Native Batch System (Project under CNCF)项目地址: https://gitcode.com/GitHub_Trending/vol/volcano创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考