写点什么

Kubernetes 如何将 Pod 分配给节点

作者:玄月九
  • 2022 年 5 月 01 日
  • 本文字数:11815 字

    阅读完需:约 39 分钟

1. 前言

在 Kubernetes 中,调度是指将 Pod 放置到合适的 Node 上。调度器通过 Kubernetes 的监测(Watch)机制来发现集群中新创建且尚未被调度到 Node 上的 Pod,然后根据调度策略将这些 Pod 调度到一个合适的 Node 上来运行( 更新 Pod 的 nodeName 字段 )。当然我们也可以约束一个 Pod 只能在特定的 Node 上运行。

本文将对以下几种常见的调度方式进行简单介绍。并分享下使用这几种调度方式的一些实践。

  • nodeName:直接指定 Node 主机名。

  • nodeSelector:Node 选择器,为 Node 打上标签,然后 Pod 中通过 nodeSelector 选择打上标签的 Node。

  • Affinity 亲和性调度

  • nodeAffinity:Node 亲和性。

  • podAffinity:Pod 亲和性。

  • podAntiAffinity:Pod 反亲和性。

  • taints 与 tolerations:污点与容忍度,为 Node 打上污点,在 Pod 中配置对污点的容忍度。


注意:文中的 Node 和节点是一个意思,Label 和标签是一个意思。

2. nodeName

nodeName 是节点选择约束的最简单方法,但是由于其自身限制,通常不使用它。

nodeName 是 PodSpec 的一个字段。 如果 nodeName 在 PodSpec 中指定了,则它优先于其他节点选择方法。

使用 nodeName 来选择节点的一些限制:

  • 如果指定的 Node 不存在,则该 pod 将不会运行,并且在某些情况下可能会被自动删除。

  • 如果指定的 Node 没有资源来容纳 Pod,Pod 将会调度失败,并且会显示失败原因,例如 OutOfmemory 或 OutOfcpu。

  • 云环境中的 Node 名称并非总是可预测或稳定的。

下面是使用 nodeName 字段的 Pod 配置文件的例子,pod 将运行在 node-a 节点上。

apiVersion: v1kind: Podmetadata:  name: nginx  namespace: defaultspec:  containers:  - name: nginx    image: nginx  nodeName: node-a
复制代码

下面是使用 nodeName 字段的 Deployment 配置文件的例子,pod 将运行在 node-b 节点上。

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx  namespace: defaultspec:  replicas: 1  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - image: nginx:latest        name: nginx      nodeName: node-b
复制代码

3. nodeSelector

nodeSelector 是节点选择约束的最简单推荐形式。nodeSelector 是 PodSpec 的一个字段。

我们知道 label 是 Kubernetes 中一个非常重要的概念,用户可以非常灵活的利用 Label 来管理集群中的资源,最常见的一个就是 Service 通过匹配 Label 去匹配 Pod 资源,而 Pod 的调度也可以根据 Node 的 Label 来进行调度。

nodeSelector 用于将 Pod 调度到匹配 Label 的 Node 上,所以要先给 Node 打上标签(键值对),然后在 Pod 配置清单中选择指定 Node 的标签。

执行 kubectl get nodes 命令获取集群的 Node 名称。 选择一个要增加标签的 Node ,然后执行 kubectl label nodes <node-name> <label-key>=<label-value> 命令将标签添加到所选择的 Node 上。

实际使用中,通常先规划 Node 用途,然后打标签。例如将两台 Node 划分给不同团队使用,node1 给支付团队使用,用 kubectl label nodes node1 role=pay 命令打上 role=pay 的标签;node2 给风控团队使用,用 kubectl label nodes node2 role=risk 命令打上 role=risk 的标签。

可以执行 kubectl get nodes --show-labels 查看 Node 的标签,来验证新增的标签是否生效。

下面是使用 nodeSelector 字段的 Deployment 的例子,Pod 将运行在有 role=pay 标签的 Node 上。

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx  namespace: defaultspec:  replicas: 1  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - image: nginx:latest        name: nginx      nodeSelector:        role: pay
复制代码

如果 Node 的标签在 Pod 运行时发生变更,从而不再满足 Pod 的 nodeSelector 规则,那么该 Node 上已经存在的 Pod 将继续在该 Node 上运行。

不过需要注意的是 nodeSelector 属于强制性的,如果目标节点没有可用的资源,Pod 就会一直处于 Pending 状态。

4. Affinity 亲和性调度

nodeSelector 的方式比较直观,但是不够灵活,控制粒度偏大,亲和性调度可以提供更加灵活的调度方式。

亲和性调度关键的增强点包括:

  • 语言更具表现力,不仅仅是对完全匹配规则的 AND。

  • 可以指定规则是软性(偏好)要求,而不是硬性要求,如果调度器无法满足该要求,仍然可以调度该 Pod。

  • 可以使用 Pod 的标签来约束哪些 Pod 可以或者不可以被调度到一起。

亲和性调度包括以下三种:

  • nodeAffinity:Node 亲和性,类似于 nodeSelector,可以根据节点上的标签来约束 Pod 可以调度到哪些 Node。

  • podAffinity:Pod 亲和性。

  • podAntiAffinity:Pod 反亲和性。

目前这三种亲和性调度都可以分成软策略和硬策略两种方式:

  • preferredDuringSchedulingIgnoredDuringExecution(软策略):如果没有满足调度要求的 Node ,Pod 就会忽略这条规则,继续完成调度过程。说白了就是 Node 满足条件最好,不满足条件也无所谓。

  • requiredDuringSchedulingIgnoredDuringExecution(硬策略):如果没有满足条件的 Node ,就不断重试直到满足条件为止。

对于 Node 亲和性,软硬策略名称中的 "IgnoredDuringExecution" 部分表示如果 Node 的标签在 Pod 运行时发生变更,从而不再满足 Pod 的亲和性规则,那么该 Node 上已经存在的 Pod 将继续在该 Node 上运行( 类似于 nodeSelector )。

对于 Pod 亲和性和 Pod 反亲和性,软硬策略名称中的 "IgnoredDuringExecution" 部分表示如果在 Pod 运行期间改变了 Pod 的标签,导致不满足 Pod 亲和性或 Pod 反亲和性规则,则 Pod 仍将继续在该 Node 上运行。

4.1 nodeAffinity

Node 亲和性主要是用来控制 Pod 要部署在哪些 Node 上,以及不能部署在哪些 Node 上。

Node 亲和性可以进行一些逻辑组合了,不只是简单的相等匹配。

Node 亲和性通过 PodSpec 字段中 affinity 字段下的 nodeAffinity 字段进行指定。

下面是一个使用 Node 亲和性的 Deployment 的例子。

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx  namespace: defaultspec:  replicas: 1  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - image: nginx:latest        name: nginx      affinity:        nodeAffinity:          requiredDuringSchedulingIgnoredDuringExecution:  # 硬策略            nodeSelectorTerms:            - matchExpressions:  # 匹配逻辑              - key: role                operator: NotIn                values:                - monitor          preferredDuringSchedulingIgnoredDuringExecution:  # 软策略          - weight: 1            preference:              matchExpressions:  # 匹配逻辑              - key: role                operator: In                values:                - flink
复制代码

这个例子中的 Pod ,首先肯定不能运行在有 role=monitor 标签的 Node 上;如果某个 Node 有 role=flink 标签,Pod 就优先调度到该 Node 上;如果所有 Node 都没有 role=flink 标签,Pod 将随机调度。

nodeAffinity 参数解析,如下图。

nodeAffinity

节点硬亲和参数:

  • nodeSelectorTerms:节点选择列表,比 nodeSelector 高级一点。

节点软亲和参数:

  • preference:节点选择器,与相应的权重相关联。

  • weight:preference 的权重,取值范围是 1-100,数字越大优先级越高,Pod 调度到对应此 Label 的 Node 的概率越高。

