目录
[二、kubernetes scheduler 基本原理](#二、kubernetes scheduler 基本原理)
[Node相关策略 编辑](#Node相关策略 编辑)
一、Scheduler调度器简介
Scheduler调度器做为Kubernetes三大核心组件之一, 承载着整个集群资源的调度功能,其根据特定调度算法和策略,将Pod调度到最优工作节点上,从而更合理与充分的利用集群计算资源。
调度器在调度时需要考虑以下几个问题:
- 公平:如何保证每个节点都能被分配资源;
- 资源高效利用:集群所有资源最大化被使用;
- 效率:调度的性能要好,能够尽快的对大批量的pod完成调度工作;
- 灵活:允许用户根据自己的需求控制调度的逻辑;
为了达成以上几个目标,kubernetes官方调度器做了很多工作,我们接下来一一进行讲解。
二、kubernetes scheduler 基本原理
1、scheduler在整体流程中所处的位置
Pod从创建到运行的整个流程如下图:

对于kubernetes scheduler组件的主要工作集中在第4个步骤上。
kubernetes scheduler 作为一个单独的进程部署在 master 节点上,它会监听 kube-apiserver 进程去发现 PodSpec.NodeName 为空的 Pod(该值不为空的话即用户将pod指定了对应的节点了,无需使用调度器进行pod调度,直接分配该节点部署即可),然后根据指定的算法将 Pod 调度到合适的 Node 上,这一过程也叫绑定(Bind)。scheduler 的输入是需要被调度的 Pod 和 Node 的信息,输出是经过调度算法筛选出条件最优的 Node,并将该 Pod 绑定到这个 Node 上。
所以实际上kube-scheduler做的事是两点:
1、监听kube-apiserver 进程去发现 PodSpec.NodeName 为空的 Pod;
2、根据node以及pod信息,利用调度算法完成Pod和Node的绑定,发送绑定信息给到apiserver。
apiserver接到绑定信息后会保存到Etcd中(第5步),后续真正将Pod部署运行到Node节点上,是由kubelet干的活。
附:从pod创建到运行的序列图

2、scheduler调度器基本原理
Scheduler结构图如下所示:
原文链接:https://blog.csdn.net/qq_42987484/article/details/103935574

通过上图我们可以看到,调度器实际上主要是由两个控制循环来完成对pod,service等的调度的。
Informer Path
第一个循环Informer Path中,通过一系列的informer来对pod,node,service等信息进行list and watch。当对应资源(比如pod)有改变时,informer会收到来自api server的变化通知,然后informer会将资源的变动信息更新到Scheduler Cache调度器缓存中,用于后续调度算法的判定依据(这里使用cache的好处就是避免了对api server的大量重复的请求操作,特别是后面的调度算法判定阶段,从而提升调度效率)。
如果有新增的资源,比如有个新的pod,那么informer会将其添加到调度队列中。这里的调度队列是一个优先级的队列,它在保证FIFO的基本功能的同时,还能满足调度器的一些特殊操作,比如基于优先级的抢占操作。
Scheduling Path
在Scheduling Path中,调度器会从调度队列里不断取出需要调度的资源,然后通过Predicates预选算法对Scheduler Cache中的Nodes进行过滤,拿到合适的Node列表。然后根据Priorities优选算法对第一步的Node列表进行打分,选出得分最高的Node。
经过Priorities后,修改资源的nodeName字段为选出的Node,并更新Scheduler Cache中pod和node的信息,然后启动一个异步的线程去请求api server去修改持久化的pod信息。
通过异步线程这样做的好处是提高了调度器的调度效率。如果异步线程失败了也无所谓,cache中的信息会随着后续的更新而恢复正常,调度失败的资源会在后续进行重新调度。当然这种基于乐观绑定的设计,就需要kubelet在实际运行资源的时候再次通过基本的调度算法进行确认:看当前pod是否能够在当前node运行。同时为了进一步提升调度的效率,调度器对Predicates预选和Priorities优选过程都是启动多个线程来并发地对多个资源进行判定,同时在Priorities优先的阶段以MapReduce的方式来进行打分。整个过程只有在资源出队和更新cache的时候会加锁,从而保证了调度器的执行效率。
3、调度算法
上面讲了预算算法和优选算法两个阶段。
预先过程:过滤掉不满足条件的节点;(过滤)
优选过程:对通过预选的节点按照优先级排序,这是"优选";(打分)
最后从优选中在选择优先级最高的节点。
如果以上中间任何一步骤有错误,就直接返回出错。
预选算法和优选算法随着k8s版本的更新在不停的进行迭代。如果预选阶段没有合适的节点,Pod就会一直在"pending"状态,不断重试调度。经过预选后,如果有多个节点满足条件,就继续"优选"。
哪个节点满足调度算法的越多,就越容易被分配到哪个节点。

一般情况下,使用kube-scheduler的默认调度就能满足大部分需求。kubernetes的调度器是以插件化的形式实现的,方便用户对调度的定制与二次开发。因此用户也可以自定义预选和优选策略。
(1)Predicates(预选策略)
Predicates其实就相当于一个的filter chain,对当前所有的node list进行过滤,最后得到符合调度条件的node list。
随着版本的演进Kubernetes支持的Predicates策略逐渐丰富,v1.0版本仅支持4个策略,v1.7支持15个策略。目前可用的Predicates策略有:
一般策略
这一组 filter是最基础的filter,主要判断对应的node是否满足pod的运行条件。

Volume相关策略

Node相关策略

Pod相关策略

而针对于这么多规则,调度器在面对一个待调度的pod的时候会同时启动很多个线程来并发地计算所有node是否满足所有的条件,最后将满足条件的node list 返回。
当然对于条件计算是有一定顺序的,通常跟node相关的规则会先计算,这样就可以避免一些没有必要的规则校验,比如在一个内存严重不足的node上面计算pod的affinity是没有意义的。
(2)Priorites(优选策略)
当调度器通过Predicates拿到可调度的node list之后,我们则需要通过进一步地对比得到最适合调度的node。
kubernetes用一组优先级函数处理每一个待选的主机。每一个优先级函数priorityFunc 会返回一个0-10的分数,分数越高表示主机越"好",同时每一个函数也会对应一个表示权重的值weight。最终主机的得分用以下公式计算得出:
finalScoreNode = (weight1 * priorityFunc1) + (weight2 * priorityFunc2) + ... + (weightn * priorityFuncn)
同样,Priorites策略也在随着版本演进而丰富,v1.0版本仅支持3个策略,v1.7支持10个策略,每项策略都有对应权重,最终根据权重计算节点总分。目前可用的Priorites策略有:


三、亲和性
1、节点Node亲和性
pod.spec.nodeAffinity节点亲和性
preferredDuringSchedulingIgnoredDuringExecution:软性策略。希望达到的目标,不是硬性规定。
requiredDuringSchedulingIgnoredDuringExecution:硬性策略。必须要达到的目标,硬性规定。
(1)软性策略
亲和性之软策略,简而言之就是如果node节点有与pod的软策略匹配的,那么就会调度Pod到对应node节点上,如果node节点没有与之适配的软策略,那么软策略就形同虚设。
vim 3.pod.yaml
我们在pod创建中,设置亲和性标签,以此去匹配有对应亲和性标签的node节点。
apiVersion: v1 # API版本,指定Kubernetes资源的版本,此处为v1
kind: Pod # 资源类型,此处是Pod
metadata: # Pod的元数据部分
name: node-preferredDuringSchedulingIgnoredDuringExecution # Pod的名称
labels: # Pod的标签部分
app: node-preferredDuringSchedulingIgnoredDuringExecution # 自定义标签,用于选择或分类Pod
spec: # Pod的规格定义
containers: # 定义Pod中的容器列表
- name: node-affinity-preferred-pod # 容器的名称
image: nginx # 容器的镜像,此处为nginx
imagePullPolicy: IfNotPresent # 镜像拉取策略,当本地不存在时才拉取镜像
affinity: # Pod的亲和性规则
nodeAffinity: # 节点亲和性规则
preferredDuringSchedulingIgnoredDuringExecution: # 软策略,在调度时生效,忽略执行时的约束
- weight: 1 # 权重值,用于表示优先级,范围为1到100
preference: # 软策略偏好条件
matchExpressions: # 匹配表达式
- key: domain # node节点的标签键,此处为"domain"
operator: In # 操作符,表示值在给定的列表中
values: # 列表,表示标签值的匹配项
- flyinlinux # 匹配node节点的标签值
1)node无亲和性标签可以匹配
查看node节点标签:

显然我们node节点标签里面没有上面软性策略设定的key和value的。所以调度结果上看pod都可以调度到node节点上。

我们写一个死循环不断杀死pod,创建pod,看pod是否轮询创建在不同node节点上。
while true ;
do
kubectl delete -f 3.pod.yaml
kubectl create -f 3.pod.yaml
kubectl get pod -o wide
sleep 1
done
结果是都调度到了node02节点上。原因可能是node02经过预选和优选之后的分数更高。比如node02节点的CPU,内存等资源消耗较少。

我们可以对node02增加负载,降低其优选阶段的分值。从而使得Pod能够调度到node01节点上运行。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: node1
name: node1
spec:
replicas: 10
selector:
matchLabels:
app: node1
template:
metadata:
labels:
app: node1
spec:
nodeName: k8s-node02 #将Pod调度到特定节点
containers:
- image: nginx:v1
name: nginx
通过对node02节点增加负载之后(起了10个pod在上面跑),node-affinity-preferred的pod就调度到了node01节点上了。

从结果上看,pod在循环销毁创建中,并没有在node01和node02节点上进行轮循,可能的原因还是两个节点的打分并不一致,优选算法只会选择打分最高的作为调度节点。如果需要实现轮循的话,需要确保两个节点打分一样,这样优选算法才会随机选择。
2)node有亲和性标签可以匹配
设置节点亲和标签。给node02增加domain=flyinlinux的标签。让pod可以使得按照亲和性标签,调度到node02节点上。

