持续集成部署-k8s-高级调度-亲和力

1. 亲和力的基本概念

Kubernetes中,亲和力(Affinity)是一种高级调度机制,用于指定Pod如何与节点进行亲和匹配。通过亲和力规则,你可以控制Pod被调度到特定节点的方式,以便更好地满足应用程序的需求和约束条件。

亲和力规则分为两种类型:node亲和性pod亲和性

  • Node亲和性:

    • Node亲和性规则允许你指定Pod应该被调度到具有特定标签的节点上。这种标签可以是节点的特性、硬件配置或者地理位置等。通过使用Node亲和性,你可以确保某些Pod只被调度到符合特定要求的节点上。
  • Pod亲和性:

    • Pod亲和性规则允许你指定Pod应该与其他Pod共同部署在同一个节点上。这种规则可以确保某些相关的Pod被调度到相同的节点上,以减少网络延迟或提高局部性能。

相较于污点和容忍而言,污点和容忍更倾向于一个排除的效果,你能容忍我的污点,你就来,你不能容忍我的污点,那你就走。

接着来看下一种新的调度效果叫做亲和力。亲和力刚好跟污点容忍相反,就是配置 Pod 尽可能的到某个地方,而反亲和力刚好跟亲和力相反,就是尽可能的不到某个地方,跟污点一个效果。

污点是使用的是污点和容忍,而亲和力匹配使用的标签 Label。亲和度越高,越有可能被调度选中。

污点配合亲和力,就能够实现更细致、更复杂、更智能的调度功能。

2. 亲和性和非亲和性

nodeSelector 提供了一种最简单的方法来将 Pod 约束到具有特定标签的节点上。 亲和性和反亲和性扩展了你可以定义的约束类型。使用亲和性与反亲和性的一些好处有:

  • 亲和性、反亲和性语言的表达能力更强。nodeSelector 只能选择拥有所有指定标签的节点。 亲和性、反亲和性为你提供对选择逻辑的更强控制能力。
  • 你可以标明某规则是“软需求”或者“偏好”,这样调度器在无法找到匹配节点时仍然调度该 Pod。
  • 你可以使用节点上(或其他拓扑域中)运行的其他 Pod 的标签来实施调度约束, 而不是只能使用节点本身的标签。这个能力让你能够定义规则允许哪些 Pod 可以被放置在一起。

亲和性功能由两种类型的亲和性组成:

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

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

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

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

3. 节点亲和力的使用

看下官方文档里面的 Demo:pods/pod-with-node-affinity.yaml

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:  # 定义亲和性规则
    nodeAffinity:  # 定义节点亲和性规则
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:  # 定义选择节点的条件
        - matchExpressions: # 定义匹配表达式
          - key: topology.kubernetes.io/zone  # 指定匹配的标签键
            operator: In  # 指定匹配操作符为In,表示节点的标签键值在指定的values中
            values:   # 指定匹配的标签值列表,包括"antarctica-east1"和"antarctica-west1"
            - antarctica-east1
            - antarctica-west1
      preferredDuringSchedulingIgnoredDuringExecution:   # 定义优选的节点亲和性规则,即首选但不是必需的
      - weight: 1   # 定义优选规则的权重为1
        preference: 
          matchExpressions:   # 定义首选规则的匹配表达式
          - key: another-node-label-key # 指定匹配的标签键
            operator: In 
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:2.0

所应用的规则如下:

  • 节点必须包含一个键名为 topology.kubernetes.io/zone 的标签, 并且该标签的取值必须为 antarctica-east1antarctica-west1
  • 节点最好具有一个键名为 another-node-label-key 且取值为 another-node-label-value 的标签。

可以使用 operator 字段来为 Kubernetes 设置在解释规则时要使用的逻辑操作符。 你可以使用 InNotInExistsDoesNotExistGtLt 之一作为操作符。

  • In:满足一个就行;
  • NotIn:一个都不能满足,反亲和性;
  • Exists:只要存在,就满足;
  • DoesNotExist:只有不存在,才满足;
  • Gt:必须要大于节点上的数值才满足;
  • Lt:必须要小于节点上的数值才满足;