通用参数:

  • matchExpressions:按照 Node 的 Label 列出节点选择器列表。

  • key:指定要选择 Node Label 的 key。

  • operator:操作符,指定 key 与 value 的关系。

  • In:label 的 value 在某个列表中,key 与 value 同时存在,一个 key 多个 value 的情况下,value 之间就成了逻辑或效果。

  • NotIn:label 的 value 不在某个列表中。

  • Exists:某个 label 存在,只判断是否存在 key,不关心 value 是什么。

  • DoesNotExist:某个 label 不存在,只判断是否存在 key,不关心 value 是什么。

  • Gt:label 的 value 大于某个值。

  • Lt:label 的 value 小于某个值。

  • values:指定要选择 Node Label 的 value,值必须为数组 ["value"]。

  • 如果 operator 为 In 或 Notin ,则 value 不能为空。

  • 如果操作符为 Exists 或 DoesNotExist ,则 value 必须为空 []。

  • 如果操作符为 Gt 或 Lt,则 value 必须有单个元素,该元素将被解释为整数。

注意:

  • 如果同时指定了 nodeSelector 和 nodeAffinity,两者必须都要满足。

  • 如果 nodeSelectorTerms 下面有多个 matchExpressions ,满足任何一个就可以了;

  • 如果 matchExpressions 下面有多个条件,则必须同时满足这些条件才能正常调度 Pod。

4.2 podAffinity

出于高效通信的需求,通常把应用的 Pod 和数据库的 Pod 运行在比较近的位置(例如一个 Kubernetes 集群的 Node 分布在多个机房,想让一组 Pod 和另一组 Pod 运行在同一个机房)。这种情况就可以使用 Pod 亲和性将其调度到同一个拓扑域(机房)中。

拓扑域是用 Node 的 Label 实现,一般按照地区、机房、主机等来划分。

Pod 亲和性通过 PodSpec 中 affinity 字段下的 podAffinity 字段进行指定。

先来看一个使用 Pod 硬亲和性的 Deployment 的例子:

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx  namespace: demospec:  replicas: 1  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - image: nginx:latest        name: nginx      affinity:        podAffinity:          requiredDuringSchedulingIgnoredDuringExecution:  # 硬策略          - labelSelector:              matchExpressions:              - key: app                operator: In                values:                - mysql            topologyKey: zone  # 指定拓扑域
复制代码

假设一个 Kubernetes 集群有四个 Node(node1、node2、node3、node4),Node 的 Label 如下:

  • node1:role=business,zone=zone-a

  • node2:role=business,zone=zone-a

  • node3:role=business,zone=zone-b

  • node4:role=business,zone=zone-b

在该例中使用了 Pod 硬亲和策略,并且 topologyKey 指定了用 zone 来划分拓扑域,则 node1 和 node2 处于同一拓扑域,node3 和 node4 处于同一拓扑域。

假设该例中在 demo NameSpace 下有一个 Label 为 app=mysql 的 Pod ,并且运行在 node1,则 Label 为 app=nginx 的 Pod 将被调度到 node1 或 node2 上,如果 node1 和 node2 都没有资源,则 Label 为 app=nginx 的 Pod 将一直处于 Pending 状态。

Kubernetes 集群中的每个 Node 都默认有一个 key 为 kubernetes.io/hostname 的 label ,value 就是 Node Name。若将 topologyKey 改为 kubernetes.io/hostname,则每个 Node 是一个拓扑域,Label 为 app=mysql 的 Pod 和 Label 为 app=nginx 的 Pod 将运行在同一个 Node 上。

需要注意的是,因为 Pod 是 NameSpace 限定的,因此 Pod 的 Label 也是 NameSpace 限定的。也就是说,在该例中,Label 为 app=nginx 的 Pod 所在的 NameSpace 下如果没有 Label 为 app=mysql 的 Pod ,则 Label 为 app=nginx 的 Pod 也将一直处于 Pending 状态。

将上例中的 Pod 硬亲和策略改为 Pod 软亲和策略,如下:

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx  namespace: demospec:  replicas: 1  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - image: nginx:latest        name: nginx      affinity:        podAffinity:          preferredDuringSchedulingIgnoredDuringExecution:  # 软策略          - weight: 100            podAffinityTerm:              labelSelector:                matchExpressions:                - key: app                  operator: In                  values:                  - mysql              topologyKey: zone  # 指定拓扑域
复制代码

