第八章 Kubernetes 调度器

目录

一、Scheduler调度器简介

[二、kubernetes scheduler 基本原理](#二、kubernetes scheduler 基本原理)

1、scheduler在整体流程中所处的位置

2、scheduler调度器基本原理

3、调度算法

(1)Predicates(预选策略)

一般策略

Volume相关策略

[Node相关策略 ​编辑](#Node相关策略 编辑)

Pod相关策略

(2)Priorites(优选策略)

三、亲和性

1、节点Node亲和性

(1)软性策略

1)node无亲和性标签可以匹配

2)node有亲和性标签可以匹配

(2)节点硬性策略

1)node无亲和性标签可以匹配

2)node有亲和性标签可以匹配

2、Pod亲和性

(1)软性策略

(2)硬性策略

3、Pod反亲和性

(1)软性策略

(2)硬性策略

4、亲和性总结

四、污点和容忍

1、污点的组成

2、去掉污点让Master节点可被调度

(1)去掉master01节点的污点

(2)使用daemonSet控制器部署Pod

3、容忍

4、实际案例

五、固定节点调度

1、指定节点调度

2、指定节点标签调度


一、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、亲和性总结

四、污点和容忍

污点和容忍度 | Kubernetes

节点亲和性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节点上。

相关推荐
努力的小T24 分钟前
使用 Docker 部署 Apache Spark 集群教程
linux·运维·服务器·docker·容器·spark·云计算
东风微鸣2 小时前
TTRSS 迁移实战
docker·云原生·kubernetes·可观察性
转身後 默落4 小时前
04.Docker 镜像命令
docker·容器·eureka
IT_张三4 小时前
Docker+Kubernetes_第一章_Docker入门
java·docker·kubernetes
企鹅侠客5 小时前
kube-proxy怎么修改ipvs规则?
云原生·kubernetes·kubelet
仇辉攻防5 小时前
【云安全】云原生- K8S 污点横移
web安全·网络安全·云原生·容器·kubernetes·k8s·安全威胁分析
人工干智能8 小时前
科普:“docker”与“docker compose”
运维·docker·容器
神马都会亿点点的毛毛张8 小时前
【Docker教程】万字长文详解Docker命令
java·运维·后端·docker·容器
Anna_Tong8 小时前
阿里云 ACS:高效、弹性、低成本的容器计算解决方案
人工智能·阿里云·容器·kubernetes·serverless·云计算·devops
魏 无羡10 小时前
k8s ssl 漏洞修复
容器·kubernetes·ssl