Kubernetes 学习21 kubernetes高级调度方式

一、概述

  1、上集讲了Scheduler在实现调度时分三步实现调度过程。首先是预选,即从所有节点中选择基本符合选择条件的节点。而后在基本符合条件的节点中使用优选函数计算他们各自的得分并加以比较。并从最高得分的节点中随机选择出一个运行pod的节点,这就是我们的控制平面中scheduler所实现负责的主要功用。同时如果在某些调度场景中我们期望能够通过自己的预设去影响他的一些调度方式,比如就是把我们的pod运行在某些特定的节点之上的时候可以通过自己的预设操作去影响他的预选和优选过程从而使得调度操作能符合我们的期望。我们也说过,此类的影响方式通常有四种。

    b、节点亲和性调度:nodeAffinity

二、调度方式

  1、节点选择器调度:nodeSelector,nodeName,对nodeName来讲如果我们期望把pod调度到某一特定节点上就在pod的spec的nodeName属性中直接给定这个nodeName的名称即可,这个对应的Pod一定只能调度到这个对应的节点上,如果有一类节点都符合我们的调度方式,那么建议使用nodeSelector,意思就是说我们可以给一段节点打上特有标签,而后我们在pod.spec的属性中使用nodeSelector去匹配这些标签。节点能够适配这些标签的就是pod可运行的节点之一,否则就不是,这种选择方式可以极大的缩小预选范围。他的用法也简单,直接给定我们选定的标签即可。也可以组合多个标签,以默认的逻辑域的方式来选择,只有所有标签都满足的pod node才会运行此pod

[root@k8smaster ~]# kubectl explain pod.spec.nodeSelector
KIND:     Pod
VERSION:  v1

FIELD:    nodeSelector <map[string]string>

DESCRIPTION:
     NodeSelector is a selector which must be true for the pod to fit on a node.
     Selector which must match a node's labels for the pod to be scheduled on
     that node. More info:
     https://kubernetes.io/docs/concepts/configuration/assign-pod-node/

    a、接下来我们简单操作一波

[root@k8smaster schedule]# cat pod-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels: #也可以在此处写上{app:myapp,tier:frontend}代替下面两行
    app: myapp #应用层级标签
    tier: frontend #架构层级标签,在分层架构中属于frontend层
  annotations:
    wohaoshuai.com/created-by: "cluster admin"
spec:
  containers: #是一个列表,具体定义方式如下
  - name: myapp
    image: ikubernetes/myapp:v1
  nodeSelector:
    disktype: ssd
[root@k8smaster schedule]# kubectl apply -f pod-demo.yaml 
pod/pod-demo created
[root@k8smaster schedule]# kubectl get pods -o wide --show-labels 
NAME       READY     STATUS    RESTARTS   AGE       IP           NODE       LABELS
pod-demo   1/1       Running   0          25s       10.244.1.3   k8snode1   app=myapp,tier=frontend
[root@k8smaster schedule]# kubectl get nodes --show-labels
NAME        STATUS    ROLES     AGE       VERSION   LABELS
k8smaster   Ready     master    103d      v1.11.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8smaster,node-role.kubernetes.io/master=
k8snode1    Ready     <none>    103d      v1.11.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=k8snode1
k8snode2    Ready     <none>    103d      v1.11.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8snode2

    b、假如我们系统上不存在拥有此标签的节点时会出现什么情况呢?可以看到pod会一直处于pending状态,因为调度是无法成功的,这也就意味着nodeSelector是一种强约束,只要不满足第一个条件在预选关就无法通过就不要说优选了。

[root@k8smaster schedule]# cat pod-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels: #也可以在此处写上{app:myapp,tier:frontend}代替下面两行
    app: myapp #应用层级标签
    tier: frontend #架构层级标签,在分层架构中属于frontend层
  annotations:
    wohaoshuai.com/created-by: "cluster admin"
spec:
  containers: #是一个列表,具体定义方式如下
  - name: myapp
    image: ikubernetes/myapp:v1
  nodeSelector:
    disktype: hirddisk