我们删除pod,并重新创建pod,以此让调度器可以重新进行调度。结果显示,pod调度到了有domain=flyinlinux标签的node02节点上。

当然,我们还是需要认识到,虽然node02有pod要求的亲和性标签,但是在有些情况下,不一定调度到node02上,如果预选阶段,node02就不合格,那么也不行。比如node02的剩余资源太少了,不满足pod要求,那么就过不了预选。
什么情况下会使用软策略呢,比如我们的程序希望使用SSD固态硬盘,但是实际工作节点上可能有固态硬盘,也会有机械硬盘,这个时候,我们可以设置软策略使用SSD固态硬盘。这样的话,在实际调度上,如果有node节点是固态硬盘的话,就会尽量调度到对应的node节点上,如果没有,就调度到机械硬盘上。
(2)节点硬性策略
硬性策略就是必须要达到,达不到要求的话,就会处于pending状态。
####节点亲和性-硬策略
apiVersion: v1 # 定义 API 版本,v1 表示核心组中的资源。
kind: Pod # 资源类型为 Pod。
metadata: # 元数据部分,定义 Pod 的基本信息。
name: node-affinity-required # Pod 的名称,必须唯一。
labels: # 给 Pod 设置的标签,用于分类或选择。
app: node-affinity-required # 标签的键值对,key 是 "app",value 是 "pod-aff"。
spec: # Pod 的规格定义。
containers: # 定义 Pod 中包含的容器列表。
- name: myapp # 容器的名称。
image: nginx:v1 # 使用的镜像。
imagePullPolicy: IfNotPresent # 拉取镜像的策略,如果本地没有才拉取。
affinity: # 定义调度的亲和性规则。
nodeAffinity: # 定义 node节点 的亲和性规则。
requiredDuringSchedulingIgnoredDuringExecution: # 调度时必须满足的亲和性规则。硬策略。
nodeSelectorTerms: # 用于匹配目标 Pod 的标签。
- matchExpressions: # 匹配表达式,用于更复杂的匹配逻辑。
- key: disktype # 匹配的标签键。
operator: In # 操作符,表示标签值必须在下面的值列表中。
values: # 标签值列表。
- ssd # 要匹配的标签值
1)node无亲和性标签可以匹配
创建node-affinity-required的pod后,发现状态为pending,原因就是目前我们节点上没有disktype=ssd的标签。

