K8S 亲和性与反亲和性 深度好文

今天我们来实验 pod 亲和性。官网描述如下

假设有如下三个节点的 K8S 集群:

k8s31master 是控制节点

k8s31node1、k8s31node2 是工作节点

容器运行时是 containerd

一、镜像准备

1.1、镜像拉取

bash 复制代码
docker pull tomcat:8.5-jre8-alpine
docker pull nginx:1.14.2

1.2、镜像导出

bash 复制代码
docker save -o tomcat-8.5-jre8-alpine.tar.gz docker.io/library/tomcat:8.5-jre8-alpine
docker save -o nginx-1.14.2.tar.gz docker.io/library/nginx:1.14.2

1.3、镜像导入工作节点 containerd

bash 复制代码
# k8s31node1 执行
[root@k8s31node1 ~]# ctr -n=k8s.io images import tomcat-8.5-jre8-alpine.tar.gz
[root@k8s31node1 ~]# ctr -n=k8s.io images import nginx-1.14.2.tar.gz
[root@k8s31node1 ~]# ctr -n=k8s.io images ls|grep tomcat
[root@k8s31node1 ~]# ctr -n=k8s.io images ls|grep nginx

# k8s31node2 执行
[root@k8s31node2 ~]# ctr -n=k8s.io images import tomcat-8.5-jre8-alpine.tar.gz
[root@k8s31node2 ~]# ctr -n=k8s.io images import nginx-1.14.2.tar.gz
[root@k8s31node2 ~]# ctr -n=k8s.io images ls|grep tomcat
[root@k8s31node2 ~]# ctr -n=k8s.io images ls|grep nginx

说明:

  • ctr 是 containerd 命令
  • ctr images import:导入镜像
  • -n=k8s.io:K8S 镜像存储命名空间

1.4、亲和性介绍

  • 亲和性(affinity)属性,位于 pod.spec.affinity,它有三种亲和性:
bash 复制代码
kubectl explain pod.spec.affinity

分别是 nodeAffinity(节点亲和性)、podAffinity(pod间亲和性)、podAntiAffinity(pod间反亲和性),它们可以分为两类:

  • 节点亲和性 功能类似于 nodeSelector字段,但它的表达能力更强,并且允许你指定软规则。
  • Pod 间亲和性/反亲和性允许你根据其他 Pod 的标签来约束 Pod。

简单来说:

nodeAffinity 定义了 pod 倾向于(亲和)被调度到哪些节点上。

podAffinity 定义了 pod 倾向于(亲和)跟哪些 pod 调度在一起。

podAntiAffinity 定义 pod 倾向于不(反亲和)跟哪些 pod 调度在一起。

二、nodeAffinity(节点亲和性)

  • 查看帮助文档
bash 复制代码
kubectl explain pod.spec.affinity.nodeAffinity

节点亲和性概念上类似于 nodeSelector, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。 节点亲和性有两种:

  • requiredDuringSchedulingIgnoredDuringExecution: 调度器只有在规则被满足的时候才能执行调度。此功能类似于 nodeSelector, 但其语法表达能力更强。
  • preferredDuringSchedulingIgnoredDuringExecution: 调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。

在上述类型中,IgnoredDuringExecution 意味着如果节点标签在 Kubernetes 调度 Pod 后发生了变更,Pod 仍将继续运行。

简单来说:

required 表示必须有节点满足这个位置定义的亲和性,这是个硬性条件,硬亲和性。

preferred 表示有节点尽量满足这个位置定义的亲和性,这不是一个必须的条件,软亲和性。

2.1、required 硬亲和性

  • 查看 requiredDuringSchedulingIgnoredDuringExecution
bash 复制代码
kubectl explain pod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution

它有一个必填字段 nodeSelectorTerms。

  • 查看 nodeSelectorTerms
bash 复制代码
kubectl explain pod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms

它是 NodeSelectorTerm 数组