在要求该Pod被调度到拥有特定标签的节点上(拓扑域为"antarctica-east1"或"antarctica-west1")。同时也定义了一个首选规则,希望该Pod被调度到带有特定标签的节点上(标签键为"another-node-label-key",标签值为"another-node-label-value")。

通过这样的配置,Kubernetes调度器将会优先考虑满足必需的节点亲和性规则,如果没有满足的节点,则会考虑首选的节点亲和性规则。

NotInDoesNotExist 可用来实现节点反亲和性行为。 你也可以使用节点污点 将 Pod 从特定节点上驱逐。

说明:
如果你同时指定了 nodeSelectornodeAffinity,两者 必须都要满足, 才能将 Pod 调度到候选节点上。
如果你在与 nodeAffinity 类型关联的 nodeSelectorTerms 中指定多个条件, 只要其中一个 nodeSelectorTerms 满足(各个条件按逻辑或操作组合)的话,Pod 就可以被调度到节点上。
如果你在与 nodeSelectorTerms 中的条件相关联的单个 matchExpressions 字段中指定多个表达式, 则只有当所有表达式都满足(各表达式按逻辑与操作组合)时,Pod 才能被调度到节点上。

4. 节点亲和性权重

可以为 preferredDuringSchedulingIgnoredDuringExecution 亲和性类型的每个实例设置 weight 字段,其取值范围是 1100。 当调度器找到能够满足 Pod 的其他调度请求的节点时,调度器会遍历节点满足的所有的偏好性规则, 并将对应表达式的 weight 值加和。

最终的加和值会添加到该节点的其他优先级函数的评分之上。 在调度器为 Pod 作出调度决定时,总分最高的节点的优先级也最高。

配置文件:pods/pod-with-affinity-anti-affinity.yaml

apiVersion: v1
kind: Pod
metadata:
  name: with-affinity-anti-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/os
            operator: In
            values:
            - linux
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: label-1
            operator: In
            values:
            - key-1
      - weight: 50
        preference:
          matchExpressions:
          - key: label-2
            operator: In
            values:
            - key-2
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:2.0

如果存在两个候选节点,都满足 preferredDuringSchedulingIgnoredDuringExecution 规则, 其中一个节点具有标签 label-1:key-1,另一个节点具有标签 label-2:key-2, 调度器会考察各个节点的 weight 取值,并将该权重值添加到节点的其他得分值之上,

说明:
如果你希望 Kubernetes 能够成功地调度此例中的 Pod,你必须拥有打了 kubernetes.io/os=linux 标签的节点。

5. 验证节点亲和性

看下当前 k8s 环境节点的标签:

[root@docker-54 ~]# kubectl get no --show-labels
NAME        STATUS   ROLES                  AGE    VERSION   LABELS
docker-54   Ready    control-plane,master   201d   v1.22.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-54,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
docker-55   Ready    <none>                 201d   v1.22.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-55,kubernetes.io/os=linux,type=microservice
docker-56   Ready    <none>                 201d   v1.22.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-56,kubernetes.io/os=linux,type=microservice
[root@docker-54 ~]# 

可以看到,上面节点都已经具备标签kubernetes.io/os=linux了。

为了验证上面的节点亲和力配置,我这里将对两个从节点,分别添加标签:

[root@docker-54 ~]# kubectl label no docker-55 label-1=key-1
node/docker-55 labeled
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl label no docker-56 label-2=key-2
node/docker-56 labeled
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl get no --show-labels            
NAME        STATUS   ROLES                  AGE    VERSION   LABELS
docker-54   Ready    control-plane,master   202d   v1.22.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-54,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
docker-55   Ready    <none>                 201d   v1.22.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-55,kubernetes.io/os=linux,label-1=key-1,type=microservice
docker-56   Ready    <none>                 201d   v1.22.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-56,kubernetes.io/os=linux,label-2=key-2,type=microservice
[root@docker-54 ~]# 

