Concepts

Detailed explanations of Kubernetes system concepts and abstractions.

Edit This Page

分配 Pod 到 Node 上

您可以限制 pod 只能在特定 node 上运行,或者更倾向于在某些特定 node 上运行。 有几种方法可以做到这一点,他们都使用 label selectors 进行选择。 一般来说,这种约束是不必要的,因为 scheduler 会自动进行合理的安排(例如,将 pod 分布在所有 node 上,而不是将 pod 分配到空闲资源不足的 node 上,等等) 但是在某些情况下,您可能需要对 node 有更多的控制权,例如,确保一个 pod 调度到安装有 SSD 的机器上,或者将来自两个不同服务并且需要大量通信的 pod 分配到同一个可用区域内。

您可以 在我们的 docs 库中 找到这些示例文件。

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:
  containers:
  - name: nginx
    image: nginx

然后添加一个 nodeSelector 如下:

pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

当您运行 kubectl create -f pod.yaml 后,这个 pod 就会被调度到附加了相应标签的 node 上了!您可以通过运行 kubectl get pods -o wide 然后查看 pod 被分配到的 “NODE” 来验证是否成功执行。

插曲:内置的 node 标签

除了 自己打标签,node 已经预先附加了一个标准标签集。在 Kubernetes 1.4 版本中,这些标签有

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

nodeSelector 提供了一个非常简单的方法来将 pod 约束到具有特定标签的节点。而亲和性/反亲和性特性(目前处于 beta 阶段),极大地扩展了您可以表达的约束类型。关键的改进有

  1. 表达语言更丰富(不仅仅是 “AND 精确匹配”)
  2. 你可以指定规则是 “软”/“偏好” 的,而不是一个硬性要求,所以即使 scheduler 不能满足它的规则,pod 仍然会被调度
  3. 您可以针对 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 亲和性,叫做 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution。您可以认为它们分别是 “硬性” 和 “软性” 的,即前者要求 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
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

这个 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 亲和性语法支持以下运算符:InNotInExistsDoesNotExistGtLt。 实际上并没有明确的 “node 反亲和性” 概念,不过 NotInDoesNotExist 提供了这种行为。

如果您指定了 nodeSelectornodeAffinity,那么 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 亲和性和反亲和性,叫做 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution,分别表示 “硬性” 和 “软性” 的要求。可以查看上述的 node 亲和性描述。 requiredDuringSchedulingIgnoredDuringExecution 的一个例子是,”将服务 A 和服务 B 的 pod 调度到同一个域内,因为这些服务相互之间有大量通信”。preferredDuringSchedulingIgnoredDuringExecution 反亲和性的一个例子是 “在整个域内平均分布这个服务的所有 pod”(这里如果用一个硬性的要求是不可行的,因为您可能要创建比域更多的 pod)。

在 PodSpec 中,通过 affinity 中的 podAffinity 字段指定 Pod 间的亲和性。 并且在 PodSpec 中,通过 affinity 中的 podAntiAffinity 字段 指定Pod 间的反亲和性。

以下是一个使用 pod 亲和性的 pod 示例:

pod-with-pod-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

该 pod 亲和性示例定义了一个 pod 亲和性规则和一个 pod 反亲和性规则。在这个示例中,podAffinity 使用的是 requiredDuringSchedulingIgnoredDuringExecutionpodAntiAffinity 使用 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 上(如果 topologyKeyfailure-domain.beta.kubernetes.io/zone 的话,表示如果 node 正在运行的 pod 拥有 key 为 “security” 并且 value 为 “S2” 的标签,那么这个 pod 就不能调度到该 node 上)。请参阅 设计文档 来获得更多 pod 亲和性和反亲和性的示例,同时也提供了 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 的示例。

pod 亲和性和反亲和性的合法操作是 InNotInExistsDoesNotExist

原则上,topologyKey 可以是任何合法的标签 key。然而,出于性能和安全原因,对 topologyKey 有一些限制:

  1. 对于亲和性和 RequiredDuringScheduling 的 pod 反亲和性,不允许 topologyKey 为空。
  2. 对于 RequiredDuringScheduling 的 pod 反亲和性,引入 LimitPodHardAntiAffinityTopology 准入控制器来限制 topologyKey 只能是 kubernetes.io/hostname。如果要使其适用于自定义拓扑结构,则可以修改准入控制器,或者直接禁用它。
  3. 对于 PreferredDuringScheduling 的 pod 反亲和性,空的 topologyKey 将被理解为 “所有的拓扑结构”(这里的 “所有的拓扑结构” 仅限于 kubernetes.io/hostnamefailure-domain.beta.kubernetes.io/zonefailure-domain.beta.kubernetes.io/region 的组合)。
  4. 除上述情况外,topologyKey 可以是任何合法的标签 key。

除了 labelSelectortopologyKey 外,您可以选择指定一个 labelSelector 应该匹配的 namespaces 列表(这与 labelSelectortopologyKey 的定义级别相同)。 如果省略的话,默认为拥有亲和性(或反亲和性)的 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 默认为 Equal

注意: 有两种特殊情况:

tolerations:
- operator: "Exists"
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。特别是,

例如,假设您给一个 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 出现问题时 pod 的驱逐行为(alpha 特性)

当 node 出现问题时 pod 的驱逐行为(alpha 特性)

上述章节我们提到了 NoExecute taint 的效果,它将对正在运行的 pod 产生如下影响

上述行为是一个 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/unreachablenode.alpha.kubernetes.io/notReady,两个 toleration 的 effect 都是 NoExecute 并且都不指定 tolerationSeconds。这确保在出现这些问题时,DaemonSet 的 pod 将永远不被驱逐,这也符合这个特性被禁用时的行为。

Analytics

Create an Issue Edit this Page