您可以限制 pod 只能在特定 node 上运行,或者更倾向于在某些特定 node 上运行。 有几种方法可以做到这一点,他们都使用 label selectors 进行选择。 一般来说,这种约束是不必要的,因为 scheduler 会自动进行合理的安排(例如,将 pod 分布在所有 node 上,而不是将 pod 分配到空闲资源不足的 node 上,等等) 但是在某些情况下,您可能需要对 node 有更多的控制权,例如,确保一个 pod 调度到安装有 SSD 的机器上,或者将来自两个不同服务并且需要大量通信的 pod 分配到同一个可用区域内。
您可以 在我们的 docs 库中 找到这些示例文件。
- nodeSelector
- 第零步:前提条件
- 第一步:附加标签到 node 上
- 第二步:在您的 pod 配置中增加一个 nodeSelector 字段
- 插曲:内置的 node 标签
- 亲和性(Affinity)和反亲和性(Anti-affinity)
- Node 亲和性(beta 特性)
- pod 间的亲和性和反亲和性(beta 特性)
- Taint 和 toleration(beta 特性)
- 使用示例
- 当 node 出现问题时 pod 的驱逐行为(alpha 特性)
nodeSelector
nodeSelector
是最简单的控制方式。 nodeSelector
是 PodSpec 中的一个字段,它指定了键-值对的映射。如果想要 pod 能够运行在某个 node 上,那么这个 node 必须具有所有指定的键-值对的标签(node 也能拥有其它标签)。最常用的方式是单键-值对。
我们来看一个如何使用 nodeSelector
的例子。
第零步:前提条件
该示例假设您已经对 Kubernetes pod 有一个基本的了解,并且您已经 启动了一个 Kubernetes 集群。
第一步:附加标签到 node 上
运行 kubectl get nodes
来获得您集群所有 node 的名称。选择想要附加标签的 node,然后运行 kubectl label nodes <node-name> <label-key>=<label-value>
来把标签附加到您选择的 node 上。例如,如果您的 node 名称是 ‘kubernetes-foo-node-1.c.a-robinson.internal’,并且希望附加的标签为 ‘disktype=ssd’,那么您可以运行 kubectl label nodes kubernetes-foo-node-1.c.a-robinson.internal disktype=ssd
。
如果上述命令失败并且出现 “invalid command” 错误,那么可能是因为您使用了没有 label 命令的早期 kubectl 版本。如果是这种情况,请参阅 之前的版本 来获得如何手动设置 node 标签的教程。
另外请注意,标签键必须采用 DNS 标签的形式(如在 identifiers doc 中所述),也就是标签键中不能包含任何的大写字符。
您可以通过运行 kubectl get nodes --show-labels
来验证上述命令是否正确运行,并且验证 node 是否已经有了标签。
第二步:在您的 pod 配置中增加一个 nodeSelector 字段
使用任意一个您想要运行的 pod 配置文件,然后添加一个 nodeSelector。如以下的示例:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
s:
- name: nginx
image: nginx
然后添加一个 nodeSelector 如下:
pod.yaml |
|
当您运行 kubectl create -f pod.yaml
后,这个 pod 就会被调度到附加了相应标签的 node 上了!您可以通过运行 kubectl get pods -o wide
然后查看 pod 被分配到的 “NODE” 来验证是否成功执行。
插曲:内置的 node 标签
除了 自己打标签,node 已经预先附加了一个标准标签集。在 Kubernetes 1.4 版本中,这些标签有
kubernetes.io/hostname
failure-domain.beta.kubernetes.io/zone
failure-domain.beta.kubernetes.io/region
beta.kubernetes.io/instance-type
beta.kubernetes.io/os
beta.kubernetes.io/arch
亲和性(Affinity)和反亲和性(Anti-affinity)
nodeSelector
提供了一个非常简单的方法来将 pod 约束到具有特定标签的节点。而亲和性/反亲和性特性(目前处于 beta 阶段),极大地扩展了您可以表达的约束类型。关键的改进有
- 表达语言更丰富(不仅仅是 “AND 精确匹配”)
- 你可以指定规则是 “软”/“偏好” 的,而不是一个硬性要求,所以即使 scheduler 不能满足它的规则,pod 仍然会被调度
- 您可以针对 node 上正在运行的其它 pod (或者其它的拓扑域)制定标签,而不仅仅是 node 自身,这就能指定哪些 pod 能够(或者不能够)落在同一节点。
亲和性特性包含了两种类型的亲和性,”node 亲和性” 和 “pod 间的亲和性/反亲和性”。 Node 亲和性类似于已有的 nodeSelector
,但是拥有上述的第一和第二个优点。Pod 间的亲和性/反亲和性以 pod 标签作为约束,而不仅仅是 node 标签,就像上述第三点所述,它同时也拥有上述第一和第二个特性。
nodeSelector
将会像往常一样继续工作,但最终将被弃用,因为 node 亲和性可以表达 nodeSelector
能够表达的所有约束。
Node 亲和性(beta 特性)
Node 亲和性在 Kubernetes 1.2 版本中作为 alpha 特性引入。 Node 亲和性在概念上类似于 nodeSelector
– 它能够基于 node 标签约束哪些 pod 能够被分配到 node 上。
目前有两种类型的 node 亲和性,叫做 requiredDuringSchedulingIgnoredDuringExecution
和 preferredDuringSchedulingIgnoredDuringExecution
。您可以认为它们分别是 “硬性” 和 “软性” 的,即前者要求 pod 必须 满足指定的规则才能调度到 node 上(就像 nodeSelector
,但使用更具表达性的语法),后者指定 偏向,即让 scheduler 尝试但是不保证完全满足规则。与 nodeSelector
工作方式类似,名称中的 “IgnoredDuringExecution” 部分意味着,如果一个 node 的标签在运行时发生改变,从而导致 pod 的亲和性规则不再被满足时,pod 也仍然会继续运行在 node 上。以后我们计划提供 requiredDuringSchedulingRequiredDuringExecution
,这类似于 requiredDuringSchedulingIgnoredDuringExecution
,但是它会从不再满足 pod 的 node 亲和性的 node 上驱逐 Pod。
因此 requiredDuringSchedulingIgnoredDuringExecution
的一个例子是 “只能在有 Intel CPU 的 node 上运行 pod”,preferredDuringSchedulingIgnoredDuringExecution
的例子是 “尝试在可用域 XYZ 的范围内运行 pod,但是如果不满足,那么也允许在其它地方运行 pod”。
pod-with-node-affinity.yaml |
|
这个 node 亲和性规则意思是,pod 只能被调度在标签满足 key 为 kubernetes.io/e2e-az-name
并且 value 为 e2e-az1
或者 e2e-az2
的 node 上。另外,在满足条件的所有 node 中,更倾向于拥有 key 为 another-node-label-key
并且 value 为 another-node-label-value
的标签的 node。
您可以在例子中看到 In
运算符。新的 node 亲和性语法支持以下运算符:In
、NotIn
、Exists
、DoesNotExist
、Gt
和 Lt
。 实际上并没有明确的 “node 反亲和性” 概念,不过 NotIn
和 DoesNotExist
提供了这种行为。
如果您指定了 nodeSelector
和 nodeAffinity
,那么 pod 必须满足这 两个 规则才能调度到候选节点上。
如果您在 nodeAffinity
类型中指定了多个 nodeSelectorTerms
,那么 pod 将会被调度到 只要满足其中一个 nodeSelectorTerms
的 node 上。
如果您在 nodeSelectorTerms
中指定了多个 matchExpressions
,那么 pod 将会被调度到 满足所有 matchExpressions
的 node 上。
如果需要了解更多关于 node 亲和性的信息,请 点击这里 查看设计文档。
pod 间的亲和性和反亲和性(beta 特性)
在 Kubernetes 1.4 版本中引入了 Pod 间的亲和性和反亲和性。 Pod 间的亲和性和反亲和性允许您 根据已经在 node 上运行的 pod 的标签 来限制 pod 调度在哪个 node 上,而不是基于 node 上的标签。这些规则的形式是 “如果 X 已经运行一个或多个符合规则 Y 的 pod,那么这个 pod 应该(如果是反亲和性,则是不应该)运行在 X 上”。这里 Y 指具有关联命名空间列表的 LabelSelector(或者关联 “all” 命名空间);和 node 不同,由于 pod 都是有命名空间的(因此 pod 上的标签都隐式具有命名空间),所以基于 pod 标签的标签选择器(label selector)必须指定命名空间。概念上,X 是一个拓扑域,类似于 node、rack、cloud provider zone 和 cloud provider region 等等。您可以使用 topologyKey
来表示这个 X,topologyKey
是系统用来表示这个拓扑域的 node 标签,例如,查看上述 插曲:内置的 node 标签 中列出的标签 key。
和 node 亲和性一样,目前有两种类型的 pod 亲和性和反亲和性,叫做 requiredDuringSchedulingIgnoredDuringExecution
和 preferredDuringSchedulingIgnoredDuringExecution
,分别表示 “硬性” 和 “软性” 的要求。可以查看上述的 node 亲和性描述。 requiredDuringSchedulingIgnoredDuringExecution
的一个例子是,”将服务 A 和服务 B 的 pod 调度到同一个域内,因为这些服务相互之间有大量通信”。preferredDuringSchedulingIgnoredDuringExecution
反亲和性的一个例子是 “在整个域内平均分布这个服务的所有 pod”(这里如果用一个硬性的要求是不可行的,因为您可能要创建比域更多的 pod)。
在 PodSpec 中,通过 affinity
中的 podAffinity
字段指定 Pod 间的亲和性。 并且在 PodSpec 中,通过 affinity
中的 podAntiAffinity
字段 指定Pod 间的反亲和性。
以下是一个使用 pod 亲和性的 pod 示例:
pod-with-pod-affinity.yaml |
|
该 pod 亲和性示例定义了一个 pod 亲和性规则和一个 pod 反亲和性规则。在这个示例中,podAffinity
使用的是 requiredDuringSchedulingIgnoredDuringExecution
而 podAntiAffinity
使用 preferredDuringSchedulingIgnoredDuringExecution
。这个 pod 亲和性规则说的是这个 pod 落在的 node 必须满足一个条件:node 必须和至少一个拥有 key 为 “security” 并且 value 为 “S1” 标签的运行中 pod 同一个域(更确切地说,pod 能够落在的 node N 必须满足:N 有一个 key 为 failure-domain.beta.kubernetes.io/zone
并且 value 为某个值 V 的标签,这样集群中就至少有一个 node 拥有这个标签,并且有一个 key 为 “security” 并且 value 为 “S1” 的标签的 pod 在之上运行)。pod 反亲和性规则说的是,如果 node 正在运行的 pod 带有 key 为 “security” 并且 value 为 “S2” 的标签,那么倾向于不把 pod 调度到该 node 上(如果 topologyKey
是 failure-domain.beta.kubernetes.io/zone
的话,表示如果 node 正在运行的 pod 拥有 key 为 “security” 并且 value 为 “S2” 的标签,那么这个 pod 就不能调度到该 node 上)。请参阅 设计文档 来获得更多 pod 亲和性和反亲和性的示例,同时也提供了 requiredDuringSchedulingIgnoredDuringExecution
和 preferredDuringSchedulingIgnoredDuringExecution
的示例。
pod 亲和性和反亲和性的合法操作是 In
、NotIn
、Exists
和 DoesNotExist
。
原则上,topologyKey
可以是任何合法的标签 key。然而,出于性能和安全原因,对 topologyKey 有一些限制:
- 对于亲和性和
RequiredDuringScheduling
的 pod 反亲和性,不允许topologyKey
为空。 - 对于
RequiredDuringScheduling
的 pod 反亲和性,引入LimitPodHardAntiAffinityTopology
准入控制器来限制topologyKey
只能是kubernetes.io/hostname
。如果要使其适用于自定义拓扑结构,则可以修改准入控制器,或者直接禁用它。 - 对于
PreferredDuringScheduling
的 pod 反亲和性,空的topologyKey
将被理解为 “所有的拓扑结构”(这里的 “所有的拓扑结构” 仅限于kubernetes.io/hostname
、failure-domain.beta.kubernetes.io/zone
和failure-domain.beta.kubernetes.io/region
的组合)。 - 除上述情况外,
topologyKey
可以是任何合法的标签 key。
除了 labelSelector
和 topologyKey
外,您可以选择指定一个 labelSelector
应该匹配的 namespaces
列表(这与 labelSelector
和 topologyKey
的定义级别相同)。 如果省略的话,默认为拥有亲和性(或反亲和性)的 pod 所属的命名空间。如果定义了但是值为空,则表示使用 “all” 命名空间。
如果是 requiredDuringSchedulingIgnoredDuringExecution
的亲和性和反亲和性,那么 pod 必须满足所有相关的 matchExpressions
才能调度到 node 上。
请 点击这里 查看设计文档,以获得更多 pod 间亲和性/反亲和性的信息。
Taint 和 toleration(beta 特性)
Node 亲和性,根据之前的描述,是 pod 的一个属性,将其 吸引 到一个 node 集上(以倾向性或硬性要求的形式)。Taint 则刚好相反 – 它们允许一个 node 排斥 一些 pod。
Taint 和 toleration 一起工作,以确保 pod 不会被调度到不适当的 node 上。如果一个或多个 taint 应用于一个 node,这表示该节点不应该接受任何不能容忍 taint 的 pod。 Toleration 应用到 pod 上,则表示允许(但不要求)pod 调度到具有匹配 taint 的 node 上。
您可以通过使用 kubectl taint 命令来给 node 添加一个 taint。例如,
kubectl taint nodes node1 key=value:NoSchedule
在节点 node1
上增加一个 taint。这个 taint 的 key 为 key
且 value 为 value
,并且这个 taint 的作用是 NoSchedule
。 这表示除非 pod 拥有一个匹配的 toleration,否则它将无法调度到 node1
上。 您需要在 pod 的 PodSpec 指定一个 toleration。以下两个 toleration 都能 “匹配” 上述 kubectl taint
创建出来的 taint,因此拥有以下任一 toleration 的 pod 都能调度到 node1
上:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
tolerations:
- key: key
operator: Exists
value: value
effect: NoSchedule
一个 toleration 能够 “匹配” 一个 taint 除了需要所有的 key
和所有的 effect
都相等之外,还需要:
-
operator
是Exists
(这种情况下就不用指定value
),或者 -
operator
是Equal
并且所有的value
都相等
如果不指定的话 Operator
默认为 Equal
。
注意: 有两种特殊情况:
-
key
为空并且 operator 为Exists
表示能够匹配所有的 key、value 和 effect,也就是这能容忍所有情况。
tolerations:
- operator: "Exists"
- 一个空的
effect
能够匹配所有 key 为key
的 effect。
tolerations:
- key: "key"
operator: "Exists"
以上示例使用了 effect
中的 NoSchedule
。另外,您也可以使用 effect
中的 PreferNoSchedule
。 这是 NoSchedule
的一个 “偏向性” 或者 “软性” 的版本 – 系统将会 尝试 避免将一个无法容忍 taint 的 pod 调度到 node 上,但这不是必须的。第三种 effect
的类型是 NoExecute
,接下来会进行讲解。
您可以添加多个 taint 到一个 node 上,也能添加多个 toleration 到一个 pod 上。 Kubernetes 以过滤器的形式来处理多个 taint 和 toleration 的情况:从 node 的所有 taint 开始,如果 pod 拥有匹配的 toleration 则将 taint 忽略掉;最后剩下的没有被忽略的 taint 将作用于这个 pod。特别是,
- 如果最后剩下的 taint 中有至少一个带有 effect 为
NoSchedule
,那么 Kubernetes 将不会把这个 pod 调度到这个 node - 如果最后剩下的 taint 中没有带有 effect 为
NoSchedule
但是有至少一个 taint 带有 effect 为PreferNoSchedule
,那么 Kubernetes 将会 尝试 避免将 pod 调度到这个 node 上 - 如果最后剩下的 taint 中有至少一个带有 effect 为
NoExecute
,那么将会把 pod 从这个 node 上驱逐(如果 pod 已经运行在 node 上),或者不会将其调度到这个 node 上(如果 pod 还没有运行在 node 上)。
例如,假设您给一个 node 添加了如下 taint
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule
并且有一个 pod 拥有如下两个 toleration:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
在这种情况下,这个 pod 将不会调度到这个 node 上,因为没有 toleration 能够匹配第三个 taint。 但是如果 pod 已经运行在 node 上,那么当添加 taint 后它也会继续运行,因为第三个 taint 是三个 taint 中唯一不被 pod 所容忍的,而且这个 taint 是 NoSchedule。
通常来说,如果一个 node 添加了一个 effect 为 NoExecute
的 taint,那么任何不能容忍这个 taint 的 pod 都会被驱逐,并且任何能够容忍这个 taint 的 pod 将永远不会被驱逐。 但是,一个 effect 为 NoExecute
的 toleration 能够指定一个附加的字段 tolerationSeconds
,用来指定在 node 添加了 taint 后,pod 能够停留在 node 上的时间。例如,
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
表示如果这个 pod 已经运行在 node 上并且 node 添加了一个匹配的 taint,那么这个 pod 将会在 node 上停留 3600 秒,然后才会被驱逐。 但是如果 taint 在这个时间前被移除,那么这个 pod 将不会被驱逐。
使用示例
Taint 和 toleration 是一种用来引导 pod 远离 node 或者驱逐不应该运行的 pod 的灵活的方法。使用示例有
- 专用的 node:如果您想要设置一些 node 为专用的,以让特定的用户才能使用,您可以给这些 node 添加一个 taint(比如说,
kubectl taint nodes nodename dedicated=groupName:NoSchedule
),然后添加一个对应的 toleration 到它们的 pod 上(这个通过编写一个自定义的 准入控制器 会比较容易实现)。具有这些 toleration 的 pod 将被允许使用已添加 taint 的 node(专用的),同时也能使用集群中的其它 node。如果您想要让 node 只能被特定的 pod 使用 并且 保证这些 pod 只能 使用这些专用的 node,那么您应该额外添加一个和 taint 类似的 label 到这些 node 集上(例如,dedicated=groupName
),然后准入控制器也应该额外添加一个 node 亲和性以让 pod 只能调度到具有dedicated=groupName
标签的 node 上。 - 具有特殊硬件的 node:在一个集群中有一小部分 node 有专门的硬件(例如 GPU),理想情况下应该让不需要这些专门硬件的 pod 避开这些节点,这样才能为后续的需要专门硬件的 pod 留有充足的空间。可以通过给具有特殊硬件的 node 添加 taint(例如,
kubectl taint nodes nodename special=true:NoSchedule
或者kubectl taint nodes nodename special=true:PreferNoSchedule
),然后给需要这些特殊硬件的 pod 添加一个对应的 toleration。就如上述专用的 node 示例,通过 准入控制器 来使用 toleration 会比较容易。 例如,准入控制器可以使用 pod 的一些特性来确保 pod 能够使用这些特殊的 node,因此准入控制器应该添加 toleration。您需要一些额外的机制,来确保需要特殊硬件的 pod 只能 被调度到具有特殊硬件的 node 上,例如,您可以使用 不透明的整数资源 来表示这些特殊资源,然后在 PodSpec 中像其它资源一样进行请求;或者您也可以给具有特殊硬件的 node 添加标签,然后在需要这些硬件的 pod 上使用 node 亲和性。
将在下一章节中介绍 * 当 node 出现问题时 pod 的驱逐行为(alpha 特性)
当 node 出现问题时 pod 的驱逐行为(alpha 特性)
上述章节我们提到了 NoExecute
taint 的效果,它将对正在运行的 pod 产生如下影响
- 不能容忍对应 taint 的 pod 将立即被驱逐
- 能够容忍对应 taint 但是没有在 toleration specification 中指定
tolerationSeconds
则继续保持在原节点上 - 能够容忍对应 taint 并且指定
tolerationSeconds
则在原节点上保持指定的时间
上述行为是一个 beta 版本特性。另外,Kubernetes 1.6 版本为 “用 taint 来表达 node 问题(目前只有 “node unreachable” 和 “node not ready”,分别对应 NodeCondition 从 “Ready” 变为 “Unknown” 或者 “False”)” 提供了 alpha 支持。当 TaintBasedEvictions
这个 alpha 特性被启用后(您可以通过在 --feature-gates
中包含 TaintBasedEvictions=true
来实现,例如 --feature-gates=FooBar=true,TaintBasedEvictions=true
),NodeController 将会自动添加 taint 并且禁用基于 Ready NodeCondition 的正常驱逐 pod 的流程。 (注意:为了维持现有的 rate limiting 在 node 出现问题时驱逐 pod 的默认行为,系统实际上以 rate-limited 的方式添加 taint。这可以防止当 master 和 node 相互不可达时大量 pod 被驱逐的情况。) 这个 alpha 特性,结合 tolerationSeconds
,允许 pod 指定在出现这些问题时它将要在 node 上停留多长时间。
例如,一个拥有大量本地状态的应用程序可能希望在网络出现问题时能够在 node 上停留较长时间,以期待网络能够自动恢复从而避免 pod 被驱逐。 这种情况下 pod 的 toleration 将如下所示
tolerations:
- key: "node.alpha.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
(对于 node not ready 的情况,修改 key 为 node.alpha.kubernetes.io/notReady
。)
请注意,Kubernetes 自动为 pod 添加 key 为 node.alpha.kubernetes.io/notReady
并且 tolerationSeconds=300
的 toleration,除非用户提供的 pod 配置中为 node.alpha.kubernetes.io/notReady
指定另外的 toleration。 同样地,Kubernetes 也自动为 pod 添加 key 为 node.alpha.kubernetes.io/unreachable
并且 tolerationSeconds=300
的 toleration,除非用户提供的 pod 配置中为 node.alpha.kubernetes.io/unreachable
指定另外的 toleration。
这些自动添加的 toleration 确保在 node 出现这些问题后,为 pod 提供一个继续在 node 上停留5分钟的默认行为。 这两个默认的 toleration 是通过 DefaultTolerationSeconds admission controller 添加的。
DaemonSet 的 pod 会添加两个 toleration:node.alpha.kubernetes.io/unreachable
和 node.alpha.kubernetes.io/notReady
,两个 toleration 的 effect 都是 NoExecute
并且都不指定 tolerationSeconds
。这确保在出现这些问题时,DaemonSet 的 pod 将永远不被驱逐,这也符合这个特性被禁用时的行为。