2)node有亲和性标签可以匹配
我们在节点node01上增加disktype=ssd的标签,在节点node02上增加disktype=hdd的标签。

删除pod,重新创建后,调度到了符合条件的node01节点上了。

注意,虽然这两个操作和上面的软策略类似,但是硬策略下,如果我们给node01加压,pod也不会调度到node02节点上的。因为设置了硬性要求,不能不遵守。
2、Pod亲和性
Pod也可以设置亲和性条件。Pod的亲和性和反亲和性是针对的Pod的标签,节点的亲和性是针对的节点node的标签。原理没有本质区别,只是对象不一样。
pod.spec.podAffinity pod亲和性
preferredDuringSchedulingIgnoredDuringExecution:软性策略。希望达到的目标,不是硬性规定。
requiredDuringSchedulingIgnoredDuringExecution:硬性策略。必须要达到的目标,硬性规定。
Pod的匹配流程是先匹配符合要求的Pod,然后再匹配看哪些节点符合拓扑域要求
topologykey(拓扑域):也是一种标签,但是匹配的是标签的key。同一个key-value算同一个拓扑域。拓扑域可以是主机,也可以是一台机架,也可以是一个机房,也可以是一个数据中心。
(1)软性策略
####pod亲和性-软策略
apiVersion: v1 # 指定 API 的版本为 v1。
kind: Pod # 声明 Kubernetes 资源的类型为 Pod。
metadata:
name: pod-aff-prefer # 定义 Pod 的名称。
labels: # 定义 Pod 的标签。
app: pod-aff # 添加标签键值对,用于标识该 Pod。
spec:
containers:
- name: myapp # 容器的名称。
image: nginx:v1 # 使用的镜像为 nginx。
imagePullPolicy: IfNotPresent # 镜像拉取策略,若本地已存在镜像则不重新拉取。
affinity: # 定义亲和性规则。
podAffinity: # Pod 亲和性规则,要求与特定 Pod 靠近。
preferredDuringSchedulingIgnoredDuringExecution: # 定义优先级规则,非强制。
- weight: 1 # 亲和性的权重,值越大优先级越高。
podAffinityTerm: # 定义亲和性的具体匹配条件。
labelSelector: # 标签选择器,用于匹配目标 Pod。
matchExpressions: # 表达式形式匹配标签。
- key: app # 目标 Pod 必须包含的标签键。
operator: In # 匹配运算符,表示标签值在指定值列表中。
values: # 标签的值列表。
- pod-1 # 匹配的目标 Pod 的标签值。
topologyKey: kubernetes.io/hostname # 拓扑域键,表示亲和性作用的拓扑域。