[root@k8smaster schedule]# kubectl apply -f pod-demo.yaml 
pod/pod-demo created
[root@k8smaster schedule]# kubectl get pods -o wide
NAME       READY     STATUS    RESTARTS   AGE       IP        NODE
pod-demo   0/1       Pending   0          21s       <none>    <none>
[root@k8smaster schedule]# kubectl describe pods pod-demo
Name:               pod-demo
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               <none>
Labels:             app=myapp
                    tier=frontend
Annotations:        kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{"wohaoshuai.com/created-by":"cluster admin"},"labels":{"app":
"myapp","tier":"frontend"},"nam...                    wohaoshuai.com/created-by=cluster admin
Status:             Pending
IP:                 
Containers:
  myapp:
    Image:        ikubernetes/myapp:v1
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-jvtl7 (ro)
Conditions:
  Type           Status
  PodScheduled   False 
Volumes:
  default-token-jvtl7:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-jvtl7
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  disktype=hirddisk
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  2s (x16 over 45s)  default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.

      我们现在手动给一节点打上该标签他就会立即生效了

[root@k8smaster schedule]# kubectl label nodes k8snode2 disktype=hirddisk --overwrite
node/k8snode2 labeled
[root@k8smaster schedule]# kubectl get pods -o wide
NAME       READY     STATUS    RESTARTS   AGE       IP           NODE
pod-demo   1/1       Running   0          6m        10.244.2.3   k8snode2

  2、节点亲和性调度nodeAffinity,在我们pod.spec内嵌有一个属性就叫Affinity,他的值是一个对象,这个对象中我们有下面三种,之所以叫affinity是因为其内嵌的所谓的节点亲和性,pod亲和性,和Pod反亲和性都在这儿定义,此外我们此处说的是nodeAffinity

[root@k8smaster schedule]# kubectl explain pods.spec.affinity
KIND:     Pod
VERSION:  v1

RESOURCE: affinity <Object>

DESCRIPTION:
     If specified, the pod's scheduling constraints

     Affinity is a group of affinity scheduling rules.

FIELDS:
   nodeAffinity    <Object>
     Describes node affinity scheduling rules for the pod.

   podAffinity    <Object>
     Describes pod affinity scheduling rules (e.g. co-locate this pod in the
     same node, zone, etc. as some other pod(s)).

   podAntiAffinity    <Object>
     Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod
     in the same node, zone, etc. as some other pod(s)).

 

[root@k8smaster ~]# kubectl explain pods.spec.affinity.nodeAffinity
KIND:     Pod
VERSION:  v1

RESOURCE: nodeAffinity <Object>

DESCRIPTION:
     Describes node affinity scheduling rules for the pod.

     Node affinity is a group of node affinity scheduling rules.

FIELDS:
   preferredDuringSchedulingIgnoredDuringExecution    <[]Object> #尽量满足条件。否则只能勉强接受找其他节点
     The scheduler will prefer to schedule pods to nodes that satisfy the
     affinity expressions specified by this field, but it may choose a node that
     violates one or more of the expressions. The node that is most preferred is
     the one with the greatest sum of weights, i.e. for each node that meets all
     of the scheduling requirements (resource request, requiredDuringScheduling
     affinity expressions, etc.), compute a sum by iterating through the
     elements of this field and adding "weight" to the sum if the node matches
     the corresponding matchExpressions; the node(s) with the highest sum are
     the most preferred.

   requiredDuringSchedulingIgnoredDuringExecution    <Object> #需要满足条件,和nodeselector一样,没有任何节点满足这儿的亲和定义那么他一定不会去运行,就处于pending状态
     If the affinity requirements specified by this field are not met at
     scheduling time, the pod will not be scheduled onto the node. If the
     affinity requirements specified by this field cease to be met at some point
     during pod execution (e.g. due to an update), the system may or may not try
     to eventually evict the pod from its node.

    a、首先我们创建一个yaml文件

[root@k8smaster schedule]# cat pod-nodeaffinity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-node-affinity-demo
  namespace: default
  labels: #也可以在此处写上{app:myapp,tier:frontend}代替下面两行
    app: myapp #应用层级标签
    tier: frontend #架构层级标签,在分层架构中属于frontend层
  annotations:
    wohaoshuai.com/created-by: "cluster admin"
