K8s 亲和性和反亲和性

Kubernetes 亲和性和反亲和性

Kubernetes 的亲和性和反亲和性是用于控制 Pod 调度策略的强大工具。它允许根据节点或其他 Pod 的属性来指定 Pod 应该或不应该被调度到哪些节点上。通常情况下Pod被分配到哪些Node是不需要我们操心的,这个过程会由scheduler自动实现。但有时,我们需要让Pod按照我们的预想运行在Node上,例如某些应用 “必须/或者尽量” 跑在具有SSD存储的节点上;有些彼此相关的Pod应用应该跑在同一个节点上;有些业务Pod为了实现高可用需要将多个副本分散到不同的主机上,使每个主机有且只能运行服务的一个副本。为此,k8s为我们提供了这样的策略,我们可以通过使用 “亲和性/反亲和性” 制定一些规则来实现我们的需求。

亲和性(Affinity)

亲和性用于指定 Pod 应该被调度到哪些节点上。可以基于节点的标签、节点的拓扑结构(例如,可用区或机架)或其他 Pod 的标签。

亲和性类型:

  • 节点亲和性(Node Affinity): 基于节点的标签或拓扑结构来指定 Pod 应该被调度到哪些节点上。Pod 与 Node 之间的匹配规则。

  • Pod 亲和性(Pod Affinity): 基于其他 Pod 的标签来指定 Pod 应该被调度到哪些节点上。Pod 与 Pod 之间的匹配规则。

反亲和性(Anti-affinity)

反亲和性用于指定 Pod 不应该被调度到哪些节点上。可以基于节点的标签、节点的拓扑结构或其他 Pod 的标签。

反亲和性类型:

  • 节点反亲和性(Node Anti-affinity): 基于节点的标签或拓扑结构来指定 Pod 不应该被调度到哪些节点上。

  • Pod 反亲和性(Pod Anti-affinity): 基于其他 Pod 的标签来指定 Pod 不应该被调度到哪些节点上。Pod 与 Pod 不能在一起的规则。

亲和性调度:

  • 硬策略可以理解为必须遵守的规则,就是如果没有满足条件的节点的话,就不断重试直到满足条件为止。对应的配置规则为requiredDuringSchedulingIgnoredDuringExecution

  • 软策略可以理解为尽量遵守的规则,就是如果现在没有满足调度要求的节点的话,pod就会忽略这条规则,继续完成调度的过程,说白了就是满足条件最好了,没有的话也无所谓。对应的配置规则为 preferredDuringSchedulingIgnoredDuringExecution

所有的规则以 Label 作为元数据。对于 label 的匹配规则,可选的操作符有:

操作符规则说明
Inlabel 的值在某个列表中
NotInlabel 的值不在某个列表中
Exists某个 label 存在
DoesNotExist某个 label 不存在
Gtlabel 的值大于某个值(字符串比较)
Ltlabel 的值小于某个值(字符串比较)

如果 nodeAffinity 中 nodeSelectorTerms 有多个选项,则节点满足任何一个条件就可以;

如果 matchExpressions 有多个选项,则只有同时满足这些逻辑选项的节点才能运行 Pod。

亲和性和反亲和性作用范围:

在 Kubernetes 的亲和性和反亲和性配置中,topologyKey 用于指定拓扑域,决定了亲和性或反亲和性规则的作用范围。

topologyKey 的作用:

  • 定义拓扑域: topologyKey 指定了用于匹配节点或 Pod 的拓扑域。例如,kubernetes.io/hostname 表示节点,failure-domain.beta.kubernetes.io/zone 表示可用区。

  • 限制亲和性/反亲和性范围: topologyKey 限制了亲和性或反亲和性规则的应用范围。例如,如果 topologyKey 设置为 kubernetes.io/hostname,则亲和性或反亲和性规则只会在同一个节点上生效。

topologyKey 的常见值:

  • kubernetes.io/hostname: 表示节点。

  • failure-domain.beta.kubernetes.io/zone: 表示可用区。

  • failure-domain.beta.kubernetes.io/region: 表示区域。

  • topology.kubernetes.io/region: 表示区域(与 failure-domain.beta.kubernetes.io/region 相同)。

  • topology.kubernetes.io/zone: 表示可用区(与 failure-domain.beta.kubernetes.io/zone 相同)。

使用场景:

  • 亲和性:

    • 将特定类型的 Pod 调度到具有特定资源或功能的节点上。

    • 将 Pod 调度到同一可用区或机架上的节点上,以提高网络性能。

    • 将 Pod 调度到与其他 Pod 相同的节点上,以实现数据共享或协同工作。

  • 反亲和性:

    • 将 Pod 分散到不同的节点上,以提高可用性和容错性。

    • 将 Pod 分散到不同的可用区或机架上,以提高容错性和地理冗余。

    • 将 Pod 分散到不同的节点上,以避免资源竞争或性能冲突。

配置:

亲和性和反亲和性可以通过 Pod 的 affinity 字段进行配置。

示例:

节点亲和性:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd

这个配置指定 Pod 必须被调度到具有标签 disktype: ssd 的节点上。

Pod 反亲和性:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - my-app
        topologyKey: kubernetes.io/hostname

这个配置指定 Pod 不能与具有标签 app: my-app 的其他 Pod 在同一个节点上运行。


