一、什么是亲和力

亲和力(Affinity)是 Kubernetes 中的一种机制,用于指定 Pod 与其他资源(如节点、Pod 或标签)之间的关联性和偏好。

亲和力定义了 Pod 对其他资源的偏好和互动方式,以便在调度过程中进行合适的匹配。通过使用亲和力规则,可以将 Pod 调度到满足特定条件的节点上,或者将 Pod 与其他相关的 Pod 进行关联,以实现特定的调度需求和业务需求。

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

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

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

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

pod亲和力、pod反亲和力、节点亲和力用途如下:

  • 节点亲和力:用于控制Pod和节点之间的关系
  • pod亲和力:用于控制Pod和Pod之间的关系,允许和某些Pod部署在同一个位置
  • pod反亲和力:用于控制Pod和Pod之间的关系,不允许和某些Pod部署在同一个位置

同时每种亲和力也分为了强制和非强制调度策略,用于实现调度规则是属于尽量满足还是必须满足。

二、亲和力出现背景

我们可以使用Taint让节点不能随便部署Pod,也通过NodeSelector+Toleration实现了将Pod部署到指定的节点上。但是NodeSelector对节点的选择是强制性的,假如配置了NodeSelector为ssd=true,那么如果集群中没有匹配的节点,这个Pod将一直处于Pending状态。而在生产环境中可能会有这样的需求:

  • 某些Pod优先选择有ssd=true标签的节点,如果没有再考虑部署到其他节点。
  • 某些Pod需要部署在ssd=true和type=physical的节点上,但是优先部署在ssd=true的节点上。
  • 同一类应用的Pod尽量不要部署或不允许部署在同一个节点或者符合某个标签的一类节点上。
  • 相互依赖的两个Pod尽量或必须部署在同一个节点上。

诸如此类的需求单单使用NodeSelector、NodeName、Taint和Toleration是无法实现的,所以Kubernetes引用了Affinity(亲和力)的概念,用于满足生产环境中更复杂的需求。

三、亲和力分类

Affinity根据功能的不同分为节点亲和力(Node Affinity)和Pod亲和力(Pod Affinity),每种亲和力还有两种不同的“实现方法”,即必须和尽量,所以亲和力又分为软亲和力(非强制性)和硬亲和力(强制性)。还可以看出Pod之间可以相互排斥,也可以相互吸引,所以Pod亲和力又分为PodAffinity(Pod亲和力)和PodAntiAffinity(Pod反亲和力),每种亲和力均可以使用软亲和力和硬亲和力。

亲和力分类

图中参数说明:

  • Affinity:亲和力
  • Node Affinity:节点亲和力
  • Pod Affinity:Pod亲和力
  • PodAntiAffinity:Pod反亲和力
  • 硬亲和力:requiredDuringSchedulinglgnoredDuringExecution

  • 软亲和力:preferredDuringSchedulinglgnoredDuringExecution

注意事项:如果同时配置了节点亲和力、Pod亲和力、Pod反亲和力,需要同时满足才行

四、亲和力常见使用场景

pod反亲和力

  • 同一个应用不同副本调度到不同的节点,增加服务的高可用性
  • 同一个应用不同副本调度到不同的机房,增大容灾能力
  • 同一个项目的不同应用尽量分配到不同的位置,增加项目的高可用性

节点亲和力

  • 特殊应用尽量分配到专用节点,提升应用性能

pod亲和力

  • 相互依赖的服务,分配到同一个域内

五、亲和力配置详解

5.1 节点亲和力配置详解

1.yaml文件展示

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - az-2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
          operator: In
          values:
          - another-node-label-value
  containers:
  - name: with-node-affinity

上述配置的Pod只能部署在具有label的key为kubernetes.io/e2e-az-name、value为e2e-az1或az-2的节点上,但是因为配置了软亲和力,所以在满足上述条件时,会尽量部署在具有another-node-label-key= another-node-label-value的节点上。当然这个条件不是强制要求的,没有该标签的Node不会影响部署。