在 Pod 软亲和策略下,如果有 Node 能满足 Pod 软亲和策略,Label 为 app=nginx 的 Pod 就优先调度到该 Node 上。就算没有 Node 能满足软亲和策略,Label 为 app=nginx 的 Pod 也可以被随机调度到一个 Node 上。

podAffinity 参数如下图。

podAffinity

参数解析,这里只讲下和 labelSelector 以及 topologyKey 同级的 namespaces,其他参数的解析参考 Node 亲和性章节。

上文有讲到,因为 Pod 是 NameSpace 限定的,因此 Pod 的 Label 也是 NameSpace 限定的。podAffinity 的 namespaces 参数就用于指定 labelSelector 应用于哪个 NameSpace ,空列表或 null 表示“此 Pod 所在的 NameSpace”。

4.3 podAntiAffinity

Pod 亲和性主要解决哪些 Pod 可以部署在同一个拓扑域中的问题,而 Pod 反亲和性主要是解决哪些 Pod 不能部署在同一个拓扑域中的问题,它们都是处理 Pod 与 Pod 之间的关系。

例如出于安全或者分布式考虑,要将 Pod 运行在不同区域、不同机房、不同主机,这个时候 Pod 的关系就是反亲和性。

Pod 反亲和性通过 PodSpec 中 affinity 字段下的 podAntiAffinity 字段进行指定。

Pod 反亲和性也用到了拓扑域,也有软性策略和硬性策略。

使用硬性策略时需要考虑拓扑域和 Pod 的数量。因为 Pod 数可能大于拓扑域的数量,如果使用 Pod 硬性反亲和,可能导致 Pod 无法被调度。

假设一个应用(Deployment)的 Pod 副本数为 4,并且希望这 4 个 Pod 必须分布在不同的以 zone 划分的拓扑域中,硬性反亲和策略如下:

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx  namespace: demospec:  replicas: 4  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - image: nginx:latest        name: nginx      affinity:        podAntiAffinity:          requiredDuringSchedulingIgnoredDuringExecution:  # 硬策略          - labelSelector:              matchExpressions:              - key: app                operator: In                values:                - nginx            topologyKey: zone  # 指定拓扑域
复制代码

假设一个 Kubernetes 集群有三个 Node(node1、node2、node3),Node 的 Label 如下:

  • node1:role=business,zone=zone-a

  • node2:role=business,zone=zone-b

  • node3:role=business,zone=zone-b

以 zone 划分拓扑域,则只有两个拓扑域(node1 为一个拓扑域,node2 和 node3 为一个拓扑域),而示例中 Label 为 app=nginx 的应用有四个 Pod ,并且硬性反亲和要求每个拓扑域只能有一个 Pod,因此有两个 Pod 将无法被调度。

Pod 硬性反亲和常用于 StatefulSet 部署的有状态应用,例如用 StatefulSet 部署一个三节点的 Redis 集群,要求每个节点必须分布在不同的主机上。这种场景就可以使用 PodAntiAffinity 规则和 topologyKey: "kubernetes.io/hostname"

apiVersion: apps/v1spec:  template:    metadata:      labels:        app: redis    spec:      affinity:        podAntiAffinity:          requiredDuringSchedulingIgnoredDuringExecution:  # 硬策略          - labelSelector:              matchExpressions:              - key: app                operator: In                values:                - redis            topologyKey: kubernetes.io/hostname  # 每个主机就是一个拓扑域
复制代码

对于无状态应用,最常见的用法就是使用 Pod 软性反亲和,尽量让同一个应用的 Pod 调度到不同的主机上,若无法满足,同一台主机也可以调度上同一个应用的多个 Pod。

Pod 软性反亲和示例如下:

apiVersion: apps/v1kind: Deploymentmetadata:  name: golang-demo  namespace: demospec:  replicas: 4  selector:    matchLabels:      app: golang-demo  template:    metadata:      labels:        app: golang-demo    spec:      containers:      - image: golang-demo:latest        name: golang-demo      affinity:        podAntiAffinity:          preferredDuringSchedulingIgnoredDuringExecution:  # 软策略          - weight: 100            podAffinityTerm:              labelSelector:                matchExpressions:                - key: app                  operator: In                  values:                  - golang-demo              topologyKey: kubernetes.io/hostname  # 每个主机就是一个拓扑域
复制代码

