《k8s-1.13版本源码分析》-调度器框架
本文原始地址(gitbook格式):https://farmer-hutao.github.io/k8s-source-code-analysis/core/scheduler/scheduler-framework.html
本项目github地址:https://github.com/farmer-hutao/k8s-source-code-analysis
1. 写在前面
今天我们从pkg/scheduler/scheduler.go出发,分析Scheduler的整体框架。前面讲Scheduler设计的时候有提到过源码的3层结构,pkg/scheduler/scheduler.go也就是中间这一层,负责Scheduler除了具体node过滤算法外的工作逻辑~
这一层我们先尽可能找主线,顺着主线走通一遍,就像走一个迷宫,一条通路走出去后心里就有地了,但是迷宫中的很多角落是未曾涉足的。我们尽快走通主流程后,再就一些主要知识点专题攻破,比如k8s里面的List-Watch,Informer等好玩的东西。
2. 调度器启动运行
从goland的Structure中可以看到这个源文件(pkg/scheduler/scheduler.go)主要有这些对象:

大概浏览一下可以很快找到我们的第一个关注点应该是Scheduler这个struct和Scheduler的Run()方法:
pkg/scheduler/scheduler.go:58
// Scheduler watches for new unscheduled pods. It attempts to find
// nodes that they fit on and writes bindings back to the api server.
type Scheduler struct {
config *factory.Config
}
Copy
这个struct在上一讲有跟到过,代码注释说的是:
Scheduler watch新创建的未被调度的pods,然后尝试寻找合适的node,回写一个绑定关系到api server.
这个注释有个小问题就是用了复数形式,其实最后过滤出来的只有一个node;当然这种小问题知道就好,提到github上人家会觉得你在刷commit.接着往下看,Scheduler绑定了一个Run()方法,如下:
pkg/scheduler/scheduler.go:276
// Run begins watching and scheduling. It waits for cache to be synced, then starts a goroutine and returns immediately.
func (sched *Scheduler) Run() {
if !sched.config.WaitForCacheSync() {
return
}
go wait.Until(sched.scheduleOne, 0, sched.config.StopEverything)
}
Copy
注释说这个函数开始watching and scheduling,也就是调度器主要逻辑了!注释后半段说到Run()方法起了一个goroutine后马上返回了,这个怎么理解呢?我们先看一下调用Run的地方:
cmd/kube-scheduler/app/server.go:240
// Prepare a reusable runCommand function.
run := func(ctx context.Context) {
sched.Run()
<-ctx.Done()
}
Copy
可以发现调用了sched.Run()之后就在等待ctx.Done()了,所以Run中启动的goroutine自己不退出就ok.
wait.Until这个函数做的事情是:每隔n时间调用f一次,除非channel c被关闭。这里的n就是0,也就是一直调用,前一次调用返回下一次调用就开始了。这里的f当然就是sched.scheduleOne,c就是sched.config.StopEverything.
3. 一个pod的调度流程
于是我们的关注点就转到了sched.scheduleOne这个方法上,看一下:
scheduleOne does the entire scheduling workflow for a single pod. It is serialized on the scheduling algorithm's host fitting.
注释里说scheduleOne实现1个pod的完整调度工作流,这个过程是顺序执行的,也就是非并发的。结合前面的wait.Until逻辑,也就是说前一个pod的scheduleOne一完成,一个return,下一个pod的scheduleOne立马接着执行!
这里的串行逻辑也好理解,如果是同时调度N个pod,计算的时候觉得一个node很空闲,实际调度过去启动的时候发现别人的一群pod先起来了,端口啊,内存啊,全给你抢走了!所以这里的调度算法执行过程用串行逻辑很好理解。注意哦,调度过程跑完不是说要等pod起来,最后一步是写一个binding到apiserver,所以不会太慢。下面我们看一下scheduleOne的主要逻辑:
pkg/scheduler/scheduler.go:513
func (sched *Scheduler) scheduleOne() {
pod := sched.config.NextPod()
suggestedHost, err := sched.schedule(pod)
if err != nil {
if fitError, ok := err.(*core.FitError); ok {
preemptionStartTime := time.Now()
sched.preempt(pod, fitError)
}
return
}
assumedPod := pod.DeepCopy()
allBound, err := sched.assumeVolumes(assumedPod, suggestedHost)
err = sched.assume(assumedPod, suggestedHost)
go func() {
err := sched.bind(assumedPod, &v1.Binding{
ObjectMeta: metav1.ObjectMeta{Namespace: assumedPod.Namespace, Name: assumedPod.Name, UID: assumedPod.UID},
Target: v1.ObjectReference{
Kind: "Node",
Name: suggestedHost,
},
})
}()
}
Copy
上面几行代码只保留了主干,对于我们理解scheduleOne的过程足够了,这里来个流程图吧:

