将 Pod 分配到节点
您可以约束一个 Pod,使其限制在特定 节点(s) 上运行,或者优先在特定节点上运行。有几种方法可以做到这一点,推荐的方法都使用 标签选择器 来简化选择。通常,您不需要设置任何此类约束;调度程序 将自动执行合理的放置(例如,将您的 Pod 分散到各个节点上,以免将 Pod 放置在资源不足的节点上)。但是,在某些情况下,您可能希望控制 Pod 部署到的节点,例如,确保 Pod 位于具有附加到它的 SSD 的节点上,或者将来自两个不同服务的 Pod(它们之间进行大量通信)共同放置到同一个可用区中。
您可以使用以下任何方法来选择 Kubernetes 在哪里调度特定 Pod
- nodeSelector 字段与 节点标签 匹配
- 亲和性和反亲和性
- nodeName 字段
- Pod 拓扑传播约束
节点标签
与许多其他 Kubernetes 对象一样,节点也有 标签。您可以 手动添加标签。Kubernetes 还会在集群中的所有节点上填充一组 标准标签。
注意
这些标签的值是特定于云提供商的,不能保证可靠。例如,kubernetes.io/hostname
的值在某些环境中可能与节点名称相同,而在其他环境中则可能不同。节点隔离/限制
向节点添加标签允许您将 Pod 定向到特定节点或节点组上进行调度。您可以使用此功能来确保特定 Pod 仅在具有特定隔离、安全或监管属性的节点上运行。
如果您使用标签进行节点隔离,请选择 kubelet 无法修改的标签键。这可以防止受损节点在其自身上设置这些标签,从而使调度程序将工作负载调度到受损节点上。
NodeRestriction
准入插件 阻止 kubelet 设置或修改具有 node-restriction.kubernetes.io/
前缀的标签。
要使用该标签前缀进行节点隔离
- 确保您正在使用 节点授权程序 并且已启用
NodeRestriction
准入插件。 - 使用
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 分配到节点。
亲和性和反亲和性
nodeSelector
是将 Pod 约束到具有特定标签的节点的最简单方法。亲和性和反亲和性扩展了您可以定义的约束类型。亲和性和反亲和性的一些好处包括
- 亲和性/反亲和性语言更具表现力。
nodeSelector
仅选择具有所有指定标签的节点。亲和性/反亲和性让您对选择逻辑有更多控制。 - 您可以指示规则是软的还是首选的,以便调度程序即使找不到匹配的节点,也会调度 Pod。
- 您可以使用节点上运行的其他 Pod(或其他拓扑域)上的标签来约束 Pod,而不仅仅是节点标签,这使您可以定义哪些 Pod 可以共同放置在节点上的规则。
亲和性功能包含两种类型的亲和性
- 节点亲和性的功能类似于
nodeSelector
字段,但更具表现力,允许您指定软规则。 - Pod 间亲和性/反亲和性 允许您根据其他 Pod 上的标签来约束 Pod。
节点亲和性
节点亲和性在概念上类似于 nodeSelector
,允许您根据节点标签来约束 Pod 可以调度到的节点。节点亲和性有两种类型
requiredDuringSchedulingIgnoredDuringExecution
:除非满足规则,否则调度程序无法调度 Pod。这与nodeSelector
的功能类似,但语法更具表现力。preferredDuringSchedulingIgnoredDuringExecution
:调度程序尝试找到满足规则的节点。如果找不到匹配的节点,调度程序仍然会调度 Pod。
注意
在前面的类型中,IgnoredDuringExecution
表示如果节点标签在 Kubernetes 调度 Pod 后发生更改,则 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 从特定节点。
注意
如果您同时指定 nodeSelector
和 nodeAffinity
,则两者都必须满足,Pod 才能调度到节点上。
如果您在与 nodeAffinity
类型关联的 nodeSelectorTerms
中指定多个术语,那么如果可以满足指定的术语之一,则 Pod 可以调度到节点上(术语是 OR 的)。
如果您在与 nodeSelectorTerms
中的术语关联的单个 matchExpressions
字段中指定多个表达式,那么只有在满足所有表达式时,Pod 才能调度到节点上(表达式是 AND 的)。
有关更多信息,请参阅 使用节点亲和性将 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 成功调度此示例中的 Pod,则必须具有带有kubernetes.io/os=linux
标签的现有节点。每个调度配置文件的节点亲和性
Kubernetes v1.20 [beta]
在配置多个 调度配置文件 时,您可以将配置文件与节点亲和性关联,这在配置文件仅适用于特定节点集时非常有用。为此,请在 调度器配置 中的 NodeAffinity
插件 的 args
字段中添加 addedAffinity
。例如
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
对最终用户不可见,因此其行为可能对他们来说出乎意料。使用与调度器配置文件名称有明确关联的节点标签。
注意
DaemonSet 控制器(它 为 DaemonSet 创建 Pod)不支持调度配置文件。当 DaemonSet 控制器创建 Pod 时,默认的 Kubernetes 调度器会放置这些 Pod 并遵守 DaemonSet 控制器中的任何nodeAffinity
规则。Pod 间亲和性和反亲和性
Pod 间亲和性和反亲和性允许您根据已在该节点上运行的 **Pod** 的标签来约束 Pod 可以被调度到的节点,而不是节点标签。
Pod 间亲和性和反亲和性规则采用以下形式:“此 Pod 应该(或者,在反亲和性的情况下,不应该)在 X 中运行,如果 X 已经运行一个或多个满足规则 Y 的 Pod”,其中 X 是一个拓扑域,例如节点、机架、云提供商区域或区域,或类似的,而 Y 是 Kubernetes 试图满足的规则。
您可以将这些规则 (Y) 表达为 标签选择器,并附带一个可选的关联命名空间列表。Pod 是 Kubernetes 中的命名空间对象,因此 Pod 标签也隐式地具有命名空间。任何针对 Pod 标签的标签选择器都应指定 Kubernetes 应该在其中查找这些标签的命名空间。
您可以使用 topologyKey
来表达拓扑域 (X),topologyKey
是系统用来表示域的节点标签的键。有关示例,请参阅 众所周知的标签、注释和污点。
注意
Pod 间亲和性和反亲和性需要大量的处理,这会显著降低大型集群中的调度速度。我们不建议在超过数百个节点的集群中使用它们。注意
Pod 反亲和性要求节点始终如一地标记,换句话说,集群中的每个节点都必须具有与topologyKey
匹配的适当标签。如果某些或所有节点缺少指定的 topologyKey
标签,则会导致意外行为。Pod 间亲和性和反亲和性的类型
与 节点亲和性 类似,Pod 亲和性和反亲和性有两种类型,如下所示
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
例如,您可以使用 requiredDuringSchedulingIgnoredDuringExecution
亲和性来告诉调度器将两个服务的 Pod 共同放置在同一个云提供商区域中,因为它们之间经常通信。类似地,您可以使用 preferredDuringSchedulingIgnoredDuringExecution
反亲和性将服务的 Pod 分散到多个云提供商区域中。
要使用 Pod 间亲和性,请使用 Pod spec 中的 affinity.podAffinity
字段。对于 Pod 间反亲和性,请使用 Pod spec 中的 affinity.podAntiAffinity
字段。
使用 Pod 间亲和性调度一组 Pod 到自身
如果当前正在调度的 Pod 是具有自身亲和性的系列中的第一个,则如果它通过了所有其他亲和性检查,则允许它被调度。这是通过验证集群中没有其他 Pod 匹配此 Pod 的命名空间和选择器,Pod 匹配它自己的条款,以及所选节点匹配所有请求的拓扑来确定的。这确保即使所有 Pod 都指定了 Pod 间亲和性,也不会出现死锁。
Pod 亲和性示例
考虑以下 Pod spec
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 放置在属于特定 区域 的节点上,其中其他 Pod 已被标记为 security=S1
。例如,如果我们有一个具有指定区域的集群,我们称之为“区域 V”,它由标记为 topology.kubernetes.io/zone=V
的节点组成,调度器可以将 Pod 分配到区域 V 中的任何节点,只要区域 V 中至少有一个 Pod 已经标记为 security=S1
。相反,如果区域 V 中没有标记为 security=S1
的 Pod,调度器将不会将示例 Pod 分配到该区域中的任何节点。
反亲和性规则指定调度器应尝试避免将 Pod 调度到属于特定 区域 的节点上,其中其他 Pod 已被标记为 security=S2
。例如,如果我们有一个具有指定区域的集群,我们称之为“区域 R”,它由标记为 topology.kubernetes.io/zone=R
的节点组成,调度器应避免将 Pod 分配到区域 R 中的任何节点,只要区域 R 中至少有一个 Pod 已经标记为 security=S2
。相反,如果区域 R 中没有标记为 security=S2
的 Pod,则反亲和性规则不会影响调度到区域 R。
要更熟悉 Pod 亲和性和反亲和性的示例,请参阅 设计提案。
您可以在 Pod 亲和性和反亲和性的 operator
字段中使用 In
、NotIn
、Exists
和 DoesNotExist
值。
阅读 运算符 以了解有关它们如何工作的更多信息。
原则上,topologyKey
可以是任何允许的标签键,但出于性能和安全原因,以下情况除外
- 对于 Pod 亲和性和反亲和性,
topologyKey
字段在requiredDuringSchedulingIgnoredDuringExecution
和preferredDuringSchedulingIgnoredDuringExecution
中都不允许为空。 - 对于
requiredDuringSchedulingIgnoredDuringExecution
Pod 反亲和性规则,准入控制器LimitPodHardAntiAffinityTopology
将topologyKey
限制为kubernetes.io/hostname
。如果您想允许自定义拓扑,可以修改或禁用准入控制器。
除了 labelSelector
和 topologyKey
之外,您还可以选择指定一个命名空间列表,labelSelector
应该使用与 labelSelector
和 topologyKey
相同级别的 namespaces
字段匹配这些命名空间。如果省略或为空,namespaces
默认设置为定义亲和性/反亲和性的 Pod 的命名空间。
命名空间选择器
Kubernetes v1.24 [稳定]
您还可以使用 namespaceSelector
选择匹配的命名空间,namespaceSelector
是对命名空间集的标签查询。亲和性项将应用于由 namespaceSelector
和 namespaces
字段选择的命名空间。请注意,空 namespaceSelector
({}) 匹配所有命名空间,而空或空的 namespaces
列表和空 namespaceSelector
匹配定义规则的 Pod 的命名空间。
matchLabelKeys
Kubernetes v1.29 [alpha]
注意
matchLabelKeys
字段是一个 alpha 级别的字段,在 Kubernetes 1.30 中默认情况下处于禁用状态。当您想要使用它时,您必须通过 MatchLabelKeysInPodAffinity
功能门控 来启用它。
Kubernetes 包含一个可选的 matchLabelKeys
字段,用于 Pod 亲和性或反亲和性。该字段指定标签的键,这些标签应该与传入 Pod 的标签匹配,以满足 Pod(反)亲和性。
这些键用于从 Pod 标签中查找值;这些键值标签与使用 labelSelector
字段定义的匹配限制相结合(使用 AND
)。组合过滤会选择将用于 Pod(反)亲和性计算的现有 Pod 集。
一个常见的用例是将 matchLabelKeys
与 pod-template-hash
一起使用(在作为部署的一部分管理的 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.29 [alpha]
注意
mismatchLabelKeys
字段是一个 alpha 级别的字段,在 Kubernetes 1.30 中默认情况下处于禁用状态。当您想要使用它时,您必须通过 MatchLabelKeysInPodAffinity
功能门控 来启用它。
Kubernetes 包含一个可选的 mismatchLabelKeys
字段,用于 Pod 亲和性或反亲和性。该字段指定标签的键,这些标签 **不应** 与传入 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=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-1 | node-2 | node-3 |
---|---|---|
webserver-1 | webserver-2 | webserver-3 |
cache-1 | cache-2 | cache-3 |
总体效果是,每个缓存实例很可能被运行在同一节点上的单个客户端访问。这种方法旨在最大限度地减少偏差(负载不平衡)和延迟。
您可能还有其他使用 Pod 反亲和性的原因。请参阅 ZooKeeper 教程,其中介绍了使用与本示例相同的技术,通过反亲和性配置 StatefulSet 以实现高可用性。
nodeName
nodeName
是比亲和性或 nodeSelector
更直接的节点选择方式。nodeName
是 Pod 规范中的一个字段。如果 nodeName
字段不为空,调度器将忽略 Pod,并且名为该节点的 kubelet 将尝试将 Pod 放置在该节点上。使用 nodeName
将覆盖使用 nodeSelector
或亲和性和反亲和性规则。
使用 nodeName
选择节点的一些限制包括:
- 如果名为该节点的节点不存在,Pod 将不会运行,并且在某些情况下可能会被自动删除。
- 如果名为该节点的节点没有足够的资源来容纳 Pod,Pod 将失败,并且其原因将指示失败的原因,例如 OutOfmemory 或 OutOfcpu。
- 云环境中的节点名称并不总是可预测或稳定的。
警告
nodeName
旨在供自定义调度器或需要绕过任何已配置调度器的更高级用例使用。绕过调度器可能会导致 Pod 失败,因为分配的节点可能会被过度订阅。您可以使用 节点亲和性 或 nodeSelector
字段 将 Pod 分配到特定节点,而无需绕过调度器。以下是一个使用 nodeName
字段的 Pod 规范示例
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: kube-01
上面的 Pod 仅会在节点 kube-01
上运行。
Pod 拓扑传播约束
您可以使用 *拓扑传播约束* 来控制 Pods 在集群中如何跨故障域(例如区域、区域、节点或您定义的任何其他拓扑域)传播。您可能这样做是为了提高性能、预期可用性或整体利用率。
阅读 Pod 拓扑传播约束,详细了解其工作原理。
运算符
以下是您可以在上面提到的 nodeAffinity
和 podAffinity
的 operator
字段中使用的所有逻辑运算符。
运算符 | 行为 |
---|---|
In | 标签值存在于提供的字符串集中 |
NotIn | 标签值不包含在提供的字符串集中 |
Exists | 对象上存在具有此键的标签 |
DoesNotExist | 对象上不存在具有此键的标签 |
以下运算符只能与 nodeAffinity
一起使用。
运算符 | 行为 |
---|---|
Gt | 字段值将被解析为整数,并且该整数小于通过解析此选择器命名的标签的值而得到的整数 |
Lt | 字段值将被解析为整数,并且该整数大于通过解析此选择器命名的标签的值而得到的整数 |
注意
Gt
和 Lt
运算符不适用于非整数值。如果给定值无法解析为整数,则 Pod 将无法调度。此外,Gt
和 Lt
不适用于 podAffinity
。下一步
- 详细了解 污点和容忍度。
- 阅读 节点亲和性 和 Pod 间亲和性/反亲和性 的设计文档。
- 了解 拓扑管理器 如何参与节点级资源分配决策。
- 了解如何使用 nodeSelector。
- 了解如何使用 亲和性和反亲和性。