结果:发现pod被调度到了node02节点上。node02节点上并没有亲和性设定的软性策略条件的。这其实就是软性策略的含义:如果没有匹配的也不强求。
接下来,我们在某个节点上以及对应的某个pod上配齐pod的亲和性软性策略的条件(有目标pod的标签匹配;有node节点的标签。两者同时满足),检查我们的pod能否调度到该节点上。
我们创建一个test-pod的pod。其标签为app=pod-1。这个就符合了pod的亲和性软性策略的其中之一的条件------可匹配目标pod的标签。
apiVersion: v1
kind: Pod
metadata:
name: test-pod
labels:
app: pod-1
spec:
containers:
- name: test-container
image: nginx:v1
imagePullPolicy: IfNotPresent
创建该pod,查看pod标签有app=pod-1。

该pod调度到了node02节点上,我们看下node02节点上是否有kubernetes.io/hostname的键名。

然后,我们删除刚创建的pod-aff-prefer的pod,重新创建,通过调度结果发现pod调度到了node02节点上。

由于test-pod本身是调度在node02上,如果将其改成node01节点上,pod-aff-prefer的pod应该会调度到node01节点上。
(2)硬性策略
####pod亲和性-硬性策略
apiVersion: v1 # 定义 API 版本,v1 表示核心组中的资源。
kind: Pod # 资源类型为 Pod。
metadata: # 元数据部分,定义 Pod 的基本信息。
name: pod-aff-req # Pod 的名称,必须唯一。
labels: # 给 Pod 设置的标签,用于分类或选择。
app: pod-aff # 标签的键值对,key 是 "app",value 是 "pod-aff"。
spec: # Pod 的规格定义。
containers: # 定义 Pod 中包含的容器列表。
- name: myapp # 容器的名称。
image: nginx:v1 # 使用的镜像。
imagePullPolicy: IfNotPresent # 拉取镜像的策略,如果本地没有才拉取。
affinity: # 定义调度的亲和性规则。
podAffinity: # 定义 Pod 的亲和性规则。
requiredDuringSchedulingIgnoredDuringExecution: # 调度时必须满足的亲和性规则。
- labelSelector: # 用于匹配目标 Pod 的标签。
matchExpressions: # 匹配表达式,用于更复杂的匹配逻辑。
- key: app # 匹配的标签键。
operator: In # 操作符,表示标签值必须在下面的值列表中。
values: # 标签值列表。
- pod-1 # 要匹配的值,这里是 "pod-1"。
topologyKey: kubernetes.io/hostname # 拓扑键,表示调度在相同主机名的节点上。
创建pod,发现处于pending状态。原因就是因为我们的pod发现硬策略条件没法达成,无法调度起来。