spec:
  containers: #是一个列表,具体定义方式如下
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    nodeAffinity: 
      requiredDuringSchedulingIgnoredDuringExecution: #必须满足条件
        nodeSelectorTerms: #节点选择项
        - matchExpressions: #匹配表达式
          - key: zone       #如果我们当前节点中有任何一节点拥有此标签及他的值为foo和bar即可以把这个pod调度到此节点上来。
            operator: In
            values: 
            - foo
            - bar
[root@k8smaster schedule]# kubectl apply -f pod-nodeaffinity-demo.yaml 
pod/pod-node-affinity-demo created
[root@k8smaster schedule]# kubectl get pods #可以看到处于pending状态,因为这里定义的是硬亲和性所以他就被pending了
NAME                     READY     STATUS    RESTARTS   AGE
pod-node-affinity-demo   0/1       Pending   0          7s

    b、然后我们将硬亲和性改为软亲和性,即把必须满足条件改为尽量满足条件,可以看到pod可以运行

[root@k8smaster schedule]# cat pod-nodeaffinity-demo-2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-node-affinity-demo-2
  namespace: default
  labels: #也可以在此处写上{app:myapp,tier:frontend}代替下面两行
    app: myapp #应用层级标签
    tier: frontend #架构层级标签,在分层架构中属于frontend层
  annotations:
    wohaoshuai.com/created-by: "cluster admin"
spec:
  containers: #是一个列表,具体定义方式如下
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    nodeAffinity: 
      preferredDuringSchedulingIgnoredDuringExecution: #尽量满足条件
      - preference: #节点选择项
          matchExpressions: #匹配表达式
          - key: zone       #如果我们当前节点中有任何一节点拥有此标签及他的值为foo和bar即可以把这个pod调度到此节点上来。
            operator: In
            values: 
            - foo
            - bar
        weight: 60
[root@k8smaster schedule]# kubectl apply -f pod-nodeaffinity-demo-2.yaml 
pod/pod-node-affinity-demo-2 created
[root@k8smaster schedule]# kubectl get pods
NAME                       READY     STATUS    RESTARTS   AGE
pod-node-affinity-demo-2   1/1       Running   0          42s

  3、pod亲和性调度,pod自身的亲和性调度我们在定义时他有两种表现形式,第一叫podAffinity,即Pod与pod更倾向于运行在一起。一般是为了高效通信的需求我们偶尔需要把这些pod对象组织在相近的位置,比如在同一节点,同一机架,同一区域地区等等。这样pod与pod之间更方便通信。比如我们组织一个nmt的分层结构,那么ntm能够在同一个机架上运行那么彼此之间通信技术就要好得多。如果说机房一千台主机,整个集群的主机在不同的机房不同的地域,把nginx部署在北京机房,把mysql 和tomcat部署在南京机房,两者之间通信就有很大问题。这种情况下我们就需要pod之间的亲和性,现在部署两套nmt,第一套和第二套之间是反亲和的。不在同一位置可能更好一点。所以这就是pod的亲和性。第二叫podAntiAffinity,即pod的反亲和性。

    a、pod的亲和性方式定义比较独特,一般而言我们通过节点亲和性来定义pod对象间的亲和性和反亲和性。一般来讲此时我们用户必须为pod指定可运行的节点标签,所以我们说使用节点的亲和性也能定义pod的亲和性。我们目的就是让一组Pod能运行在一起或不能运行在一起,使用节点亲和性就能达到这个目的。比如nmt三个pod他们使用同样的节点亲和性标签,然后我们在节点上打标签的时候就确保他们三个选择的标签的节点就是在同一位置,他们就在同一机架上,也能做到这个需求。既然如此我们为什么还要去定义pod亲和性呢?我们用节点亲和性就能达到pod亲和或反亲和的目的。因为我们使用节点亲和性去限制pod他不是一种较优的选择方式,毕竟我们必须实现确保节点和Pod双方向得完全匹配到。而且还要精心布局节点是被如何打标签的。所以这种方式使用起来可能难度较大,而较理想的实现方式无非是允许调度器把第一个pod随机选择一个位置,但是第二个Pod就要根据第一个pod所在的位置进行调度。而不是根据节点事先布局好进行调度。

    b、如果我们有一组pod nmt,我们先去调度了n,那么n在的位置一定是m和t在的位置,这就是亲和性。n在的位置一定不能是m和t所在的位置就表示反亲和性。所以我们以第一个pod所在的节点和现存的很多个pod所在的节点作为评判后续Pod能否到他所在的这个节点的方式就叫pod亲和性。那么这样就有问题了,我怎么去判定哪些节点是相同位置的,哪些节点是不同位置的。虽然听起来很简单但是我们还是要去组织pod标签的,因为毕竟从某种角度上来讲如果没有节点标签的话,比如我们有4个节点在集群正常运行,那么当第一个pod运行在第一个节点上之后如何去确保第二三四个节点是否能运行与第一个pod亲和的pod,刚刚说过,nmt是亲和的话n被放在第一个节点上那么m就应该也放在第一个节点上或相近的节点上。所以我们在定义Pod亲和性时必须有一个判定前提就是pod和pod要在同一个位置和不在同一个位置的判定标准是什么。那就是什么叫同一位置什么叫不同位置这就是关键。所以事先要有这么一种判断基准和前提。

    c、比如我们以节点名称判断他们是否是同一位置那么每个节点都处于不同位置。因此每一个节点就是一个不同的位置。如果把第一个pod运行在节点一上以这种方式为分类标准那么以nt为例,n在第一个节点t也要在节点一上他们才认为在同一位置才满足亲和性。接下来我们换一种判定标准,比如我们判定标准是否是同一位置是节点上有标签,节点上有标签rack值相同的就是同一位置。比如node1和node2都有标签rack=rack1,node3和node4 都有标签rack=rack2,那么我们把n放在node1中,那么node1和node2都表示亲和的。因此我们必须有标准判定哪些节点是处于同一位置接下来我们调度才能有章可循。所以将来调度时定义的时候可以把同一机架上的服务器都打上这么一个标签叫rack,而且给每个机架和机柜都编上名字。rack的值就叫这个名字。

    d、还有一种调度,上述四个节点加入在两个不同机架上,但他们四个有可能在同一排机柜或同一机房,因此如果我们按他们所在的排这样的标签 row来定义,一共有五排,rack1和rack2刚好在第一排,这四个节点的第二个标签就为row,并且都为row1即,row=row1。这个时候会发现如果第一个pod放在node1上,第二个pod在其它三个节点都可以了。因为我们是基于第二个标签row来判定是否属于同一位置。值相同的都是同一位置。

    e、pod亲和性一样的也有硬亲和性和软亲和性,用起来和刚刚的node的亲和性一样的让人头疼