上面我对 docker-55 节点添加了 标签 label-1=key-1,对 docker-56 节点添加了 标签 label-2=key-2

这里我直接使用上面配置文件的亲和性的内容,来修改我这边当前环境里面的一个 deploy ,直接编辑 deploy:kubectl edit deploy nginx-deploy

  template:
    metadata:
      labels:
        app: nginx-deploy
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: label-1
                operator: In
                values:
                - key-1
          - weight: 50
            preference:
              matchExpressions:
              - key: label-2
                operator: In
                values:
                - key-2

注意这里亲和力的配置也是跟 containers 是平级的。

保存之后,看下 Pod 的调度情况:

[root@docker-54 ~]# kubectl get po -o wide
NAME                           READY   STATUS        RESTARTS      AGE    IP             NODE        NOMINATED NODE   READINESS GATES
nginx-deploy-b8976d7b5-b86q4   1/1     Terminating   0             55m    10.244.1.78    docker-55   <none>           <none>
nginx-deploy-b8976d7b5-fvqn8   1/1     Terminating   1 (55m ago)   115m   10.244.2.119   docker-56   <none>           <none>
nginx-deploy-c7f9688fd-mqfmf   1/1     Running       0             27s    10.244.2.123   docker-56   <none>           <none>
nginx-deploy-c7f9688fd-w6vhk   1/1     Running       0             29s    10.244.2.122   docker-56   <none>           <none>
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl get po -o wide
NAME                           READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
nginx-deploy-c7f9688fd-mqfmf   1/1     Running   0          54s   10.244.2.123   docker-56   <none>           <none>
nginx-deploy-c7f9688fd-w6vhk   1/1     Running   0          56s   10.244.2.122   docker-56   <none>           <none>
[root@docker-54 ~]# 

可以看到,原来这两个 Pod 是分散在 两个从节点上的,在添加了上面的亲和力配置之后,Pod 都调度到了 docker-56 节点。

看下上面的节点亲和力配置,虽然两个从节点都满足了第一个要求,具有标签kubernetes.io/os=linux,但是具有标签 label-2=key-2的节点权重高,所以 Pod 会优先调度到具体此标签的节点上。

接着再次修改亲和力的配置,将标签 label-2=key-2的判断条件由 In 改为 NotIn,猜测下,这时候两个 Pod 都不能调度到带有标签 label-2=key-2的节点上了。

验证看下:

[root@docker-54 ~]# kubectl get po -o wide
NAME                            READY   STATUS              RESTARTS   AGE     IP             NODE        NOMINATED NODE   READINESS GATES
nginx-deploy-75578bc7f7-47cqt   0/1     ContainerCreating   0          2s      <none>         docker-55   <none>           <none>
nginx-deploy-75578bc7f7-78j75   1/1     Running             0          4s      10.244.1.79    docker-55   <none>           <none>
nginx-deploy-c7f9688fd-mqfmf    1/1     Running             0          7m52s   10.244.2.123   docker-56   <none>           <none>
nginx-deploy-c7f9688fd-w6vhk    1/1     Terminating         0          7m54s   10.244.2.122   docker-56   <none>           <none>
[root@docker-54 ~]# kubectl get po -o wide
NAME                            READY   STATUS        RESTARTS   AGE     IP             NODE        NOMINATED NODE   READINESS GATES
nginx-deploy-75578bc7f7-47cqt   1/1     Running       0          4s      10.244.1.80    docker-55   <none>           <none>
nginx-deploy-75578bc7f7-78j75   1/1     Running       0          6s      10.244.1.79    docker-55   <none>           <none>
nginx-deploy-c7f9688fd-mqfmf    1/1     Terminating   0          7m54s   10.244.2.123   docker-56   <none>           <none>
nginx-deploy-c7f9688fd-w6vhk    1/1     Terminating   0          7m56s   10.244.2.122   docker-56   <none>           <none>
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl get po -o wide
nginx-deploy-75578bc7f7-47cqt   1/1     Running       0          52s      10.244.1.80    docker-55   <none>           <none>
nginx-deploy-75578bc7f7-78j75   1/1     Running       0          56s      10.244.1.79    docker-55   <none>           <none>