那我们准备下它所需要的条件:
创建一个具有app=pod-1的pod;在node节点上有kubernetes.io/hostname这样的键名。
条件具备之后,发现pod-aff-req自动被调度起来了,并且是和test-pod同一个node节点上。
3、Pod反亲和性
pod.spec.affinity.podAntiAffinity pod反亲和性
Pod反亲和性和亲和性相反,就是让新建Pod不要和具备设定条件的Pod在一个节点上。
preferredDuringSchedulingIgnoredDuringExecution:软性策略。希望不要在一起。
requiredDuringSchedulingIgnoredDuringExecution:硬性策略。必须不要在一起。
(1)软性策略
创建一个test-pod,标签是app=pod-2的pod。实验目标就是验证后面新建的pod在match表达式中选择pod为app=pod-2,新建pod是否能达成希望不要调度在之前的pod节点上。
apiVersion: v1
kind: Pod
metadata:
name: test-pod
labels:
app: pod-2
spec:
containers:
- name: test-container
image: nginx:v1
imagePullPolicy: IfNotPresent
创建Pod,检查结果。
####pod反亲和性-软性策略
apiVersion: v1 # 定义 API 版本,v1 表示核心组中的资源。
kind: Pod # 资源类型为 Pod。
metadata:
name: pod-antiaff-prefer # Pod 的名称,必须唯一。
labels:
app: pod-aff # 标签的键值对,key 是 "app",value 是 "pod-aff"。
spec:
containers:
- name: myapp # 容器名称。
image: nginx:v1 # 容器镜像
imagePullPolicy: IfNotPresent # 拉取策略:仅当本地没有镜像时才拉取。
affinity:
podAntiAffinity: # 定义 Pod 的反亲和性规则。
preferredDuringSchedulingIgnoredDuringExecution: # 定义软调度规则。
- weight: 1 # 软规则的权重,值在 1-100 之间。
podAffinityTerm: # 定义亲和条件。
labelSelector: # 用于匹配 Pod 标签的选择器。
matchExpressions:
- key: app # 匹配的标签键。
operator: In # 操作符,表示标签值必须包含在列表中。
values:
- pod-2 # 要匹配的值,这里是 "pod-2"。
topologyKey: kubernetes.io/hostname # 拓扑键,表示调度到不同主机节点上。
我们查看下结果,发现pod-antiaff-prefer与test-pod调度在不同的节点上。