NodeSelectorTerm 定义了两种匹配模式:

  • matchExpressions 数组
  • matchFields 数组

2.1.1、matchExpressions

matchExpressions:它允许你使用表达式来匹配节点的标签。例如,你可以使用 In、NotIn、Exists、DoesNotExist、Gt、Lt 等操作符来创建复杂的标签匹配规则。

bash 复制代码
kubectl explain pod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchExpressions

key:标签名称。

operator:匹配操作。

values:值列表。[] 或 - 形式都可以。

  • 编写资源文件

pod-node-affinity-required-match-expressions-demo.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-node-affinity-required-match-expressions
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: zone
            operator: In
            values:
            - east
            - south
  containers:
  - name: tomcat
    image: tomcat:8.5-jre8-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080

matchExpressions 表达式的意思是,寻找具有 label key 为 zone,值为 east 或 south 的节点,把 pod 绑定上去。

  • 运行并查看
bash 复制代码
kubectl apply -f pod-node-affinity-required-match-expressions-demo.yaml
kubectl get pod -owide

会发现 pod 并没有被正确调度。

因为我现在工作节点上,并没有一个节点有 zone 标签,值为 east 或 south。

required 是硬亲和性,必须满足表达式,pod 才能被正确调度。

  • 查看 pod 日志
bash 复制代码
kubectl describe pod pod-node-affinity-required-match-expressions

也能发现报 node affinity 错误。

  • 给 k8s31node1 打上标签 zone=east
bash 复制代码
kubectl label node k8s31node1 zone=east
kubectl get node --show-labels
  • 观察 pod 现在可以正常调度了
  • 如果这个时候,我们变更 k8s31node1 的标签
bash 复制代码
kubectl label node k8s31node1 zone-
# zone- 表示删除标签 zone
kubectl get pod -owide

会发现 pod 并没有被驱逐。

2.1.2、matchFields

matchFields:它允许你根据资源的非标签字段进行匹配,例如资源的名称、状态等。

bash 复制代码
kubectl explain pod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchFields

matchFields 跟 matchExpressions 匹配模式一样。

key:标签名称。

operator:匹配操作。

values:值列表。[] 或 - 形式都可以。

  • 编写资源文件

pod-node-affinity-required-match-fields-demo.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-node-affinity-required-match-fields
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchFields:
          - key: metadata.name
            operator: In
            values: ['k8s31node2']
  containers:
  - name: tomcat
    image: tomcat:8.5-jre8-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080

matchFields 表达式的意思是,寻找节点具有 metadata.name 属性,且值是 k8s31node2 的节点,把 pod 绑定上去。

怎么看 node 节点具有哪些属性?

bash 复制代码
# 以 json 格式或 yaml 格式输出 节点信息
kubectl get node k8s31node2 -o json
kubectl get node k8s31node2 -o yaml
  • 运行并查看
bash 复制代码
kubectl apply -f pod-node-affinity-required-match-fields-demo.yaml
kubectl get pod -owide

可以发现,它被正确调度到 k8s31node2 上。

2.2、preferred 软亲和性

  • 查看 preferredDuringSchedulingIgnoredDuringExecution
bash 复制代码
kubectl explain pod.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution

有两个必填字段:

preference:偏好。偏好 也是一个 NodeSelectorTerm,所以也会有 matchExpressions 和 matchFields。

weight:权重。1-100 的数,weight 是相对权重,权重越高,pod 调度的几率越大。

2.2.1、matchExpressions

  • 编写资源文件

pod-node-affinity-preferred-match-expressions-demo.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata: 
  name: pod-node-affinity-preferred-match-expressions
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - preference:
          matchExpressions:
          - key: disk
            operator: In
            values: ['SSD']
        weight: 20
      - preference:
          matchExpressions:
          - key: disk
            operator: In
            values: ['HDD']
        weight: 10
  containers:
  - name: tomcat
    image: tomcat:8.5-jre8-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080

