一、实验环境及简介

1.1、实验环境

基于 centos7 搭建的 k8s 集群,搭建方法见 CentOS7配置k8s集群环境
集群包含一个 master 节点,两个 node 节点,IP 如下:

节点IP
master192.168.10.21
node1192.168.10.23
node2192.168.10.22

1.2、亲和性

Affinity 翻译成中文是“亲和性”,它对应的是 Anti-Affinity,我们翻译成“互斥”。这两个词比较形象,可以把 Pod 选择 node 的过程类比成磁铁的吸引和互斥,不同的是除了简单的正负极之外,Pod 和 node 或 Pod 和 Pod 的吸引和互斥是可以灵活配置的。
亲和度调度可分为两种:节点亲和度和 Pod 亲和度。

  • 节点亲和性:通过为节点添加标签,创建 Pod 时通过 yaml 配置文件设置对节点的亲和度。即 node 对 Pod 有吸引力。
  • Pod 亲和性:通过为 Pod 添加标签,再创建新的 pod 时可根据与其他已存在 pod 的标签选择部署到那个 node 上。即 Pod 之间有吸引力。

两种不同的亲和性调度都有两种策略:硬亲和性调度和软亲和性调度

  • RequiredDuringSchedulingIgnoredDuringExecution:
    必须满足制定的规则才可以调度 Pod 到 Node 上。相当于硬限制
  • PreferredDuringSchedulingIgnoreDuringExecution:
    强调优先满足制定规则,调度器会尝试调度 Pod 到 Node 上,但并不强求,相当于软限制。多个优先级规则还可以设置权重值,以定义执行的先后顺序。

1.3、污点和容忍度

污点是 node 的属性,容忍度是 Pod 的属性。如果说亲和性是“吸引”,那么污点就是“排斥”。不能容忍 node 污点的 Pod 不会被调度到该 node 上。只有能够容忍 node 污点的 Pod 才可以被调度到该 node 上。通常搭建好集群后,新建的 Pod 不会被调度到 master 节点上,就是因为 k8s 默认对 master 节点添加了污点。

注意:亲和度是为 node 或 pod 加标签,一个 node 或 pod 可以有多个不同的标签。污点是对 node 添加污点属性,与标签类似。在下面的实验中可以看到如何实现。

二、节点亲和性调度

节点亲和性规则有两种。一是硬亲和性,实现强执行规则,二是软亲和性,实现柔性调度规则。定义节点亲和规则的关键点有两个,一是为节点配置合乎需求的标签,另一个是为 Pod 对象定义合理的标签选择器。

2.1、节点硬亲和性

节点的亲和性可以通过 pod.spec.affinity.nodeAffinity 字段定义,nodeAffinity 字段中支持使用 matchExpressions 属性构建更为复杂的标签选择器。
对 node 标签的操作参考博客 k8s对node添加Label

  • 首先对两个 node 加标签,设置两个 node 标签分别为 node1 和 node2 。加标签的语法如下。
kubectl label nodes <node-name> <label-key>=<label-value> 
[root@master ~]# kubectl label nodes node1 type=node1 
[root@master ~]# kubectl label nodes node2 type=node2
# 查看节点标签
[root@master ~]# kubectl get node --show-labels
NAME     STATUS   ROLES    AGE   VERSION   LABELS
master   Ready    master   9d    v1.19.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=,type=master
node1    Ready    <none>   9d    v1.19.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux,type=node1
node2    Ready    node2    9d    v1.19.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux,node-role.kubernetes.io/node2=,type=node2,zone=foo

可以看到 node 信息最后的 type 标签分别为 node1 和 node2 。

  • 设置 pod 亲和性为 type=node1
    编辑配置文件 test-node1.yaml
[root@master ~]# vim test01.yaml
# 定义一个 pod 资源清单,将该 pod 调度至有标签为 type=node1 的节点上
apiVersion: v1
kind: Pod
metadata:
  name: test01
spec:
  # 亲和性调度
  affinity:
    # 节点亲和性调度
    nodeAffinity:
      # 硬策略
      requiredDuringSchedulingIgnoredDuringExecution:
        # 指定亲和性
        nodeSelectorTerms:
        - matchExpressions:
          - {key: type, operator: In, values: ["node1"]}
  containers:
    - name: test01
      image: nginx
 # 部署pod并查看其信息