不考虑scheduleOne的所有细节和各种异常情况,基本是上图的流程了,主流程的核心步骤当然是suggestedHost, err := sched.schedule(pod)这一行,这里完成了不需要抢占的场景下node的计算,我们耳熟能详的预选过程,优选过程等就是在这里面。
4. 潜入第三层前的一点逻辑
ok,这时候重点就转移到了suggestedHost, err := sched.schedule(pod)这个过程,强调一下这个过程是“同步”执行的。
pkg/scheduler/scheduler.go:290
// schedule implements the scheduling algorithm and returns the suggested host.
func (sched *Scheduler) schedule(pod *v1.Pod) (string, error) {
host, err := sched.config.Algorithm.Schedule(pod, sched.config.NodeLister)
if err != nil {
pod = pod.DeepCopy()
sched.config.Error(pod, err)
sched.config.Recorder.Eventf(pod, v1.EventTypeWarning, "FailedScheduling", "%v", err)
sched.config.PodConditionUpdater.Update(pod, &v1.PodCondition{
Type: v1.PodScheduled,
Status: v1.ConditionFalse,
LastProbeTime: metav1.Now(),
Reason: v1.PodReasonUnschedulable,
Message: err.Error(),
})
return "", err
}
return host, err
}
Copy
schedule方法很简短,我们关注一下第一行,调用sched.config.Algorithm.Schedule()方法,入参是pod和nodes,返回一个host,继续看一下这个Schedule方法:
pkg/scheduler/algorithm/scheduler_interface.go:78
type ScheduleAlgorithm interface {
Schedule(*v1.Pod, NodeLister) (selectedMachine string, err error)
Preempt(*v1.Pod, NodeLister, error) (selectedNode *v1.Node, preemptedPods []*v1.Pod, cleanupNominatedPods []*v1.Pod, err error)
Predicates() map[string]FitPredicate
Prioritizers() []PriorityConfig
}
Copy
发现是个接口,这个接口有4个方法,实现ScheduleAlgorithm接口的对象意味着知道如何调度pods到nodes上。默认的实现是pkg/scheduler/core/generic_scheduler.go:98 genericScheduler这个struct.我们先继续看一下ScheduleAlgorithm接口定义的4个方法:
- Schedule() //给定pod和nodes,计算出一个适合跑pod的node并返回;
- Preempt() //抢占
- Predicates() //预选
- Prioritizers() //优选
前面流程里讲到的sched.config.Algorithm.Schedule()也就是genericScheduler.Schedule()方法了,这个方法位于:pkg/scheduler/core/generic_scheduler.go:139一句话概括这个方法就是:尝试将指定的pod调度到给定的node列表中的一个,如果成功就返回这个node的名字。最后看一眼签名:
func (g *genericScheduler) Schedule(pod *v1.Pod, nodeLister algorithm.NodeLister) (string, error)
Copy
从如参和返回值其实可以猜到很多东西,行,今天就到这里,具体的逻辑下回我们再分析~

