你可以约束一个
以便 限制 其只能在特定的上运行,
或优先在特定的节点上运行。
有几种方法可以实现这点,推荐的方法都是用
[标签选择算符]来进行选择。
通常这样的约束不是必须的,因为调度器将自动进行合理的放置(比如,将 Pod 分散到节点上,
而不是将 Pod 放置在可用资源不足的节点上等等)。但在某些情况下,你可能需要进一步控制
Pod 被部署到哪个节点。例如,确保 Pod 最终落在连接了 SSD 的机器上,
或者将来自两个不同的服务且有大量通信的 Pods 被放置在同一个可用区。
你可以使用下列方法中的任何一种来选择 Kubernetes 对特定 Pod 的调度:
与很多其他 Kubernetes 对象类似,节点也有[标签]。
你可以[手动地添加标签]。
Kubernetes 也会为集群中所有节点添加一些标准的标签。
参见[常用的标签、注解和污点]以了解常见的节点标签。
这些标签的取值是取决于云提供商的,并且是无法在可靠性上给出承诺的。
例如,kubernetes.io/hostname
的取值在某些环境中可能与节点名称相同,
而在其他环境中会取不同的值。
通过为节点添加标签,你可以准备让 Pod 调度到特定节点或节点组上。
你可以使用这个功能来确保特定的 Pod 只能运行在具有一定隔离性,安全性或监管属性的节点上。
如果使用标签来实现节点隔离,建议选择节点上的
无法修改的标签键。
这可以防止受感染的节点在自身上设置这些标签,进而影响调度器将工作负载调度到受感染的节点。
[NodeRestriction
准入插件]防止
kubelet 使用 node-restriction.kubernetes.io/
前缀设置或修改标签。
要使用该标签前缀进行节点隔离:
node-restriction.kubernetes.io/
前缀的标签添加到 Node 对象,example.com.node-restriction.kubernetes.io/fips=true
或example.com.node-restriction.kubernetes.io/pci-dss=true
。nodeSelector
是节点选择约束的最简单推荐形式。你可以将 nodeSelector
字段添加到
Pod 的规约中设置你希望目标节点所具有的[节点标签]。
Kubernetes 只会将 Pod 调度到拥有你所指定的每个标签的节点上。
进一步的信息可参见[将 Pod 指派给节点]。
nodeSelector
提供了一种最简单的方法来将 Pod 约束到具有特定标签的节点上。
亲和性和反亲和性扩展了你可以定义的约束类型。使用亲和性与反亲和性的一些好处有:
nodeSelector
只能选择拥有所有指定标签的节点。亲和性功能由两种类型的亲和性组成:
nodeSelector
字段,但它的表达能力更强,并且允许你指定软规则。节点亲和性概念上类似于 nodeSelector
,
它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。
节点亲和性有两种:
requiredDuringSchedulingIgnoredDuringExecution
:nodeSelector
,preferredDuringSchedulingIgnoredDuringExecution
:在上述类型中,IgnoredDuringExecution
意味着如果节点标签在 Kubernetes
调度 Pod 后发生了变更,Pod 仍将继续运行。
你可以使用 Pod 规约中的 .spec.affinity.nodeAffinity
字段来设置节点亲和性。
例如,考虑下面的 Pod 规约:
在这一示例中,所应用的规则如下:
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 中指定多个条件,
只要其中一个 nodeSelectorTerms
满足(各个条件按逻辑或操作组合)的话,Pod 就可以被调度到节点上。
如果你在与 nodeSelectorTerms
中的条件相关联的单个 matchExpressions
字段中指定多个表达式,
则只有当所有表达式都满足(各表达式按逻辑与操作组合)时,Pod 才能被调度到节点上。
参阅[使用节点亲和性来为 Pod 指派节点],
以了解进一步的信息。
你可以为 preferredDuringSchedulingIgnoredDuringExecution
亲和性类型的每个实例设置weight
字段,其取值范围是 1 到 100。
当调度器找到能够满足 Pod 的其他调度请求的节点时,调度器会遍历节点满足的所有的偏好性规则,
并将对应表达式的 weight
值加和。
最终的加和值会添加到该节点的其他优先级函数的评分之上。
在调度器为 Pod 作出调度决定时,总分最高的节点的优先级也最高。
例如,考虑下面的 Pod 规约:
如果存在两个候选节点,都满足 preferredDuringSchedulingIgnoredDuringExecution
规则,
其中一个节点具有标签 label-1:key-1
,另一个节点具有标签 label-2:key-2
,
调度器会考察各个节点的 weight
取值,并将该权重值添加到节点的其他得分值之上,
如果你希望 Kubernetes 能够成功地调度此例中的 Pod,你必须拥有打了kubernetes.io/os=linux
标签的节点。
在配置多个[调度方案]时,
你可以将某个方案与节点亲和性关联起来,如果某个调度方案仅适用于某组特殊的节点时,
这样做是很有用的。
要实现这点,可以在[调度器配置]中为
[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
除遵从 Pod 规约中设置的节点亲和性之外,
还适用于将 .spec.schedulerName
设置为 foo-scheduler
。
换言之,为了匹配 Pod,节点需要满足 addedAffinity
和 Pod 的 .spec.NodeAffinity
。
由于 addedAffinity
对最终用户不可见,其行为可能对用户而言是出乎意料的。
应该使用与调度方案名称有明确关联的节点标签。
DaemonSet 控制器[为 DaemonSet 创建 Pod],
但该控制器不理会调度方案。
DaemonSet 控制器创建 Pod 时,默认的 Kubernetes 调度器负责放置 Pod,
并遵从 DaemonSet 控制器中设置的 nodeAffinity
规则。
Pod 间亲和性与反亲和性使你可以基于已经在节点上运行的 Pod 的标签来约束
Pod 可以调度到的节点,而不是基于节点上的标签。
Pod 间亲和性与反亲和性的规则格式为“如果 X 上已经运行了一个或多个满足规则 Y 的 Pod,
则这个 Pod 应该(或者在反亲和性的情况下不应该)运行在 X 上”。
这里的 X 可以是节点、机架、云提供商可用区或地理区域或类似的拓扑域,
Y 则是 Kubernetes 尝试满足的规则。
你通过[标签选择算符]
的形式来表达规则(Y),并可根据需要指定选关联的名字空间列表。
Pod 在 Kubernetes 中是名字空间作用域的对象,因此 Pod 的标签也隐式地具有名字空间属性。
针对 Pod 标签的所有标签选择算符都要指定名字空间,Kubernetes
会在指定的名字空间内寻找标签。
你会通过 topologyKey
来表达拓扑域(X)的概念,其取值是系统用来标示域的节点标签键。
相关示例可参见[常用标签、注解和污点]。
Pod 间亲和性和反亲和性都需要相当的计算量,因此会在大规模集群中显著降低调度速度。
我们不建议在包含数百个节点的集群中使用这类设置。
Pod 反亲和性需要节点上存在一致性的标签。换言之,
集群中每个节点都必须拥有与 topologyKey
匹配的标签。
如果某些或者所有节点上不存在所指定的 topologyKey
标签,调度行为可能与预期的不同。
与[节点亲和性]类似,Pod 的亲和性与反亲和性也有两种类型:
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
例如,你可以使用 requiredDuringSchedulingIgnoredDuringExecution
亲和性来告诉调度器,
将两个服务的 Pod 放到同一个云提供商可用区内,因为它们彼此之间通信非常频繁。
类似地,你可以使用 preferredDuringSchedulingIgnoredDuringExecution
反亲和性来将同一服务的多个 Pod 分布到多个云提供商可用区中。
要使用 Pod 间亲和性,可以使用 Pod 规约中的 .affinity.podAffinity
字段。
对于 Pod 间反亲和性,可以使用 Pod 规约中的 .affinity.podAntiAffinity
字段。
考虑下面的 Pod 规约:
本示例定义了一条 Pod 亲和性规则和一条 Pod 反亲和性规则。Pod 亲和性规则配置为requiredDuringSchedulingIgnoredDuringExecution
,而 Pod 反亲和性配置为preferredDuringSchedulingIgnoredDuringExecution
。
亲和性规则表示,仅当节点和至少一个已运行且有 security=S1
的标签的
Pod 处于同一区域时,才可以将该 Pod 调度到节点上。
更确切的说,调度器必须将 Pod 调度到具有 topology.kubernetes.io/zone=V
标签的节点上,并且集群中至少有一个位于该可用区的节点上运行着带有security=S1
标签的 Pod。
反亲和性规则表示,如果节点处于 Pod 所在的同一可用区且至少一个 Pod 具有security=S2
标签,则该 Pod 不应被调度到该节点上。
更确切地说, 如果同一可用区中存在其他运行着带有 security=S2
标签的 Pod 节点,
并且节点具有标签 topology.kubernetes.io/zone=R
,Pod 不能被调度到该节点上。
查阅[设计文档]
以进一步熟悉 Pod 亲和性与反亲和性的示例。
你可以针对 Pod 间亲和性与反亲和性为其 operator
字段使用 In
、NotIn
、Exists
、DoesNotExist
等值。
原则上,topologyKey
可以是任何合法的标签键。出于性能和安全原因,topologyKey
有一些限制:
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
中,topologyKey
requiredDuringSchedulingIgnoredDuringExecution
要求的 Pod 反亲和性,LimitPodHardAntiAffinityTopology
要求 topologyKey
只能是kubernetes.io/hostname
。如果你希望使用其他定制拓扑逻辑,除了 labelSelector
和 topologyKey
,你也可以指定 labelSelector
要匹配的命名空间列表,方法是在 labelSelector
和 topologyKey
所在层同一层次上设置 namespaces
。
如果 namespaces
被忽略或者为空,则默认为 Pod 亲和性/反亲和性的定义所在的命名空间。
用户也可以使用 namespaceSelector
选择匹配的名字空间,namespaceSelector
是对名字空间集合进行标签查询的机制。
亲和性条件会应用到 namespaceSelector
所选择的名字空间和 namespaces
字段中所列举的名字空间之上。
注意,空的 namespaceSelector
({}
)会匹配所有名字空间,而 null 或者空的namespaces
列表以及 null 值 namespaceSelector
意味着“当前 Pod 的名字空间”。
Pod 间亲和性与反亲和性在与更高级别的集合(例如 ReplicaSet、StatefulSet、
Deployment 等)一起使用时,它们可能更加有用。
这些规则使得你可以配置一组工作负载,使其位于所定义的同一拓扑中;
例如优先将两个相关的 Pod 置于相同的节点上。
以一个三节点的集群为例。你使用该集群运行一个带有内存缓存(例如 Redis)的 Web 应用程序。
在此例中,还假设 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
下例的 Deployment 为 Web 服务器创建带有标签 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 的示例,该 StatefulSet 配置了反亲和性以实现高可用,
所使用的是与此例相同的技术。
nodeName
是比亲和性或者 nodeSelector
更为直接的形式。nodeName
是 Pod
规约中的一个字段。如果 nodeName
字段不为空,调度器会忽略该 Pod,
而指定节点上的 kubelet 会尝试将 Pod 放到该节点上。
使用 nodeName
规则的优先级会高于使用 nodeSelector
或亲和性与非亲和性的规则。
使用 nodeName
来选择节点的方式有一些局限性:
nodeName
旨在供自定义调度程序或需要绕过任何已配置调度程序的高级场景使用。
如果已分配的 Node 负载过重,绕过调度程序可能会导致 Pod 失败。
你可以使用[节点亲和性]或 [nodeselector
字段]将
Pod 分配给特定 Node,而无需绕过调度程序。
下面是一个使用 nodeName
字段的 Pod 规约示例:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: kube-01
上面的 Pod 只能运行在节点 kube-01
之上。
你可以使用 拓扑分布约束(Topology Spread Constraints) 来控制
在集群内故障域之间的分布,
故障域的示例有区域(Region)、可用区(Zone)、节点和其他用户自定义的拓扑域。
这样做有助于提升性能、实现高可用或提升资源利用率。
阅读 [Pod 拓扑分布约束]
以进一步了解这些约束的工作方式。