将 Pod 分配给节点

你可以约束Pod,使其被限制在特定的节点上运行,或者偏好在特定的节点上运行。有几种方法可以做到这一点,推荐的方法都使用标签选择器来方便选择。通常,你不需要设置任何此类约束;调度器会自动进行合理的放置(例如,将你的 Pod 分散到各个节点上,以免将 Pod 放置在资源不足的节点上)。但是,在某些情况下,你可能希望控制将 Pod 部署到哪个节点,例如,确保 Pod 最终位于连接了 SSD 的节点上,或者将来自两个通信频繁的不同服务的 Pod 放置在同一个可用区中。

你可以使用以下任何方法来选择 Kubernetes 调度特定 Pod 的位置

节点标签

与许多其他 Kubernetes 对象一样,节点也有标签。你可以手动附加标签。Kubernetes 还会填充集群中所有节点上的标准标签集

节点隔离/限制

向节点添加标签可以让你将 Pod 定位到特定的节点或节点组上进行调度。你可以使用此功能来确保特定的 Pod 仅在具有某些隔离、安全或法规属性的节点上运行。

如果使用标签进行节点隔离,请选择kubelet无法修改的标签键。这可以防止被入侵的节点在自身上设置这些标签,从而使调度器将工作负载调度到被入侵的节点上。

`NodeRestriction` 准入插件阻止 kubelet 设置或修改带有 `node-restriction.kubernetes.io/` 前缀的标签。

要使用该标签前缀进行节点隔离

  1. 确保你正在使用节点授权器,并且已启用 `NodeRestriction` 准入插件。
  2. 向你的节点添加带有 `node-restriction.kubernetes.io/` 前缀的标签,并在你的节点选择器中使用这些标签。例如,`example.com.node-restriction.kubernetes.io/fips=true` 或 `example.com.node-restriction.kubernetes.io/pci-dss=true`。

nodeSelector

`nodeSelector` 是最简单的推荐节点选择约束形式。你可以将 `nodeSelector` 字段添加到你的 Pod 规范中,并指定你希望目标节点拥有的节点标签。Kubernetes 仅将 Pod 调度到具有你指定的每个标签的节点上。

有关更多信息,请参阅将 Pod 分配给节点

亲和性(Affinity)和反亲和性(anti-affinity)

`nodeSelector` 是将 Pod 约束到具有特定标签的节点的最简单方法。亲和性和反亲和性扩展了你可以定义的约束类型。亲和性和反亲和性的一些好处包括

  • 亲和性/反亲和性语言更具表现力。`nodeSelector` 仅选择具有所有指定标签的节点。亲和性/反亲和性使你可以更好地控制选择逻辑。
  • 你可以指定一个规则是软性的首选的,以便即使调度程序找不到匹配的节点,仍然可以调度 Pod。
  • 你可以使用节点上(或其他拓扑域上)运行的其他 Pod 的标签来约束 Pod,而不仅仅是节点标签,这使你可以定义哪些 Pod 可以共存于一个节点上的规则。

亲和性特性由两种类型的亲和性组成

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

节点亲和性

节点亲和性在概念上类似于 `nodeSelector`,允许你根据节点标签约束可以将 Pod 调度到哪个节点。有两种类型的节点亲和性

  • `requiredDuringSchedulingIgnoredDuringExecution`:除非满足规则,否则调度器无法调度 Pod。这与 `nodeSelector` 的功能相同,但具有更具表现力的语法。
  • `preferredDuringSchedulingIgnoredDuringExecution`:调度器尝试查找满足规则的节点。如果没有可用的匹配节点,调度器仍然会调度 Pod。

你可以使用 Pod 规范中的 `.spec.affinity.nodeAffinity` 字段来指定节点亲和性。

