调度

    在K8s中,调度是指将Pod放置到合适的节点上。调度器通过 K8s 的监测机制来发现集群中新创建且尚未被调度到节点上的Pod。调度器会将所发现的每一个未调度的Pod调度到一个合适的节点上来运行。

kube-scheduler调度器

   kube-scheduler组件是K8s集群的默认调度器,并且是集群控制面的一部分。对每一个新创建的Pod或者是未被调度的 Pod,kube-scheduler 会选择一个最优的节点去运行这个Pod。然而,Pod内的每一个容器对资源都有不同的需求,而且Pod本身也有不同的需求。因此,Pod 在被调度到节点上之前,根据这些特定的调度需求,需要对集群中的节点进行一次过滤。在一个集群中满足一个 Pod调度请求的所有节点称之为可调度节点。如果没有任何一个节点能满足 Pod 的资源请求,那么这个 Pod 将一直停留在未调度状态直到调度器能够找到合适的Node节点。kube-scheduler调度器先在集群中找到一个Pod的所有可调度节点,然后根据一系列函数对这些可调度节点打分,选出其中得分最高的节点来运行 Pod。之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做绑定。

    在做调度决定时需要考虑的因素包括:单独和整体的资源请求、硬件/软件/策略限制、 亲和以及反亲和要求、数据局部性、负载间的干扰等等。

kube-scheduler调度流程

   kube-scheduler 给一个 Pod 做调度选择时包含两个步骤:

  • 过滤
  • 打分

    过滤阶段会将所有满足Pod调度需求的节点选出来。PodFitsResources 过滤函数会检查候选节点的可用资源能否满足 Pod 的资源请求。在过滤之后,得出一个节点列表,里面包含了所有可调度节点;通常情况下, 这个节点列表包含不止一个节点。如果这个列表是空的,代表这个 Pod 不可调度。

    在打分阶段,调度器会为 Pod 从所有可调度节点中选取一个最合适的节点。根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。最后,kube-scheduler 会将 Pod 调度到得分最高的节点上。如果存在多个得分最高的节点,kube-scheduler 会从中随机选取一个。

    支持以下两种方式配置调度器的过滤和打分行为:

  • 调度策略:允许你配置过滤所用的 断言(Predicates) 和打分所用的 优先级(Priorities)。
  • 调度配置:允许你配置实现不同调度阶段的插件, 包括:QueueSort、Filter、Score、Bind、Reserve、Permit 等等。你也可以配置 kube-scheduler 运行不同的配置文件。

将Pod分配给指定节点

    可以通过一些手段约束一个Pod以便限制其只能在特定的节点上运行,或优先在特定的节点上运行。有几种方法可以实现这点:

  • 节点标签
  • 亲和性与反亲和性
  • nodeName字段
  • Pod拓扑分布约束
  • 污点和容忍度

节点标签

    标签(Labels)是附加到 K8s 对象(比如 Pod)上的键值对。标签旨在用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义。标签可以用于组织和选择对象的子集。标签可以在创建时附加到对象,随后可以随时添加和修改。每个对象都可以定义一组键/值标签。每个键对于给定对象必须是唯一的,如下:

"metadata": {  "labels": {    "key1" : "value1",    "key2" : "value2"  }}

    与很多其他 K8s 对象类似,节点也有标签(Labels),也可以手动给节点添加标签。K8s 也会为集群中所有节点添加一些标准的标签。

给节点添加标签

    执行如下命令给指定的k8s-node1节点添加标签 key1=value1。

#给指定的节点打标签kubectl label nodes k8s-node1 key1=value1#移除标签kubectl label nodes k8s-node1 key1-

创建一个将被调度到你选择的节点的 Pod

    如下Pod 配置文件描述了一个拥有节点选择器 key1: value1 的 Pod。这表明该 Pod 将被调度到有 key1: value1 标签的节点上。


apiVersion: v1kind: Podmetadata:  name: nginx  labels:    env: testspec:  containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent  nodeSelector:    key1: value1

    可以看到该pod确实创建在具有该标签的node节点上。

亲和性和反亲和性

   nodeSelector 提供了一种最简单的方法来将 Pod 约束到具有特定标签的节点上,而亲和性和反亲和性扩展了你可以定义的约束类型。使用亲和性与反亲和性的一些好处有如下:

  • 亲和性、反亲和性语言的表达能力更强。nodeSelector 只能选择拥有所有指定标签的节点,而亲和性、反亲和性为你提供对选择逻辑的更强控制能力。
  • 你可以标明某规则是“软需求”或者“偏好”,这样调度器在无法找到匹配节点时仍然调度该 Pod。
  • 你可以使用节点上(或其他拓扑域中)运行的其他 Pod 的标签来实施调度约束, 而不是只能使用节点本身的标签。这个能力让你能够定义规则允许哪些 Pod 可以被放置在一起。

亲和性功能由两种类型的亲和性组成:

  • 节点亲和性功能类似于 nodeSelector 字段,但它的表达能力更强,并且允许你指定软规则。
  • Pod 间亲和性、反亲和性允许你根据其他 Pod 的标签来约束 Pod。