[root@master ~]# kubectl apply -f test01.yaml
pod/test01 created
[root@master ~]# kubectl get pod test01-o wide
NAME     READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test01   1/1     Running   0          35s   10.244.1.25   node1   <none>           <none>

可以看到 Pod 被部署到 node1 节点上

  • node1 资源变化如下
    在这里插入图片描述
  • 设置 pod 亲和性为 type=node2
    编辑配置文件 test-node2.yaml
[root@master ~]# vim test01.yaml
# 定义一个 pod 资源清单,将该 pod 调度至有标签为 type=node2 的节点上
apiVersion: v1
kind: Pod
metadata:
  name: test01
spec:
  # 亲和性调度
  affinity:
    # 节点亲和性调度
    nodeAffinity:
      # 硬策略
      requiredDuringSchedulingIgnoredDuringExecution:
        # 指定亲和性
        nodeSelectorTerms:
        - matchExpressions:
          - {key: type, operator: In, values: ["node2"]}
  containers:
    - name: test01
      image: nginx
 # 部署pod并查看其信息 
[root@master ~]# kubectl apply -f test01.yaml
pod/test01 created
[root@master ~]# kubectl get pod test01 -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test01   1/1     Running   0          48s   10.244.2.62   node2   <none>

可以看到 Pod 被部署到 node2 节点上

  • node2 资源变化如下
    在这里插入图片描述

2.2、节点软亲和性

节点软亲和性为节点选择提供了一种柔性的控制器逻辑,当条件不满足时,也能够接受被编排与其他不符合条件的节点之上。同时他还为每种倾向性提供了 weight 属性以便用于自定义优先级,范围是1-100,越大越优。

  • 为 pod 设置软亲和性
[root@master ~]# vim test01.yaml
# 定义一个 pod 资源清单,将该 pod 调度至有标签为 type=node2 的节点上
apiVersion: v1
kind: Pod
metadata:
  name: test01
spec:
  # 亲和性调度
  affinity:
    # 节点亲和性调度
    nodeAffinity:
      # 软策略
      preferredDuringSchedulingIgnoredDuringExecution:
        # 指定亲和性及权重
        - weight: 60
          preference:
            matchExpressions:
            - {key: type, operator: In, values: ["node1"]}
        - weight: 30
          preference:
            matchExpressions:
            - {key: tpye, operator: In, values: ["node2"]}
  containers:
    - name: test01
      image: nginx
 # 部署pod并查看其信息
[root@master ~]# kubectl apply -f test01.yaml
pod/test01 created
[root@master ~]# kubectl get pod test-node3  -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test01   1/1     Running   0          35s   10.244.1.25   node1   <none>           <none>

可以看到,由于 Pod 对 node1 的亲和性权重较高,因此被部署到了 node1 上。

  • node1 资源变化如下
    在这里插入图片描述

三、Pod亲和度

出于某些需求,将一些 Pod 对象组织在相近的位置(同一节点、机架、区域等),此时这些这些pod对象间的关系为亲和性。
Pod 的亲和性调度也存在硬亲和性和软亲和性的区别,他们表示的约束意义同节点亲和性相似。

3.1、Pod 硬亲和度

Pod硬亲和性调度也使用 requiredDuringSchedulingIgnoreDuringExecution 属性进行定义。

  • 首先创建两个基础 Pod,并对它打标签,分别部署到 node1 和 node2 上。
# 部署第一个基础 Pod
[root@master ~]# vim test-1.yaml
# 定义一个 pod 资源清单,设置标签为 webweb,部署到 node1 上
apiVersion: v1
kind: Pod
metadata:
  name: test-1
  labels:
    app: webweb
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - {key: type, operator: In, values: ["node1"]}
  containers:
    - name: test-1
      image: nginx
