kube-scheduler简介node
调度是容器编排的重要环节,须要通过严格的监控和控制,现实生产一般对调度有各种限制,譬如某些服务必须在业务独享的机器上运行,或者从灾备的角度考虑尽可能把服务调度到不一样机器,这些需求在Kubernetes集群依靠调度组件kube-scheduler知足。nginx
kube-scheduler是Kubernetes中的关键模块,扮演管家的角色听从一套机制为Pod提供调度服务,例如基于资源的公平调度、调度Pod到指定节点、或者通讯频繁的Pod调度到同一节点等。容器调度自己是一件比较复杂的事,由于要确保如下几个目标:redis
公平性:在调度Pod时须要公平的进行决策,每一个节点都有被分配资源的机会,调度器须要对不一样节点的使用做出平衡决策。算法
资源高效利用:最大化群集全部资源的利用率,使有限的CPU、内存等资源服务尽量更多的Pod。centos
效率问题:能快速的完成对大批量Pod的调度工做,在集群规模扩增的状况下,依然保证调度过程的性能。api
灵活性:在实际运做中,用户每每但愿Pod的调度策略是可控的,从而处理大量复杂的实际问题。所以平台要容许多个调度器并行工做,同时支持自定义调度器。缓存
为达到上述目标,kube-scheduler经过结合Node资源、负载状况、数据位置等各类因素进行调度判断,确保在知足场景需求的同时将Pod分配到最优节点。显然,kube-scheduler影响着Kubernetes集群的可用性与性能,Pod数量越多集群的调度能力越重要,尤为达到了数千级节点数时,优秀的调度能力将显著提高容器平台性能。服务器
调度流程架构
kube-scheduler的根本工做任务是根据各类调度算法将Pod绑定(bind)到最合适的工做节点,整个调度流程分为两个阶段:预选策略(Predicates)和优选策略(Priorities)。app
预选(Predicates):输入是全部节点,输出是知足预选条件的节点。kube-scheduler根据预选策略过滤掉不知足策略的Nodes。例如,若是某节点的资源不足或者不知足预选策略的条件如“Node的label必须与Pod的Selector一致”时则没法经过预选。
优选(Priorities):输入是预选阶段筛选出的节点,优选会根据优先策略为经过预选的Nodes进行打分排名,选择得分最高的Node。例如,资源越富裕、负载越小的Node可能具备越高的排名。
通俗点说,调度的过程就是在回答两个问题:1. 候选有哪些?2. 其中最适合的是哪一个?
值得一提的是,若是在预选阶段没有节点知足条件,Pod会一直处在Pending状态直到出现知足的节点,在此期间调度器会不断的进行重试。
到这里咱们能够对Pod的整个启动流程进行总结:
资源管控中心Controller Manager建立新的Pod,将该Pod加入待调度的Pod列表。
kube-scheduler经过API Server提供的接口监听Pods,获取待调度pod,通过预选和优选两个阶段对各个Node节点打分排序,为待调度Pod列表中每一个对象选择一个最优的Node。
kube-scheduler将Pod与Node的绑定写入etcd(元数据管理服务)。
节点代理服务kubelet经过API Server监听到kube-scheduler产生的绑定信息,得到Pod列表,下载Image并启动容器,而后由kubelet负责拉起Pod。
到此为止就完成了Pod的调度与启动。
预选和优选算法介绍
kube-scheduler支持多种预选和优选算法,开发者能够根据当前场景下的重要要素进行选择。
预选策略(Predicates)
基于存储卷数量的判断
MaxEBSVolumeCount:确保已挂载的EBS存储卷数量不超过设置的最大值(默认39),调度器会检查直接或及间接使用这种类型存储的PVC,累加总数,若是卷数目超过设最大值限制,则不能调度新Pod到这个节点上。
MaxGCEPDVolumeCount:同上,确保已挂载的GCE存储卷数量不超过预设的最大值(默认16)。
MaxAzureDiskVolumeCount:同上,确保已挂载的Azure存储卷不超过设置的最大值(默认16)。
基于资源压力状态的判断
CheckNodeMemoryPressure:判断节点是否已经进入到内存压力状态,若是是则只容许调度内存为0标记的Pod。
CheckNodeDiskPressure:判断节点是否已经进入到磁盘压力状态,若是是,则不能调度新的Pod。
基于卷冲突的判断
NoDiskConflict:卷冲突判断,即若是该节点已经挂载了某个卷,其它一样使用相同卷的Pod将不能再调度到该节点。
NoVolumeZoneConflict:对于给定的某块区域,判断若是在此区域的节点上部署Pod是否存在卷冲突。
NoVolumeNodeConflict:对于某个指定节点,检查若是在此节点上部署Pod是否存在卷冲突。
基于约束关系的判断
MatchNodeSelector:检查节点标签(label)是否匹配Pod指定的nodeSelector,是则经过预选。
MatchInterPodAffinity:根据Pod之间的亲和性作判断。
PodToleratesNodeTaints:排斥性关系,即判断Pod不容许被调度到哪些节点。这里涉及到两个概念Taints(污点)和Toleration(容忍)。Node能够定义一或多个Taint,Pod能够定义一或多个Toleration,对于具备某个Taint的节点,只有遇到能容忍它的(即带有对应Toleration的)Pod,才容许Pod被调度到此节点,从而避免Pod被分配到不合适的节点。
基于适合性的判断
PodFitsResources:检查节点是否有足够资源(如CPU、内存、GPU等)知足Pod的运行需求。
PodFitsHostPorts:检查Pod容器所需的HostPort是否已被节点上其它容器或服务占用。若是已被占用,则禁止Pod调度到该节点。
PodFitsHost:检查Pod指定的NodeName是否匹配当前节点。
优选策略(Priorities)
优选过程会根据优选策略对每一个候选节点进行打分,最终把Pod调度到分值最高的节点。kube-scheduler用一组优先级函数处理每一个经过预选的节点,每一个函数返回0-10的分数,各个函数有不一样权重,最终得分是全部优先级函数的加权和,即节点得分的计算公式为:
优选的优先级函数包括:
LeastRequestedPriority(默认权重1):尽可能将Pod调度到计算资源占用比较小的Node上,这里涉及两种计算资源:内存和CPU。计算公式以下:其中,capacity表示该节点的现有容量,requested表示Pod所请求的容量。
BalancedResourceAllocation(默认权重1):CPU和内存使用率越接近的节点权重越高。该策略均衡了节点CPU和内存的配比,尽可能选择在部署Pod后各项资源更均衡的机器。该函数不能单独使用,必须和LeastRequestedPriority同时使用,由于若是请求的资源(CPU或者内存)大于节点的capacity,那么该节点永远不会被调度到。计算公式以下:
SelectorSpreadPriority(默认权重1):把属于同一个Service或者ReplicationController的Pod,尽可能分散在不一样的节点上,若是指定了区域,则尽可能把Pod分散在该区域的不一样节点。一般来讲节点上已运行的Pod越少,节点分数越高。计算公式以下,是基于节点的计算和基于区域的计算的加权和,其中,maxPriority表明系数,默认为10,maxCount为节点最多容许运行的Pod数量,nodeCount为该节点已经存在的Pod数量,maxCountByZone为该区域最多容许的Pod数量,zoneCount为区域内已经运行的Pod数量。
NodeAffinityPriority(默认权重1):尽可能调度到标签匹配Pod属性要求的节点,判断行为与预选中的MatchNodeSelector类似,将来可能会彻底将其取代。
该函数提供两种选择器:requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution。前者是强要求,指定将Pod调度到节点上必须知足的全部规则;后者是弱要求,即尽量调度到知足特定限制的节点,但容许不知足。计算公式以下,在节点知足requiredDuringSchedulingIgnoredDuringExecution后,该节点会累加preferredDuringSchedulingIgnoredDuringExecution中全部知足条件的规则的权值weight_i,CountWeight为preferredDuringSchedulingIgnoredDuringExecution中全部规则的权值总和:
InterPodAffinityPriority(默认权重1):叠加该节点已调度的每一个Pod的权重,权重可配置文件里设定,一般亲和性越强的Pod权重越高,若是Pod知足要求则将加到权重和,具备最高权重和的节点是最优的。提供两种选择器:requiredDuringSchedulingIgnoredDuringExecution(保证所选的主机必须知足全部Pod对主机的规则要求)、preferredDuringSchedulingIgnoredDuringExecution(调度器会尽可能但不保证知足NodeSelector的全部要求)。
计算公式以下,其中,weight_i为节点上符合亲和性的每一个Pod的权重,sumCount为节点上符合亲和性的Pod权重和,maxCount为节点上全部Pod的权重和,minCount为节点上最小的Pod权重,maxPriority是系数,默认为10。
NodePreferAvoidPodsPriority(默认权重10000):避免将ReplicationController或ReplicaSet调度到节点。若是Pod由RC或者RS的Controller调度,则得分为0,不对最终加权得分产生影响;若是不是,则总分为100000,意味着覆盖其余策略,直接决定最优节点。
TaintTolerationPriority(默认权重1):Pod与Node的排斥性判断。经过Pod的tolerationList与节点Taint进行匹配,配对失败的项越少得分越高,相似于Predicates策略中的PodToleratesNodeTaints。计算公式以下:其中,totalTaints表示Taint总个数,intolerableTaints表示配对不成功的个数。
ImageLocalityPriority(默认权重1):尽可能调度到Pod所需镜像的节点。检查Node是否存在Pod所需镜像:若是不存在,返回0分;若是存在,则镜像越大得分越高。计算公式以下:其中,sumSize表示该节点上存在的Pod所需镜像大小总和,maxImgSize表示Pod所需镜像总大小,minImgSize表示Pod所需最小镜像的尺寸。
EqualPriority(默认权重1):给予全部节点相等权重,通常仅用于测试。
MostRequestedPriority(默认权重1):适用于动态伸缩集群环境,会优先调度Pod到使用率最高的节点,方便在伸缩集群时,先腾出空闲机器,从而进行停机处理。
案例演示
下面将经过四个案例来介绍如何在实战中对以上的调度策略进行选择。
场景一:调度到基于SSD的节点
某架构使用分布式缓存Redis,对磁盘读写敏感,开发者但愿调度到配置SSD的节点,可用预选策略MatchNodeSelector解决,对目标Node设置label,Redis的spec添加nodeSelector。
假设Redis的YAML文件以下:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: redis-master
labels:
name: redis
namespace: default
spec:
replicas: 4
template:
metadata:
labels:
name: redis
spec:
containers:
- name: master
image: 172.16.1.41:5000/redis:3.0.5
resources:
requests:
cpu: 100m
memory: 100Mi
节点transwarp配置了SSD,所以添加“disk=ssd”标签。
kubectl label node transwarp disk=ssd
经过更新Redis服务的YAML文件字段添加nodeSelector标签“disk=ssd”来匹配节点transwarp。
kubectl patch deploy redis-master -p '{"spec":{"template":{"spec":{"nodeSelector:"{"disk":"ssd"}}}}}'
场景二:只容许调度到CentOS节点
某系统中有各类异构系统机器,好比SLES、RHEL、CentOS7.二、CentOS7.3等,如今有一个线上任务只能运行在CentOS系统,可是不区分CentOS系统版本。固然咱们可使用MatchNodeSelector来实现匹配,然而若是以这种方式实现须要备注多个标签,所以建议用NodeAffinityPriority解决。
咱们在该服务的YAML文件中将NodeAffinityPriority选择器的指定为requiredDuringSchedulingIgnoredDuringExecution,表示强要求,指定Pod必须被调度到key为“operation system”,值为“centos 7.2”或“centos 7.3”的节点。
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: operation system
operator: In
values:
- centos 7.2
- centos 7.3
若是须要优先调度到CentOS 7.2的节点,还能够增长一个由preferredDuringSchedulingIgnoredDuringExecution决定的调度策略,优先调度到具备“another-node-label-key-system”标签,且值为“another-node-label-value-centos7.2”的节点。
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key-system
operator: In
values:
- another-node-label-value-centos7.2
该线上服务的YAML文件具体内容以下:
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: operation system
operator: In
values:
- centos 7.2
- centos 7.3
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key-system
operator: In
values:
- another-node-label-value-centos7.2
containers:
- name: with-node-affinity
image: gcr.io/google_containers/pause:2.0
注意:若是同时指定了nodeSelector和nodeAffinity,目标节点必须同时知足这两个规则;nodeSelectorTerms能够指定多个,知足任意一个便可知足;matchExpressions也能够指定多个,要求必须都知足。
场景三:调度到同一分区
现有公有云组件API服务,须要与认证服务器频繁通讯,所以要尽量的协同部署在云提供商的同一个分区中。
该场景的需求是使API服务和认证服务运行在同一分区,转化到更通用的视角就是把Pod优先调度到更具Pod亲和性的节点,故可用优选策略InterPodAffinityPriority解决。
首先咱们对API服务添加标签“security=cloud”,它的其中一段配置以下:
apiVersion: v1
kind: Pod
metadata:
name: pod-flag
labels:
security: "cloud"
spec:
containers:
- name: nginx
image: nginx
为了使认证服务分配到相同分区,须要在其YAML文件中添加requiredDuringSchedulingIgnoredDuringExecution字段去亲和具备“security=cloud”标签的Pod,同时添加topologyKey: failure-domain.beta.kubernetes.io/zone字段来限制拓扑域范围,表示把部署限制在同一个分区。
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- cloud
topologyKey: failure-domain.beta.kubernetes.io/zone
场景四:
私有云服务中,某业务使用GPU进行大规模并行计算。为保证性能,但愿确保该业务对服务器的专属性,避免将普通业务调度到部署GPU的服务器。
该场景要求Pod避免被调度到指定类型的节点,符合排斥关系,故可用策略PodToleratesNodeTaints解决。
首先先在配有GPU的node1中添加Taints污点(gpu = true),NoSchedule表示只要没有对应Toleration的Pod就不容许调度到该节点。
kubectl taint nodes nodes1 gpu=true:NoSchedule
同时,经过更新服务的YAML文件,为须要GPU资源业务的Pods添加Toleration(key: "gpu",value: "true"),表示容忍GPU服务器的Taints,这样就能够把指定业务调度到GPU服务器,同时把普通服务调度到其余节点。
apiVersion: v1
kind: Pod
metadata:
generateName: redis-
labels:
app: redis
namespace: default
spec:
containers:
- image: 172.16.1.41:5000/redis
imagePullPolicy: Always
name: redis
schedulerName: default-scheduler
tolerations:
- effect: NoSchedule
key: gpu
operator: Equal
value: true
这里须要提一下,tolerations里的effect字段表示处理排斥的策略,支持三种调度规则:NoSchedule表示不容许调度,已调度的不影响;PreferNoSchedule表示尽可能不调度;NoExecute表示不容许调度,已运行的Pod将在以后删除。
总结与展望
目前kube-scheduler已经提供了丰富的调度策略可供使用,通常状况下,使用kube-scheduler的默认调度策略就能知足大部分需求,而且其插件化的形式也方便于用户进行定制与二次开发。将来,咱们会在此基础上对其进一步优化:包括增长cache以减小predict和prioritize阶段的重复计算,已在TOS 1.9中实现;经过scheduler-extender扩展来针对local Volume、GPU等资源实现更合理调度;适配custom-metrics-server的API来实现实时调度,使得每一个节点的资源能更好的被利用。