preferred 匹配的意思是:

将 pod 节点优先调度到有标签 disk=SSD 的节点上。因为 disk=SSD 的 weight 数值更大,优先级更高。

业务含义是,将 pod 节点优先调度到拥有固态硬盘的节点上,没有固态硬盘的话,调度到拥有机械硬盘的节点上。

  • 运行并查看
bash 复制代码
kubectl apply -f pod-node-affinity-preferred-match-expressions-demo.yaml
kubectl get pod -owide

虽然我们现在系统上并没有 disk=SSD 与 disk=HDD 的节点,但是 pod 依然可以正常调度。这是因为 preferred 是一种软亲和性,即使找不到符合条件的节点,调度器 scheduler 依然会调度该 pod。

  • 给节点打标签
bash 复制代码
# 给 k8s31node1 节点打上 disk=HDD
kubectl label node k8s31node1 disk=HDD
# 给 k8s31node2 节点打上 disk=SSD
kubectl label node k8s31node2 disk=SSD
# 查看节点信息
kubectl get node --show-labels
  • 删除原来的 pod 并运行
bash 复制代码
kubectl delete -f pod-node-affinity-preferred-match-expressions-demo.yaml
kubectl apply -f pod-node-affinity-preferred-match-expressions-demo.yaml
kubectl get pod -owide

可以看到 pod 被优先调度到 k8s31node2,因为它标签是 disk=SSD,权重最高。

  • 如果这个时候,我们变更 k8s31node2 的标签
bash 复制代码
kubectl label node k8s31node2 disk-
kubectl get pod -owide

会发现 pod 并没有被驱逐。

matchFields 的情况与 required 类似,就不举例了。

2.3、节点亲和性总结

  • 节点亲和性 nodeAffinity 包括 required 和 preferred
  • required 是硬亲和性,只有条件满足,pod 才会被调度。
  • preferred 是软亲和性,条件匹配,优先按条件调度,条件不匹配,按默认算法调度。
  • matchExpressions 是按节点标签表达式来进行匹配。
  • matchFields 是按节点属性来进行匹配。
  • 无论 required 还是 preferred,在 pod 运行期,标签变更,pod 不会被驱逐。

2.4、还原实验环境

删除 default 命名空间下所有 pod,

删除节点所有标签,为下一个实验做准备。

三、podAffinity(pod间亲和性)

podAffinity 定义了 pod 倾向于(亲和)跟哪些 pod 调度在同一个位置。

  • 查看帮助文档
bash 复制代码
kubectl explain pod.spec.affinity.podAffinity

与节点亲和性类似,Pod 的亲和性与反亲和性也有两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

例如,你可以使用 requiredDuringSchedulingIgnoredDuringExecution 亲和性来告诉调度器,将两个服务的 Pod 放到同一个云提供商可用区内,因为它们彼此之间通信非常频繁。

类似地,你可以使用 preferredDuringSchedulingIgnoredDuringExecution 反亲和性来将同一服务的多个 Pod 分布到多个云提供商可用区中。

3.1、required 硬亲和性

  • 查看 requiredDuringSchedulingIgnoredDuringExecution
bash 复制代码
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution

它是 PodAffinityTerm 数组。

PodAffinityTerm 它有三个比较重要的字段:

toplogyKey:拓扑键,必填。我们在定义 pod 间亲和性时有一个前提,就是 B pod 想调度到跟 A pod 同一个位置,那么怎么定义这个位置?就是以这个字段来定义的。其取值是系统用来标示域的节点标签键。也就是说,不同节点具有相同标签 key,且 key 所对应的 value 也相同,则它们被定义为同一个位置。

假设有如下服务器集群,在可用区A中有节点 node1、node2,它们拥有相同的节点标签(zone=A)则 node1 与 node2 被定义为同一个位置。node3 因为拥有不同的节点标签(zone=B)所以 node3 被视为不同位置。

