Skip to content

调度器如何选择节点

CubeMaster 的调度器负责从集群中选择一个合适的节点来运行沙箱。它采用 5 阶段选择器链,类似 Kubernetes 的调度框架设计。


1. 调度器全景


2. 初始化:InitScheduler

CubeMaster/pkg/scheduler/init.goInitScheduler() 注册所有选择器:

go
func InitScheduler(ctx context.Context) {
    scheduler.preSelector = prefilter.NewPreFilter()
    scheduler.backoffSelector = backofffilter.NewBackoffFilter()
    scheduler.filter = filter.NewSelector()
    scheduler.score = score.NewSelector(ctx)
    scheduler.postScore = postscore.NewSelector()
    initTask(ctx)
}

选择器结构


3. Select():调度入口

CubeMaster/pkg/scheduler/schedule.goSelect() 是调度入口:

go
func Select(selCtx *selctx.SelectorCtx) (nodes *node.Node, err error) {
    // 1. PreFilter
    if err := runPreFilter(selCtx); err != nil {
        // 失败时尝试 BackoffFilter
        if err = runBackoffFilter(selCtx); err != nil {
            return nil, err
        }
    }

    // 2. Filter(并行执行)
    if err := runFilter(selCtx, scheduler.filter); err != nil {
        return BackoffSelect(selCtx)
    }

    // 3. Score
    if err := runScoreFilter(selCtx, scheduler.score); err != nil {
        return nil, err
    }

    // 4. 最终选择
    return selCtx.LeastRandomSelect(config.GetConfig().Scheduler.PrioritySelectNum), nil
}

4. 五个阶段详解

4.1 PreFilter:预过滤

职责:从所有节点中快速筛选出可能满足条件的节点子集。

实现位置CubeMaster/pkg/selector/prefilter/

典型过滤条件

  • 节点状态是否 Ready
  • 节点是否有足够的总资源(CPU、内存)
  • 节点是否在可用区列表中

4.2 Filter:并行过滤

职责:对 PreFilter 输出的候选节点进行细粒度过滤。

实现位置CubeMaster/pkg/selector/filter/

关键特性:所有 Filter 并行执行,取交集。

go
func parallelRunFilters(selCtx *selctx.SelectorCtx, filters []filter.Selector) (node.NodeList, error) {
    eg, _ := errgroup.WithContext(selCtx.Ctx)
    for _, f := range filters {
        f := f
        eg.Go(func() error {
            result := f.Filter(selCtx)
            tmpStat.Add(result)
            return nil
        })
    }
    eg.Wait()
    return tmpStat.Intersect(), nil  // 所有 Filter 结果取交集
}

典型 Filter

  • 资源充足性检查(CPU、内存、rootfs)
  • 节点亲和性 / 反亲和性
  • 模板本地性(template_locality)
  • NUMA 拓扑感知

4.3 Score:打分

职责:对通过 Filter 的节点进行打分排序。

实现位置CubeMaster/pkg/selector/score/

打分维度

  • 资源利用率(倾向选择资源充裕的节点)
  • 负载均衡(倾向选择负载低的节点)
  • 亲和性加分

4.4 PostScore:后处理

职责:对打分结果进行最后调整。

实现位置CubeMaster/pkg/selector/postscore/

典型操作

  • 去重
  • 最终裁决

4.5 BackoffFilter:兜底过滤

职责:当正常调度路径失败时,提供兜底选择。

触发条件

  • PreFilter 失败
  • Filter 失败(无节点通过)

实现位置CubeMaster/pkg/selector/backofffilter/


5. SelectorCtx:调度上下文

CubeMaster/pkg/scheduler/selctx/SelectorCtx.go 定义了调度上下文,在整个调度过程中传递信息:

go
type SelectorCtx struct {
    Ctx      context.Context
    ReqRes   *RequestResource  // 请求的资源量
    Nodes    node.NodeList     // 候选节点列表
    LastBadNode node.NodeList  // 上次失败的节点(熔断)
    // ...
}

关键方法

方法作用
SetNodes(nodes)更新候选节点列表
Nodes()获取当前候选节点
AddLastBadNode(node)记录失败节点,下次调度避开
LeastRandomSelect(n)从 top-n 节点中随机选一个

LeastRandomSelect

最终选择不是简单取最高分,而是从 top-n 中随机选一个,避免所有沙箱都集中在同一个节点:

go
func (s *SelectorCtx) LeastRandomSelect(n int) *node.Node {
    nodes := s.Nodes()
    if nodes.Len() <= n {
        return nodes[rand.Intn(nodes.Len())]
    }
    // 取前 n 个最高分节点,随机选一个
    topN := nodes[:n]
    return topN[rand.Intn(len(topN))]
}

6. 模板本地性特殊处理

如果模板配置了 template_locality(模板必须在特定节点上运行),调度器会跳过 BackoffFilter:

go
func shouldSkipBackoffForTemplate(selCtx *selctx.SelectorCtx) bool {
    templateLocalitySelectorID := constants.SelectorFilterID + "/" + "template_locality"
    for _, selector := range scheduler.filter {
        if selector.ID() == templateLocalitySelectorID {
            return true  // 有 template_locality,跳过 Backoff
        }
    }
    return false
}

这是因为如果模板只存在于特定节点,BackoffFilter 选择其他节点也没有意义。


7. 完整调度流程


8. 调度器与 Kubernetes 调度器的对比

特性CubeMaster 调度器Kubernetes 调度器
阶段数53 (Filter → Score → Reserve)
Filter 并行否(串行)
兜底机制BackoffFilter无(pending)
最终选择top-n 随机最高分
调度队列buffer 队列优先级队列
熔断支持(lastBadNode)不支持

延伸阅读