抖机灵|k8S-调度器

kube-scheduler是kubernetes的核心组件之一,主要负责整个集群资源的调度功能,根据特定的调取算法和策略,将pod调度到最优的工作节点上去,从而更加合理充分的利用集群的资源。
默认情况下,kube-scheduler提供的默认调度器能满足我们绝大多数的需求,我们前面和大家接触的实例也基本上用的默认的策略,都可以保证我们的pod可以被分配到资源充足的节点上运行,但是在实际的线上项目中,可能我们自己会比kubernetes更加了解我们自己都应用。比如我我们希望pod可以运行在特定的几个节点上。或者这几个节点只能用来运行特定类型的应用,这就需要我们的调度器能可控。
kube-scheduler的主要作用就是根据特定的调度算法和调度策略将pod调度到合适的node节点上去,是一个独立的二进制程序,启动之后会一直监听API server,获取到的podspec.nodename为空的pod,对每个pod都会创建一个binding.
这个过程看起来比较简单,但实际的生产环境中,需要考虑的问题就有很多了
如何保证全部的节点调度到公平性, 每个节点都能被分配资源,集群资源如何被高效利用,集群资源如何才能最大化利用,保证pod调度到性能和效率
考虑到实际环境中的各种复杂情况,kubernetes的调度器才有插件化的形式实现,可以方便用户进行定制或者二次开发。我么可以定义一个调取器并以插件形式和kubernetes进行集成
kubernetes调取器的源码位于kubernetes/pkg/scheduler中,其中scheduler创建和运行的核心程序,对应的代码pkg/scheduler/scheduler.go如果要看出kube-scheduler的入口程序,对应的代码/cmd/kube-scheduler/scheduler.go
调度主要分为一下几个部分
首先是预选过程,过滤掉不满足条件的节点,这个过程称为predicates
然后是优选过程,对通过节点按照优先级排序,prioritres 打分,
最后从中选择优先级最高的节点,如果中间任何一步骤有错误,就直接返回错误
predicates 阶段首选遍历全部节点,过滤掉不满足条件的节点,属于强制性规则,这一阶段输出的所有满足要求的节点讲记录并座位第二阶段的输入,如果所有的节点都不满足条件,那么pod将会一直处于pending状态,这期间调度器会不断的重试。所以我们在部署应用的时候,如果发现有pod一直处于pending状态,那么就是没有满足调度条件的节点,这个时候可以去检查下节点资源是否可用。
priorities阶段即再次对节点进行筛选,如果有多个节点满足条件的话,那么系统回按照节点的优先级priorities大小对节点进行排序,最后选择优先级最高的节点来部署pod应用
更详细的流程是这样:
首先,客户端通过api server的restapi 或者kuberctl工具创建pod资源
api server收到用户请求后,存储相关数据到etcd数据库
调取器监听api server查看到还未被bind 调度到pod列表,循环遍历地为每个pod尝试分配节点,这个分配过程就是我们上面提到的两个阶段,预选pre 优选 pri优选阶段为节点的优先级打分,将上一阶段过滤出来的Node列表进行打分,调度器会考虑一些整体的优化策略,比如吧deloyment控制的多个pod副本尽量分布到不同的主机上,使用最低负载的主机等等
经过上面的阶段过滤后选择打最高分的node节点和pod进行binding 调度操作,然后讲结果存储到etcd中被选择出来node节点对应的kubelet去执行创建pod的相关操作。
调度框架,定了了一组扩展点,用户可以实现扩展定义的接口定义自己都调度逻辑,并将扩展注册到扩展点上,调度框架在预留扩展点时,都是有特定的目的,有些扩展点上的扩展可以改变调度程序的决策方法,有些扩展点上的扩展只是发送一个通知。
我门知道每当一个调度pod时,都会按照两个过程来执行,调度过程pre pri 和binding过程
调度过程为pod选择一个合适节点,绑定过程则将调度过程的决策应用到集群中,将调度过程和绑定过程合在一起,称之为调度上下文,scheduling context 需要注意的是调度过程是同步运行的,绑定过程可能是异步的。调度过程和绑定过程遇到错误会退出。调度程序认为当前没有改pod的可选节点,内部错误
这个时候,该pod讲被放回待调度队列,
一般的情况下我们的部署pod是通过集群的自动调度策略来选择节点的,默认情况在调度器考虑的是资源足够,并且负载尽量平均,但是有的时候我们需要能更加细粒度的去控制pod的调度。比如我们希望一些机器学习的应用只跑在GPU的节点上,但是有时候我们的服务之间交流比较频繁,又希望能够将这服务的pod调度到同一个节点上,这就需要使用一些调度方式来控制pod的调度了,主要有两个概念,亲和性和反亲和性,亲和性又分成节点亲和性 nodeAffinity 和podAffinity
nodeselector在了解亲和性 了解一个非常常用的调度方式nodeselector 我们知道label标签是kubernetes中一个非常重要的概念,用户可以非常灵活的利用label来管理集群中的资源,比如最常见的service对象通过label去匹配pod资源,而pod的调度也可以根据节点的label来进行调度。
kubectllabelnodesnodenamelabelname
我们可以通过上面的 --showlabels 参数来查看上述标签是否生效,当节点被打上可相关标签后,在调度到时候就可以使这些标签了,只需要在pod的spec字段添加nodeselector字段,里面是我们需要被调用的节点的label标签,比如下面的pod我们需要强制调度到node2spec.nodeSelector: label:###
event我们可以看到,我们的pod通过默认的default-scheduler调取器被绑定到,不过需要注意的是nodeselector属于强制性的,如果我们的目标节点没有可用的资源,我们的pod就会一直处于pending状态。
上面的例子我们可以感受到nodeselector的方式比较直观,单还是不够灵活,控制粒度偏大,接下来节点亲和性 nodeAffinity
前面kubernetes调度器的调度流程,我们知道默认的调度器在使用的时候,经过了pre pri两个阶段,但是实际生产环境中,我们需要根据自己的一些实际需要控制pod的调度,这就需要nodeaffinitypodaffinitypodanaffinity
亲和性调度可以分成软策略和应策略两种方式
软策略就是如果现在没有满足调度要求的节点的话,pod就会忽略这条规则,继续完成调度过程,就是有条件最好,没有也无所谓。
硬策略就比较强硬了,如果没有满足条件的节点的话,就不断重试知道满足条件为止
节点亲和性 nodeaffinity主要是用来控制pod要部署在那些节点上,以及不能部署在那些节点上的,它可以进行简单的逻辑组合了,不只是简单的相等匹配。
对于nodeAffinity无论是硬策略还是软策略方式,都是调度pod的预期节点上,而污点taints 恰好与之相反,如果一个节点标记为taints,除非pod也被标记为可以容忍污点节点,否则taints节点不会被调度pod
比如用户希望吧maste节点保留给kubernetes系统组件使用,或者把一组有特殊资源留给某些Pod则污点就很有用了,pod不会被调度到taint标记过的节点,我们使用kubeadm搭建的集群默认就给master节点添加了一个污点标记,所以我们看到一般没有pod调度到master上去
taints: node-relo.kubernetes.io/master:NoSchedule
Unschedulable: false
我们可以使用上面的命令查看master节点的信息,其中一条关于taints的信息,node-role.kubernetes.io/master.Noschedule 就表示master节点打了一个污点的标记,其中影响的参数是Noschedule,表示pod不会被调度到标记为taints的节点,除了Noschedule外,还有另外两个选项
kubectltaint nodesnode2test=node2:Noschedule
上面的命名将node2节点标记位污点taint,影响策略是Noschedule,之回影响新的pod调度,如果仍然希望某个pod调度到taint节点上,则必须在spec 中走出toleration定义,才能调度到节点,比如我们想要将一个pod调度到master上
由于master节点被标记为污点tanit所以我们这里想要pod能够调度
toleration:
-key : node-role.kubennetes.io/master
operator: exists
effect: Noschedule
我们可以看到一个pod副本被调度到master节点
对于tolerations属性的写法,其中key,value,effect,与node的taint设置需要保持一致,
【抖机灵|k8S-调度器】

    推荐阅读