将 Pod 分配给节点

你可以限制一个 Pod,使其被**限制**运行在特定的节点上,或者**倾向于**运行在特定的节点上。有几种方法可以实现这一点,建议的方法都使用标签选择器来辅助选择。通常,你不需要设置任何此类约束;调度器将自动进行合理的放置(例如,将你的 Pods 分布在不同节点上,以免将 Pods 放置在可用资源不足的节点上)。但是,在某些情况下,你可能希望控制 Pod 部署到哪个节点,例如,确保 Pod 最终落在连接有 SSD 的节点上,或者将两个需要大量通信的不同服务的 Pods 放在同一可用区域中。

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

节点标签

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

节点隔离/限制

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

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

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

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

  1. 确保你正在使用 Node 鉴权器 并**启用** `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` 是最简单的推荐节点选择约束形式。你可以在 Pod 规范中添加 `nodeSelector` 字段,并指定你希望目标节点拥有的节点标签。Kubernetes 只会将 Pod 调度到具有你指定的所有标签的节点上。

更多信息,请参阅将 Pod 分配到节点

亲和性和反亲和性

`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:3.8

在此示例中,适用以下规则:

  • 节点**必须**有一个键为 `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` 允许你定义节点反亲和性行为。另外,你可以使用节点污点来排斥 Pods 离开特定节点。

更多信息,请参阅使用节点亲和性将 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:3.8

如果有两个可能匹配 `preferredDuringSchedulingIgnoredDuringExecution` 规则的节点,一个带有 `label-1:key-1` 标签,另一个带有 `label-2:key-2` 标签,调度器会考虑每个节点的 `weight`,并将该权重添加到该节点的其他分数中,然后将 Pod 调度到最终分数最高的节点上。

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

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

在配置多个调度配置文件时,你可以将一个配置文件与节点亲和性关联起来,如果一个配置文件只适用于一组特定的节点,这将非常有用。为此,请在调度器配置`NodeAffinity` 插件的 `args` 字段中添加 `addedAffinity`。例如:

apiVersion: kubescheduler.config.k8s.io/v1
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` 的 Pods,并作为 PodSpec 中指定的 NodeAffinity 的补充。也就是说,为了匹配 Pod,节点需要满足 `addedAffinity` 和 Pod 的 `.spec.NodeAffinity`。

由于 `addedAffinity` 对最终用户不可见,其行为可能超出他们的预期。请使用与调度器配置文件名称明确相关的节点标签。

Pod 间亲和性与反亲和性

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

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

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

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

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

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

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

例如,你可以使用 `requiredDuringSchedulingIgnoredDuringExecution` 亲和性来告知调度器将两个服务的 Pods 共同放置在同一个云提供商区域中,因为它们之间通信频繁。类似地,你可以使用 `preferredDuringSchedulingIgnoredDuringExecution` 反亲和性将一个服务的 Pods 分散到多个云提供商区域。

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

调度行为

调度新 Pod 时,Kubernetes 调度器会根据当前集群状态评估 Pod 的亲和性/反亲和性规则:

  1. 硬约束 (节点过滤)

    • podAffinity.requiredDuringSchedulingIgnoredDuringExecutionpodAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution
      • 调度器确保根据现有 Pods,将新 Pod 分配给满足这些必需亲和性与反亲和性规则的节点。
  2. 软约束 (评分)

    • podAffinity.preferredDuringSchedulingIgnoredDuringExecutionpodAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution
      • 调度器根据节点满足这些首选亲和性与反亲和性规则的程度来为节点评分,以优化 Pod 的放置。
  3. 被忽略的字段

    • 现有 Pod 的 `podAffinity.preferredDuringSchedulingIgnoredDuringExecution`
      • 在为新 Pod 做出调度决定时,这些首选亲和性规则不予考虑。
    • 现有 Pod 的 `podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution`
      • 同样,现有 Pod 的首选反亲和性规则在调度时被忽略。

使用 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:3.8