podAntiAffinity 参数如下图。各参数的含义和 podAffinity 的参数的含义一样。

podAntiAffinity

5. 污点与容忍

nodeSelector 和 nodeAffinity 是 Pod 的属性,用于将 Pod 调度到合适的节点。

相对的,Taints(污点)是 Node 的属性,使得 Node 可以拒绝某些 Pod 在其上运行。

Tolerations(容忍度)是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的 Node 上。

如果一个 Node 上存在污点,则该 Node 不会接受任何不能容忍该污点的 Pod。

污点和容忍度(taints and tolerations)互相配合,可以避免 Pod 被调度到不合适的节点上。

例如常见的 Master 节点上不调度负载 Pod,保留给 Kubernetes 系统组件使用,保证系统组件的稳定性。再例如,Node 有特殊资源(如 GPU),大部分应用不需要而少部分应用需要。

可以使用 kubectl taint 命令给 Node 增加污点。语法是 kubectl taint nodes <node-name> <key>=<value>:<effect>

  • node-name:指定需要打污点的 Node 主机名。

  • key=value:指定污点的键值型数据。

  • effect:为污点的效果。

例如给 node1 增加一个污点,键名是 key1,键值是 value1,效果是 NoSchedule。

kubectl taint nodes node1 key1=value1:NoSchedule
复制代码

可以在 PodSpec 中定义 Pod 的容忍度。 下面两个容忍度均与污点key1=value1:NoSchedule 相匹配, 因此如果一个 Pod 拥有其中的任何一个容忍度都能够被分配到 node1 。

      tolerations:      - key: key1        operator: Equal        value: value1        effect: NoSchedule
复制代码


      tolerations:      - key: key1        operator: Exists        effect: NoSchedule
复制代码

以下是一个容忍了污点 key1=value1:NoSchedule 的 Deployment 对象。

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx  namespace: demospec:  replicas: 1  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - image: nginx:latest        name: nginx      tolerations:      - key: key1        operator: Equal        value: value1        effect: NoSchedule
复制代码

容忍度配置中的 operator 有两个值,ExistsEqual(默认值)。

一个容忍度和一个污点相匹配,是指它们有一样的 keyeffect,并且:

  • 如果 operatorExists (此时容忍度不能指定 value),或者

  • 如果 operatorEqual ,则它们的 value 应该相等。

容忍度和污点相匹配,存在两种特殊情况:

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

  • 如果 effect 为空,则可以与所有 effect 相匹配。

污点和容忍度都是键值型数据,又都额外支持一个效果(effect)标记,effect 用于定义 Node 对 Pod 的排斥效果,主要包含以下三种类型:

  • NoSchedule:除容忍此污点的 Pod 外,其它 Pod 将不再被调度到本机。

  • PreferNoSchedule:NoSchedule 的软策略版本,不能容忍此污点的 Pod 尽量不调度到当前 Node,如果没有其它 Node 可以供 Pod 选择时,该 Node 也会接受没有容忍此污点的 Pod 对象。

  • NoExecute:该类型意味着一旦 Node 的污点生效,如果该 Node 内正在运行的 Pod 没有对应的容忍度设置,Pod 会直接被驱逐。

容忍度定义中还有一个可选参数 tolerationSeconds ,前文提到过污点的 effect 值 NoExecute 会影响已经在 Node 上运行的 Pod。

  • 如果 Pod 不能忍受 effect 值为 NoExecute 的污点,那么 Pod 将马上被驱逐。

  • 如果 Pod 能够忍受 effect 值为 NoExecute 的污点,但是在容忍度定义中没有指定 tolerationSeconds,则 Pod 还会一直在这个 Node 上运行。

  • 如果 Pod 能够忍受 effect 值为 NoExecute 的污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个 Node 上继续运行这个指定的时间长度。若tolerationSeconds 为 0 或负数,则该 Node 上容忍了 effect 值为 NoExecute 的污点的 Pod 也会被立即驱逐。