节点亲和性

    节点亲和性概念上类似于 nodeSelector, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。节点亲和性有两种:

  • requiredDuringSchedulingIgnoredDuringExecution:调度器只有在规则被满足的时候才能执行调度。此功能类似于 nodeSelector, 但其语法表达能力更强。
  • preferredDuringSchedulingIgnoredDuringExecution:调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。

    注:在上述类型中,IgnoredDuringExecution 意味着如果节点标签在 Kubernetes 调度 Pod 后发生了变更,Pod 仍将继续运行。

Pod 间亲和性与反亲和性

   Pod间亲和性与反亲和性使你可以基于已经在节点上运行的Pod的标签来约束Pod可以调度到的节点,而不是基于节点上的标签。

   Pod间亲和性与反亲和性的规则格式为“如果 X 上已经运行了一个或多个满足规则 Y 的 Pod, 则这个 Pod 应该(或者在反亲和性的情况下不应该)运行在 X 上”。这里的 X 可以是节点、机架、云提供商可用区或地理区域或类似的拓扑域, Y 则是 K8s 尝试满足的规则。

    你可以通过标签选择算符的形式来表达规则(Y),并可根据需要指定关联的名字空间列表。Pod 在 K8s 中是名字空间作用域的对象,因此 Pod 的标签也隐式地具有名字空间属性。针对 Pod 标签的所有标签选择算符都要指定名字空间,K8s 会在指定的名字空间内寻找标签。

    你会通过 topologyKey 来表达拓扑域(X)的概念,其取值是系统用来标示域的节点标签键。

    注:Pod 间亲和性和反亲和性都需要相当的计算量,因此会在大规模集群中显著降低调度速度。我们不建议在包含数百个节点的集群中使用这类设置。Pod 反亲和性需要节点上存在一致性的标签。换言之, 集群中每个节点都必须拥有与 topologyKey 匹配的标签。如果某些或者所有节点上不存在所指定的 topologyKey 标签,调度行为可能与预期的不同。

用节点亲和性把Pod分配到节点

    执行如下命令给指定的k8s-node1节点添加标签 key1=value1。


#给指定的节点打标签kubectl label nodes k8s-node1 key1=value1

    下面清单描述了一个 Pod,它有一个节点亲和性配置 requiredDuringSchedulingIgnoredDuringExecution,key1=value1。这意味着 pod 只会被调度到具有 key1=value1 标签的节点上。


apiVersion: v1kind: Podmetadata:  name: nginx4spec:  affinity:    nodeAffinity:      requiredDuringSchedulingIgnoredDuringExecution:        nodeSelectorTerms:        - matchExpressions:          - key: key1            operator: In            values:            - value1              containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent

  本清单描述了一个Pod,它有一个节点亲和性设置 preferredDuringSchedulingIgnoredDuringExecution,key1=value1。这意味着 pod 将首选具有 key1=value1 标签的节点。如果没有找到key1=value1 标签的节点,则会随机调度到可用的Node节点上。


apiVersion: v1kind: Podmetadata:  name: nginx5spec:  affinity:    nodeAffinity:      preferredDuringSchedulingIgnoredDuringExecution:      - weight: 1        preference:          matchExpressions:          - key: key1            operator: In            values:            - value1           containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent

nodeName字段

    可以通过设置 nodeName 将某个 Pod 调度到特定的节点,且该 Pod 将只能被调度到特定节点。


apiVersion: v1kind: Podmetadata:  name: nginx2spec:  nodeName: k8s-node2  containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent

Pod 拓扑分布约束

    可以使用拓扑分布约束(Topology Spread Constraints)来控制Pod在集群内故障域之间的分布,故障域的示例有区域(Region)、可用区(Zone)、节点和其他用户自定义的拓扑域。这样做有助于提升性能、实现高可用或提升资源利用率。详细:Pod 拓扑分布约束: https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/topology-spread-constraints/

污点(Taint)和容忍度(Toleration)

    节点亲和性是Pod的一种属性,它使Pod被吸引到一类特定的节点(这可能是出于一种偏好,也可能是硬性要求)。而污点(Taint)则相反,污点(Taint)是应用在Node节点之上的它使节点能够排斥一类特定的Pod,具有污点Taint的node和pod是互斥关系;而容忍度(Toleration)是应用于 Pod 上的,容忍度允许(但不要求)调度器调度带有对容忍度的Pod到Node节点上,污点和容忍度的目的是优化pod在集群间的调度。作为其功能的一部分, 调度器也会评估其他参数。

    污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod, 是不会被该节点接受的。

给Node节点打污点

    给节点k8s-master增加一个污点,它的键名是key1,键值是value1,效果是NoSchedule。这表示只有拥有和这个污点相匹配的容忍度的 Pod 才有可能够被分配到k8s-node1这个节点上。


#查看节点信息kubectl describe node k8s-node1#添加污点kubectl taint nodes k8s-node1 key1=value1:NoSchedule#移除污点kubectl taint nodes k8s-node1 key1=value1:NoSchedule-