[root@k8smaster schedule]# kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution
KIND:     Pod
VERSION:  v1

RESOURCE: requiredDuringSchedulingIgnoredDuringExecution <[]Object>

DESCRIPTION:
     If the affinity requirements specified by this field are not met at
     scheduling time, the pod will not be scheduled onto the node. If the
     affinity requirements specified by this field cease to be met at some point
     during pod execution (e.g. due to a pod label update), the system may or
     may not try to eventually evict the pod from its node. When there are
     multiple elements, the lists of nodes corresponding to each podAffinityTerm
     are intersected, i.e. all terms must be satisfied.

     Defines a set of pods (namely those matching the labelSelector relative to
     the given namespace(s)) that this pod should be co-located (affinity) or
     not co-located (anti-affinity) with, where co-located is defined as running
     on a node whose value of the label with key <topologyKey> matches that of
     any node on which a pod of the set of pods is running

FIELDS:
   labelSelector    <Object> #判定pod和哪个pod亲和,因此其用来选定一组资源,即拥有这个label的一组pod
     A label query over a set of resources, in this case pods.

   namespaces    <[]string> #上面所选的资源是属于哪个名称空间的,指明这组标签选择器只能是哪个或哪组名称空间中的,如果不指定的话就是labelSelector所匹配的pod只能是当前创建的这个Pod所属的名称空间中。一般而言我们不跨名称空间去引用pod。
     namespaces specifies which namespaces the labelSelector applies to (matches
     against); null or empty list means "this pod's namespace"

   topologyKey    <string> -required- #位置拓扑键,即刚刚所说的根据标签来判断是否处于同一位置
     This pod should be co-located (affinity) or not co-located (anti-affinity)
     with the pods matching the labelSelector in the specified namespaces, where
     co-located is defined as running on a node whose value of the label with
     key topologyKey matches that of any node on which any of the selected pods
     is running. Empty topologyKey is not allowed.

  4、接下来我们创建pod亲和性案例

    a、首先创建yaml文件,定义两个pod,我们期望第一个pod所在的地方就是第二个pod所在的地方,他们一定是在同一位置,那么怎么判定他们在同一位置呢?我们用节点名来判定的话两个节点就各自是一个位置,因此pod-first在节点一,pod-second也一定在节点一