可以看到确实符合上面的亲和力配置。

这里 Master 节点 docker-54 虽然符合带有标签kubernetes.io/os=linux,但是不会被调度到它上面,是因为 Master 节点带有污点。

[root@docker-54 ~]# kubectl describe no docker-54 |grep Taints
Taints:             node-role.kubernetes.io/master:NoSchedule
[root@docker-54 ~]#

如果把这个污点删除掉,则上面的 Pod 还是有可能被调度到 Master 节点上的,接着试下看看:

[root@docker-54 ~]# kubectl get po -o wide
NAME                            READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nginx-deploy-75578bc7f7-47cqt   1/1     Running   0          52s   10.244.1.80   docker-55   <none>           <none>
nginx-deploy-75578bc7f7-78j75   1/1     Running   0          54s   10.244.1.79   docker-55   <none>           <none>
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl taint no docker-54 node-role.kubernetes.io/master:NoSchedule-
node/docker-54 untainted
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl describe no docker-54 |grep Taints                           
Taints:             <none>
[root@docker-54 ~]# 
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl delete po nginx-deploy-75578bc7f7-47cqt nginx-deploy-75578bc7f7-78j75
pod "nginx-deploy-75578bc7f7-47cqt" deleted
pod "nginx-deploy-75578bc7f7-78j75" deleted
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl get po -o wide
NAME                            READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nginx-deploy-75578bc7f7-8zz8h   1/1     Running   0          33s   10.244.0.11   docker-54   <none>           <none>
nginx-deploy-75578bc7f7-xvnfm   1/1     Running   0          33s   10.244.1.81   docker-55   <none>           <none>
[root@docker-54 ~]# 

可以看到,确实在 Master 节点去掉污点之后,删除 Pod 重新调度,发现可以调度到 Master 节点上。

在没有删除 Master 节点的污点的时候,由于 该 Pod 没有配置容忍,即使带有亲和力的标签,也不会被调度到 Master 节点上,然而在删除 Master 节点的污点之后,由于符合亲和力的必备标签,所以有可能会被调度选中。

6. Pod 间亲和性与反亲和性

Pod 间亲和性与反亲和性使你可以基于已经在节点上运行的 Pod 的标签来约束 Pod 可以调度到的节点,而不是基于节点上的标签。

Pod 间亲和性与反亲和性的规则格式为“如果 X 上已经运行了一个或多个满足规则 Y 的 Pod, 则这个 Pod 应该(或者在反亲和性的情况下不应该)运行在 X 上”。 这里的 X 可以是节点、机架、云提供商可用区或地理区域或类似的拓扑域, Y 则是 Kubernetes 尝试满足的规则。

你通过标签选择算符 的形式来表达规则(Y),并可根据需要指定选关联的名字空间列表。 PodKubernetes 中是名字空间作用域的对象,因此 Pod标签也隐式地具有名字空间属性。 针对 Pod 标签的所有标签选择算符都要指定名字空间,Kubernetes 会在指定的名字空间内寻找标签。

你会通过 topologyKey 来表达拓扑域(X)的概念,其取值是系统用来标示域的节点标签键。 相关示例可参见常用标签、注解和污点。

说明:
Pod 间亲和性和反亲和性都需要相当的计算量,因此会在大规模集群中显著降低调度速度。 我们不建议在包含数百个节点的集群中使用这类设置。

说明:
Pod 反亲和性需要节点上存在一致性的标签。换言之, 集群中每个节点都必须拥有与 topologyKey 匹配的标签。 如果某些或者所有节点上不存在所指定的 topologyKey 标签,调度行为可能与预期的不同。

7. Pod 间亲和性与反亲和性的类型

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

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

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

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

要使用 Pod 间亲和性,可以使用 Pod 规约中的 .affinity.podAffinity 字段。 对于 Pod 间反亲和性,可以使用 Pod 规约中的 .affinity.podAntiAffinity 字段。