文件参数说明如下: requiredDuringSchedulingIgnoredDuringExecution:硬亲和力配置

  • nodeSelectorTerms:节点选择器配置,可以配置多个matchExpressions(满足其一),每个 matchExpressions下可以配置多个key、value类型的选择器(都需要满足),其中values可以配置多个 (满足其一)

preferredDuringSchedulingIgnoredDuringExecution:软亲和力配置

  • weight:软亲和力的权重,权重越高优先级越大,范围1-100

  • preference:软亲和力配置项,和weight同级,可以配置多个,matchExpressions和硬亲和力一致

operator:标签匹配的方式

  • In:相当于key = value的形式

  • NotIn:相当于key != value的形式

  • Exists:节点存在label的key为指定的值即可,不能配置values字段

  • DoesNotExist:节点不存在label的key为指定的值即可,不能配置values字段

  • Gt:大于value指定的值

  • Lt:小于value指定的值

注意:如果同时指定了NodeSelector和NodeAffinity,需要两者都满足才能被调度;如果配置了多个NodeSelectorTerms,满足其一即可调度到指定的节点上;如果配置了多个MatchExpressions,需要全部满足才能调度到指定的节点上;如果删除了被调度节点上的标签,Pod不会被删除,也就是说亲和力配置只有在调度的时候才会起作用。

5.2 Pod亲和力和反亲和力配置详解

NodeAffinity是根据节点上的标签选择性地调度,可以让Pod部署在指定标签的节点上,或者不部署在指定标签的节点上,调度时是根据节点上的标签进行选择的。而Pod亲和力和反亲和力是根据其他Pod的标签进行匹配的,比如想要A服务的Pod不能和具有service=b标签的Pod部署在同一个节点上,此时可以使用Pod亲和力和反亲和力进行配置,该调度是基于Pod的标签进行选择的。

1.yaml文件展示

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
        matchExpressions:
        - key: security
          operator: In
          values:
          - S1
        topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          namespaces:
          - default
          topologyKey: failure-domain.beta.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
  image: nginx

文件参数说明如下:

  • labelSelector:Pod选择器配置,可以配置多个,支持key-value(matchLabels)和正则两种方式
  • matchExpressions:和节点亲和力配置一致
  • operator:配置和节点亲和力一致,但是没有Gt和Lt
  • In:相当于key=value的形式
  • NotIn:相当于key != value的形式
  • Exists:存在这个Key名的Pod
  • DoesNotExist:不存在这个Key名的Pod
  • topologyKey:匹配的拓扑域的key,也就是节点上label的key,key和 value相同的为同一个域,可以用于标注不同的机房和地区
  • namespaces: 和哪个命名空间的Pod进行匹配,为空为当前命名空间
  • namespaceSelector:使用标签匹配命名空间,为空代表为当前命空间,如果{}代表为表示所有命名空间,支持key-value和正则两种方式,写法和上述labelSelector一致

特别注意的地方:

  • 1)matchExpressions下面如果存在多个key,需要同时满足才行,是与的关系
  • 2)matchExpressions.key.values下面如果存在多个value,满足一个value匹配即可,是或的关系

上述Pod亲和力配置的是硬亲和力,也就是必须和具有security为S1标签的Pod部署在同一个域内,域的名称为failure-domain.beta.kubernetes.io/zone。但同时又配置了Pod反亲和力,尽量不和具有security为S2标签的Pod部署在同一个域内,所以该Pod要求在failure-domain.beta.kubernetes.io/zone的域内,要和security为S1部署在一起,尽量不和security为S2部署在一起。

由于Pod的标签是Pod本身的配置,因此它和Pod一样具有隔离性,也就是说在配置Pod亲和力和反亲和力时可以匹配其他Namespace下的Pod的标签。

注意:由于在使用Pod亲和力和Pod反亲和力时,需要进行大量的计算,会降低大规模集群下的调度速率,因此在集群节点超过数百时,并不建议使用过多的Pod亲和力配置。