kubernetes源码学习之kube-scheduler

kube-scheduler是kubernetes中的调度程序,负责从api server中获得待分发的pod列表,并为他们找到最合适运行的Node。
基于kubernetes 1.27

基本框架

下面是kubernetes官发给出的框架图,先对kubernetes pod调度的大致流程有一个认识
kubernetes源码学习之kube-scheduler_第1张图片

看一下有了初步的印象之后,再简单看看里面的操作流程。

调度逻辑里面一共有4个关键的步骤:

  • filter
    预选,过滤掉不满足pod运行条件的Node。包括prefilter、filter、postfilter。
    • prefilter 初步过滤,包括一些基础的标准或规则,更加轻量级。
    • filter 过滤条件更加复杂和耗时。
    • postfilter 常用来处理没有找到合适pod的场景,比如preempt抢占。
  • prioritize
    优选。根据优选函数对每个符合条件的Node进行打分。按照各项的分值*权重合并后,找到分值最高的Node。包括secore、prescore、normalizeScore
    • prescore 准备数据,为score提供需要的信息。
    • score 根据各个plugins计算每个节点的得分。
    • normalizeScore 在计算最终得分之前将各个plugins的得分规范化处理一次,即map-reduce的reduce部分。
  • reserve
    检测并预留node上的资源。包括reserve、unreserve、permit
    • reserve 检测和预留资源。
    • unreserve 如果某个plugin的资源不足以运行pod,则逆序unreserve之前已经reserve成功的plugins。
    • permit 准入限制。包括三个状态:approve、deny、wait。如果permit plugins返回wait,会阻塞等待一定时间才进行bind cycle操作,wait超时会变成deny,并重新调度。
  • bind
    Node绑定操作。
    • prebind 绑定前的准备工作,包括分配网络、磁盘等。
    • bind 正式的绑定操作,默认是通过更新pod的NodeName来完成。
    • postbind 善后处理。主要是清理一些关联资源、更新状态或者发送通知。

根据上面的步骤,kubernetes调度一个节点可以简化成下面流程:

  1. 获取一个待调度的Pod
  2. 并发计算每一个node是否满足pod的调度条件(执行predicate函数map),如果满足则加入到备选列表中
  3. 使用map-reduce聚合计算每一个node的分值(执行priority函数map),选出分最高的一个
  4. 如果前面没有选出可以调度的node,则进行抢占逻辑(在允许抢占的情况下)
    • 将之前调度失败的Node,预驱逐所有优先级比当前pod低的其他pod,看驱逐后是否能调度当前pod,这些node加入备选列表。
    • 将调度pod放入备选node中,再按照优先级将预驱逐的pod重新添加回来,直到node不能再选点
    • 在预驱逐过程中,计算了驱逐各个pod需要破坏多少pdb限制。最后求出各个节点如果要调度当前pod,一共需要破坏多少pdb。
    • 选出破坏pdb限制最少、所有pod优先级最高的node
    • 将调度的pod绑定到选中的Node,将需要驱逐的pod重新加到待调度列表中。
  5. 在选中的Node上预留资源,如果预留失败则pod会重新进入调度队列
  6. 预留成功后,将对Node再做一次准入校验,确定节点是否可以立刻运行当前的pod
  7. 将pod与选中的节点进行绑定

源码分析

现在咱们应该已经对scheduler的基础逻辑有了一个大概的认识了,可以深入分析源码的实现啦。
在分析前,我先给一个代码的跳转流程,方便后续阅读时可以快速找到对应代码位置

  1. 创建一个cobra终端应用(NewSchedulerCommand / cmd/kube-scheduler/app/server.go)
  2. 注册默认的配置以及调度算法并运行scheduler(runCommand / cmd/kube-scheduler/app/server.go)
  3. 初始化创建scheduler并运行(Run / cmd/kube-scheduler/app/server.go)
  4. 起两个goroutine,一个将BackoffQ的pod移到activeQ,一个将UnschedulableQ的pod移到activeQ(Run / pkg/scheduler/scheduler.go)
  5. 循环执行Pod调度,一个Pod调度完成立刻执行下一个(Run / pkg/scheduler/scheduler.go)
  6. 将一个Pod调度到Node上(scheduleOne / pkg/scheduler/schedule_one.go)
  7. 执行pod调度流程(schedulingCycle / pkg/scheduler/schedule_one.go)
    • 预选。findNodesThatFitPod
    • 优选。prioritizeNodes
  8. 如果scheduler调度失败,则执行postFilter逻辑。(RunPostFilterPlugins / pkg/scheduler/framework/runtime/framework.go)
    • postFilter包括抢占preempt(preempt / pkg/scheduler/framework/preemption/preemption.go)
  9. 占用并预留节点资源(runReservePluginReserve / pkg/scheduler/framework/runtime/framework.go)
  10. 将pod绑定到选中的Node上(bind / pkg/scheduler/scheduler.go)

后续待更新

你可能感兴趣的:(kubernetes,kubernetes,源码分析,kube-scheduler,k8s,k8s调度,云原生)