[root@k8smaster schedule]# cat pod-required-affinity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-first
  labels: 
    app: myapp 
    tier: frontend 
spec:
  containers: 
  - name: myapp
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  labels: 
    app: db 
    tier: db
spec:
  containers: 
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","sleep 3600"]
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: 
      - labelSelector:
          matchExpressions:
          - {key: app,operator: In,values: ["myapp"]} #意思是当前这个pod要跟一个有着标签app其值为myapp的pod在一起。
        topologyKey: kubernetes.io/hostname  #只要hostname一样就认为是同一个位置,只要不一样就认为不是同一个位置。 
---
[root@k8smaster schedule]# kubectl apply -f pod-required-affinity-demo.yaml 
pod/pod-first created
pod/pod-second created
[root@k8smaster schedule]# kubectl get pods -o wide
NAME         READY     STATUS    RESTARTS   AGE       IP           NODE
pod-first    1/1       Running   0          49s       10.244.2.5   k8snode2
pod-second   1/1       Running   0          49s       10.244.2.6   k8snode2

  5、接下来我们介绍pod的反亲和性

[root@k8smaster schedule]# cat pod-required-anti-affinity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-first
  labels: 
    app: myapp 
    tier: frontend 
spec:
  containers: 
  - name: myapp
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  labels: 
    app: db 
    tier: db
spec:
  containers: 
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","sleep 3600"]
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  #一定不能在一起
      - labelSelector:
          matchExpressions:
          - {key: app,operator: In,values: ["myapp"]} #意思是当前这个pod要跟一个有着标签app其值为myapp的pod在一起。
        topologyKey: kubernetes.io/hostname  #只要hostname一样就认为是同一个位置,只要不一样就认为不是同一个位置。 
---
[root@k8smaster schedule]# kubectl apply -f pod-required-anti-affinity-demo.yaml 
pod/pod-first created
pod/pod-second created
[root@k8smaster schedule]# kubectl get pods -o wide
NAME         READY     STATUS    RESTARTS   AGE       IP           NODE
pod-first    1/1       Running   0          8s        10.244.1.5   k8snode1
pod-second   1/1       Running   0          8s        10.244.2.7   k8snode2

    现在我们将两个节点打同一个标签,值也一样,那么他们就在同一个位置了,如果第一个pod运行在这上面,那么第二个pod就不能运行在这上面了

[root@k8smaster schedule]# kubectl label nodes k8snode1 zone=foo
node/k8snode1 labeled
[root@k8smaster schedule]# kubectl label nodes k8snode2 zone=foo
node/k8snode2 labeled
[root@k8smaster schedule]# cat pod-required-anti-affinity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-first
  labels: 
    app: myapp 
    tier: frontend 
spec:
  containers: 
  - name: myapp
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  labels: 
    app: db 
    tier: db
spec:
  containers: 
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","sleep 3600"]
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: 
      - labelSelector:
          matchExpressions:
          - {key: app,operator: In,values: ["myapp"]} #意思是当前这个pod要跟一个有着标签app其值为myapp的pod在一起。
        topologyKey: zone 
