k8s 调度约束
kube-scheduler 是 Kubernetes 集群的默认调度器,并且是集群控制面的一部分。
# 1. 调度器的工作机制
# 1.1 预备工作
- 1、缓存所有的node节点,记录他们的规格:cpu、内存、磁盘空间、gpu显卡数等;
- 2、缓存所有运行中的pod,按照pod所在的node进行区分,统计每个node上的pod request了多少资源。request是pod的QoS配置
- 3、list & watch pod资源,当检查到有新的Pending状态的pod出现,就将它加入到调度队列中
- 4、调度器的worker组件从队列中取出pod进行调度
# 1.2 调度过程
# 1.2.1 过滤阶段
1、先将当前所有的node放入队列
2、执行predicates算法,对队列中的node进行筛选。
这里算法检查了一些pod运行的必要条件,包括port不冲突、cpu和内存资源QoS(如果有的话)必须满足、挂载volume(如果有的话)类型必须匹配、nodeSelector规则必须匹配、硬性的affinity规则必须匹配、node的状态(condition)必须正常,taint_toleration硬规则等等。
# 1.2.2 打分阶段
3、执行priorities算法,对队列中剩余的node进行评分。
这里有许多评分项,各个项目有各自的权重:整体cpu,内存资源的平衡性、node上是否有存在要求的镜像、同rs的pod是否有调度、node affinity的软规则、taint_toleration软规则等等。
4、最终评分最高的node会被选出。
即代码中suggestedHost, err := sched.schedule(pod) 依据(plugin/pkg/scheduler/scheduler.go)的返回值。如果存在多个得分最高的 Node,kube-scheduler 会从中随机选取一个。
5、调度器执行assume方法。
该方法在pod调度到node之前,就以“该pod运行在目标node上” 为场景更新调度器缓存中的node 信息,也即预备工作中的1、2两点。这么做是为了让pod在真正调度到node上时,调度器也可以同时做后续其他pod的调度工作。
6、调度器执行bind方法,该方法创建一个Binding资源,apiserver检查到创建该资源时,会主动更新pod的nodeName字段。完成调度。
# 2. 调度方式
全自动调度:运行在哪个节点上完全由master的scheduler经过一系列的算法计算得出, 用户无法进行干预。
nodeselector定向调度: 指定pod调度到一些node上, 通过设置node的标签和deployment的nodeSelctor属性相匹配。
- 多个node有相同标签, scheduler 会选择一个可用的node进行调度;
- nodeselector定义的标签匹配不上任何node上的标签, 这个pod是无法调度;
- k8s给 node 预定义了一些标签, 通过kubectl describe node xxxx进行查看用户可以使用k8s给node预定义的标签。
NodeAffinity: node节点亲和性
- 硬限制 : 必须满足指定的规则才可以调度pod到node上。
- 软限制 : 优先满足指定规则,调度器会尝试调度pod到node上, 但不强求。
PodAffinity: pod亲和与互斥调度, 根据在节点上正在运行的pod标签而不是节点的标签进行判断和调度
- 亲和: 匹配标签两个pod调度到同一个node上;
- 互斥: 匹配标签两个pod不能运行在同一个node上。
# 3. 调度策略
# 3.1 nodeSelector:node标签选择器
常规情况下,会直接使用nodeSelector这种调度策略。
labels(标签) 是k8s里面用来编标记资源的一种常用的方式,我们可以给node标记特殊的标签,然后nodeSelector会将pod调度到带有指定labels的node上
#增加标签
kubectl label nodes node01 test=test
#在spec.template.spec段添加:
nodeSelector:
test: test
2
3
4
5
6
# 3.2 affinity 和 anti-affinity:亲和性和反亲和性
亲和性可以分为具体可以细分为 硬亲和性 和 软亲和性。
软亲和性:如果调度的时候,没有满足要求,也可以继续调度,即能满足最好,不能也无所谓。
软策略: preferredDuringSchedulingIgnoredDuringExecution;
硬亲和性:是指调度的时候必须满足特定的要求,如果不满足,那么pod将不会被调度到当前node。
硬策略: requiredDuringSchedulingIgnoredDuringExecution;
# 3.2.1 nodeAffinity node亲和性
主要是用来控制 pod 能部署在哪些节点上,以及不能部署在哪些节点上的。它可以进行一些简单的逻辑组合,不只是简单的相等匹配。
# 3.2.2 podAffinity pod亲和性
pod的亲和性主要用来解决pod可以和哪些pod部署在同一个集群里面,即拓扑域(由node组成的集群)里面。
# 3.2.3 podAntiAffinity pod反亲和性
pod的反亲和性是为了解决pod不能和哪些pod部署在一起的问题。
二者都是为了解决pod之间部署问题。
需要注意的是,Pod 间亲和与反亲和需要大量的处理,这可能会显著减慢大规模集群中的调度,不建议在具有几百个节点的集群中使用,而且Pod 反亲和需要对节点进行一致的标记,即集群中的每个节点必须具有适当的标签能够匹配 topologyKey。如果某些或所有节点缺少指定的 topologyKey 标签,可能会导致意外行为。
topologyKey的值一般是kubernetes的内置标签:
- 对于亲和性和软反亲和性,不允许空topologyKey;
- 对于硬反亲和性,LimitPodHardAntiAffinityTopology控制器用于限制topologyKey只能是kubernetes.io/hostname;
- 对于软反亲和性,空topologyKey被解读成kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone and failure-domain.beta.kubernetes.io/region的组合;
kubernetes.io/hostname标签是Kubernetes集群节点的内建标签,它的值为当前节点的主机名,对于各个节点来说都是不同的
查看kubernetes内置标签:
kubectl -get nodes --show-labels
○ kubernetes.io/hostname
○ failure-domain.beta.kubernetes.io/zone
○ failure-domain.beta.kubernetes.io/region
○ beta.kubernetes.io/instance-type
○ beta.kubernetes.io/os
○ beta.kubernetes.io/arch
2
3
4
5
6
7
# 3.3 nodeAffinity示例
在spec段:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这个pod只允许被调度到带有kubernetes.io/e2e-az-name=e2e-az1或e2e-az2的label的node上,也即只允许被调度到e2e-az1或者e2e-az2两个可用域中;
另外,pod要尽量调度到包含another-node-label-key的值为another-node-label-value的node上
matchExpressions 结构记录各种表达式,一个表达式包含key、operator、values,分别表示关键字、关键字匹配关系、关键字匹配值。
operator 匹配关系包括:
- In:label 的值在某个标签中
- NotIn:label 的值不在某个标签中
- Gt:label 的值大于某个值
- Lt:label 的值小于某个值
- Exists:某个 label 存在
- DoesNotExist:某个 label 不存在
NotIn和DoesNotExist是node anti-affinity的一种表现。
- 如果一个pod的描述信息中同时包含了 nodeSelector 和 nodeAffinity,那么调度时两个规则都要满足;
- 如果一个 nodeAffinity 中包含了多条 nodeSelectorTerms , 调度器只需要满足其中一条;
- 如果一个 nodeSelectorTerms中记录了多条matchExpressions,那么调度器要满足所有的matchExpressions。
# 3.4 podAffinity pod亲和性 和 podAntiAffinity pod反亲和性示例
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
podAffinity 规则中 topologyKey 是zone,也就是可用域,说明这条规则可以规划出调度到的域。
首先,node上必须至少有一个running状态的pod包含key为security,value为S1的label。只要满足这个条件,那么这个node和其同一个域(拥有相同的failure-domain.beta.kubernetes.io/zone 为key,且值相同的label)的node均会被调度。
podAntiAffinity规则中topologyKey是hostname,表明该规则约定了某种node尽量不会被调度到,这种node上已经运行了包含key为security,value为S2的label的pod。
假如现在有node a,b,c,其中a和b拥有相同的zone,且b上运行了一个pod,这个pod有一个label,key为security,value为S1。那么我们创建如上的一个亲和性规则的3副本时,三个副本都会被调度到a或者b上。假如b上同时运行了一个pod,这个pod有一个label,key为security,value为S2,那么所有的副本都会调度到node a上。
# 3.5 软限制
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- bluwhale-application
topologyKey: kubernetes.io/hostname
weight: 100
2
3
4
5
6
7
8
9
10
11
12
# 3.6 硬限制
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- bluwhale-application
topologyKey: kubernetes.io/hostname
2
3
4
5
6
7
8
9
10
# 3.7 taint 和 toleration:污点和容忍
node 可以被打上污点标记,并配置污点容忍策略。而pod的描述信息中如果包含了相同的污点容忍策略,就可以被调度到这个node上,反之则不可、或尽量不允许。
# 3.7.1 taint 污点
给node a 打上污点 name=huang, 策略为不可调度:
kubectl taint nodes a name=huang:NoSchedule
# 3.7.2 toleration 容忍
若pod中有以下描述则可以调度:
tolerations:
- key: "role"
operator: "Equal"
value: "messi"
effect: "NoSchedule"
或者:
tolerations:
- key: "name"
operator: "Exist"
effect: "NoSchedule"
2
3
4
5
6
7
8
9
10
11
# 3.7.3 硬规则
类似的硬性规则体现在effect字段中,还有NoExecute,它比NoSchedule更严格,不止pod不能调度上去,node上原有的pod如果不能容忍污点,就会被驱逐(eviction),配合字段 tolerationSeconds 可以规定这些会被驱逐的pod能在node上呆多久。
# 3.7.4 软规则
除了NoExecute,NoSchedule,还有一条软规则:PreferNoSchedule。
配置effect=PreferNoSchedule后,没有相关污点策略的pod会尽量避免调度到该node上。