目录

1、亲和性

1.1 nodeSelector 节点选择

1.2 亲和性和反亲和性调度

1.2.1 nodeAffinity 节点亲和性

1.2.2 podAffinity pod亲和性

1.2.3 podAntiAffinity pod反亲和性

2、污点与容忍

2.1 污点策略

2.2 容忍

2.3 取消污点

3、驱逐维护

3.1 挂维护,禁止调度

3.2 驱逐

3.3 解除维护,解除禁止调度


1、亲和性

        在默认情况下,Pod 通过默认调度策略来选择跑在哪个节点上,默认调度器算法考虑的因素有资源,负载等等,但有些场景比如

  1. 我就想把pod部署在某个node节点上,或者就想跟某个pod部署在同一node节点上;
  2. 我就不想把pod部署在某个node节点上,或者不想跟某个pod部署在同一node节点上;

这就需要用到 Kubernetes 里面的一个概念:亲和性和反亲和性

当然亲和性和反亲和性根据上面的情况有分成:

  • 节点亲和性(nodeAffinity)
  • Pod 亲和性(podAffinity)

1.1 nodeSelector 节点选择

        节点选择是如何实现的,是通过给node增加label标签,然后需要用到nodeSelector标签选择来实现。

我们可以通过下面的命令查看我们的 node 的 label:

$ kubectl get nodes --show-labels
NAME      STATUS    ROLES     AGE       VERSION   LABELS
master    Ready     master    247d      ***       ***
node02    Ready     <none>    247d      ***       ***
node03    Ready     <none>    227d      ***       ***

现在我们先给节点node02增加一个tag=unintelligible的标签,命令如下:

$ kubectl label nodes node02 tag=unintelligible
node "node02" labeled

我们可以通过上面的--show-labels参数可以查看上述标签是否生效

        当 node 打上了标签后,Pod 只需要在spec里添加nodeSelector字段,标签选择节点刚打上的 label 即可。

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: busybox
  name: busybox
spec:
  containers:
  - command:
    - sleep
    - "3600"
    image: busybox
    imagePullPolicy: Always
    name: busybox
  nodeSelector:
    tag: unintelligible

通过kubectl create -f创建pod后,可以通过kubectl get pod -o wide查看验证一下

$ kubectl get pods -o wide -l app=busybox
NAME           READY     STATUS    RESTARTS   AGE       IP             NODE
busybox       1/1       Running   164        1m        10.244.4.10   node02

也可以通过describe查看详细事件        

        需要注意的是nodeSelector是强制性的,必须在目标节点启动,如果目标节点不满足条件,比如资源不足啥的,那 Pod 就会 Pending 等待,直到可以创建

通过上面的例子我们可以感受到nodeSelector的方式比较直观,但是还够灵活,控制粒度偏大,接下来我们再和大家了解下更加灵活的方式:节点亲和性(nodeAffinity)。

1.2 亲和性和反亲和性调度

        通过上面的例子我们可以感受到nodeSelector的方式比较直观,但是还够灵活,控制粒度偏大,那么就有了更加灵活的方式:节点亲和性(nodeAffinity) Pod 亲和性(podAffinity)。

亲和性调度可以分成软策略硬策略两种方式:

  • 软策略:满足条件最好,不满足也无所谓,如果你没有满足调度要求的节点的话,pod 就会忽略这条规则,继续完成调度过程
  • 硬策略:必须满足 比较强硬,如果没有满足条件的节点的话,就不断重试直到满足条件为止。

对于亲和性和反亲和性都有这两种规则可以设置:
软策略:preferredDuringSchedulingIgnoredDuringExecution

硬策略:requiredDuringSchedulingIgnoredDuringExecution

别问我咋念,也别问我咋写,我也记不住。。。

1.2.1 nodeAffinity 节点亲和性

节点亲和性就是控制 pod 要部署在哪些主机上,不能部署在哪些主机上。

比如现在我们用一个 Deployment 来管理3个 pod 副本,现在我们来控制下这些 pod 的调度,如下

apiVersion: apps/v1
kind: Deployment
metadata:
  name: affinity
  labels:
    app: affinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: affinity
      role: test
  template:
    metadata:
      labels:
        app: affinity
        role: test
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: http
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:  # 硬
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: NotIn
                values:
                - node03
          preferredDuringSchedulingIgnoredDuringExecution:  # 软
          - weight: 1
            preference:
              matchExpressions:
              - key: tag
                operator: In
                values:
                - unintelligible