# 部署
[root@master ~]# kubectl apply -f test-1.yaml
pod/test-1 created
# 部署第二个基础 Pod
[root@master ~]# vim test00.yaml
# 定义一个 pod 资源清单,设置标签为 web,部署到 node2 上
apiVersion: v1
kind: Pod
metadata:
  name: test00
  labels:
    app: web
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - {key: type, operator: In, values: ["node2"]}
  containers:
    - name: test00
      image: nginx
[root@master ~]# kubectl apply -f test00.yaml
pod/test00 created
# 查看基础 Pod 位置
[root@master ~]# kubectl get pod -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test-1   1/1     Running   0          13s   10.244.1.27   node1   <none> 
test00   1/1     Running   0          37s   10.244.2.63   node2   <none>  

可以看到,一个位于 node1,一个位于 node2。

  • 亲和 test00 的 Pod。
[root@master ~]# vim test-pod.yaml
apiVersion: v1
kind: Pod
metadata: 
  name: test-pod
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - {key: app, operator: In, values: ["web"]}
        topologyKey: kubernetes.io/hostname
  containers:
  - name: test-pod
    image: nginx
[root@master ~]# kubectl apply -f test-pod.yaml
pod/test-pod1created
[root@master ~]# kubectl get pod test-pod1 -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test-pod   1/1     Running   0          15s   10.244.2.64   node2   <none>           <none>

可以看到 test-pod 被调度到了 node2 上。

  • test-pod 与 test00 一起在 node2 上运行
    在这里插入图片描述
    node2 资源利用率如下
    在这里插入图片描述

3.2、Pod 软亲和度

Pod软亲和调度使用方法与Node软亲和调度方法类似。

[root@master ~]# vim test-pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod2
spec:
  # 亲和性调度
  affinity:
    # 节点亲和性调度
    podAffinity:
      # 软策略
      preferredDuringSchedulingIgnoredDuringExecution:
      # 指定亲和性及权重
      - weight: 80
          podAffinityTerm:
            labelSelector:
              matchExpressions               
              - {key: app, operator: In, values: ["web"]}
            topologyKey: kubernetes.io/hostname
      - weight: 20
          podAffinityTerm:
            labelSelector:
              matchExpressions:
              - {key: app, operator: In, values: ["webweb"]}
            topologyKey: kubernetes.io/hostname
  containers:
    - name: test-pod2
      image: nginx
# 生成 Pod 并查看位置信息
[root@master ~]# kubectl apply -f test01.yaml
pod/test-pod2 created
[root@master ~]# kubectl get pod test-pod2 -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test-pod2   1/1     Running   0          20s   10.244.2.65   node2   <none>           <none>

可以看到 test-pod2 被调度到 node2 上。

  • 修改 yaml 文件中的权重,使其余 test-pod01 亲和性更高,则会被部署到 node1 上。
# 先删除 test-pod2
[root@master ~]# kubectl delete pod test-pod2
pod "test-pod2" deleted
# 修改配置文件
[root@master ~]# vim test-pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod2
spec:
  # 亲和性调度
  affinity:
    # 节点亲和性调度
    podAffinity:
      # 软策略
      preferredDuringSchedulingIgnoredDuringExecution:
      # 指定亲和性及权重
      - weight: 20
          podAffinityTerm:
            labelSelector:
              matchExpressions               
              - {key: app, operator: In, values: ["web"]}
            topologyKey: kubernetes.io/hostname
      - weight: 80
          podAffinityTerm:
            labelSelector:
              matchExpressions:
              - {key: app, operator: In, values: ["webweb"]}
            topologyKey: kubernetes.io/hostname
  containers:
    - name: test-pod2
      image: nginx
# 生成 Pod 并查看位置信息
[root@master ~]# kubectl apply -f test01.yaml
pod/test-pod2 created
[root@master ~]# kubectl get pod test-pod2 -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test-pod2   1/1     Running   0          3m12s   10.244.1.28   node1   <none>           <none>
  • 权重更大的标签为 app:webweb,因此test-pod2 与 test-1 一起在 node1 上运行,
    在这里插入图片描述
    node2 资源利用率如下
    在这里插入图片描述

四、污点和容忍度

污点:定义在节点之上的键值性属性数据,用于让节点拒绝将Pod调度运行于其上,除非该Pod对象具有接纳节点污点的容忍度