如果我们进一步实验,把node01和node02上都部署标签app=pod-2的pod的话,那么pod-antiaff-prefer的pod是否还能被调度?按照软性策略应该还是能被调度的。因为软性策略不是强制硬性,虽然两个节点上都有烦的pod,但是还是要被调度和运行的。
再创建一个新的test-pod2的pod,标签还是app=pod-2。将其固定调度在node01节点上,这样两个节点上都有pod-antiaff-prefer不喜欢的pod。
apiVersion: v1
kind: Pod
metadata:
name: test-pod2
labels:
app: pod-2
spec:
nodeName: k8s-node01
containers:
- name: test-container
image: nginx:v1
imagePullPolicy: IfNotPresent
结果上,pod-antiaff-prefer还是能够被调度的,虽然两个node上都有不想亲和的pod存在。
(2)硬性策略
####pod反亲和性-硬性策略
apiVersion: v1
kind: Pod
metadata:
name: pod-antiaff-req
labels:
app: pod-antiaff-req
spec:
containers:
- name: pod-aff-req-c
image: nginx:v1
imagePullPolicy: IfNotPresent
affinity:
podAntiAffinity: # 定义 Pod 的反亲和性规则。
requiredDuringSchedulingIgnoredDuringExecution: # 定义硬调度规则。
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-2
topologyKey: kubernetes.io/hostname
由于我们两个node节点上都存在 pod-antiaff-req不喜欢的pod,结果就是 pod-antiaff-req处于pending状态。

4、亲和性总结

四、污点和容忍
节点亲和性 是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点 (这可能出于一种偏好,也可能是硬性要求)。
污点(Taint) 则相反------它使节点能够排斥一类特定的 Pod。
容忍度(Toleration) 是应用于 Pod 上的。容忍度允许调度器调度带有对应污点的 Pod。 容忍度允许调度但并不保证调度:作为其功能的一部分, 调度器也会评估其他参数。
污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod, 是不会被该节点接受的。
node节点以及Pod的亲和性可以让Pod调度到目标节点上,Pod的反亲和性可以让Pod不被调度到某些节点上。为什么还需要污点和容忍度配合来避免Pod被分配到不合适的节点上?
个人理解是由于没有node节点的反亲和性,以及软策略。污点和容忍度配合大致就是达到node反亲和性软策略的目的。
比如一个人相亲,他说他有睡觉打呼噜的特点,其中有一个女的,她完全不能接受这个特点,那么两者是不会走一起的,另外一个女的,她对于这个特点能够容忍,那么他们两个是有可能走到一起的。
1、污点的组成

污点设置的表达式:
key=value: effect #value不为空
key: effect #value为空
其中:NoExecut这样的effect,在实际工作中会有不少场景需要。比如我们节点出现诸如损坏、系统需要更新、其他迭代等,需要把当前的pod先驱逐出去,然后将物理机从k8s分离开来,将物理机修复好之后,我们再join进入k8s集群中。不是直接对该物理节点拔网线或者电源促使k8s集群在其他物理节点上重建pod,而是上面这种通过调度的方式驱逐,系统会更加稳定,业务处理更加可靠。
我们看下我们之前的Master01节点为什么一直没有pod被调度其上运行。

master01节点上有一个污点,且注明了是NoSchedule。这个就表示k8s不会将Pod调度到这个具有该污点的节点上的。如果想让master01节点能够被调度pod,有两种方式:
1、去掉污点或者改变污点的effect为PreferNoSchedule。
2、Pod注明能够容忍这个污点。
下面我们实验下,如何通过2种方法(2节-3节)让pod能够调度到master01节点上。
2、去掉污点让Master节点可被调度
我们将master01节点上的污点去掉,看pod能否调度到master01节点上。
(1)去掉master01节点的污点
kubectl taint node k8s-master01 node-role.kubernetes.io/control-plane=:NoSchedule-
注意:我们master01的污点没有设置value的,只有key和effect。虽然没有value,但是在key和value中间要加上=符合;另外去掉污点须在命令最后加-的符号。

(2)使用daemonSet控制器部署Pod
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: test-contorller
spec:
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- name: test-container
image: nginx:v1
imagePullPolicy: IfNotPresent
创建daemonset控制器后,查看pod部署情况,发现在master01节点上部署了pod,三个节点都被分配了Pod。