上面这个 pod 强硬要求不能运行在 node03 这个节点上,剩下的node都可以,但是如果有节点满足tag=unintelligible的话就优先调度到这个节点上。

$ kubectl create -f node-affinity.yaml
deployment.apps "affinity" created
$ kubectl get pods -l app=affinity -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP             NODE
affinity-7b4c946854-5gfln   1/1       Running   0          47s       10.244.4.121   node02
affinity-7b4c946854-l8b47   1/1       Running   0          47s       10.244.4.122   node02
affinity-7b4c946854-r86p5   1/1       Running   0          47s       10.244.4.124   node02

从结果可以看出 pod 都被部署到了 node02

另外匹配的方法有如下几种

  • In:label 值在列表中
  • NotIn:label 值不在列表中
  • Gt:label 值大于指定值
  • Lt:label 值小于指定值
  • Exists: label已经存在
  • DoesNotExist:label不存在

nodeSelectorTerms下面可以有多个matchExpressions,满足任何一个条件就可以了

matchExpressions下也可以有多个选项,必须同时满足这个matchExpressions下的所有条件

1.2.2 podAffinity pod亲和性

        pod 亲和性主要是想把pod和某个依赖的pod放在一起,而 pod 反亲和性主要想把 pod和某个pod分开。

        pod亲和性举例:比如集群是跨机房的,两个pod之间依赖度比较高,部署到了两个机房,互相访问就比较耗费网络资源,延迟比较大,这时候就需要pod亲和性

        pod反亲和性举例:比如某个pod平时占用IO比较大,另一个pod占用IO也比较大,那就别往一起凑合了,分开点吧

同样,我们来测试下 pod 的亲和性:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: affinity
  labels:
    app: affinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: affinity
      role: test
  template:
    metadata:
      labels:
        app: affinity
        role: test
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: http
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:  # 硬策略
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - busybox
            topologyKey: kubernetes.io/hostname

 pod 需要调度到没有app=busybox这个label的pod所在的node上,就是说这个node上的pod不能有app=busybox

topologyKey:个人理解为满足这个条件的标签集合,标签是 kubernetes.io/hostname

        当然也可以是其他标签,我们用当前这个例子来解释一下,比如busybox运行在node2上,node2的标签应该是kubernetes.io/hostname=node2,那后pod亲和性调度就要调度到kubernetes.io/hostname=node2的上面,pod反亲和性调度就要调度到kubernetes.io/hostname不等于node2

        在举个例子,比如一个k8s集群,北京有100个节点,每个节点都有个标签zone=beijing,上海有100个节点,每个节点都有个标签为zone=shanghai

        如果设置了pod亲和性,app  In  busybox ,且busybox的pod所在的机器标签zone=beijing,那么新的pod也一定会调度到zone=beijing上

        如果设置pod反亲和性,app  In  busybox ,且busybox的pod所在的机器标签zone=beijing,那么新的pod一定会调度到zone=shanghai上,一个都不会落到beijing

我们查看有标签app=busybox的 pod 列表:

$ kubectl get pods -o wide -l app=busybox
NAME           READY     STATUS    RESTARTS   AGE       IP             NODE
test-busybox   1/1       Running   164        1h        10.244.4.205   node02

我们看到这个 pod 运行在了 node02 的节点上面,所以按照上面的亲和性来说,上面我们部署的3个 pod 副本也应该运行在 node02 节点上:

$ kubectl get pods -o wide -l app=affinity
NAME                        READY     STATUS    RESTARTS   AGE       IP             NODE
affinity-564f9d7db9-lzzvq   1/1       Running   0          3m        10.244.4.216   node02
affinity-564f9d7db9-p79cq   1/1       Running   0          3m        10.244.4.217   node02
affinity-564f9d7db9-spfzs   1/1       Running   0          3m        10.244.4.218   node02

如果我们把 busybox 和 affinity 这个 Deployment 都删除,然后重新创建 affinity 这个资源,将不能正常调度,一直处于pending状态

1.2.3 podAntiAffinity pod反亲和性

         pod 反亲和性是反着来的,比如一个节点上运行了某个 pod,那么我们的 pod 则希望被调度到其他节点上去,同样我们把上面的 podAffinity 直接改成 podAntiAffinity