8. 调度一组具有 Pod 间亲和性的 Pod

如果当前正被调度的 Pod 在具有自我亲和性的 Pod 序列中排在第一个, 那么只要它满足其他所有的亲和性规则,它就可以被成功调度。 这是通过以下方式确定的:确保集群中没有其他 Pod 与此 Pod 的名字空间和标签选择算符匹配; 该 Pod 满足其自身定义的条件,并且选定的节点满足所指定的所有拓扑要求。 这确保即使所有的 Pod 都配置了 Pod 间亲和性,也不会出现调度死锁的情况。

Pod 亲和性示例
考虑下面的 Pod 规约:pods/pod-with-pod-affinity.yaml

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:     # 定义亲和性规则
    podAffinity:    # 定义Pod亲和性规则
      requiredDuringSchedulingIgnoredDuringExecution:  # 定义在调度时必须满足的Pod亲和性规则
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone  # 指定拓扑域的标签键 也就是 依赖的 Pod 所在的节点必须要有这个标签才行
    podAntiAffinity:  # 定义Pod反亲和性规则
      preferredDuringSchedulingIgnoredDuringExecution:   # 定义优选的Pod反亲和性规则,即首选但不是必需的
      - weight: 100  # 定义优选规则的权重为100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: registry.k8s.io/pause:2.0

上面示例定义了一条 Pod 亲和性规则和一条 Pod 反亲和性规则。Pod 亲和性规则配置为 requiredDuringSchedulingIgnoredDuringExecution,而 Pod 反亲和性配置为 preferredDuringSchedulingIgnoredDuringExecution

亲和性规则规定,只有节点属于特定的 区域 且该区域中的其他 Pod 已打上 security=S1 标签时,调度器才可以将示例 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(称之为 “Zone V”)的集群,此区域由带有 topology.kubernetes.io/zone=V 标签的节点组成,那么只要 Zone V 内已经至少有一个 Pod 打了 security=S1 标签, 调度器就可以将此 Pod 调度到 Zone V 内的任何节点。相反,如果 Zone V 中没有带有 security=S1 标签的 Pod, 则调度器不会将示例 Pod 调度给该区域中的任何节点

反亲和性规则规定,如果节点属于特定的 区域 且该区域中的其他 Pod 已打上 security=S2 标签,则调度器应尝试避免将 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(我们称之为 “Zone R”)的集群,此区域由带有 topology.kubernetes.io/zone=R 标签的节点组成,只要 Zone R 内已经至少有一个 Pod 打了 security=S2 标签, 调度器应避免将 Pod 分配给 Zone R 内的任何节点。相反,如果 Zone R 中没有带有 security=S2 标签的 Pod, 则反亲和性规则不会影响将 Pod 调度到 Zone R。

通过这样的配置,Kubernetes调度器将会优先考虑满足必需的Pod亲和性规则,如果没有满足的节点,则会考虑首选的Pod反亲和性规则。

可以针对 Pod 间亲和性与反亲和性为其 operator 字段使用 InNotInExistsDoesNotExist 等值。

原则上,topologyKey 可以是任何合法的标签键。出于性能和安全原因,topologyKey 有一些限制:

对于 Pod 亲和性而言,在 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 中,topologyKey 不允许为空。
对于 requiredDuringSchedulingIgnoredDuringExecution 要求的 Pod 反亲和性, 准入控制器 LimitPodHardAntiAffinityTopology 要求 topologyKey 只能是 kubernetes.io/hostname。如果你希望使用其他定制拓扑逻辑, 你可以更改准入控制器或者禁用之。
除了 labelSelectortopologyKey,你也可以指定 labelSelector 要匹配的名字空间列表,方法是在 labelSelectortopologyKey 所在层同一层次上设置 namespaces。 如果 namespaces 被忽略或者为空,则默认为 Pod 亲和性/反亲和性的定义所在的名字空间。

9. 验证 Pod 亲和性