3、容忍
容忍度是Pod规格的一部分,用于指定Pod愿意忽略哪些容忍。如果一个Pod的spec中定义了与节点上的一个或多个容忍相匹配的容忍度,则该Pod可以被调度到该节点上。
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
#允许 Pod 容忍一个特定的污点,从而可以调度到带有该污点的节点上。
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoExecute"
tolerationSeconds: 3600
#允许 Pod 容忍一个带有 NoExecute 效果的污点(Taint),并设置了一个时限,表示如果在这个时间内没有移除该污点,Pod 会被驱逐
特殊类型
tolerations:
- key: "key"
operator: "Exists"
effect: "NoSchedule"
#当不指定污点value时,兼容所有value。只要key存在,策略是NoSchedule,表示可以容忍所有具有这样的key的污点
tolerations:
- operator: "Exists"
#容忍所有的污点
tolerations:
- key: "key"
operator: "Exists"
#可以容忍有这样key的污点
在有多个master存在时,防止资源浪费,可以如下设置
kubectl taint nodes nodeName node-role.kubernetes.io/master=:PreferNoSchedule
#尽可能不被调度,但是在node节点压力大撑不住时候可以调度
我们在master01节点上添加尽可能不调度的污点。

然后创建deployment控制器,并将其副本设置为100,检查看是否有pod被调度到了master01节点上。
[root@k8s-master01 8]# kubectl create deployment myapp --image=nginx:v1 --replicas=100
deployment.apps/myapp created
pod副本数增加到300时,发现部分pod被调度到了master01节点上。

4、实际案例
我们有AI的Pod需要被调度到有GPU的节点之上,不要被调度到没有GPU的节点之上。这时候需要有两个条件:
1、在GPU节点上增加污点,key为computeengine,value为gpu,并且effect是Noschedule,含义是不会把pod调度在这样的污点上,这样普通pod都不会被调度到node02节点;
2、在Pod创建时,增加节点的亲和性,并且是硬性策略,策略条件就是匹配具有computeengine=gpu的节点。

五、固定节点调度
1、指定节点调度

回顾下本文一开始讲到的Scheduler调度器监听kube-apiserver 进程去发现 PodSpec.NodeName 为空的 Pod。其实就是如果PodSpec.Nodename不为空,即在我们的资源清单文件中指明了使用那个node节点的话,Scheduler调度器就不会使用预选算法以及优选算法了,直接就将Pod调度到指定节点上去。所以指定节点调度是所有调度里面最高最强调度了。其他比如亲和性、污点和容忍都要靠边站。
首先我们把master01节点之前添加的污点 PreferNoSchedule去掉。只剩下NoSchedule。这样所有pod都不会被调度到master01节点上。

然后我们创建deployment,设定pod调度到master01节点上,检查是否正确。
vim 11.deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-test
spec:
replicas: 10
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
nodeName: k8s-master01
containers:
- name: myweb
image: nginx:v1
imagePullPolicy: IfNotPresent

结果deployment创建的pod全部都被调度到了master01节点上。
2、指定节点标签调度
NodeSelector 是 Kubernetes 中的一个概念,它允许用户通过标签选择器(label selectors)来指定 Pod 应该被调度到哪些节点上运行。这一机制为Pod的调度提供了灵活性,使得用户可以根据节点的标签属性来控制Pod的分布。
怎么用NodeSelector?
-
使用NodeSelector之前,首先需要在目标节点上设置标签标签是键值对,用于描述节点的特征。
-
在Pod的定义文件(YAML或JSON)中,可以通过.spec.nodeSelector字段来设置NodeSelector。
-
Pod将会被调度到至少有一个标签满足NodeSelector中所有指定条件的节点上
####指定节点标签调度
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-test
spec:
replicas: 10
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
nodeSelector:
testkey: testvalue #选择node节点上具有标签key-value
containers:
- name: myweb
image: nginx:v1
imagePullPolicy: IfNotPresent
一开始,我们没有在node节点上添加testkey=testvalue 的标签,所以deployment控制器创建的10个pod副本都处于pending状态,因为找不到这样标签的node节点。

然后,我们对master01节点上增加testkey=testvalue的标签。
由于master01节点上有污点且是NoSchedule(这个是是仅次于指定节点调度),即使目前指定节点标签调度优先级也低于这个,所以pod依然处于pending状态。

最后,我们对node01节点上增加estkey=testvalue的标签。由于node01节点上没有设置污点,所以pod应该被调度到node01节点上。