apiVersion: apps/v1
kind: Deployment
metadata:
  name: affinity
  labels:
    app: affinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: affinity
      role: test
  template:
    metadata:
      labels:
        app: affinity
        role: test
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: http
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:  # 硬策略
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - busybox
            topologyKey: kubernetes.io/hostname

2、污点与容忍

  nodeAffinity无论是硬策略还是软策略方式,都是调度 pod 到预期节点上,而Taints恰好与之相反,如果一个节点标记为 Taints ,除非 pod 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度 pod。

        污点:taints

        容忍:tolerations

2.1 污点策略

污点策略有以下选项:

  • NoSchedule:表示 pod 不会被调度到标记为 taints 的节点
  • PreferNoSchedule:NoSchedule 的软策略版本,表示尽量不调度到污点节点上去
  • NoExecute:该选项意味着一旦 Taint 生效,如该节点内正在运行的 pod 没有对应 Tolerate 设置,会直接被逐出

污点 taint 标记节点的命令如下:

$ kubectl taint nodes node02 test=node02:NoSchedule
node "node02" tainted

2.2 容忍

        上面的命名将 node02 节点标记为了污点,影响策略是 NoSchedule,只会影响新的 pod 调度,如果仍然希望某个 pod 调度到 taint 节点上,则必须在 Spec 中做出Toleration定义,才能调度到该节点

比如现在我们想要将一个 pod 调度到 node02节点

apiVersion: apps/v1
kind: Deployment
metadata:
  name: taint
  labels:
    app: taint
spec:
  replicas: 3
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: taint
  template:
    metadata:
      labels:
        app: taint
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - name: http
          containerPort: 80
      tolerations:
      - key: "test"
        operator: "Equal"
        value: "value1"
        effect: "NoSchedule"

        由于 node02节点被标记为了污点节点,所以我们这里要想 pod 能够调度到 node02 节点去,就需要增加容忍的声明,以下两种方法均可:

tolerations:
- key: "test"
  operator: "Equal"
  value: "node02"
  effect: "NoSchedule"
tolerations:
- key: "test"
  operator: "Exists"
  effect: "NoSchedule"

一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果,并且:

  • 如果 operator 是 Exists (此时容忍度不能指定 value
  • 如果 operator 是 Equal ,则它们的 value 应该相等
  • 如果 operator 不指定,则默认为Equal

另外:

  • 空的 key 如果再配合 Exists 就能匹配所有的 key 与 value,也是是能容忍所有 node 的所有 Taints
  • 空的 effect 匹配所有的 effect

然后创建上面的资源,查看结果会发现有的pod会落在node02上

设置NoExcute 的驱逐时间

        通常情况下,如果给一个节点添加了一个 effect 值为NoExecute的污点, 则任何不能忍受这个污点的 Pod 都会马上被驱逐, 任何可以忍受这个污点的 Pod 都不会被驱逐。

        但是,如果 Pod 存在一个 effect 值为NoExecute的容忍度指定了可选属性tolerationSeconds的值,则表示 Pod 还能继续在节点上运行的时间。

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

        这表示如果这个 Pod 正在运行,这时候被打上了NoExecute的污点, 那么 Pod 还将继续运行 3600 秒,然后被驱逐。 如果在3600秒内污点被删除了,Pod就不会被驱逐。

2.3 取消污点

取消节点的污点标记,只需要在刚刚创建污点命令的结尾添加个“-”即可:

$ kubectl taint nodes node02 test=node02:NoSchedule-
node "node02" untainted

3、驱逐维护

3.1 挂维护,禁止调度

# kubectl cordon node02

        设置node02不可调度,通过get node查看各节点状态,发现node02为SchedulingDisabled,此时新的pod不会调度到该节点上,但是现有的pod还是正常运行。

3.2 驱逐

# kubectl drain node02 --delete-local-data --ignore-daemonsets --force

参数说明:

--delete-emptydir-data 即使有使用emptyDir的pod也删除
--ignore-daemonsets  删除daemonsets管理的pod
--force  强制删除

        我们可以通过不断kubectl get pod  -o wide  观察到pod在驱逐迁移的过程

3.3 解除维护,解除禁止调度

# kubectl uncordon node02

        解除后新的pod可以调度到node2节点,但是之前驱逐走的不会主动回来,需要重启pod后通过系统调度选择才有可能回到此节点

Logo

开源、云原生的融合云平台

更多推荐