例如,考虑以下 Pod 规范

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - antarctica-east1
            - antarctica-west1
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 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-east1` 或 `antarctica-west1`。
  • 节点最好具有带有键 `another-node-label-key` 和值 `another-node-label-value` 的标签。

你可以使用 `operator` 字段来指定 Kubernetes 在解释规则时要使用的逻辑运算符。你可以使用 `In`、`NotIn`、`Exists`、`DoesNotExist`、`Gt` 和 `Lt`。

阅读运算符,以了解更多关于它们如何工作的信息。

`NotIn` 和 `DoesNotExist` 允许你定义节点反亲和性行为。或者,你可以使用节点污点来排斥特定节点上的 Pod。

有关更多信息,请参阅使用节点亲和性将 Pod 分配到节点

节点亲和性权重

您可以为 preferredDuringSchedulingIgnoredDuringExecution 亲和性类型的每个实例指定一个介于 1 到 100 之间的weight。当调度器找到满足 Pod 的所有其他调度要求的节点时,调度器会遍历节点满足的每个首选规则,并将该表达式的 weight 值添加到总和中。

最终总和将添加到该节点其他优先级函数的分数中。当调度器为 Pod 做出调度决策时,总分最高的节点将优先考虑。

例如,考虑以下 Pod 规范

apiVersion: v1
kind: Pod
metadata:
  name: with-affinity-preferred-weight
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,并将权重添加到该节点的其他分数中,并将 Pod 调度到最终得分最高的节点上。

每个调度配置文件的节点亲和性

功能状态: Kubernetes v1.20 [beta]

配置多个调度配置文件时,您可以将配置文件与节点亲和性关联,如果配置文件仅适用于特定的一组节点,则此功能非常有用。为此,请将 addedAffinity 添加到NodeAffinity 插件args 字段中,该插件位于调度器配置中。例如:

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - schedulerName: foo-scheduler
    pluginConfig:
      - name: NodeAffinity
        args:
          addedAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: scheduler-profile
                  operator: In
                  values:
                  - foo

addedAffinity 将应用于所有将 .spec.schedulerName 设置为 foo-scheduler 的 Pod,以及 PodSpec 中指定的 NodeAffinity。也就是说,为了匹配 Pod,节点需要满足 addedAffinity 和 Pod 的 .spec.NodeAffinity

由于最终用户看不到 addedAffinity,因此它的行为可能会让他们感到意外。使用与调度器配置文件名称具有明确关联的节点标签。

Pod 间亲和性和反亲和性

Pod 间亲和性和反亲和性允许您根据已在该节点上运行的 Pod 的标签(而不是节点标签)来约束 Pod 可以调度到哪些节点上。

Pod 间亲和性和反亲和性规则的形式为“如果 X 已经运行了一个或多个满足规则 Y 的 Pod,则此 Pod 应该(或在反亲和性的情况下,不应该)在 X 中运行”,其中 X 是拓扑域,例如节点、机架、云提供商区域或类似区域,而 Y 是 Kubernetes 尝试满足的规则。

您可以将这些规则 (Y) 表示为带有可选的关联命名空间列表的标签选择器。Pod 是 Kubernetes 中的命名空间对象,因此 Pod 标签也隐式具有命名空间。任何用于 Pod 标签的标签选择器都应指定 Kubernetes 应在其中查找这些标签的命名空间。

您可以使用 topologyKey 来表示拓扑域 (X),它是系统用于表示域的节点标签的键。有关示例,请参阅众所周知的标签、注解和污点

Pod 间亲和性和反亲和性的类型

节点亲和性类似,Pod 亲和性和反亲和性有两种类型,如下所示:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

例如,您可以使用 requiredDuringSchedulingIgnoredDuringExecution 亲和性来告诉调度器将两个服务的 Pod 放置在同一个云提供商区域中,因为它们之间会进行大量通信。同样,您可以使用 preferredDuringSchedulingIgnoredDuringExecution 反亲和性来将服务的 Pod 分布到多个云提供商区域中。

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

使用 Pod 间亲和性将一组 Pod 调度到自身

如果当前正在调度的 Pod 是与自身具有亲和性的一系列 Pod 中的第一个,则如果它通过所有其他亲和性检查,则允许调度它。这通过验证集群中没有其他 Pod 与此 Pod 的命名空间和选择器匹配、该 Pod 与其自身的术语匹配以及所选节点与所有请求的拓扑匹配来确定。这确保即使所有 Pod 都指定了 Pod 间亲和性,也不会发生死锁。

Pod 亲和性示例

考虑以下 Pod 规约:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 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,而反亲和性规则使用“软性”的 preferredDuringSchedulingIgnoredDuringExecution

亲和性规则指定,只有当节点属于其他 Pod 已标记有 security=S1 的特定区域时,调度器才允许将示例 Pod 放置在该节点上。例如,如果我们有一个包含指定区域(我们称之为“区域 V”)的集群,该区域由标记有 topology.kubernetes.io/zone=V 的节点组成,则只要在区域 V 中至少有一个已标记有 security=S1 的 Pod,调度器就可以将 Pod 分配到区域 V 中的任何节点。相反,如果区域 V 中没有带有 security=S1 标签的 Pod,则调度器不会将示例 Pod 分配到该区域中的任何节点。

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

要更加熟悉 Pod 亲和性和反亲和性的示例,请参阅设计提案

您可以在 Pod 亲和性和反亲和性的 operator 字段中使用 InNotInExistsDoesNotExist 值。

阅读运算符,以了解更多关于它们如何工作的信息。

原则上,topologyKey 可以是任何允许的标签键,但出于性能和安全原因,以下情况除外:

  • 对于 Pod 亲和性和反亲和性,在 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 中都不允许使用空的 topologyKey 字段。
  • 对于 requiredDuringSchedulingIgnoredDuringExecution Pod 反亲和性规则,准入控制器 LimitPodHardAntiAffinityTopologytopologyKey 限制为 kubernetes.io/hostname。如果您想允许自定义拓扑,可以修改或禁用准入控制器。

除了 labelSelectortopologyKey 之外,您还可以选择指定一个命名空间列表,labelSelector 应使用与 labelSelectortopologyKey 相同的级别的 namespaces 字段与之匹配。如果省略或为空,则 namespaces 默认为出现亲和性/反亲和性定义的 Pod 的命名空间。

命名空间选择器

功能状态: Kubernetes v1.24 [稳定版]

您还可以使用 namespaceSelector 来选择匹配的命名空间,namespaceSelector 是对一组命名空间的标签查询。亲和性项将应用于通过 namespaceSelectornamespaces 字段选择的命名空间。请注意,空的 namespaceSelector ({}) 会匹配所有命名空间,而空的或空列表 namespaces 以及空的 namespaceSelector 会匹配规则定义所在的 Pod 的命名空间。

matchLabelKeys

功能状态: Kubernetes v1.31 [beta](默认启用:true)

Kubernetes 包括 Pod 亲和性或反亲和性的可选 matchLabelKeys 字段。该字段指定在满足 Pod(反)亲和性时,应该与传入 Pod 的标签匹配的标签的键。

这些键用于从 Pod 标签中查找值;这些键值标签与使用 labelSelector 字段定义的匹配限制相结合(使用 AND)。组合过滤选择将纳入 Pod(反)亲和性计算的现有 Pod 集。

一个常见的用例是将 matchLabelKeyspod-template-hash 一起使用(在作为 Deployment 一部分管理的 Pod 上设置,其中每个修订的值是唯一的)。在 matchLabelKeys 中使用 pod-template-hash 可以让您定位属于与传入 Pod 相同修订版的 Pod,从而使滚动升级不会破坏亲和性。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: application-server
...
spec:
  template:
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - database
            topologyKey: topology.kubernetes.io/zone
            # Only Pods from a given rollout are taken into consideration when calculating pod affinity.
            # If you update the Deployment, the replacement Pods follow their own affinity rules
            # (if there are any defined in the new Pod template)
            matchLabelKeys:
            - pod-template-hash

mismatchLabelKeys

功能状态: Kubernetes v1.31 [beta](默认启用:true)

Kubernetes 为 Pod 亲和性或反亲和性包含一个可选的 mismatchLabelKeys 字段。该字段指定当满足 Pod(反)亲和性时,不应与传入 Pod 的标签匹配的标签键。

一个示例用例是确保 Pod 进入仅调度来自同一租户或团队的 Pod 的拓扑域(节点、区域等)。换句话说,您希望避免在同一拓扑域上同时运行来自两个不同租户的 Pod。

apiVersion: v1
kind: Pod
metadata:
  labels:
    # Assume that all relevant Pods have a "tenant" label set
    tenant: tenant-a
...
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      # ensure that pods associated with this tenant land on the correct node pool
      - matchLabelKeys:
          - tenant
        topologyKey: node-pool
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      # ensure that pods associated with this tenant can't schedule to nodes used for another tenant
      - mismatchLabelKeys:
        - tenant # whatever the value of the "tenant" label for this Pod, prevent
                 # scheduling to nodes in any pool where any Pod from a different
                 # tenant is running.
        labelSelector:
          # We have to have the labelSelector which selects only Pods with the tenant label,
          # otherwise this Pod would hate Pods from daemonsets as well, for example,
          # which aren't supposed to have the tenant label.
          matchExpressions:
          - key: tenant
            operator: Exists
        topologyKey: node-pool

更实际的用例

当 Pod 间亲和性和反亲和性与更高层次的集合(如 ReplicaSet、StatefulSet、Deployment 等)一起使用时,它们会更加有用。这些规则允许您配置一组工作负载应共同放置在同一定义的拓扑结构中;例如,优先将两个相关的 Pod 放置在同一节点上。

例如:假设有一个三节点集群。您使用该集群运行一个 Web 应用程序和一个内存缓存(如 Redis)。对于此示例,还假设 Web 应用程序和内存缓存之间的延迟应尽可能低。您可以使用 Pod 间亲和性和反亲和性来尽可能将 Web 服务器与缓存共同放置。

在以下 Redis 缓存的 Deployment 示例中,副本获取标签 app=storepodAntiAffinity 规则告诉调度器避免将具有 app=store 标签的多个副本放置在单个节点上。这会在单独的节点中创建每个缓存。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

以下 Web 服务器的 Deployment 示例创建具有标签 app=web-store 的副本。Pod 亲和性规则告诉调度器将每个副本放置在具有标签 app=store 的 Pod 的节点上。Pod 反亲和性规则告诉调度器永远不要将多个 app=web-store 服务器放置在单个节点上。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.16-alpine

创建上述两个 Deployment 会导致以下集群布局,其中每个 Web 服务器与缓存共同放置在三个不同的节点上。

node-1node-2node-3
webserver-1webserver-2webserver-3
cache-1cache-2cache-3

总体效果是每个缓存实例很可能被运行在同一节点上的单个客户端访问。这种方法旨在最大程度地减少偏差(负载不均衡)和延迟。

您可能还有其他使用 Pod 反亲和性的原因。有关使用与此示例相同的高可用性反亲和性配置的 StatefulSet 示例,请参阅 ZooKeeper 教程

nodeName

nodeName 是一种比亲和性或 nodeSelector 更直接的节点选择形式。nodeName 是 Pod 规约中的一个字段。如果 nodeName 字段不为空,则调度器将忽略该 Pod,并且指定节点上的 kubelet 会尝试将 Pod 放置在该节点上。使用 nodeName 会覆盖使用 nodeSelector 或亲和性和反亲和性规则。

使用 nodeName 选择节点的一些限制是

  • 如果指定的节点不存在,则 Pod 将不会运行,在某些情况下可能会被自动删除。
  • 如果指定的节点没有足够的资源来容纳 Pod,则 Pod 将会失败,并且其原因将指示失败的原因,例如内存不足或 CPU 不足。
  • 云环境中的节点名称并非总是可预测或稳定的。

以下是使用 nodeName 字段的 Pod 规约示例

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

上面的 Pod 将仅在节点 kube-01 上运行。

Pod 拓扑分布约束

您可以使用拓扑分布约束来控制 Pod 如何在集群中跨故障域(如区域、区域、节点)或您定义的任何其他拓扑域分布。您可能会这样做以提高性能、预期可用性或整体利用率。

阅读 Pod 拓扑分布约束,了解有关它们如何工作的更多信息。

操作符

以下是您可以在上述 nodeAffinitypodAffinityoperator 字段中使用的所有逻辑操作符。

操作符行为
In标签值存在于提供的字符串集中
NotIn标签值不包含在提供的字符串集中
Exists对象上存在具有此键的标签
DoesNotExist对象上不存在具有此键的标签

以下操作符只能与 nodeAffinity 一起使用。

操作符行为
Gt字段值将被解析为整数,该整数小于解析由该选择器命名的标签的值所产生的整数
Lt字段值将被解析为整数,该整数大于解析由该选择器命名的标签的值所产生的整数

下一步

上次修改时间为 2024 年 7 月 26 日 太平洋标准时间上午 5:03:将 MatchLabelKeysInPodAffinity 升级到 beta (#45181) (108e2a9f0f)