此示例定义了一个 Pod 亲和性规则和一个 Pod 反亲和性规则。Pod 亲和性规则使用“硬”的 `requiredDuringSchedulingIgnoredDuringExecution`,而反亲和性规则使用“软”的 `preferredDuringSchedulingIgnoredDuringExecution`。

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

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

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

你可以在 Pod 亲和性与反亲和性的 `operator` 字段中使用 `In`、`NotIn`、`Exists` 和 `DoesNotExist` 值。

阅读运算符以了解更多这些运算符的工作原理。

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

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

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

命名空间选择器

特性状态: Kubernetes v1.24 [stable]

你还可以使用 `namespaceSelector` 选择匹配的命名空间,它是一个针对命名空间集的标签查询。亲和性术语应用于由 `namespaceSelector` 和 `namespaces` 字段选择的命名空间。请注意,空的 `namespaceSelector` ({}) 匹配所有命名空间,而 null 或空的 `namespaces` 列表和 null `namespaceSelector` 匹配定义规则的 Pod 的命名空间。

matchLabelKeys

特性状态: Kubernetes v1.33 [stable] (默认启用:true)

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

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

一个常见的用例是将 `matchLabelKeys` 与 `pod-template-hash`(在作为 Deployment 一部分管理的 Pods 上设置,其值对于每个修订版本都是唯一的)结合使用。在 `matchLabelKeys` 中使用 `pod-template-hash` 允许你定位与传入 Pod 属于同一修订版本的 Pods,这样滚动升级就不会破坏亲和性。

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.33 [stable] (默认启用:true)

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

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

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 have anti-affinity against Pods from daemonsets as well, for example,
          # which aren't supposed to have the tenant label.
          matchExpressions:
          - key: tenant
            operator: Exists
        topologyKey: node-pool

更实际的用例

当与更高级别的集合(如 ReplicaSet、StatefulSet、Deployment 等)一起使用时,Pod 间亲和性与反亲和性会更有用。这些规则允许你配置一组工作负载应共同位于同一已定义拓扑中;例如,倾向于将两个相关的 Pod 放置在同一个节点上。

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

在以下 Redis 缓存的 Deployment 示例中,副本获得标签 `app=store`。`podAntiAffinity` 规则告诉调度器避免将多个带有 `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 反亲和性的原因。请参阅 ZooKeeper 教程,了解使用与此示例相同的技术配置反亲和性以实现高可用性的 StatefulSet 示例。

nodeName

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

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

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

以下是使用 `nodeName` 字段的 Pod 规范示例:

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

上述 Pod 只会在节点 `kube-01` 上运行。

nominatedNodeName

功能状态: Kubernetes v1.34 [alpha] (默认禁用)

`nominatedNodeName` 可用于外部组件为挂起中的 Pod 提名节点。这种提名是尽力而为的:如果调度器确定 Pod 无法部署到被提名节点,则可能会忽略它。

此外,该字段可以由调度器(覆盖)写入:

  • 如果调度器通过抢占找到一个提名节点。
  • 如果调度器决定 Pod 的去向,并将其移至绑定周期。
    • 请注意,在这种情况下,只有当 Pod 必须通过 `WaitOnPermit` 或 `PreBind` 扩展点时,才会设置 `nominatedNodeName`。

以下是使用 `nominatedNodeName` 字段的 Pod 状态示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
...
status:
  nominatedNodeName: kube-01

Pod 拓扑分散约束

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

阅读Pod 拓扑分散约束以了解这些约束的工作原理。

运算符

以下是你可以用于上述 `nodeAffinity` 和 `podAffinity` 的 `operator` 字段的所有逻辑运算符。

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

以下运算符只能与 `nodeAffinity` 一起使用。

运算符行为
Gt字段值将被解析为整数,并且该整数小于通过解析此选择器所命名的标签的值而得到的整数
Lt字段值将被解析为整数,并且该整数大于通过解析此选择器所命名的标签的值而得到的整数

下一步

最后修改时间:2025 年 8 月 2 日下午 5:54(太平洋标准时间):更新 (ebc53a5b54)