---
[root@k8smaster schedule]# kubectl apply -f pod-required-anti-affinity-demo.yaml 
pod/pod-first created
pod/pod-second created
[root@k8smaster schedule]# kubectl get pods
NAME         READY     STATUS    RESTARTS   AGE
pod-first    1/1       Running   0          9s
pod-second   0/1       Pending   0          9s

  6、接下来说最后一种调度方式,污点调度。也叫容忍调度。前面介绍的两种调度方式都是让pod去选择的,节点都是被动选择的,节点无论运行在哪儿调来了就要运行。因此污点调度方式就是给了节点选择了主动权,给其打一个污点,不容忍的话就运行不上来。因此污点调度逻辑就是给了节点能选择让哪些pod能够调度的能力。从这个角度来讲其实污点就是定义了节点上的键值属性数据。键值属性数据有三类,第一类叫标签,第二类叫注解,第三类叫污点。污点通常是用在节点上的,不像前两类是所有资源对象都可以。因此污点也是一种键值属性,他主要用于让节点拒绝pod,不是说Pod想运行在哪儿就运行在哪儿。因此需要在pod对象上定义容忍度,因此容忍度又是pod对象上的第三种键值型数据。它里面给的是一个列表就表示他所能容忍的污点的列表。前面说的节点选择器和节点亲和性都是在pod上添加标签的方式来调度,不同的是此种调度方式是在节点上添加污点。而且节点亲和性是pod对象的一种属性,pod亲和性也是Pod对象的一种属性。但是污点却成了节点的属性。而后k8s分别有一个预选策略和优选函数分别完成基于污点和容忍度的调度逻辑。预选策略为PodToleratesNodeTaints。优选函数为TaintToleration。他们其实都会把他们作为选择标准中的一种标准来定义。污点定义在node.spec中

    a、首先我们来看一下污点定义。taint的effect定义的对pod排斥效果有三个:

      第一为NoSchedule:仅影响调度过程。即pod能容忍他的污点就可以调度到当前节点,但是如果后来这个节点的污点改了,即加了个新污点,使得此前调度上来的Pod不能容忍了那么这个Pod会怎么处理呢?其实他是不会被驱离的,因为他仅影响调度过程,对现存的pod不产生影响;

      第二为NoExecute:他不仅影响调度过程还影响现存pod对象,不容忍的pod对象将被驱逐

      第三为PreferNoSchedule:最好不调度上来,实在调度上来也行

[root@k8smaster schedule]# kubectl explain nodes.spec.taints
KIND:     Node
VERSION:  v1

RESOURCE: taints <[]Object>

DESCRIPTION:
     If specified, the node's taints.

     The node this Taint is attached to has the "effect" on any pod that does
     not tolerate the Taint.

FIELDS:
   effect    <string> -required- #要求必须要有,用来定义当pod不能容忍这个污点时我们要采取的行为。用来定义对pod对象的排斥效果
     Required. The effect of the taint on pods that do not tolerate the taint.
     Valid effects are NoSchedule, PreferNoSchedule and NoExecute.

   key    <string> -required-
     Required. The taint key to be applied to a node.

   timeAdded    <string>
     TimeAdded represents the time at which the taint was added. It is only
     written for NoExecute taints.

   value    <string> #可以为空
     Required. The taint value corresponding to the taint key.

    b、在pod对象上定义容忍度时还支持两种操作,即等值比较和存在性判断。即是否存在这个污点和这个污点值是什么。等值比较即我们定义了容忍度即容忍度和污点必须在 key,value和effect上必须完全匹配。存在性判断即二者的key和effect必须匹配,但是value可以使用空值,只需要判断存在与否即可。

    c、一个节点也可以变成多个污点,一个pod也可以有多个容忍度,不过二者在匹配是要遵循如下逻辑  

      首先处理每个有着与之匹配容忍度的污点,而不能匹配到的污点如果存在有一个污点使用NoScheduler标识很显然这个Pod就调度不过来了。假如我们在pod上定义了三个容忍度,在节点上定义了两个污点,那么这个pod一定能运行在这个节点上么?不一定的,你容忍的只包含对方三个污点中的一个,另外一个污点没有容忍,这三个污点中可能只有一个他两是匹配的,所以我们要检查这种匹配度到底有多高。我们去逐一检查容忍度和节点的污点能否匹配,比如第一个pod的容忍度来匹配两个节点,如果有一个匹配就说明容忍度没问题了。然后再检查第二个,第三个。每一个污点都需要被容忍,如果存在不被容忍那就要看这个污点的条件了,如果效果是PreferNoSchedule就还是可以运行的,即我们虽然不能容忍这些污点但是这些污点的效果是PreferNoSchedule的话就还是可以调度过来的。如果是NoSchedule那么就一定是不能调度过来的。另外如果是NoExecute的话就会被驱离。

         

    d、我们可以describe master节点可以看到其上有污点,为NoSchedule

  7、我们现在来管理节点污点,管理节点污点我们可以使用命令 kubectl taint。

    a、给node1打上污点,可以看到后面调度pod时就不会被调度到这个节点上了