容忍度:定义在Pod对象上的键值型的属性数据,用于配置其可容忍的节点污点,而且调度器仅能将Pod对象调度至其能够容忍该节点污点的节点之上。
污点信息在节点的node.Spec字段中定义,容忍度信息在pod的pod.spec字段中定义,他们都是键值型数据,但都额外支持一个效果(effect)标记,语法格式为“key=value:effect”。Effect主要用于定义对Pod对象的排斥等级,主要的类型如下:

(1)NoSchedule:不能容忍此污点的Pod对象不可调度至当前节点,属于强制约束性的关系,对节点现存Pod对象不受影响。

(2)PreferNoSchedule:NoSchedule的柔性约束版本,不能容忍此污点的Pod对象尽量不要调度至当前节点

(3)NoExecute:不能容忍此污点的新Pod对象不可调度至当前节点,属强制型约束关系,而且节点上现存的Pod对象因节点污点变动或Pod容忍度变动而不再满足匹配规则时Pod对象将被驱逐。

  • 为两个 node 添加污点
[root@master ~]# kubectl taint nodes node1 node-type=production:NoSchedule
node/node1 tainted
[root@master ~]# kubectl taint nodes node2 node-type=web:NoSchedule
node/node2 tainted
  • 不能容忍任何污点的 Pod
[root@master ~]# vim test-taint1.yaml 
apiVersion: v1
kind: Pod
metadata: 
  name: test-taint1
spec:
  containers:
  - name: test-taint1
    image: nginx
# 部署
[root@master ~]# kubectl apply -f test-taint1.yaml
pod/test-taint1 created
[root@master ~]# kubectl get pod test-taint1 -o wide
NAME          READY   STATUS    RESTARTS   AGE     IP       NODE     NOMINATED NODE   READINESS GATES
test-taint1   0/1     Pending   0          8m12s   <none>   <none>   <none>           <none>

可以看到 test-taint1 一直处于 padding 状态,查看其 describe 信息。

[root@master ~]# kubectl describe pod test-taint1
Name:         test-taint1
Namespace:    default
Priority:     0
Node:         <none>
Labels:       <none>
Annotations:  <none>
Status:       Pending
IP:           
......
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  36s   default-scheduler  0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 1 node(s) had taint {node-type: production}, that the pod didn't tolerate, 1 node(s) had taint {node-type: web}, that the pod didn't tolerate.
  Warning  FailedScheduling  36s   default-scheduler  0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 1 node(s) had taint {node-type: production}, that the pod didn't tolerate, 1 node(s) had taint {node-type: web}, that the pod didn't tolerate.

可看到最后的 Warning 信息显示该 pod 不能容忍两个 node 的污点,部署失败。

  • 容忍 node1 污点的 Pod
[root@master ~]# vim test-taint2.yaml 
apiVersion: v1
kind: Pod
metadata: 
  name: test-taint2
spec:
  tolerations:
  - key: node-type
    operator: "Equal"
    value: "production"
    effect: "NoSchedule"
  containers:
  - name: test-taint2
    image: nginx
# 部署
[root@master ~]# kubectl apply -f test-taint2.yaml // 容忍node1
pod/test-taint2 created
[root@master ~]# kubectl get pod test-taint2 -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test-taint2   1/1     Running   0          18s   10.244.1.29   node1   <none>           <none>

可以看到 test-taint2 被调度到了 node1 上。dashboard 显示信息如下。
在这里插入图片描述

  • 容忍 node2 污点的 Pod
[root@master ~]# vim test-taint3.yaml 
apiVersion: v1
kind: Pod
metadata: 
  name: test-taint3
spec:
  tolerations:
  - key: node-type
    operator: "Equal"
    value: "web"
    effect: "NoSchedule"
  containers:
  - name: test-taint3
    image: nginx
# 部署
[root@master ~]# kubectl apply -f test-taint3.yaml // 容忍node1
pod/test-taint3 created
[root@master ~]# kubectl get pod test-taint3 -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test-taint3   1/1     Running   0          6s    10.244.2.67   node2   <none>           <none>

可以看到 test-taint3 被调度到了 node2 上。
dashboard 显示信息如下。
在这里插入图片描述

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