Volcano 中“不靠谱”的 Queue

1,176 阅读3分钟

前言

前文Volcano 调度器 已经从宏观上分析了 Volcano 源码。Volcano Queue 是 Volcano 中最重要的 CRD 之一,其定义了资源的上下限(guarantee 和 capability)以及应得值(deserved)。从官方文档和字面理解来看,guarantee 是 queue 必须能够保证的值,也就是 pod 中的 request,capability 是 queue 最大所能获得的值,而 weight 决定了每个 Queue 在资源被占满的情况下应得的值。

然而,近期来细读源码发现,其实这三参数并没有想象的那么“有用”,本文一一道来。

挖源码

从源码中看 Volcano Queue 具有以下参数:

// QueueSpec represents the template of Queue.
type QueueSpec struct {
   Weight     int32
   Capability v1.ResourceList

   // Depreicated: replaced by status.State
   State QueueState
   // Reclaimable indicate whether the queue can be reclaimed by other queue
   Reclaimable *bool

   // extendCluster indicate the jobs in this Queue will be dispatched to these clusters.
   ExtendClusters []Cluster

   // Guarantee indicate configuration about resource reservation
   Guarantee Guarantee `json:"guarantee,omitempty" protobuf:"bytes,4,opt,name=guarantee"`

   // If specified, the queue's scheduling constraints
   // +optional
   Affinity *Affinity `json:"affinity,omitempty" protobuf:"bytes,6,opt,name=affinity"`

   // Type define the type of queue
   Type string `json:"type,omitempty" protobuf:"bytes,7,opt,name=type"`

   // Parent define the parent of queue
   // +optional
   Parent string `json:"parent,omitempty" protobuf:"bytes,8,opt,name=parent"`
}

找源码的引用,发现 capability、guarantee 和 weight 主要是在 proportion Plugin 中被使用(admission 中没有做校验)。 因此进入到 proportion Plugin 中查看 OnSessionOpen 函数(前文有提到,Plugin 都是通过实现该函数影响调度)。

该函数的伪代码如下:

// 计算总资源量和总保证量
totalResource = sum{node.status.Allocatable} + OversubscriptionResource
totalGuarantee = sum(queue.guarantee)

// 计算 realCapability
for: all queues:
  queue.realCapability = min(queue.capability, totalResource - totalGuarantee + queue.guarantee)
  // 计算 queue 的 allocated, request 
  // ...

// 计算 deserve
for: 
  remain = totalResource
  oldremain = remain
  queue.deserved = remain * queue.weight / sum(queue.weight)
  queue.deserved = max(queue.deserved, queue.guarantee)
  increasedDeserved, decreasedDeserved = diff(queue.deserved, queue.oldDeserved) // 计算 deserved 变化值
  remain = remain-queue.increasedDeserved+queue.decreasedDeserved
// 若 remain 被用光了,或者此次也无可调度了,则跳出循环
  if remain==0 || oldremain == remain 
    break

// 注册各类钩子到 Action 中,影响 Enqueue 和 Allocate 逻辑
// ...

此处可以发现可以现象:

  1. Queue 的 capability 其实在调度时被替换过,则最终取得是和 totalResource - sum( 其他 queue.guarantee) 比较后的最小值,因此该值也是个不靠谱的值,并不是用户填了多少最终就能达到多少;
  2. Queue 的 weight 参数以 totalResource 为基准,根据分配计算出 Queue 的 deserved,然后该值最终会与 guarantee 做比较,取最大值,因此也是不靠谱的值;
  3. Queue 的 guarantee 不会在 admission 中做验证,而且并不直接参数到后期的各类钩子函数中,也是不靠谱的值,也就是说指定的 guarantee 并不一定能保证被分配到。

作者为了验证自己的猜想,去创建了一个 guarantee 为 10000 的 Queue,发现并没有做阻拦,此时明显是有问题的。

apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
  name: queue-test
spec:
  capability:
    cpu: 15
  guarantee:
    resource:
      cpu: 10000
  reclaimable: true
  weight: 3

结论

Volcano Queue 中的 capability、guarantee 和 weight 可能并不会像预期的那样工作。admission 并不会对 capability 和 guarantee 做校验,因此 guarantee 的值并不一定能得到保证,capability 也未必是真的最大值,而 weight 计算后的 deserved 值也会受 guarantee 所影响。因此在设置参数的时候需要用户自己去保证。

细心的读者可以发现,在指定合理的 capability 和 weight,不指定 guarantee 的情况下,大体还是能够符合用户需求的,而加入 guarantee 后带来了很多复杂性,且目前并没有被很好的解决。官方文档中也并没有针对 guarantee 做详细介绍,可能官方也认识该特性不是很成熟,不建议用户使用。