[root@k8smaster schedule]# kubectl taint node k8snode1 node-type=production:NoSchedule
node/k8snode1 tainted
[root@k8smaster schedule]# cat deploy-demo.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector: 
    matchLabels:
      app: myapp
      release: canary
  template:
    metadata:
      labels:
        app: myapp
        release: canary
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v2
        ports: 
        - name: http
          containerPort: 80 

[root@k8smaster schedule]# kubectl apply -f deploy-demo.yaml 
deployment.apps/myapp-deploy created
[root@k8smaster schedule]# kubectl get pods -o wide
NAME                            READY     STATUS    RESTARTS   AGE       IP            NODE
myapp-deploy-67f6f6b4dc-8t44r   1/1       Running   0          58s       10.244.2.9    k8snode2
myapp-deploy-67f6f6b4dc-jgt5n   1/1       Running   0          58s       10.244.2.10   k8snode2
myapp-deploy-67f6f6b4dc-l2p7v   1/1       Running   0          58s       10.244.2.11   k8snode2

    b、我们现在给node2打上污点,并设置为NoExecute,可以看到pod在两个节点上都没法运行了

[root@k8smaster schedule]# kubectl taint node k8snode2 node-type=dev:NoExecute
node/k8snode2 tainted
[root@k8smaster schedule]# kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
myapp-deploy-67f6f6b4dc-8km2p   0/1       Pending   0          1m
myapp-deploy-67f6f6b4dc-gwwf4   0/1       Pending   0          1m
myapp-deploy-67f6f6b4dc-xmzfz   0/1       Pending   0          1m

    c、现在我们在pod模板中加入容忍度,可以看到pod可以running起来

[root@k8smaster schedule]# cat deploy-demo.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector: 
    matchLabels:
      app: myapp
      release: canary
  template:
    metadata:
      labels:
        app: myapp
        release: canary
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v2
        ports: 
        - name: http
          containerPort: 80 
      tolerations: #定义容忍度
      - key: "node-type" #节点类型
        opeartor: "Equal" #等值比较,必须精确容忍对方的污点值,如果为Exists则表示只要node-type在我们就能容忍
        value: "production" #值必须是production
        effect: "NoSchedule"  #效果为NoSchedule

[root@k8smaster schedule]# kubectl apply -f deploy-demo.yaml 
deployment.apps/myapp-deploy configured
[root@k8smaster schedule]# kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
myapp-deploy-65cc47f858-6t79t   1/1       Running   0          5s
myapp-deploy-65cc47f858-gv4sw   1/1       Running   0          7s
myapp-deploy-65cc47f858-jkrrp   1/1       Running   0          3s

    d、我们现在将等值比较改为Exists并且效果为都容忍,可以看到两个节点就都可以调度了

[root@k8smaster schedule]# cat deploy-demo.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector: 
    matchLabels:
      app: myapp
      release: canary
  template:
    metadata:
      labels:
        app: myapp
        release: canary
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v2
        ports: 
        - name: http
          containerPort: 80 
      tolerations: #定义容忍度
      - key: "node-type" #节点类型,因为两个节点都有node-type键并且operator为Exists因此两个节点都可以调度到。
        operator: "Exists" #等值比较,必须精确容忍对方的污点值,如果为Exists则表示只要node-type在我们就能容忍
        value: "" #因为是Exists所以为空值即可
        effect: ""  #为空时表示都容忍

[root@k8smaster schedule]# kubectl get pods -o wide
NAME                            READY     STATUS    RESTARTS   AGE       IP            NODE
myapp-deploy-5d9c6985f5-46rtw   1/1       Running   0          54s       10.244.2.12   k8snode2
myapp-deploy-5d9c6985f5-7nhwq   1/1       Running   0          51s       10.244.1.9    k8snode1
myapp-deploy-5d9c6985f5-p8gsg   1/1       Running   0          49s       10.244.2.13   k8snode2

 

posted @ 2019-08-20 11:51  Presley  阅读(635)  评论(0)    收藏  举报