toplogyKey 它是一个拓扑的概念,同一个机架、可用区、地域里面所有节点,都可以被 K8S 视为同一个位置而被统一调度。

labelSelector:标签选择器。通过 labelSelector 选取一组能作为亲和对象的已存在的 pod 资源。它定义了两种匹配模式:

  • matchExpressions <[]LabelSelectorRequirement>
  • matchLabels <map[string]string>

namespaces:名称空间。pod 在 Kubernetes 中是名称空间作用域的对象,因此 pod 的标签也隐式地具有名称空间属性。 针对 pod 标签的所有 标签选择器 都要指定名称空间,Kubernetes 会在指定的名称空间内寻找标签。

如果不指定 namespaces,那么 标签选择器 就是在当前要创建的 pod 的名称空间里查找符合条件的一组 pod。

3.1.1、matchExpressions

matchExpressions:它允许你使用表达式来匹配 Pod 的标签。例如,你可以使用 In、NotIn、Exists、DoesNotExist 等操作符来创建复杂的标签匹配规则。

bash 复制代码
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.labelSelector.matchExpressions

key:标签名称。

operator:匹配操作。

values:值列表。[] 或 - 形式都可以。

  • 实验准备

使用 kubeadm join 往集群中加入一个新的工作节点 k8s31node3:

  • 镜像准备
bash 复制代码
# 将 tomcat、nginx 镜像也导入到 k8s31node3
# k8s31node3 执行
[root@k8s31node3 ~]# ctr -n=k8s.io images import tomcat-8.5-jre8-alpine.tar.gz
[root@k8s31node3 ~]# ctr -n=k8s.io images import nginx-1.14.2.tar.gz
[root@k8s31node3 ~]# ctr -n=k8s.io images ls|grep tomcat
[root@k8s31node3 ~]# ctr -n=k8s.io images ls|grep nginx
  • 资源文件编写

假设我们现在有两个 pod,nginx 跟 tomcat,nginx 反向代理 tomcat,它们之间要频繁通信,所以我们希望 pod-nginx 跟 pod-tomcat 能调度到同一个可用区内。

pod-pod-affinity-required-match-expressions-tomcat.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: tomcat
  labels:
    app: tomcat
spec:
  containers:
  - name: tomcat
    image: tomcat:8.5-jre8-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080
  • 运行并查看
bash 复制代码
kubectl apply -f pod-pod-affinity-required-match-expressions-tomcat.yaml
kubectl get pod -owide

tomcat 被调度到 k8s31node3 节点上。

  • 资源文件编写

pod-pod-affinity-required-match-expressions-nginx.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: zone
        labelSelector:
          matchExpressions:
          - {key: app, operator: In, values: ["tomcat"]}
  containers:
  - name: nginx
    image: nginx:1.14.2
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
  • 运行并查看
bash 复制代码
kubectl apply -f pod-pod-affinity-required-match-expressions-nginx.yaml
kubectl get pod -owide

发现 nginx 无法被调度。

查看 pod 日志

kubectl describe pod nginx

报 pod 亲和性不符合。

原因是我们现在所有节点上,并没有定义 topologyKey=zone 这个键,而 required 是属于硬亲和性,在节点调度期,找不到符合调度规则的节点,系统不会对 pod 进行调度。

  • 给节点打标签
bash 复制代码
kubectl label node k8s31node1 zone=A
kubectl label node k8s31node2 zone=B
kubectl label node k8s31node3 zone=B
kubectl get pod -owide

可以看到 nginx 被调度到 tomcat 所在的节点 k8s31node3 上了。

倘若这个时候,我们起一个 nginx:

pod-pod-affinity-required-match-expressions-nginx2.yaml

