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 的匹配规则,可选的操作符有:
操作符 | 规则说明 |
---|---|
In | label 的值在某个列表中 |
NotIn | label 的值不在某个列表中 |
Exists | 某个 label 存在 |
DoesNotExist | 某个 label 不存在 |
Gt | label 的值大于某个值(字符串比较) |
Lt | label 的值小于某个值(字符串比较) |
如果 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
配置。
以下是具体的步骤:
找到
podAntiAffinity
配置: 在 Pod 的 YAML 文件中,找到spec.affinity.podAntiAffinity
部分。修改
topologyKey
:确认
topologyKey
设置为合适的拓扑域,例如kubernetes.io/hostname
(表示节点)或failure-domain.beta.kubernetes.io/zone
(表示可用区)。topologyKey
决定了反亲和性规则的作用范围。将
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 状态。
导致此错误的常见原因包括:
资源不足: 节点可能没有足够的 CPU、内存或其他资源来运行 Pod。
污点和容忍度不匹配: 节点可能具有 Pod 不容忍的污点(Taint)。
亲和性/反亲和性规则过于严格: Pod 可能设置了过于严格的亲和性或反亲和性规则,导致无法找到合适的节点。
节点选择器冲突: Pod 的节点选择器可能与亲和性/反亲和性规则冲突。
节点亲和性冲突: Pod 的节点亲和性规则可能相互冲突,导致无法找到合适的节点。
解决步骤:
检查资源限制和节点容量:
使用
kubectl describe node <节点名称>
检查节点的资源容量。使用
kubectl describe pod <pod名称>
检查 Pod 的资源请求和限制。确保节点有足够的可用资源来满足 Pod 的需求。
检查污点和容忍度:
使用
kubectl describe node <节点名称>
检查节点上的污点。使用
kubectl describe pod <pod名称>
检查 Pod 的容忍度设置。确保 Pod 容忍所有节点上的污点,或者修改污点和容忍度设置以使其匹配。
审查亲和性/反亲和性规则:
检查 Pod 定义中的
affinity
和antiAffinity
字段。确保规则不会过于严格,例如要求 Pod 必须与特定标签的 Pod 位于同一节点,但该标签的 Pod 数量不足。
尝试使用
preferredDuringSchedulingIgnoredDuringExecution
代替requiredDuringSchedulingIgnoredDuringExecution
,以便调度器在找不到完全匹配的节点时可以做出妥协。检查节点选择器:
检查 Pod 定义中的
nodeSelector
字段。确保节点选择器不会亲和性/反亲和性规则冲突。
检查事件日志:
使用
kubectl describe pod <pod名称>
查看 Pod 的事件日志,以获取有关调度失败的更多详细信息。
其他建议:
尝试删除并重新创建 Pod,看看问题是否解决。
尝试使用
kubectl cordon <节点名称>
将节点标记为不可调度,然后使用kubectl uncordon <节点名称>
重新启用调度,看看是否可以解决问题。如果使用的是自定义调度器,请检查其配置是否正确。
参考: