k8s--pod 调度、定向调度、亲和性调度、污点和容忍 (一)

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: k8s--pod 调度、定向调度、亲和性调度、污点和容忍

介绍


在默认情况下,一个 pod 在哪个 node 节点上运行,是由 Scheduler 组件采用相应的算法算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足需求,因为在很多情况下,我们想控制某些 pod 到达某些节点上,那么应该怎么做呢?这就涉及到 kubernetes 对 pod 的调度规则,kubernetes 提供了四大类调度方式

  • 自动调度:运行在哪个节点上完全由 Scheduler 经过一系列的算法计算得出
  • 定向调度:有两种,根据节点名称(NodeName)或者节点选择器(NodeSelector)
  • 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
  • 污点(容忍)调度:Taints、Toleration

默认就是自动调度,所以下面来看其他的几种调度方式


定向调度


定向调度,指的是利用在 pod 上声明 nodeName 或者 nodeSelector,以此将 pod 调度到期望的 node 节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标 node 不存在,也会向上面进行调度,只不过 pod 运行失败而已


NodeName

NodeName 用于强制将 pod 调度到指定的 name 的 node 节点上,这种方式,其实是直接跳过 Scheduler 的调度逻辑,直接将 pod 调度到指定名称的节点

接下来,实验一下:创建一个 pod-nodename.yaml 文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
  namespace: zouzou
spec:
  containers:
  - name: nginx
    image: nginx:1.14
  nodeName: dce-10-6-215-190 # 指定调度到 dce-10-6-215-190 节点上

如果你不知道 nodeName 的话,使用下面命令查看

kubectl get node

创建 pod,查看 pod

# 创建 pod
kubectl apply -f pod-nodename.yaml

查看 pod 的信息,可以看到 pod 已经分配给了 dce-10-6-215-190 的节点上