就只是把上一个 nginx.yaml 改了一下 metadata.name 为 nginx2 而已。

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: nginx2
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: zone
        labelSelector:
          matchExpressions:
          - {key: app, operator: In, values: ["tomcat"]}
  containers:
  - name: nginx
    image: nginx:1.14.2
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
bash 复制代码
kubectl apply -f pod-pod-affinity-required-match-expressions-nginx2.yaml
kubectl get pod -owide

会发现 nginx2 被调度到 k8s31node2 上了。

  • 分析

整个的部署图如下:

  • tomcat 首先被调度到 node3,这个过程是随机的,scheduler 调度器根据自己内部的调度算法来决定的。
  • nginx 被调度时,因为 nginx 跟 tomcat podAffinity,所以它要被调度到跟 tomcat 具有相同 topologyKey 的节点上,这个时候 node3 跟 node2 都满足要求(node1 因为 topologyKey 的值是 A,所以不满足要求),scheduler 调度器觉得 node3 这个时候的负载不高,所以也把 nginx 调度到 node3 上。
  • nginx2 被调度时,走 nginx 一样的逻辑,所以 node3 跟 node2 都满足要求,但此时 node3 负载已经很高了(跑着两个 pod),所以 scheduler 调度器决定将 nginx2 调度到 node2。
  • 还原实验环境

删除 nginx 跟 nginx2 以便进行下面的实验。

bash 复制代码
kubectl delete pod nginx
kubectl delete pod nginx2

3.1.2、matchLabels

matchLabels 的匹配方式,相对于 matchExpressions 更简单,它是以 键值对 的方式进行匹配的。

bash 复制代码
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.labelSelector.matchLabels
  • 编写资源文件

pod-pod-affinity-required-match-labels-nginx.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: zone
        labelSelector:
          matchLabels:
            app: tomcat
  containers:
  - name: nginx
    image: nginx:1.14.2
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80

pod-pod-affinity-required-match-labels-nginx2.yaml

俩个文件之间的差异,只在 metadata.name

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: nginx2
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: zone
        labelSelector:
          matchLabels:
            app: tomcat
  containers:
  - name: nginx
    image: nginx:1.14.2
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
  • 运行并查看
bash 复制代码
kubectl apply -f pod-pod-affinity-required-match-labels-nginx.yaml
kubectl apply -f pod-pod-affinity-required-match-labels-nginx2.yaml
kubectl get pod -owide

可以看到跟 matchExpressions 是一样的效果。

  • 节点标签变更

倘如我们这个时候,将所有节点的 zone 标签删除,看看会有什么效果。

bash 复制代码
kubectl label node k8s31node1 zone-
kubectl label node k8s31node2 zone-
kubectl label node k8s31node3 zone-
kubectl get pod -owide

可以看到 pod 并不会被驱逐。

  • 还原实验环境

删除 tomcat、nginx 跟 nginx2 以便进行下面的实验。

bash 复制代码
kubectl delete pod tomcat
kubectl delete pod nginx
kubectl delete pod nginx2

3.2、preferred 软亲和性

  • 查看 preferredDuringSchedulingIgnoredDuringExecution
bash 复制代码
kubectl explain pod.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution

它是 WeightedPodAffinityTerm 数组。

WeightedPodAffinityTerm 它有两个必填字段:

podAffinityTerm :PodAffinityTerm 对象。它跟上面 required 是一模一样的。所以也必然有 toplogyKey、labelSelector、namespaces。

weight:权重。1-100 的数,weight 是相对权重,权重越高,pod 调度的几率越大。

假设现在有如下部署图:

node1、node2、node3 分别有标签 zone=A、zone=B、zone=C,表示它们分别位于可用区 A B C。

node1、node2、node3 上分别运行着 pod tomcat1、tomcat2、tomcat3。

tomcat1、tomcat2、tomcat3 分别有标签 app=tomcat1、app=tomcat2、app=tomcat3,它们的权重,分别是 10、30、20。

现在有一个新的 pod-nginx,加入进来,我们看看 K8S 是如何调度的-->

  • 编写 tomcat 配置文件