按照上面的配置文件,这里先将 Master节点打上topology.kubernetes.io/zone=R 标签,给从节点打上topology.kubernetes.io/zone=V 标签。

[root@docker-54 ~]# kubectl label no docker-54 topology.kubernetes.io/zone=R  
node/docker-54 labeled
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl label no docker-55 docker-56 topology.kubernetes.io/zone=V
node/docker-55 labeled
node/docker-56 labeled
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl get no --show-labels                                      
NAME        STATUS   ROLES                  AGE    VERSION   LABELS
docker-54   Ready    control-plane,master   202d   v1.22.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-54,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=,topology.kubernetes.io/zone=R
docker-55   Ready    <none>                 202d   v1.22.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-55,kubernetes.io/os=linux,label-1=key-1,topology.kubernetes.io/zone=V,type=microservice
docker-56   Ready    <none>                 202d   v1.22.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-56,kubernetes.io/os=linux,label-2=key-2,topology.kubernetes.io/zone=V,type=microservice
[root@docker-54 ~]#

接着新建 s2 标签的配置文件:s2-nginx-deploy-affinity.yaml

apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息
  labels: # 标签
    app: nginx-deploy # 具体的 key: value 配置形式
  name: nginx-deploy-s2 # deployment 的名字
  namespace: default # 所在的命名空间
spec:
  replicas: 1 # 期望副本数
  revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
  selector: # 选择器,用于找到匹配的 RS
    matchLabels: # 按照标签匹配
      app: nginx-deploy # 匹配的标签key/value
  strategy: # 更新策略
    rollingUpdate: # 滚动更新配置
      maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
      maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
    type: RollingUpdate # 更新类型,采用滚动更新
  template: # pod 模板
    metadata: # pod 的元信息
      labels: # pod 的标签
        app: nginx-deploy
        topology.kubernetes.io/zone: V
        security: S2
    spec: # pod 期望信息
      nodeSelector:
        kubernetes.io/hostname: docker-54
      containers: # pod 的容器
      - image: nginx:1.7.9 # 镜像
        imagePullPolicy: IfNotPresent # 拉取策略
        name: nginx # 容器名称
        resources:
          limits:
            cpu: 200m
            memory: 128Mi
          requests:
            cpu: 100m
            memory: 128Mi
      restartPolicy: Always # 重启策略
      terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间

这里上面配置的意思是部署一个 Nginx 的 Pod,使用标签选择器,部署到 docker-54 节点上,并且这个 Pod 带有两个后面测试 Pod 亲和力用到的标签:topology.kubernetes.io/zone: Vsecurity: S2

直接创建这个 deployment 看下,使用节点选择器直接调度到 Master 节点上,应该不会成功,因为我这里还没有处理 Master 节点上的污点,看下效果:

[root@docker-54 deployments]# kubectl create -f s2-nginx-deploy-affinity.yaml 
deployment.apps/nginx-deploy-s2 created
[root@docker-54 deployments]# 
[root@docker-54 ~]# kubectl get po -o wide
NAME                               READY   STATUS    RESTARTS       AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nginx-deploy-75578bc7f7-8zz8h      1/1     Running   11 (46m ago)   11h   10.244.0.11   docker-54   <none>           <none>
nginx-deploy-75578bc7f7-xvnfm      1/1     Running   11 (46m ago)   11h   10.244.1.81   docker-55   <none>           <none>
nginx-deploy-s2-5fd5bf9d5c-52d2k   0/1     Pending   0              19s   <none>        <none>      <none>           <none>
[root@docker-54 ~]# 
[root@docker-54 deployments]# kubectl get po -o wide --show-labels
NAME                               READY   STATUS    RESTARTS       AGE    IP            NODE        NOMINATED NODE   READINESS GATES   LABELS
nginx-deploy-75578bc7f7-8zz8h      1/1     Running   11 (50m ago)   11h    10.244.0.11   docker-54   <none>           <none>            app=nginx-deploy,pod-template-hash=75578bc7f7
nginx-deploy-75578bc7f7-xvnfm      1/1     Running   11 (50m ago)   11h    10.244.1.81   docker-55   <none>           <none>            app=nginx-deploy,pod-template-hash=75578bc7f7
nginx-deploy-s2-5fd5bf9d5c-52d2k   0/1     Pending   0              5m2s   <none>        <none>      <none>           <none>            app=nginx-deploy,pod-template-hash=5fd5bf9d5c,security=S2,topology.kubernetes.io/zone=V
[root@docker-54 deployments]# 