在pod中定义容忍度

    可以在 PodSpec 中定义 Pod 的容忍度。下面两个容忍度均与上面例子中使用kubectl taint命令创建的污点相匹配, 因此如果一个 Pod 拥有其中的任何一个容忍度都可能能够被分配到node1:


tolerations:- key: "key1"  operator: "Equal"  value: "value1"  effect: "NoSchedule"


tolerations:- key: "key1"  operator: "Exists"  effect: "NoSchedule"

实例


apiVersion: v1kind: Podmetadata:  name: myapp  labels:    env: testspec:  containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent  tolerations:  - key: "key1"    operator: "Exists"    effect: "NoSchedule"

    一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果。

operator 的参数含义:

  • 如果 operator 是 Exists (此时容忍度不能指定 value)
  • 如果 operator 是 Equal ,则它们的 value 应该相等。
  • 如果 operator 不指定,则默认为Equal。

    注:如果一个容忍度的 key 为空且 operator 为 Exists, 表示这个容忍度与任意的 key、value 和 effect 都匹配,即这个容忍度能容忍任何污点。

effect 的参数含义:

  • 如果effect是NoSchedule,则新的不能容忍的pod不能再调度过来,但是之前运行在node节点中的Pod不受影响。
  • 如果effect是NoExecute,则新的不能容忍的pod不能调度过来,老的pod也会被驱逐。
  • 如果effect是PreferNoScheduler,则表示尽量不调度到污点节点中去。

注:如果 effect 为空,则可以与所有键名 key1 的效果相匹配。

多个污点的匹配规则

    可以给一个节点添加多个污点,也可以给一个Pod添加多个容忍度。K8s处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的effect 值决定了 Pod 是否会被分配到该节点,特别是以下情况:

  • 如果未被过滤的污点中存在至少一个effect值为NoSchedule的污点, 则 Kubernetes 不会将Pod分配到该节点。
  • 如果未被过滤的污点中不存在 effect 值为NoSchedule的污点, 但是存在 effect 值为PreferNoSchedule的污点, 则 Kubernetes 会尝试不将 Pod 分配到该节点。
  • 如果未被过滤的污点中存在至少一个 effect 值为NoExecute 的污点, 则 Kubernetes 不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。

    例如,假设您给一个节点添加了如下污点:


kubectl taint nodes node1 key1=value1:NoSchedulekubectl taint nodes node1 key1=value1:NoExecutekubectl taint nodes node1 key2=value2:NoSchedule

假定有一个 Pod,它有两个容忍度:


tolerations:- key: "key1"  operator: "Equal"  value: "value1"  effect: "NoSchedule"- key: "key1"  operator: "Equal"  value: "value1"  effect: "NoExecute"

    上述 Pod 不会被分配到 node1 节点,因为其没有容忍度和第三个污点相匹配。 

    但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。

    通常情况下,如果给一个节点添加了一个 effect 值为 NoExecute 的污点, 则任何不能忍受这个污点的 Pod 都会马上被驱逐,任何可以忍受这个污点的 Pod 都不会被驱逐。但是,如果 Pod 存在一个 effect 值为 NoExecute 的容忍度指定了可选属性tolerationSeconds 的值,则表示在给节点添加了上述污点之后, Pod 还能继续在节点上运行的时间。例如:


tolerations:- key: "key1"  operator: "Equal"  value: "value1"  effect: "NoExecute"  tolerationSeconds: 3600

    这表示如果这个 Pod 正在运行,同时一个匹配的污点被添加到其所在的节点, 那么 Pod 还将继续在节点上运行 3600 秒,然后被驱逐。如果在此之前上述污点被删除了,则 Pod 不会被驱逐。

说明

  • 默认情况下,只有master节点会有污点(role.kubernetes.io/master:NoSchedule),Node节点默认没有污点。这也是为什么默认情况下Pod不会被分配到master节点的原因。
  • 当只有一个节点没污点,其他节点都有污点时,创建pod的yml文件的容忍度没匹配上所有的污点时,创建的pod节点在没有污点的节点上。
  • 当只有一个节点没污点,其他节点都有污点时,创建pod的yml文件的容忍度匹配上污点时,创建的pod节点也在没有污点的节点上。
  • 当所有node的节点都有污点,创建pod的yml文件的容忍度能匹配上污点时,创建的pod节点在匹配的污点的节点上。
  • 当所有node节点都有污点,但是创建pod的yml文件的容忍度没匹配上所有的污点时,创建的pod节点的NODE这里为空。

3

给master节点分配pod

    默认情况下创建的pod是不会被分配到master节点上的,因为master节点默认被打上了node-role.kubernetes.io/master:NoSchedule的污点。


#查询k8s-master节点的Taintskubectl describe node k8s-master | grep Taints#取消污点kubectl taint nodes k8s-master role.kubernetes.io/master:NoSchedule-

注:其实也可以省略取消master节点污点这一步。

然后使用nodeName字段指定k8s-master


apiVersion: v1kind: Podmetadata:  name: nginxspec:  nodeName: k8s-master  containers:  - name: nginx    image: nginx    imagePullPolicy: IfNotPresent