例如某 Pod 运行在没有污点的 node2 上,该 Pod 有如下容忍度定义:

      tolerations:      - key: key2        operator: Equal        value: value2        effect: NoExecute        tolerationSeconds: 3600
复制代码

当给 node2 打一个 key2=value2:NoExecute 的污点后,该 Pod 将在 node2 上继续运行 3600 秒,然后才被驱逐。如果污点在此之间被移除,则 Pod 将不会被驱逐。

实践中,若集群中的一组机器专用于为运行非生产典型的容器应用而备置,那么就应该为其添加污点,以确保仅那些能够容忍此污点的非生产型 Pod 对象可以调度到其上,另外,有特殊硬件(如 GPU)的 Node 需要专用于运行特殊应用,也可以为其添加污点以排除其它 Pod 对象。

不过,有些系统级别应用,如 kube-proxy 或者 kube-flannel 等是以 DaemonSet 形式运行在集群上,这些资源就需要容忍所有污点,以使每一个 Node 上都可以运行 DaemonSet 的 Pod。

      tolerations:      - operator: Exists
复制代码

6. Kubernetes 调度实践

6.1 Label 和污点规划

Kubernetes 的调度依赖 Node 和 Pod 的 Label ,以及 Node 的污点。所以要对 Label 和污点进行合理规划。

在生产环境中,可以让每个业务应用的 Pod 都有一个 Key 为 app 的 Label ,Label 的 Value 就是应用名,例如 java-demo 应用的 Pod 都有 app=java-demo 的 Label 。

Kubernetes Node 的 Label 可以分为以下几类:

  • role=master ,给 Kubernetes 的 Master 节点打上 role=master 的 Label 。

  • role=lb ,用以运行 nginx-ingress-controller ,同时也是整个集群的流量入口。不允许其他 Pod 调度到 role=lb 的 Node。

  • role=monitor ,用以部署 Prometheus 等监控相关的应用。不允许非监控相关的 Pod 调度到 role=monitor 的 Node。

  • role=business ,用以运行大部分无状态业务应用。

各类 Node 的污点情况如下:

  • Master 节点默认有污点 node-role.kubernetes.io/master:NoSchedule

  • Label 为 role=ingress 的 Node 有污点 role=lb:NoSchedule

  • Label 为 role=monitor 的 Node 有污点 role=monitor:NoSchedule

  • Label 为 role=business 的 Node 没有污点。

另外各大云商的 Kubernetes 服务都提供有虚拟节点,以 DaemonSet 方式部署的应用,一般不需要调度到虚拟节点。以阿里云 Kubernetes 服务为例,阿里云的虚拟节点默认有 Label type=virtual-kubelet 和污点 virtual-kubelet.io/provider=alibabacloud:NoSchedule 。 

6.2 Pod 的调度配置

接下来对常见应用的调度方式做下介绍,主要包括以下应用:

  • 无状态业务应用,涉及 Node 硬亲和和 Pod 软性反亲和。

  • 监控相关的应用,涉及 nodeSelector 和污点容忍。

  • Nginx Ingress Controller,涉及 nodeSelector 和 Pod 软性反亲和以及污点容忍。

  • 日志收集组件 Logtail DaemonSet,涉及污点容忍和 Node 硬亲和。

6.2.1 无状态业务应用

对于一般的无状态业务应用,调度要求如下:

  • Pod 只允许被调度到 Label 为 role=business 的 Node 。

  • 出于分布式考虑,同一个应用的多个 Pod 应尽量被调度到不同的 Node 上。

可以使用 Node 硬亲和和 Pod 软性反亲和来达到以上要求。Deployment 示例如下:

apiVersion: apps/v1kind: Deploymentmetadata:  name: java-demo  namespace: opsspec:  replicas: 5  selector:    matchLabels:      app: java-demo  template:    metadata:      labels:        app: java-demo    spec:      affinity:        nodeAffinity:          requiredDuringSchedulingIgnoredDuringExecution:    # Node 硬亲和            nodeSelectorTerms:            - matchExpressions:              - key: role                operator: In                values:                - business        podAntiAffinity:          preferredDuringSchedulingIgnoredDuringExecution:   # Pod 软性反亲和          - podAffinityTerm:              labelSelector:                matchExpressions:                - key: app                  operator: In                  values:                  - java-demo              topologyKey: kubernetes.io/hostname            # 每个 Node 就是一个拓扑域            weight: 10      containers:      - name: java-demo        image: java-demo:202203281811
复制代码

6.2.2 监控相关的应用

对于监控相关的应用,要求必须调度到 Label 为 role=monitor 的 Node 上,但是 Label 为 role=monitor 的 Node 有污点。

可以使用 nodeSelector 和污点容忍来调度监控应用的 Pod 。以 Grafana 为例,配置如下:

apiVersion: apps/v1kind: Deploymentmetadata:  name: grafana  namespace: monitorspec:  replicas: 1  selector:    matchLabels:      app: grafana  template:    metadata:      labels:        app: grafana    spec:      containers:      - name: grafana        image: grafana:latest      nodeSelector:        role: monitor      tolerations:    # 容忍污点      - key: role        operator: Equal        value: monitor        effect: NoSchedule
复制代码

6.2.3 Nginx Ingress Controller

对于 Nginx Ingress Controller,要求多个 Pod 尽量被调度到不同的 Node 上,并且调度到 Label 为 role=lb 的 Node 上,但是 Label 为 role=lb 的 Node 有污点。

可以使用 nodeSelector 和 Pod 软性反亲和以及污点容忍来调度 Nginx Ingress Controller 的 Pod 。配置如下:

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx-ingress-controller  namespace: kube-systemspec:  replicas: 5  selector:    matchLabels:      app: ingress-nginx  template:    metadata:      labels:        app: ingress-nginx    spec:      containers:      - name: nginx-ingress-controller        image: nginx-ingress-controller:v0.30.0.2-9597b3685-aliyun      nodeSelector:        role: lb      tolerations:    # 容忍污点      - key: role        operator: Equal        value: lb        effect: NoSchedule      affinity:        podAntiAffinity:          preferredDuringSchedulingIgnoredDuringExecution:   # Pod 软性反亲和          - podAffinityTerm:              labelSelector:                matchExpressions:                - key: app                  operator: In                  values:                  - ingress-nginx              topologyKey: kubernetes.io/hostname    # 每个 Node 就是一个拓扑域            weight: 10
复制代码

6.2.4 Logtail DaemonSet

Logtail 是阿里云日志服务的 agent ,用以采集 Kubernetes 中应用的日志。

Logtail 以 DaemonSet 方式部署,每个 Node 上一个 Logtail Pod,但是不需要部署到 Master 节点。

可以让 Logtail DaemonSet 容忍所有污点,并用 Node 硬亲和将 Master 节点排除。配置如下:

apiVersion: apps/v1kind: DaemonSetmetadata:  name: logtail-ds  namespace: kube-systemspec:  template:    spec:      affinity:        nodeAffinity:          requiredDuringSchedulingIgnoredDuringExecution:    # Node 硬亲和            nodeSelectorTerms:            - matchExpressions:              - key: role                operator: NotIn                values:                - master              - key: type                operator: NotIn                values:                - virtual-kubelet      tolerations:      - operator: Exists     # 容忍所有污点
复制代码

7. 总结

本文对 Kubernetes 的 nodeSelector、Node 亲和性、Pod 亲和与反亲和、污点与容忍等调度方式进行了简单介绍,并分享了使用这几种调度方式的一些实践。文中的几种调度方式,可以进行多种组合,满足复杂的调度场景。在实际使用中,建议完全理解这几种调度方式,并合理规划 Pod 和 Node 的 Label ,合理规划 Node 的污点。

发布于: 2022 年 05 月 01 日阅读数: 21
用户头像

玄月九

关注

人生就是不停地战斗 2018.08.08 加入

亦狂亦侠亦温文

评论

发布
暂无评论
Kubernetes 如何将 Pod 分配给节点_Kubernetes_玄月九_InfoQ写作社区