kubernetes scheduler status

Table of Contents

1. 前言

众所周知 k8s 调度器中有 6 个状态,Success Error Unschedulable UnschedulableAndUnresolvable Wait Skip 但是注释中对于这几个状态的解释并不是特别的清楚 这篇博客主要是为了讲一下这几个状态之前区别,以及当我们在写调度器插件时,在不同的情况下应该用什么

2. Success

success 没什么好说的,通过没问题就是 Success, 返回一个空指针也会被当作 success

3. Error

代码中的注释是这样的:

Error is used for internal plugin errors, unexpected input, etc.

3.1. Prefilter 返回 Error

以 Prefilter 为例,当 Prefilter 返回 Error 时, 会直接返回一个空的节点列表与 error

这里要注意一个事情,这个 error 是 status.AsError() 出来的,他一定不是 FitError

这个 error 会被向上传递,传递到 schedulePod 这一层

schedulePod 继续向上传递到了 schedulingCycle 这一层开始进行处理

schedulingCycle 这一层的处理是通用的,所有处于 schedulingCycle 周期的插件如果返回了错误都会到这里统一处理

1.如果是因为没有节点可用的错误 err == ErrNoNodesAvailable; 会将状态改变为 UnschedulableAndUnresolvable 然后交给上一层即 scheduleOne 2.尝试将 err 转变为 FitError, 如果不能转变则会返回一个 Error 状态交给上层 可以转变就继续 3.判断是否有 postfilter ,没有就抛一个 UnschedulableAndUnresolvable schedulingCycle 就结束了。如果有 postfilter 就尝试运行 运行完 postfilter 会存在两种情况返回了错误和没有返回,但是其实没有什么影响只是日志不太相同

在最后都会向上层返回一个 Unschedulable 的状态

这是时候即使 Prefilter 尽管都是返回了 Error 但是对于 scheduleOne 来说会可能有三种状态 Error UnschedulableAndUnresolvable Unschedulable

这时候我们来 scheduleOne 来看看是如何处理不同的 status 的: — 只要不是 Success 全部丢给 FailureHandler 进行处理

处理完调度流程就结束了

handleSchedulingFailure 在 scheduleone.go:L935 这里简述三种状态的处理

3.2. Prefilter 返回 Unschedulable 或 UnschedulableAndUnresolvable

从状态中拿一下消息然后打个日志,然后向上一层返回 nil, diagnosis, nil 这意味着不是 err 是 nil,node 也是 nil 而 diagnosis 中只有 prefilter 插件返回来的消息 (diagnosis 结构,在哪用的 怎么读) 在 schedulePod 这一层,虽然 err 是 nil 但是 node 也是 nil,会向上抛一个 FitError 交给 schedulecyle 处理

3.3. handleSchedulingFailure

metrics 记录 (为什么要记,在哪用,怎么实现的)

从 status 结构体中拿到 error

如果是 ErrNoNodesAvailable 打日志 如果可以转变为 FitError, 将 error 中记录的失败的插件名丢到 podInfo 里 如果是 IsNotFound, 打日志之后从 error 中确定是否是 node not found,并且只处理 node not found 这一种情况;这时候会尝试通过 client 获取一下 node 信息,如果还是找不到 node 就把这个 node 从 cache 中移除 (什么场景会出现这种情况,从 error 反推一下) 如果都不是就打个日志

接下来从 informer 中检查一下 pod 是不是在 cache 中,如果在 cache 中;可能出现 od 已经被颁订了,但是 schduler 没收到返回所以状态没有更新。(场景补充) 在就检查一下 cache 里 pod nodeName 字段是不是有值。有值说明 pod 已经被分配了,不需要重新加到队列里. (这里需要补充 一些 informer 的原理,与上一个相同). 如果 pod nodeName 值是空的,就把 pod 重新加回到队列里 (SchedulingQueue.AddUnschedulableIfNotPresent 的简单说明)

检查 SchedulingQueue 是不是空的(空指针?),把 pod 加到队列中 SchedulingQueue.AddNominatedPod (加两次? nominatingInfo 从哪来)

event 增加一个 v1.EventTypeWarning FailedScheduling 的事件

更新 pod condition : v1.PodScheduled ConditionFalse

3.4. 简单的结论

所以在 schedulingCycle 这里错误处理写的不是很好 因为虽然看起来对于错误有了聚合,但是实际上还是各自处理 因为对于 Prefilter 直接返回 Error 的部分,一定都会进入 2 的处理流程,因为这个错误一定不是 ErrNoNodesAvailable 类型也不是 FitError

ScheduleResult{nominatingInfo: clearNominatedNode}, podInfo, framework.AsStatus(err)

虽然 SKIP 和 WAIT 在此处于 Error 的处理流程没啥区别,但是由于会影响下面的流程也不归类这里重点区分 Error 和 Unschedulable

对于 Unschedulable 或 UnschedulableAndUnresolvable 一定会返回 FitError 进入到流程 3

那我们考虑在没有 PostFilter 的场景下,PreFilter 如果返回 Error,就会结束当前的调度,返回一个 Error 状态 如果 PreFilter 返回 Unschedulable 或 UnschedulableAndUnresolvable, 返回一个 Unschedulable 的状态

对于没有成功的状态还是统一丢到 handleSchedulingFailure 里, 只要到了这里就没什么太大的区别了,调度循环结束,只是 pod condition 有些不同

其实 UnschedulableAndUnresolvable 还是有些区别,而 Error 和 UnSchedulable 就像 context 中的 Background 和 TODO,语义区别实际没影响

Created: 2023-11-24 Fri 09:50

Validate