# 可以看到 pod 分配到了 dce-10-6-215-190 的节点上,有人说,可能是随机分配的,那你也可以写一个不存在的 node 地址试下
[root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodename -n zouzou -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP               NODE               NOMINATED NODE   READINESS GATES
pod-nodename   1/1     Running   0          89s   172.29.190.141   dce-10-6-215-190   <none>           <none>


NodeSelector

NodeSelector 用于将 pod 调度到添加了指定标签的 node 节点上,它是通过 kubernetes 的 label-selector 机制实现的,也就是说,在 pod 创建之前,会由 scheduler 使用 MatchNodeSelector 调度策略进行 label 匹配,找出目标 node,然后将 pod 调度到目标节点,该匹配规则是强制约束。

接下来,实验一下:首先查看下节点的 labels

[root@dce-10-6-215-215 tmp]# kubectl get node --show-labels
NAME               STATUS   ROLES             AGE    VERSION    LABELS
dce-10-6-215-190   Ready    <none>            7d3h   v1.18.20   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=dce-10-6-215-190,kubernetes.io/os=linux
dce-10-6-215-200   Ready    <none>            7d1h   v1.18.20   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=dce-10-6-215-200,kubernetes.io/os=linux
dce-10-6-215-215   Ready    master,registry   8d     v1.18.20   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=dce-10-6-215-215,kubernetes.io/os=linux,node-role.kubernetes.io/master=,node-role.kubernetes.io/registry=

上面有很多的标签,为了测试方便,在此基础上给两个 node 节点添加两个简单的标签

kubectl label node dce-10-6-215-190 nodeenv=pro
kubectl label node dce-10-6-215-200 nodeenv=test

在来查看下对应的标签

这样我们就给两个节点打上了标签

创建一个 pod-nodeselector.yaml 文件,并使用它创建 Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
  namespace: zouzou
spec:
  containers:
  - name: nginx
    image: nginx:1.14
  nodeSelector: 
    nodeenv: pro # 指定调度到具有 nodeenv=pro 标签的节点上

创建 pod 查看详情,可以看到,pod 分配给了 nodeenv=pro 的标签节点上

# 创建 pod
[root@dce-10-6-215-215 tmp]# kubectl apply -f pod-nodeselector.yaml
pod/pod-nodeselector created
# 查看 pod 在 dce-10-6-215-190 上了,你也可以写个不存在的标签测试是不是随机分配的
[root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodeselector -n zouzou -o wide
NAME               READY   STATUS    RESTARTS   AGE   IP               NODE               NOMINATED NODE   READINESS GATES
pod-nodeselector   1/1     Running   0          48s   172.29.190.142   dce-10-6-215-190   <none>           <none>


亲和性调度


上面,介绍了两种定向调度的方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的 node,那么 pod 将不会被运行,即使在集群中还有可用 node 列表也不行,这就限制了它的使用场景

基于上面的问题,kubernetes 还提供了一种亲和度(Affinity)调度。它在 NodeSelector 的基础上进行了扩展,可以通过配置的形式,实现优先选择满足条件的 Node 进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活

Affinity 主要分为三类

  • nodeAffinity(node 亲和性):以 node 为目标,解决 pod 可以调度到哪些 node 上的问题
  • podAffinity(pod 亲和性):以 pod 为目标,解决 pod 可以和哪些已存在的 pod 部署在同一个拓扑域中的问题
  • podAntiAffinity(pod 反亲和性):以 pod 为目标,解决 pod 不能和哪些已存在 pod 部署在同一个拓扑域中的问题

关于亲和性和反亲和性使用场景的说明:

亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用尽可能的靠近,这样可以减少因网络通信而带来的性能损耗,比如 web 应用和 mysql,要频繁的从数据库里查询和新增数据,所以应该让他们尽可能的在一起

反亲和性:当应用采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个 node 上,这样可以提高服务的高可用性,当一个服务器挂掉之后,其他的 pod 还可以提供服务


NodeAffinity

首先来看一下 NodeAffinity 的可配置项:

pod.spec.affinity.nodeAffinity
  requiredDuringSchedulingIgnoredDuringExecution  # Node节点必须满足指定的所有规则才可以,相当于硬限制
    nodeSelectorTerms  # 节点选择列表
      matchFields   # 按节点字段列出的节点选择器要求列表
      matchExpressions   # 按节点标签列出的节点选择器要求列表(推荐)
        key    # 键
        values # 值
        operat or # 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
  preferredDuringSchedulingIgnoredDuringExecution # 优先调度到满足指定的规则的 Node,相当于软限制 (倾向)
    preference   # 一个节点选择器项,与相应的权重相关联
      matchFields  # 按节点字段列出的节点选择器要求列表
      matchExpressions  # 按节点标签列出的节点选择器要求列表(推荐)
        key    # 键
        values # 值
        operator # 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
    weight # 倾向权重,在范围1-100。

关系符的说明

- matchExpressions:
  - key: nodeenv              # 匹配存在标签的 key 为 nodeenv 的节点
    operator: Exists
  - key: nodeenv              # 匹配标签的 key 为 nodeenv,且 value 是 "xxx" 或 "yyy" 的节点
    operator: In
    values: ["xxx","yyy"]
  - key: nodeenv              # 匹配标签的 key 为 nodeenv,且 value 大于 "xxx" 的节点
    operator: Gt
    values: "xxx"

接下来首先演示一下 requiredDuringSchedulingIgnoredDuringExecution

创建 pod-nodeaffinity-required.yaml,内容如下

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-required
  namespace: zouzou
spec:
  containers:
  - name: nginx
    image: nginx:1.14
  affinity:  # 亲和性设置
    nodeAffinity: # 设置 node 亲和性
      requiredDuringSchedulingIgnoredDuringExecution: # 硬限制,没有满足就会失败
        nodeSelectorTerms: # node 选择器,可以有多个
        - matchExpressions: # 匹配标签
          - key: nodeenv # 标签的 key 要为 nodeenv
            operator: In  # 操作符,在里面
            values: ["xxx","yyy"] # 值在["xxx","yyy"]中的标签

创建 pod

#  创建 pod
kubectl create -f pod-nodeaffinity-required.yaml

查看 pod 和 pod 的 event

# 查看 pod ,发现 pod 没有调度到节点上,因为 nodeenv 的值没有 xxx 或者 yyy,而且是硬限制
[root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodeaffinity-required -n zouzou -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
pod-nodeaffinity-required   0/1     Pending   0          81s   <none>   <none>   <none>           <none>
# 从 event 可以看到,调度失败,有三个 node 节点,但是没有满足标签选择器的 node
[root@dce-10-6-215-215 tmp]# kubectl describe pod pod-nodeaffinity-required -n zouzou
Name:         pod-nodeaffinity-required
Namespace:    zouzou
Priority:     0
Node:         <none>
Labels:       <none>
......
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  104s  default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.
  Warning  FailedScheduling  30s   default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.

从上面我们可以看出,硬限制当没有匹配的标签时,会调度失败。因为我们两个 node 节点,只有 nodeenv=pro 和 nodeenv=test 的节点,没有 nodeenv=xxx 或者 nodeenv=yyy 的节点

接下来,我们删除 pod,把 yyy 改为 pro,因为 nodeenv=pro 是存在的

# 删除 pod
kubectl delete -f pod-nodeaffinity-required.yaml

修改后在创建 pod

# 创建 pod
kubectl create -f pod-nodeaffinity-required.yaml

查看 pod

# 可以看到,pod 分配给了 dce-10-6-215-190 的节点上,并成功运行了
[root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodeaffinity-required -n zouzou -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP               NODE               NOMINATED NODE   READINESS GATES
pod-nodeaffinity-required   1/1     Running   0          17s   172.29.190.143   dce-10-6-215-190   <none>           <none>
# 从 event 里,也可以看出来,是成功调度的
[root@dce-10-6-215-215 tmp]# kubectl describe pod pod-nodeaffinity-required -n zouzou
Name:         pod-nodeaffinity-required
Namespace:    zouzou......
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  24s   default-scheduler  Successfully assigned zouzou/pod-nodeaffinity-required to dce-10-6-215-190
  Normal  Pulled     21s   kubelet            Container image "nginx:1.14" already present on machine
  Normal  Created    21s   kubelet            Created container nginx
  Normal  Started    20s   kubelet            Started container nginx

接下来再演示一下 requiredDuringSchedulingIgnoredDuringExecution

创建 pod-nodeaffinity-preferred.yaml,内容如下

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-preferred
  namespace: zouzou
spec:
  containers:
  - name: nginx
    image: nginx:1.14
  affinity:  # 亲和性设置
    nodeAffinity: # 设置 node 亲和性
      preferredDuringSchedulingIgnoredDuringExecution: # 软限制,没有匹配的标签也会分配给某个 node
      - weight: 1 # 权重
        preference: # 一个节点选择器项,与相应的权重相关联
          matchExpressions:  # 匹配标签
          - key: nodeenv # 标签的 key 要为 nodeenv
            operator: In  # 操作符,在里面
            values: ["xxx","yyy"] # 匹配 env 的值在["xxx","yyy"]中的标签(当前环境没有)

创建 pod

kubectl create -f pod-nodeaffinity-preferred.yaml

查看 pod 和 pod event

# 可以看到,成功分配给了 dce-10-6-215-200 的节点上
[root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodeaffinity-preferred -n zouzou -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP              NODE               NOMINATED NODE   READINESS GATES
pod-nodeaffinity-preferred   1/1     Running   0          53s   172.29.34.238   dce-10-6-215-200   <none>           <none>
# 从 event 中也可以看出,pod 是调度成功的
[root@dce-10-6-215-215 tmp]# kubectl describe pod pod-nodeaffinity-preferred -n zouzou
Name:         pod-nodeaffinity-preferred
Namespace:    zouzou
Priority:     0
Node:         dce-10-6-215-200/10.6.215.200
Start Time:   Sat, 16 Apr 2022 11:45:15 +0800
......
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  66s   default-scheduler  Successfully assigned zouzou/pod-nodeaffinity-preferred to dce-10-6-215-200
  Normal  Pulled     63s   kubelet            Container image "nginx:1.14" already present on machine
  Normal  Created    63s   kubelet            Created container nginx
  Normal  Started    62s   kubelet            Started container ngin

从上面的结果可以看出,软限制,当没有匹配到对应的标签时,也不会调度失败,会根据算法分配给合适的节点

NodeAffinity规则设置的注意事项:

  • 如果同时定义了 nodeSelector 和 nodeAffinity,那么必须两个条件都得到满足,Pod 才能运行在指定的 Node 上
  • 如果 nodeAffinity 指定了多个 nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
  • 如果一个 nodeSelectorTerms 中有多个 matchExpressions ,则一个节点必须满足所有的才能匹配成功
  • 如果一个 pod 所在的 Node 在 Pod 运行期间其标签发生了改变,不再符合该 Pod 的节点亲和性需求,则系统将忽略此变化

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
26天前
|
Kubernetes 应用服务中间件 调度
云上应用管理问题之如何在Kubernetes集群中配置跨可用区的Pod调度
云上应用管理问题之如何在Kubernetes集群中配置跨可用区的Pod调度
|
3月前
|
运维 Kubernetes 调度
【kubernetes】关于k8s集群的污点、容忍、驱逐以及k8s集群故障排查思路
【kubernetes】关于k8s集群的污点、容忍、驱逐以及k8s集群故障排查思路
|
2月前
|
Kubernetes API 调度
Pod无法调度到可用的节点上(K8s)
完成k8s单节点部署后,创建了一个pod进行测试,后续该pod出现以下报错: Warning FailedScheduling 3h7m (x3 over 3h18m) default-scheduler 0/1 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..
159 0
|
3月前
|
Kubernetes 应用服务中间件 调度
k8s-高级调度-污点容忍、亲和性调度
k8s-高级调度-污点容忍、亲和性调度
|
3月前
|
Kubernetes 算法 调度
k8s群集调度之 pod亲和 node亲和 标签指定
k8s群集调度之 pod亲和 node亲和 标签指定
|
11天前
|
canal Kubernetes Docker
基于Kubernetes v1.25.0和Docker部署高可用集群(03部分)
基于Kubernetes v1.25.0和Docker部署高可用集群(03部分)
|
6天前
|
Kubernetes 安全 数据安全/隐私保护
Kubernetes(K8S) 集群安全机制
Kubernetes(K8S) 集群安全机制
15 2
|
12天前
|
Kubernetes Ubuntu Linux
基于Kubernetes v1.25.0和Docker部署高可用集群(02部分)
基于Kubernetes v1.25.0和Docker部署高可用集群(02部分)
|
13天前
|
Kubernetes API 调度
Airbnb的动态kubernetes集群扩缩容
Airbnb的动态kubernetes集群扩缩容
29 5
|
14天前
|
存储 Kubernetes Java
基于Kubernetes v1.25.0和Docker部署高可用集群(说明篇)
docker与kubernetes的区别是:docker是管理当前主机上的容器,k8s是管理多台主机、跨平台的分布式管理系统。Kubernetes的设计初衷是支持可插拔架构,从而利于扩展kubernetes的功能