// Less is the function used by the activeQ heap algorithm to sort pods.
// It sorts pods based on their priority. When priorities are equal, it uses
// PodQueueInfo.timestamp.
func (pl *PrioritySort) Less(pInfo1, pInfo2 *framework.QueuedPodInfo) bool {
p1 := pod.GetPodPriority(pInfo1.Pod)
p2 := pod.GetPodPriority(pInfo2.Pod)
return (p1 > p2) || (p1 == p2 && pInfo1.Timestamp.Before(pInfo2.Timestamp))
}
2)PreFilter
PreFilter 在 scheduling cycle 开始时就被调用,只有当所有的 PreFilter 插件都返回 success 时,才能进入下一个阶段,否则 Pod 将会被拒绝掉,标识此次调度流程失败。PreFilter 类似于调度流程启动之前的预处理,可以对 Pod 的信息进行加工。同时 PreFilter 也可以进行一些预置条件的检查,去检查一些集群维度的条件,判断否满足 pod 的要求。
3)Filter
Filter 插件是 scheduler v1 版本中的 Predicate 的逻辑,用来过滤掉不满足 Pod 调度要求的节点。为了提升效率,Filter 的执行顺序可以被配置,这样用户就可以将可以过滤掉大量节点的 Filter 策略放到前边执行,从而减少后边 Filter 策略执行的次数,例如我们可以把 NodeSelector 的 Filter 放到第一个,从而过滤掉大量的节点。Node 节点执行 Filter 策略是并发执行的,所以在同一调度周期中多次调用过滤器。
4)PostFilter
新的 PostFilter 的接口定义在 1.19 的版本会发布,主要是用于处理当 Pod 在 Filter 阶段失败后的操作,例如抢占,Autoscale 触发等行为。
5)PreScore
PreScore 在之前版本称为 PostFilter,现在修改为 PreScore,主要用于在 Score 之前进行一些信息生成。此处会获取到通过 Filter 阶段的节点列表,我们也可以在此处进行一些信息预处理或者生成一些日志或者监控信息。
6)Scoring
Scoring 扩展点是 scheduler v1 版本中 Priority 的逻辑,目的是为了基于 Filter 过滤后的剩余节点,根据 Scoring 扩展点定义的策略挑选出最优的节点。
Scoring 扩展点分为两个阶段:
打分:打分阶段会对 Filter 后的节点进行打分,scheduler 会调用所配置的打分策略
归一化: 对打分之后的结构在 0-100 之间进行归一化处理
7)Reserve
Reserve 扩展点是 scheduler v1 版本的 assume 的操作,此处会对调度结果进行缓存,如果在后边的阶段发生了错误或者失败的情况,会直接进入 Unreserve 阶段,进行数据回滚。
8)Permit
Permit 扩展点是 framework v2 版本引入的新功能,当 Pod 在 Reserve 阶段完成资源预留之后,Bind 操作之前,开发者可以定义自己的策略在 Permit 节点进行拦截,根据条件对经过此阶段的 Pod 进行 allow、reject 和 wait 的 3 种操作。allow 表示 pod 允许通过 Permit 阶段。reject 表示 pod 被 Permit 阶段拒绝,则 Pod 调度失败。wait 表示将 Pod 处于等待状态,开发者可以设置超时时间。
2. binding cycle
binding cycle 需要调用 apiserver 的接口,耗时较长,为了提高调度的效率,需要异步执行,所以此阶段线程不安全。
1)Bind
Bind 扩展点是 scheduler v1 版本中的 Bind 操作,会调用 apiserver 提供的接口,将 pod 绑定到对应的节点上。
2)PreBind 和 PostBind
开发者可以在 PreBind 和 PostBind 分别在 Bind 操作前后执行,这两个阶段可以进行一些数据信息的获取和更新。
3)UnReserve
UnReserve 扩展点的功能是用于清理到 Reserve 阶段的的缓存,回滚到初始的状态。当前版本 UnReserve 与 Reserve 是分开定义的,未来会将 UnReserve 与 Reserve 统一到一起,即要求开发者在实现 Reserve 同时需要定义 UnReserve,保证数据能够有效的清理,避免留下脏数据。
scheduler-plugins
Kubernetes 负责 Kube-scheduler 的小组 sig-scheduling 为了更好的管理调度相关的 Plugin,新建了项目 scheduler-plugins 来方便用户管理不同的插件,用户可以直接基于这个项目来定义自己的插件。接下来我们以其中的 Qos 的插件来为例,演示是如何开发自己的插件。
Qos 的插件主要基于 Pod 的 QoS(Quality of Service) class 来实现的,目的是为了实现调度过程中如果 Pod 的优先级相同时,根据 Pod 的 Qos 来决定调度顺序,调度顺序是: 1. Guaranteed (requests == limits) 2. Burstable (requests < limits) 3. BestEffort (requests and limits not set)
1)插件构造
首先插件要定义插件的对象和构造函数:
// QoSSort is a plugin that implements QoS class based sorting.
type Sort struct{}
// New initializes a new plugin and returns it.
func New(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
return &Sort{}, nil
}
// QueueSortPlugin is an interface that must be implemented by "QueueSort" plugins.
// These plugins are used to sort pods in the scheduling queue. Only one queue sort
// plugin may be enabled at a time.
type QueueSortPlugin interface {
Plugin
// Less are used to sort pods in the scheduling queue.
Less(*PodInfo, *PodInfo) bool
}
// Less is the function used by the activeQ heap algorithm to sort pods.
// It sorts pods based on their priority. When priorities are equal, it uses
// PodInfo.timestamp.
func (*Sort) Less(pInfo1, pInfo2 *framework.PodInfo) bool {
p1 := pod.GetPodPriority(pInfo1.Pod)
p2 := pod.GetPodPriority(pInfo2.Pod)
return (p1 > p2) || (p1 == p2 && compQOS(pInfo1.Pod, pInfo2.Pod))
}
func compQOS(p1, p2 *v1.Pod) bool {
p1QOS, p2QOS := v1qos.GetPodQOS(p1), v1qos.GetPodQOS(p2)
if p1QOS == v1.PodQOSGuaranteed {
return true
} else if p1QOS == v1.PodQOSBurstable {
return p2QOS != v1.PodQOSGuaranteed
} else {
return p2QOS == v1.PodQOSBestEffort
}
}
我们在启动的 main 函数中注册自己定义的插件和相应的构造函数:
// cmd/main.go
func main() {
rand.Seed(time.Now().UnixNano())
command := app.NewSchedulerCommand(
app.WithPlugin(qos.Name, qos.New),
)
if err := command.Execute(); err != nil {
os.Exit(1)
}
}