pod-pod-affinity-preferred-match-expressions-tomcat1.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: tomcat1
  labels:
    app: tomcat1
spec:
  nodeName: k8s31node1
  containers:
  - name: tomcat
    image: tomcat:8.5-jre8-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080

nodeName 指定它运行在 node1 节点上。

pod-pod-affinity-preferred-match-expressions-tomcat2.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: tomcat2
  labels:
    app: tomcat2
spec:
  nodeName: k8s31node2
  containers:
  - name: tomcat
    image: tomcat:8.5-jre8-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080

nodeName 指定它运行在 node2 节点上。

pod-pod-affinity-preferred-match-expressions-tomcat3.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: tomcat3
  labels:
    app: tomcat3
spec:
  nodeName: k8s31node3
  containers:
  - name: tomcat
    image: tomcat:8.5-jre8-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080

nodeName 指定它运行在 node3 节点上。

  • 启动 tomcat
bash 复制代码
kubectl apply -f pod-pod-affinity-preferred-match-expressions-tomcat1.yaml
kubectl apply -f pod-pod-affinity-preferred-match-expressions-tomcat2.yaml
kubectl apply -f pod-pod-affinity-preferred-match-expressions-tomcat3.yaml
kubectl get pod -owide --show-labels
  • 给节点打标签
bash 复制代码
kubectl label node k8s31node1 zone=A
kubectl label node k8s31node2 zone=B
kubectl label node k8s31node3 zone=C
kubectl get node --show-labels
  • 编写 nginx 配置文件

pod-pod-affinity-preferred-match-expressions-nginx.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    podAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 10
        podAffinityTerm: 
          topologyKey: zone
          labelSelector:
            matchExpressions:
            - {key: app, operator: In, values: ["tomcat1"]}
      - weight: 30
        podAffinityTerm:
          topologyKey: zone
          labelSelector:
            matchExpressions:
            - {key: app, operator: In, values: ["tomcat2"]}
      - weight: 20
        podAffinityTerm:
          topologyKey: zone
          labelSelector:
            matchExpressions:
            - {key: app, operator: In, values: ["tomcat3"]}
  containers:
  - name: nginx
    image: nginx:1.14.2
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80

app=tomcat2 的权重最高,所以 nginx 需要被调度到跟 tomcat2 相同 zone 下的服务器上,因为该 zone 只有一个 node2,所以 nginx 被调度到 node2 上。

  • 删掉 nginx,并修改所有 pod label
bash 复制代码
kubectl delete pod nginx
kubectl label pod tomcat1 app=tomcat --overwrite=true
kubectl label pod tomcat2 app=tomcat --overwrite=true
kubectl label pod tomcat3 app=tomcat --overwrite=true
# --overwrite=true 表示覆盖原来 label 的值
kubectl get pod -owide --show-labels
  • 重新运行 nginx,我们看看会发生什么
bash 复制代码
kubectl apply -f pod-pod-affinity-preferred-match-expressions-nginx.yaml
kubectl get pod -owide --show-labels

现在没有一个 pod 的标签符合 标签选择器 的规则,但是 nginx 还是能被正常的调度,因为 preferred 是一种软亲和性。标签选择器的规则不匹配,scheduler 调度器会根据内部的算法选择合适的节点来绑定pod。

3.3、pod 间亲和性总结

  • pod 间亲和性 podAffinity 包括 required 和 preferred
  • required 是硬亲和性,只有条件满足,pod 才会被调度。
  • preferred 是软亲和性,条件匹配,优先按条件调度,条件不匹配,按默认算法调度。
  • matchExpressions 是按 pod 标签表达式来进行匹配。
  • matchLabels 也是按 pod 标签来进行匹配,不过它是以键值对的方式来表示匹配规则。
  • 无论 required 还是 preferred,在 pod 运行期,不管是 节点 标签变更,还是被亲和的 pod 标签变更,pod 都不会被驱逐。
  • podAffinity 中有一个很重要的概念是 toplogyKey,理解它对于理解 pod 调度非常重要。