K8s pod 反亲和性配置策略由“必须”改为“尽量”

在 Kubernetes 中,Pod 反亲和性(podAntiAffinity)用于防止 Pod 被调度到具有特定标签的节点上。默认情况下,反亲和性规则是“必须”遵守的,这意味着如果找不到符合规则的节点,Pod 将无法被调度。

要将反亲和性策略由“必须”改为“尽量”,需要修改 Pod 定义中的 podAntiAffinity 配置。

以下是具体的步骤:

  1. 找到 podAntiAffinity 配置: 在 Pod 的 YAML 文件中,找到 spec.affinity.podAntiAffinity 部分。

  2. 修改 topologyKey:

    • 确认 topologyKey 设置为合适的拓扑域,例如 kubernetes.io/hostname(表示节点)或 failure-domain.beta.kubernetes.io/zone(表示可用区)。

    • topologyKey 决定了反亲和性规则的作用范围。

  3. 将 requiredDuringSchedulingIgnoredDuringExecution 改为 preferredDuringSchedulingIgnoredDuringExecution:

    • requiredDuringSchedulingIgnoredDuringExecution 表示调度器必须遵守反亲和性规则。

    • preferredDuringSchedulingIgnoredDuringExecution 表示调度器会尽量遵守反亲和性规则,但如果找不到符合条件的节点,仍然会调度 Pod。

示例:

假设有一个 Pod 定义如下:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - my-app
        topologyKey: kubernetes.io/hostname

这个配置要求 Pod 不能与具有标签 app: my-app 的其他 Pod 在同一个节点上运行。

要将策略改为“尽量”,可以将 requiredDuringSchedulingIgnoredDuringExecution 改为 preferredDuringSchedulingIgnoredDuringExecution

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - my-app
          topologyKey: kubernetes.io/hostname

修改后,调度器会尽量将 Pod 调度到没有运行 app: my-app Pod 的节点上。但如果所有节点都运行了 app: my-app Pod,调度器仍然会将 Pod 调度到其中一个节点上。

注意:

  • preferredDuringSchedulingIgnoredDuringExecution 需要设置 weight 参数,用于表示优先级,值越大表示优先级越高。

  • 修改后的策略并不能完全保证 Pod 不会被调度到同一个节点上,只能说是尽量避免。


k8s pod pending 报错:xxx node(s) didn't match pod affinity/affinity, xxx node(s) didn't match pod anti-affinity rules. 问题解析

这个错误信息表明 Kubernetes 集群中的 xxx 个节点都无法满足 Pod 的亲和性或反亲和性规则,导致 Pod 处于 Pending 状态。

导致此错误的常见原因包括:

  1. 资源不足: 节点可能没有足够的 CPU、内存或其他资源来运行 Pod。

  2. 污点和容忍度不匹配: 节点可能具有 Pod 不容忍的污点(Taint)。

  3. 亲和性/反亲和性规则过于严格: Pod 可能设置了过于严格的亲和性或反亲和性规则,导致无法找到合适的节点。

  4. 节点选择器冲突: Pod 的节点选择器可能与亲和性/反亲和性规则冲突。

  5. 节点亲和性冲突: Pod 的节点亲和性规则可能相互冲突,导致无法找到合适的节点。

解决步骤:

  1. 检查资源限制和节点容量:

    • 使用 kubectl describe node <节点名称> 检查节点的资源容量。

    • 使用 kubectl describe pod <pod名称> 检查 Pod 的资源请求和限制。

    • 确保节点有足够的可用资源来满足 Pod 的需求。

  2. 检查污点和容忍度:

    • 使用 kubectl describe node <节点名称> 检查节点上的污点。

    • 使用 kubectl describe pod <pod名称> 检查 Pod 的容忍度设置。

    • 确保 Pod 容忍所有节点上的污点,或者修改污点和容忍度设置以使其匹配。

  3. 审查亲和性/反亲和性规则:

    • 检查 Pod 定义中的 affinity 和 antiAffinity 字段。

    • 确保规则不会过于严格,例如要求 Pod 必须与特定标签的 Pod 位于同一节点,但该标签的 Pod 数量不足。

    • 尝试使用 preferredDuringSchedulingIgnoredDuringExecution 代替 requiredDuringSchedulingIgnoredDuringExecution,以便调度器在找不到完全匹配的节点时可以做出妥协。

  4. 检查节点选择器:

    • 检查 Pod 定义中的 nodeSelector 字段。

    • 确保节点选择器不会亲和性/反亲和性规则冲突。

  5. 检查事件日志:

    • 使用 kubectl describe pod <pod名称> 查看 Pod 的事件日志,以获取有关调度失败的更多详细信息。

其他建议:

  • 尝试删除并重新创建 Pod,看看问题是否解决。

  • 尝试使用 kubectl cordon <节点名称> 将节点标记为不可调度,然后使用 kubectl uncordon <节点名称> 重新启用调度,看看是否可以解决问题。

  • 如果使用的是自定义调度器,请检查其配置是否正确。


参考:

anzhihe 安志合个人博客,版权所有 丨 如未注明,均为原创 丨 转载请注明转自:https://chegva.com/6195.html | ☆★★每天进步一点点,加油!★★☆ | 

您可能还感兴趣的文章!

发表评论

电子邮件地址不会被公开。 必填项已用*标注