《k8s-1.13版本源码分析》-调度器框架的更多相关文章
- 《k8s-1.13版本源码分析》-调度预选
本文大纲 预选流程 predicate的并发 一个node的predicate predicates的顺序 单个predicate执行过程 具体的predicate函数 本系列文章已经开源到githu ...
- 《k8s-1.13版本源码分析》-抢占调度
源码分析系列文章已经开源到github,地址如下: github:https://github.com/farmer-hutao/k8s-source-code-analysis gitbook:ht ...
- 《k8s-1.13版本源码分析》-调度器初始化
源码分析系列文章已经开源到github,地址如下: github:https://github.com/farmer-hutao/k8s-source-code-analysis gitbook:ht ...
- 《k8s-1.13版本源码分析》-调度优选
源码分析系列文章已经开源到github,地址如下: github:https://github.com/farmer-hutao/k8s-source-code-analysis gitbook:ht ...
- 《k8s-1.13版本源码分析》- 调度器设计
本文原始地址:https://farmer-hutao.github.io/k8s-source-code-analysis/core/scheduler/desigh.html github项目地址 ...
- 《k8s-1.13版本源码分析》-源码调试
源码分析系列文章已经开源到github,地址如下: github:https://github.com/farmer-hutao/k8s-source-code-analysis gitbook:ht ...
- 《k8s-1.13版本源码分析》- Scheduler启动前逻辑
本文原始地址(gitbook格式):https://farmer-hutao.github.io/k8s-source-code-analysis/core/scheduler/before-sche ...
- 《k8s-1.13版本源码分析》- Informer 机制
源码分析系列文章已经开源到github,地址如下: github:https://github.com/farmer-hutao/k8s-source-code-analysis gitbook:ht ...
- 《k8s-1.13版本源码分析》上github
要干嘛? 猪年新气象,今年开始,kubernetes源码分析系列文章主战场从微信公众号转至github,完全使用Markdown重写,使用gitbook生成web页面,支持在线阅读,导出pdf等各种玩 ...
随机推荐
- arcEngine开发之IMap、ILayer、IFeatureLayer和IFeatureClass关系
刚开时学习 Engine 开发时,对于这几个接口之间的关系总是理不清,因此写下这篇文章做个总结. 是什么 在 engine 开发中,我觉得使用过程中应该将每个接口对应到 ArcMap 中的具体事物中, ...
- 读《图解HTTP》有感-(HTTP首部)
写在前面 该章节是对请求报文及响应报文的首部信息进行解析.通过该章节的学习,相信大家对首部结构,及各个首部字段的作用有个基本的了解 正文 HTTP报文由HTTP报文首部.空行以及HTTP报文主体组成. ...
- 关于Java的散列桶, 以及附上一个案例-重写map集合
为速度而散列: SlowMap.java说明了创建一个新的Map并不困难.但正如它的名称SlowMap所示,它不会很快,如果有更好的选择就应该放弃它.它的问题在于对键的查询,键没有按照任何特定的顺序保 ...
- 远程备份binlog服务
Ⅰ.bonlog server介绍 对于binlog的备份,之前文章里说的是有从机,一般不备份,那现在人家就是 要备份嘛,怎么办嘛, 写个脚本每天夜里去把前一天产生的binlog拷贝出来可以不? 行啊 ...
- 唱吧DevOps的落地,微服务CI/CD的范本技术解读
1.业务架构:从单体式到微服务 K歌亭是唱吧的一条新业务线,旨在提供线下便捷的快餐式K歌方式,用户可以在一个电话亭大小的空间里完成K歌体验.K歌亭在客户端有VOD.微信和Web共三个交互入口,业务复杂 ...
- 用react重构个人网站 3-22
问题一:import React from 'react'这个写法是怎么回事? 答案:require是common.js的写法,import是ES6的写法,主要功能都是引入模块,写法上: var mo ...
- python抓取数据构建词云
1.词云图 词云图,也叫文字云,是对文本中出现频率较高的"关键词"予以视觉化的展现,词云图过滤掉大量的低频低质的文本信息,使得浏览者只要一眼扫过文本就可领略文本的主旨. 先看几个词 ...
- 提示“本地连接没有有效的ip配置-未修复“窗口
很多人在使用电脑时可能会遇到了这样一个网络问题,电脑无法连接网络,使用自带网络诊断工具诊断提示:"本地连接没有有效的ip配置".这种网络故障多数是出在使用路由器共享上网的windo ...
- 安卓----Spinner
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android=" ...
- Java同步简介
Java同步 Java中同步一直都是很重要的问题,对于初学者来说也是不太容易能理解的问题.特在此记录一下有关Java中同步和锁的知识.主要涉及到同步的概念以及Java中解决的办法和简单的例子.有关锁L ...