3.4、还原实验环境

删除 nginx tomcat1 tomcat2 tomcat3

bash 复制代码
kubectl delete pod nginx tomcat1 tomcat2 tomcat3 --force --grace-period=0

保留 node1、node2、node3 的 zone label

四、podAntiAffinity(pod间反亲和性)

podAntiAffinity 定义了 pod 倾向于不跟哪些 pod 调度在同一个位置。

  • 查看帮助文档
bash 复制代码
kubectl explain pod.spec.affinity.podAntiAffinity

可以看到,它跟 podAffinity 的定义,几乎是一摸一样的。

可以猜到,K8S 内部在进行调度的时候,应该是采用一种取反的操作。

筛选出不想亲和的 pod 所具有的 toplogyKey,然后在剩下的 toplogyKey 里选择节点进行绑定。

下面是 podAffinity 的定义。

4.1、required 硬亲和性

这一节,我们只演示 required,其他大同小异。

假设现在有如下部署图:

node1、node2、node3 分别有标签 zone=A、zone=B、zone=C,表示它们分别位于可用区 A B C。

node3 运行着 tomcat1,它有标签 app=tomcat。

现在再来一个 tomcat2,我们不希望它跟 tomcat1 在同一个可用区下。

在实际业务中,相同的可用区往往意味着同一个机房,而部署同一个应用,往往不希望它们在同一个节点、或者同一个可用区下,因为这样容易导致 单点故障。从而让整个服务不可用。

我们看看这在 K8S 中要怎么实现-->

  • 编写 tomcat1 资源文件

pod-pod-antiaffinity-required-match-expressions-tomcat1.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: tomcat1
  labels:
    app: tomcat
spec:
  nodeName: k8s31node3
  containers:
  - name: tomcat
    image: tomcat:8.5-jre8-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080

nodeName 指定它运行在 node3 节点上。

  • 编写 tomcat2 资源文件

pod-pod-antiaffinity-required-match-expressions-tomcat2.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: tomcat2
  labels:
    app: tomcat
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: zone
        labelSelector:
          matchExpressions:
          - {key: app, operator: In, values: ["tomcat"]}
  containers:
  - name: tomcat
    image: tomcat:8.5-jre8-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080
  • 运行并查看
bash 复制代码
kubectl apply -f pod-pod-antiaffinity-required-match-expressions-tomcat1.yaml
kubectl apply -f pod-pod-antiaffinity-required-match-expressions-tomcat2.yaml
kubectl get pod -owide --show-labels

可以看到 tomcat2 被调度到跟 tomcat1 不同的 zone 的服务器上(node1、node2 都可以)。

相关推荐
csdn_life1812 小时前
# Debian 10 升级到 Debian 13 指南
运维·debian·php
U盘失踪了12 小时前
Debian 使用 Xfce 桌面
linux·运维
k7Cx7e12 小时前
Debian安装 curl 时提示插入 DVD 光盘
运维·windows·debian
云飞云共享云桌面13 小时前
广东某智能装备工厂8人共享一台服务器
大数据·运维·服务器·人工智能·3d·自动化·电脑
鹓于13 小时前
OmniParser视觉鼠标自动化实战
运维·自动化·计算机外设
北冥湖畔的燕雀13 小时前
Linux Shell开发实战:从零打造命令行工具
linux·运维·服务器
!沧海@一粟!13 小时前
Esxi主机iDrac密码与IP重置指南
运维·服务器
低保和光头哪个先来13 小时前
TinyEditor 篇2:剪贴板粘贴图片并同步上传至服务器
服务器·前端·javascript·css·vue.js
原来是猿13 小时前
Linux - 基础IO【中】
linux·运维·服务器
Xzq21050913 小时前
网络编程套接字(UDP)
运维·服务器·网络