可以看到 Pod 的状态为 Pending,接着看下事件:

[root@docker-54 ~]# kubectl describe po nginx-deploy-s2-5fd5bf9d5c-52d2k 
Name:           nginx-deploy-s2-5fd5bf9d5c-52d2k
Namespace:      default
// 省略中间内容
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  31s   default-scheduler  0/3 nodes are available: 1 node(s) had taint {
    
    node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match Pod's node affinity/selector.
[root@docker-54 ~]# 

可以看到,上面显示了默认调度 3 个节点,没有一个可用,其中一个节点带有污点{node-role.kubernetes.io/master: },并且这个 Pod 没有配置容忍,所以不能调度到它上面,然后另外两个节点不包含 Pod 的节点亲和力标签。

接着来暂时去掉 Master 节点的污点,看下 Pod 能不能正常调度到 Master 节点上:

[root@docker-54 ~]# kubectl describe no docker-54 | grep Taint
Taints:             node-role.kubernetes.io/master:NoSchedule
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl taint no docker-54 node-role.kubernetes.io/master:NoSchedule-
node/docker-54 untainted
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl describe no docker-54 | grep Taint                           
Taints:             <none>
[root@docker-54 ~]# 

接着再次看下 Pod 状态:

[root@docker-54 deployments]# kubectl get po -o wide --show-labels
NAME                               READY   STATUS    RESTARTS       AGE     IP            NODE        NOMINATED NODE   READINESS GATES   LABELS
nginx-deploy-75578bc7f7-8zz8h      1/1     Running   11 (55m ago)   11h     10.244.0.11   docker-54   <none>           <none>            app=nginx-deploy,pod-template-hash=75578bc7f7
nginx-deploy-75578bc7f7-xvnfm      1/1     Running   11 (55m ago)   11h     10.244.1.81   docker-55   <none>           <none>            app=nginx-deploy,pod-template-hash=75578bc7f7
nginx-deploy-s2-5fd5bf9d5c-52d2k   1/1     Running   0              9m46s   10.244.0.12   docker-54   <none>           <none>            app=nginx-deploy,pod-template-hash=5fd5bf9d5c,security=S2,topology.kubernetes.io/zone=V
[root@docker-54 deployments]# 

可以看到 nginx-deploy-s2 的 Pod 被调度到 Maser 节点上了。这里不知道是不是 K8s 的bug,明明对节点 docker-54 打了 topology.kubernetes.io/zone=R 的标签,这个 Pod 竟然能调度到这个节点上。

然后我试了下将配置文件中 Pod 模板的标签选择器,配置成topology.kubernetes.io/zone=R 发现还是可以正常调度到 docker-54 节点。那是不是意味着 k8s 在调度时,如果仅仅配置了标签选择器,就会存在一个 BUG,就是仅比对了标签的key,没有比对标签的 value,也就是污点里面判断时用到的 Exists 的功能。这三个节点上都包含 topology.kubernetes.io/zone 的标签,虽然值不一样。

接着新建一个deploy,配置 Pod 带有 S1 的标签:s1-nginx-deploy-affinity.yaml

apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息
  labels: # 标签
    app: nginx-deploy # 具体的 key: value 配置形式
  name: nginx-deploy-s1 # deployment 的名字
  namespace: default # 所在的命名空间
spec:
  replicas: 2 # 期望副本数
  revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
  selector: # 选择器,用于找到匹配的 RS
    matchLabels: # 按照标签匹配
      app: nginx-deploy # 匹配的标签key/value
  strategy: # 更新策略
    rollingUpdate: # 滚动更新配置
      maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
      maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
    type: RollingUpdate # 更新类型,采用滚动更新
  template: # pod 模板
    metadata: # pod 的元信息
      labels: # pod 的标签
        app: nginx-deploy
        topology.kubernetes.io/zone: V
        security: S1
    spec: # pod 期望信息
      containers: # pod 的容器
      - image: nginx:1.7.9 # 镜像
        imagePullPolicy: IfNotPresent # 拉取策略
        name: nginx # 容器名称
        resources:
          limits:
            cpu: 200m
            memory: 128Mi
          requests:
            cpu: 100m
            memory: 128Mi
      restartPolicy: Always # 重启策略
      terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间

可以看到这个配置的 Pod 的标签是 topology.kubernetes.io/zone: Vsecurity: S1 ,刚好对应我们的两个从节点的标签,启动看下是否会调度到两个从节点上:

[root@docker-54 deployments]# kubectl create -f s1-nginx-deploy-affinity.yaml 
deployment.apps/nginx-deploy-s1 created
[root@docker-54 deployments]# 
[root@docker-54 deployments]# kubectl get po -o wide --show-labels            
NAME                               READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES   LABELS
nginx-deploy-s1-7bbd9fbdb7-25627   1/1     Running   0          2s    10.244.0.19    docker-54   <none>           <none>            app=nginx-deploy,pod-template-hash=7bbd9fbdb7,security=S1,topology.kubernetes.io/zone=V
nginx-deploy-s1-7bbd9fbdb7-bqwm4   1/1     Running   0          2s    10.244.2.127   docker-56   <none>           <none>            app=nginx-deploy,pod-template-hash=7bbd9fbdb7,security=S1,topology.kubernetes.io/zone=V
nginx-deploy-s2-df8df55b9-f8cb6    1/1     Running   0          79s   10.244.0.18    docker-54   <none>           <none>            app=nginx-deploy,pod-template-hash=df8df55b9,security=S2,topology.kubernetes.io/zone=R
[root@docker-54 deployments]# 

理想情况下,s1 的 deploy 的两个 Pod 应该被调度到两个带有 topology.kubernetes.io/zone=V 的标签的从节点上,但是!!!事实情况却不是这样:

[root@docker-54 deployments]# kubectl get po -o wide --show-labels            
NAME                               READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES   LABELS
nginx-deploy-s1-7bbd9fbdb7-25627   1/1     Running   0          2s    10.244.0.19    docker-54   <none>           <none>            app=nginx-deploy,pod-template-hash=7bbd9fbdb7,security=S1,topology.kubernetes.io/zone=V
nginx-deploy-s1-7bbd9fbdb7-bqwm4   1/1     Running   0          2s    10.244.2.127   docker-56   <none>           <none>            app=nginx-deploy,pod-template-hash=7bbd9fbdb7,security=S1,topology.kubernetes.io/zone=V
nginx-deploy-s2-df8df55b9-f8cb6    1/1     Running   0          79s   10.244.0.18    docker-54   <none>           <none>            app=nginx-deploy,pod-template-hash=df8df55b9,security=S2,topology.kubernetes.io/zone=R
[root@docker-54 deployments]# 

其中一个节点死活就是不去 docker-55 节点上,就非得赖上 docker-54 节点了。

我还特意检查了下 docker-55 节点上是不是配置了污点什么的:

[root@docker-54 ~]# kubectl describe no docker-55 | grep Taints
Taints:             <none>
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl describe no docker-54 | grep Taints 
Taints:             <none>
[root@docker-54 ~]# 
[root@docker-54 ~]# kubectl describe no docker-56 | grep Taints 
Taints:             <none>
[root@docker-54 ~]# 

多次删除 deploy ,还是这样,玩不下去了。。。。。。。

就这吧,辣鸡标签选择器,果然仅用他来控制调度不靠谱,怪不得有污点和亲和力的功能的出现,不争气的东西。。。。。

猜你喜欢

转载自blog.csdn.net/linmengmeng_1314/article/details/134758294