Pod的调度机制

目录

一、Pod调度概述

API Server 接受客户端提交Pod对象创建请求后的操作过程中,有一个重要的步骤就是由调度器程序 kube-scheduler 从当前集群中选择一个可用的最佳节点来接收并运行它,通常是默认的调度器 kube-scheduler 负责执行此类任务。

对于每个待创建的Pod对象来说,调度过程通常分为两个阶段:过滤 --> 打分,过滤阶段用来过滤掉不符合调度规则的Node,打分阶段建立在过滤阶段之上,为每个符合调度的Node进行打分,分值越高则被调度到该Node的机率越大。

二、Pod调度策略实现方式

  • nodeName(直接指定Node主机名)
  • nodeSelector (节点选择器,为Node打上标签,然后Pod中通过nodeSelector选择打上标签的Node)
  • 污点taint与容忍度tolerations
  • NodeAffinity 节点亲和性
  • Pod Affinity Pod亲和性
  • Pod Anti-Affinity Pod反亲和性

三、kube-scheduler调度

kube-scheduler 是Kubernetes 集群的默认调度器,并且是集群控制面(master)的一部分。对每一个新创建的Pod或者是未被调度的Pod,kube-scheduler会选择一个最优的Node去运行这个Pod。

然而,Pod内的每一个容器对资源都有不同的需求,而且Pod本身也有不同的资源需求。因此,Pod在被调度到Node上之前,根据这些特定的资源调度需求,需要对集群中的Node进行一次过滤。

在一个集群中,满足一个Pod调度请求的所有Node称之为可调度节点。如果没有任何一个Node能满足Pod的资源请求,那么这个Pod将一直停留在未调度状态直到调度器能够找到合适的Node。

调度器先在集群中找到一个Pod的所有可调度节点,然后根据一系列函数对这些可调度节点打分,然后选出其中得分最高的Node来运行Pod。之后,调度器将这个调度决定通知给kube-apiserver,这个过程叫做绑定。

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

1、kube-scheduler调度的流程

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

1.过滤(Predicates 预选策略)2.打分(Priorities 优选策略)

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

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

整体流程如下图所示:

2、过滤阶段

  1. PodFitsHostPorts:检查Node上是否不存在当前被调度Pod的端口(如果被调度Pod用的端口已被占用,则此Node被Pass)。
  2. PodFitsHost:检查Pod是否通过主机名指定了特性的Node (是否在Pod中定义了nodeName)
  3. PodFitsResources:检查Node是否有空闲资源(如CPU和内存)以满足Pod的需求。
  4. PodMatchNodeSelector:检查Pod是否通过节点选择器选择了特定的Node (是否在Pod中定义了nodeSelector)。
  5. NoVolumeZoneConflict:检查Pod请求的卷在Node上是否可用 (不可用的Node被Pass)。
  6. NoDiskConflict:根据Pod请求的卷和已挂载的卷,检查Pod是否合适于某个Node (例如Pod要挂载/data到容器中,Node上/data/已经被其它Pod挂载,那么此Pod则不适合此Node)
  7. MaxCSIVolumeCount::决定应该附加多少CSI卷,以及是否超过了配置的限制。
  8. CheckNodeMemoryPressure:对于内存有压力的Node,则不会被调度Pod。
  9. CheckNodePIDPressure:对于进程ID不足的Node,则不会调度Pod
  10. CheckNodeDiskPressure:对于磁盘存储已满或者接近满的Node,则不会调度Pod。
  11. CheckNodeCondition:Node报告给API Server说自己文件系统不足,网络有写问题或者kubelet还没有准备好运行Pods等问题,则不会调度Pod。
  12. PodToleratesNodeTaints:检查Pod的容忍度是否能承受被打上污点的Node。
  13. CheckVolumeBinding:根据一个Pod并发流量来评估它是否合适(这适用于结合型和非结合型PVCs)。

3、打分阶段

当过滤阶段执行后满足过滤条件的Node,将进行打分阶段。

  1. SelectorSpreadPriority:优先减少节点上属于同一个 Service 或 Replication Controller 的 Pod 数量
  2. InterPodAffinityPriority:优先将 Pod 调度到相同的拓扑上(如同一个节点、Rack、Zone 等)
  3. LeastRequestedPriority:节点上放置的Pod越多,这些Pod使用的资源越多,这个Node给出的打分就越低,所以优先调度到Pod少及资源使用少的节点上。
  4. MostRequestedPriority:尽量调度到已经使用过的 Node 上,将把计划的Pods放到运行整个工作负载所需的最小节点数量上。
  5. RequestedToCapacityRatioPriority:使用默认资源评分函数形状创建基于requestedToCapacity的 ResourceAllocationPriority。
  6. BalancedResourceAllocation:优先平衡各节点的资源使用。
  7. NodePreferAvoidPodsPriority:根据节点注释对节点进行优先级排序,以使用它来提示两个不同的 Pod 不应在同一节点上运行。scheduler.alpha.kubernetes.io/preferAvoidPods。
  8. NodeAffinityPriority:优先调度到匹配 NodeAffinity (Node亲和性调度)的节点上。
  9. TaintTolerationPriority:优先调度到匹配 TaintToleration (污点) 的节点上
  10. ImageLocalityPriority:尽量将使用大镜像的容器调度到已经下拉了该镜像的节点上。
  11. ServiceSpreadingPriority:尽量将同一个 service 的 Pod 分布到不同节点上,服务对单个节点故障更具弹性。
  12. EqualPriority:将所有节点的权重设置为 1。
  13. EvenPodsSpreadPriority:实现首选pod拓扑扩展约束。

4、kube-scheduler 调度示例

默认配置使用的就是kube-scheduler调度组件,下面例子启动三个Pod,看分别被分配到哪个Node。

4.1、创建 Deployment 资源清单

cat > scheduler-pod.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: scheduler-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: scheduler-pod
  template:
    metadata:
      labels:
        app: scheduler-pod
    spec:
      containers:
      - image: busybox:latest
        name: scheduler-pod
        command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
EOF

4.2、应用Deployment

kubectl apply -f scheduler-pod.yaml

4.3、查看被kube-scheduler自动调度的Pod

kubectl get pods -o wide | grep scheduler

四、nodeName调度

nodeNamed这种调度方式比较简单,我们可以指定Pod在哪台Node上进行运行,通过spec.nodeName参数来指定Node主机名称即可。

1、创建Pod资源清单

cat > nodeName-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: nodename-pod
spec:
#指定该Pod运行在k8s-node2节点上
  nodeName: k8s-node2
  containers:
  - image: busybox:latest
    name: nodename-containers
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
EOF

2、应用Pod

kubectl apply -f nodeName-pod.yaml

3、查看Pod的调度情况

nodename-pod被绑定在了k8s-node2上

kubectl get pods -o wide | grep nodename

4、验证容器是否正常启动并在运行中

kubectl logs nodename-pod

五、nodeSelector调度

nodeSelector用于将Pod调度到匹配Label的Node上,所以要先给node打上标签,然后在Pod配置清单中选择指定Node的标签。先给规划node用途,然后打标签

例如将两台node划分给不同团队使用:

1、分别给两个node添加标签

k8s-node1给开发团队(dev)用,k8s-node2给运维团队(ops)用

kubectl label node k8s-node1 team=dev

kubectl label node k8s-node2 team=ops

2、查看node节点标签

kubectl get nodes --show-labels | grep team

3、创建Pod资源清单

cat <<EOF > nodeSelector-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nodeselector-pod
spec:
  nodeSelector:                      #指定标签选择器
    team: dev                #label指定开发团队的label
  containers:
  - image: busybox:latest
    name: nodeselector-containers
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
EOF

4、应用Pod

kubectl apply -f nodeSelector-pod.yaml

5、查看Pod调度情况

kubectl get pods -o wide | grep nodeselector

6、删除标签

kubectl label node k8s-node1 team-

kubectl label node k8s-node2 team-

7、查看Pod状态

可以看到删除标签后pod正常运行

kubectl get pods -o wide | grep nodeselector

8、把Pod删除后重新创建

会发现该pod一直在等待中,找不到清单中配置标签的Node

kubectl get pods -w | grep nodeselector

9、describe查看events

事件:3个节点都不匹配 node selector

kubectl describe pods nodeselector-pod | grep -A 5 Events

六、污点taint与容忍度tolerations调度

1、概述

污点(taint)是定义在Node之上的键值型的数据,用于让节点拒绝将Pod调度运行于其上,除非该Pod对象具有接纳Node污点的容忍度。而容忍度(tolerations)是定义在Pod对象的键值型属性数据,用于配置其可容忍的Node污点,而且调度器仅能将Pod对象调度至其能够容忍该Node污点的Node之上。

  • Pod-A具备k8s-node01污点的容忍度,所以能够被调度器调度至k8s-node01上。
  • Pod-A不完全具备k8s-node02污点的容忍度,所以不能被调度至k8s-node02。
  • Pod-A虽然具备容忍度,但同样可以调度至没有污点的k8s-node03节点上。
  • Pod-B自身没有容忍度,所以只能被调度至没有污点的k8s-node03节点上。

2、污点

2.1、污点类型

污点定义在nodeSpec中,容忍度定义在PodSpec中,他们都是键值型数据,但又都额外支持一个效果(effect)标记,语法格式为 “key=value:effect” ,其中key和value的用法及格式与资源注解信息相似,而effect则用于定义对Pod对象的排斥等级,它主要包含以下三种排斥类型。

  • NoSchedule :为Node添加污点等级为NoSchedule,除容忍此污点的Pod以外的其它Pod将不再被调度到本机。
  • PreferNoSchedule:为Node添加污点等级为PreferNoSchedule,不能容忍此污点的Pod对象尽量不要调度到当前节点,如果没有其它节点可以供Pod选择时,也会接受没有容忍此污点的Pod对象。
  • NoExecute:为Node添加污点等级为NoExecute,能容忍此污点的Pod将被调度到此节点,而且节点上现存的Pod对象因节点使用了NoExceute等级的污点,则现存的Pod将被驱赶至其它满足条件的Node(除非现存Pod容忍了当前节点的污点)。

2.2、Master污点

以kubeadm部署的kubernetes集群,其Master节点将自动添加污点信息以阻止不能容忍此污点的Pod对象调度至此节点,因此用户可以手动创建Pod来容忍Master的污点。

2.3、查看k8s集群中Master节点的污点

kubectl describe nodes k8s-master | grep Taints

不过,有些系统级别应用,如 kube-proxy 或者 kube-flannel 等也是以Pod形式运行在集群上,这些资源在创建时就添加上了相应的容忍度以确保他们被 DaemonSet 控制器创建时能够调度至 Master 节点运行一个实例。

2.4、查看系统级别Pod的容忍度

kubectl get pods -n kube-system -o name | cut -d'/' -f2 | xargs -I {} kubectl describe pod {} -n kube-system | grep "Tolerations:"

另外,这类Pod是构成Kubernetes系统的基础且关键的组件,它们甚至还定义了更大的容忍度,从下面某kube-flannel实例的容忍度定义来看,它还能容忍那些报告了磁盘压力或内存压力的节点,以及未就绪的节点和不可达的节点,以确保它们能在任何状态下正常调度至集群节点上运行。

kubectl get pods -n kube-system | grep flannel

kubectl describe pods kube-flannel-ds-2rzg6 -n kube-system | grep -A 10 Tolerations:

  • :NoSchedule op=Exists

    • 表示该 Pod 可以容忍任何未指定键值的污点,但 Kubernetes 不会在具有此类污点的节点上调度新 Pod。
  • node.kubernetes.io/disk-pressure:NoSchedule op=Exists

    • 表示当节点处于磁盘压力状态时,该 Pod 不会被调度到该节点上。如果 Pod 已经在节点上运行,则它将继续运行,但不会被重新调度到其他节点。
  • node.kubernetes.io/memory-pressure:NoSchedule op=Exists

    • 表示当节点处于内存压力状态时,该 Pod 不会被调度到该节点上。如果 Pod 已经在节点上运行,则它将继续运行,但不会被重新调度到其他节点。
  • node.kubernetes.io/network-unavailable:NoSchedule op=Exists

    • 表示当节点的网络不可用时,该 Pod 不会被调度到该节点上。如果 Pod 已经在节点上运行,则它将继续运行,但不会被重新调度到其他节点。
  • node.kubernetes.io/not-ready:NoExecute op=Exists

    • 表示当节点的状态变为 NotReady 时,Kubernetes 会尝试驱逐该 Pod。这意味着如果节点的状态变为 NotReady,那么 Pod 将被终止,除非它能够容忍此污点。
  • node.kubernetes.io/pid-pressure:NoSchedule op=Exists

    • 表示当节点处于 PID 压力状态时,该 Pod 不会被调度到该节点上。如果 Pod 已经在节点上运行,则它将继续运行,但不会被重新调度到其他节点。
  • node.kubernetes.io/unreachable:NoExecute op=Exists

    • 表示当节点变得不可达时,Kubernetes 会尝试驱逐该 Pod。这意味着如果节点变得不可达,那么 Pod 将被终止,除非它能够容忍此污点。
  • node.kubernetes.io/unschedulable:NoSchedule op=Exists

    • 表示当节点被标记为 Unschedulable 时,该 Pod 不会被调度到该节点上。如果 Pod 已经在节点上运行,则它将继续运行,但不会被重新调度到其他节点。

2.5、定义污点

kubectl taint nodes <node-name> <key>=<value>:<effect>

node-name: 指定需要打污点的Node主机名 key=value:指定污点的键值型数据 effect:为污点的等级

key名称长度上线为253个字符,可以使用字母或者数字开头,支持字母、数字、连接符、点号、下划线作为key或者value。value最长是 63个字符。污点通常用于描述具体的部署规划,它们的键名形式如 node-typenode-rolenode-projectnode-geo 等。

2.6、添加污点

为k8s-node2添加污点,污点程度为NoSchedule,type=calculate为标签

kubectl taint node k8s-node2 type=calculate:NoSchedule

2.7、查看污点

这样的话我们创建Pod就不会被调度到打上污点的k8s-node2的节点上

kubectl describe node k8s-node2 | grep Taints

2.8、创建 Pod 资源清单

cat <<EOF > taint-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taint-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: taint-pod
  template:
    metadata:
      labels:
        app: taint-pod
    spec:
      containers:
      - image: busybox:latest
        name: taint-pod
        command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
EOF

2.9、应用Pod并查看Pod调度情况

可以看到 k8s-master1k8s-node2 因为有污点所以不会被分配 Pod。两个 Pod 均被分配到 k8s-node1

kubectl get pods -o wide | grep taint

2.10、删除污点

删除污点之需要指定标签的 key 及污点程度

kubectl taint node k8s-node2 type:NoSchedule-

3、容忍度

3.1、如何定义

Pod对象的容忍度可以通过其spec.tolerations字段进行添加,根据使用的操作符不同,主要有两种可用的形式:

  • Equal 操作符: 当使用 Equal 操作符时,Pod 的容忍度必须与节点的污点完全匹配,即污点的键(Key)、值(Value)和效果(Effect)都必须相同。

  • Exists 操作符:当使用 Exists 操作符时,Pod 的容忍度只需要与节点的污点键(Key)和效果(Effect)匹配即可,而不关心污点的具体值(Value)。

容忍度所用到的参数tolerations,tolerations参数下的还有以下几个二级参数:

  • operator:此值被称为运算符,值可以为[Equal|Exists],Equal表示污点的key是否等于value(默认参数),Exists只判断污点的key是否存在,使用该参数时,不需要定义value。
  • effect:指定匹配的污点程度,为空表示匹配所有等级的污点
    • NoSchedule: 当节点上存在带有 NoSchedule 效果的污点时,新的 Pod 不会被调度到该节点上。但是,如果 Pod 已经在节点上运行,则它将继续运行,不会被驱逐。
    • PreferNoSchedule : 当节点上存在带有 PreferNoSchedule 效果的污点时,新的 Pod 将尽量避免被调度到该节点上,但不是强制性的。也就是说,如果其他节点不可用或者不适合调度 Pod,则 Pod 仍可能被调度到该节点。
    • NoExecut: 当节点上存在带有 NoExecute 效果的污点时,新的 Pod 不会被调度到该节点上,并且已经运行在该节点上的 Pod 也会被驱逐。这种效果比 NoSchedule 更加强烈,因为它不仅阻止新的 Pod 被调度,还会驱逐已存在的 Pod。
  • key:指定Node上污点的键key。
  • value:指定Node上污点的值value。
  • tolerationSeconds:用于定于延迟驱赶当前Pod对象的时长,如果设置为0或者负值系统将立即驱赶当前Pod。(单位为秒)

3.2、为node打上不同等级的污点

kubectl taint nodes k8s-node1 node-role.kubernetes.io=node1:NoSchedule
kubectl taint nodes k8s-node2 node-role.kubernetes.io=node2:PreferNoSchedule
kubectl taint nodes k8s-node3 node-role.kubernetes.io=node3:NoExecute

添加一个worker节点

由于有三个不同等级的污点,为了更直观的展示,扩展为三个node

加入集群前和平时步骤一样

#在 master 节点上运行来获取加入集群所需的命令
kubeadm token create --print-join-command
#输入刚才生成的命令加入集群即可
kubectl get nodes

3.3、查看集群中节点的污点

kubectl describe node k8s-master1 k8s-node1 k8s-node2 k8s-node3 | grep Taints

在实际使用中,污点通常格式为 taint-key=value:effect。但是,在k8s-master1默认的中污点中,node-role.kubernetes.io/master:NoSchedule 被视为一个完整的键,而没有具体的值。

3.4、创建容忍NoSchedule级别污点的Pod

cat << EOF > pod-NoSchedule.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-noschedule
spec:
  containers:
  - name: noschedule-container
    image: nginx:1.14.0
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "node-role.kubernetes.io"                #指定污点的key
    operator: "Equal"           #Equal值表示我们指定的key必须要等于value
    value: "node1"                #指定value
    effect: "NoSchedule"        #指定污点级别

3.4.1、应用Pod并查看调度情况

kubectl apply -f pod-NoSchedule.yaml

kubectl get pods -o wide

3.5、创建容忍PreferNoSchedule级别污点的Pod

cat << EOF > pod-PreferNoSchedule.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-prefernoschedule
spec:
  containers:
  - name: prefernoschedule-container
    image: nginx:1.14.0
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "node-role.kubernetes.io"
    operator: "Equal"
    value: "node2"
    effect: "PreferNoSchedule"
EOF

3.5.1、应用Pod并查看调度情况

kubectl apply -f pod-PreferNoSchedule.yaml

kubectl get pods -o wide | grep pod-prefernoschedule

3.6、创建容忍NoExecute级别污点的Pod

cat<< EOF > pod-NoExecute.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-noexecute
spec:
  containers:
  - name: noexecute-container
    image: nginx:1.14.0
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "node-role.kubernetes.io"
    operator: "Equal"
    value: "node3"
    effect: "NoExecute"
    # tolerationSeconds 字段指定了 Pod 可以容忍 NoExecute 污点的时间为3600s
    tolerationSeconds: 3600
EOF

3.6.1、应用Pod并查看调度情况

kubectl apply -f pod-NoExecute.yaml

kubectl get pods -o wide | grep pod-noexecute

3.7、创建没有容忍度的Pod

cat << EOF > pod-notolerations.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-notolerations
spec:
  containers:
  - name: notolerations-container
    image: nginx:1.14.0
EOF

3.7.1、应用Pod并查看调度情况

创建了一个没有容忍项的 Pod,那么它不会被调度到带有 NoSchedule(k8s-master1/k8s-node1) 或 NoExecute(k8s-node3) 污点的节点上。

k8s-node2 的污点是 PreferNoSchedule。虽然 Pod 不会被优先调度到该节点,但如果没有其他合适的节点,Pod 仍然可以被调度到该节点上。

kubectl apply -f pod-notolerations.yaml

kubectl get pods -o wide | grep pod-notolerations

3.8、清除用于测试污点

kubectl taint node k8s-node1 node-role.kubernetes.io-
kubectl taint node k8s-node2 node-role.kubernetes.io-
kubectl taint node k8s-node3	 node-role.kubernetes.io-

七、NodeAffinity 节点亲和性调度

1、概述

节点亲和性调度程序是用来确定Pod对象调度位置的一组规则,这些规则基于节点上的自定义标签和Pod对象上指定的标签选择器进行定义。

节点亲和性允许Pod对象定义针对一组可以调度于其上的节点的亲和性或反亲和性,不过,它无法具体到某个特定的节点。例如将Pod调度至有着CPU的节点或者一个可用区域内的节点之上。

2、两种规则

定义节点亲和性规则时有两种类型的节点亲和性规则:

  • 硬亲和性(required):硬亲和性实现的是强制性规则,它是Pod调度时必须要满足的规则,而在不存在满足规则的Node时,Pod对象会被置为Pending状态。
  • 软亲和性(preferred):软亲和性规则实现的是一种柔性调度限制,它倾向于将Pod对象运行于某类特定节点之上,而调度器也将尽量满足此需求,但在无法满足需求时它将退而求其次地选择一个不匹配规则的节点之上。

3、定义节点亲和性规则注意点

  • 为节点配置合乎需求的标签。

  • 为Pod对象定义合理的标签选择器,从而能够基于标签选择器选择出符合需求的标签。

不过,如 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 名字中的后半段字符串 IgnoredDuringExecution 隐藏的意义所指,在Pod资源基于节点亲和性规则调度至某节点之后,节点标签发生了改变而不在符合此类节点亲和性规则时,调度器不会将Pod对象从此节点移除,因为它仅对新建的Pod对象生效。

4、节点硬亲和性

节点硬亲和性类似于Pod对象使用 nodeSelector 属性可以基于节点标签匹配的方式将Pod对象调度至某一个节点之上。不过它仅能基于简单的等值关系定义标签选择器,而 nodeAffinity 中支持使用 matchExpressions 属性构建更为复杂的标签选择机制。

4.1、节点硬亲和性配置模板

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-node-affinity
spec:
  containers:
  - name: affinity-container
    image: nginx:1.14.0
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: environment
            operator: In
            values:
            - production
          - key: role
            operator: In
            values:
            - web

4.2、参数解析

  1. nodeSelectorTerms:
    • 这是一个列表,其中包含一个或多个 matchExpressionsmatchFields 条件。Pod 必须至少满足其中一个 nodeSelectorTerm 中的所有条件才能被调度到节点上。
  2. matchExpressions:
    • 按照节点的标签列出节点选择器列表。每个 matchExpression 描述了一个节点标签的匹配条件。Pod 必须满足 nodeSelectorTerm 中的所有 matchExpressions 条件才能被调度到节点上。
  3. matchFields:
    • 按照节点的字段(而非标签)列出节点选择器列表。与 matchExpressions 类似,但用于节点的字段而非标签。
  4. key:
    • 指定要选择节点标签的键(key)。这是节点上标签的名称。
  5. values:
    • 指定要选择节点标签的值。值必须为数组形式,例如 ["value1"]values 的内容取决于所使用的操作符:
      • 如果操作符为 InNotInvalues 必须包含一个或多个值,表示节点标签必须具有这些值之一(In)或不能具有这些值之一(NotIn)。
      • 如果操作符为 ExistsDoesNotExistvalues 必须为空数组 []
      • 如果操作符为 GtLtvalues 必须包含一个整数值作为元素。
  6. operator:
    • 操作符定义了 keyvalues 之间的关系:
      • In: 节点标签的值必须在 values 列表中。
      • NotIn: 节点标签的值不能在 values 列表中。
      • Exists: 节点标签必须存在,而不管它的值是什么。
      • DoesNotExist: 节点标签不存在。
      • Gt: 节点标签的值必须大于 values 中指定的整数值。
      • Lt: 节点标签的值必须小于 values 中指定的整数值。

4.3、给 Node 节点添加标签

  • k8s-node1:

    • environment=sit

    • role=web

  • k8s-node2:

    • environment=sit

    • role=db

  • k8s-node3:

    • environment=prod

    • role=web

kubectl label node k8s-node1 environment=sit role=web

kubectl label node k8s-node2 environment=sit role=db

kubectl label node k8s-node3 environment=prod role=web

kubectl get nodes k8s-node1 k8s-node2 k8s-node3 --show-labels | egrep 'environment|role'

4.4、调度到同时有environment=sit和role=db标签的节点(存在)

cat << EOF > pod-sit-db.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-sit-db
spec:
  containers:
  - name: container-sit-db
    image: nginx:1.14.0
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: environment
            operator: In
            values: ["sit"]
          - key: role
            operator: In
            values: ["db"]
EOF

可以看到pod-sit-db被调度到k8s-node2上

kubectl apply -f pod-sit-db.yaml

kubectl get pods -o wide | grep pod-sit-db

4.5、调度到同时有environment=prod和role=db标签的节点(不存在)

这里values的两种写法都是可以的

cat << EOF > pod-prod-db.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-prod-db
spec:
  containers:
  - name: container-prod-db
    image: nginx:1.14.0
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: environment
            operator: In
            values:
              - prod
          - key: role
            operator: In
            values:
              - db
EOF

可以清晰地看到集群中不存在满足规则的Node时,Pod对象会被置为Pending状态。

kubectl apply -f pod-prod-db.yaml

kubectl get pods -o wide | grep pod-prod-db

describe pod pod-prod-db 查看Events

kubectl describe pods pod-prod-db | grep -A 10 Events

4.6、调度到具有 environment=sit 标签但不具有 role=db 标签的节点

cat << EOF > pod-sit-no-db.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-sit-no-db
spec:
  containers:
  - name: container-sit-no-db
    image: nginx:1.14.0
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: environment
            operator: In
            values:
              - sit
          - key: role
            operator: NotIn
            values:
              - db
EOF

具有 environment=sit 标签但不具有 role=db 标签的节点即k8s-node1

4.7、调度到具有environment 标签的节点(无论其值)

cat << EOF > pod-environment-exists.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-environment-exists
spec:
  containers:
  - name: container-environment-exists
    image: nginx:1.14.0
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: environment
            operator: Exists
            values: []
EOF

这样的话pod就会在 k8s-node1k8s-node2k8s-node3 三个node节点中随机一个调度

kubectl apply -f pod-environment-exists.yaml

kubectl get pods -o wide | grep pod-environment-exists

5、节点软亲和性

节点软亲和性为节点选择机制提供了一种柔性控制逻辑,当调度的Pod对象不再是”必须”,而是“应该”放置于某些特性节点之上,当条件不满足时,它也能够接受编排于其它不符合条件的节点之上,另外,它还为每种倾向性提供了weight属性以便用户定义其优先级,取值范围是1-100,数字越大优先级越高。

5.1、节点软亲和性配置模板

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-node-preference
spec:
  containers:
  - name: affinity-container
    image: nginx:1.14.0
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - preference:
          matchExpressions:
          - key: environment
            operator: In
            values: ["sit"]
        weight: 1

5.2、参数解析

  1. preferredDuringSchedulingIgnoredDuringExecution:
    • 这是一个列表,其中包含一个或多个 preference 条目。每个条目都有一个 weight,表示该偏好的重要程度。
  2. preference:
    • 定义了一个节点选择偏好,其中包括一系列 matchExpressions
  3. weight:
    • 在 1-100 范围内,与匹配相应的节点选项相关联的权重。数值越高权重越高代表更强的偏好。
  4. matchExpressions:
    • 按照节点的标签列出节点选择器列表。每个 matchExpression 描述了一个节点标签的匹配条件。Pod 将尽可能被调度到满足这些条件的节点上。
  5. key:
    • 指定要选择节点标签的键(key)。这是节点上标签的名称。
  6. values:
    • 指定要选择节点标签的值。值必须为数组形式,例如 ["value"]
    • 如果操作符为 In 或者 NotInvalues 则不能为空。
    • 如果操作符为 Exists 或者 DoesNotExistvalues 则必须为空 []。
    • 如果操作符为 GtLt,则 values 必须有单个元素,该元素将被解释为整数。
  7. operator:
    • 操作符定义了 key 和 values 之间的关系。
      • In: 节点标签的值必须在 values 列表中。
      • NotIn: 节点标签的值不能在 values 列表中。
      • Exists: 节点标签必须存在,而不管它的值是什么。
      • DoesNotExist: 节点标签不存在。
      • Gt: 节点标签的值必须大于 values 中指定的整数值。
      • Lt: 节点标签的值必须小于 values 中指定的整数值。

5.3、示例

cat << EOF > deployment-with-node-preferences.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-with-node-preferences
spec:
  replicas: 14
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app-container
        image: nginx:1.14.0
        imagePullPolicy: IfNotPresent
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - preference:
              matchExpressions:
              - key: environment
                operator: In
                values: ["prod"]
              - key: role
                operator: In
                values: ["db"]
            weight: 100
          - preference:
              matchExpressions:
              - key: environment
                operator: In
                values: ["sit"]
              - key: role
                operator: In
                values: ["web"]
            weight: 90
          - preference:
              matchExpressions:
              - key: environment
                operator: In
                values: ["sit"]
              - key: role
                operator: In
                values: ["db"]
            weight: 90
          - preference:
              matchExpressions:
              - key: environment
                operator: In
                values: ["prod"]
              - key: role
                operator: In
                values: ["web"]
            weight: 90
          - preference:
              matchExpressions:
              - key: environment
                operator: NotIn
                values: ["prod"]
              - key: role
                operator: In
                values: ["web"]
            weight: 80
          - preference:
              matchExpressions:
              - key: environment
                operator: Exists
                values: []
              - key: role
                operator: In
                values: ["db"]
            weight: 80
          - preference:
              matchExpressions:
              - key: environment
                operator: DoesNotExist
                values: []
              - key: role
                operator: In
                values: ["web"]
            weight: 1
EOF

5.3.1、分析

  • 偏好 1:

    • 条件:节点必须具有 environment=prodrole=db 的标签。

    • 权重:100

    • 无匹配节点。

  • 偏好 2:

    • 条件:节点必须具有 environment=sitrole=web 的标签。

    • 权重:90

    • 匹配节点:k8s-node1

  • 偏好 3:

    • 条件:节点必须具有 environment=sitrole=db 的标签。

    • 权重:90

    • 匹配节点:k8s-node2

  • 偏好 4:

    • 条件:节点必须具有 environment=prodrole=web 的标签。

    • 权重:90

    • 匹配节点:k8s-node3

  • 偏好 5:

    • 条件:节点必须具有 environment 标签,其值不为 prod,并且具有 role=web 的标签。

    • 权重:80

    • 匹配节点:k8s-node1

  • 偏好 6:

    • 条件:节点必须具有 environment 标签,并且具有 role=db的标签。

    • 权重:80

    • 匹配节点:k8s-node2

  • 偏好 7:

    • 条件:节点没有 environment 标签,并且具有 role=web 的标签。

    • 权重:1

    • 无匹配节点。

5.3.2、应用Deploymet查看调度情况

结果不出意料的出乎了预料,节点软亲和性理解概念即可平时几乎遇不上他

kubectl apply -f deployment-with-node-preferences.yaml

kubectl get pods -o wide

6、清除 Node 标签

kubectl label node k8s-node1 environment- role-
kubectl label node k8s-node2 environment- role-
kubectl label node k8s-node3 environment- role-

八、PodAffinity Pod亲和性调度

1、概述

Pod 亲和性是一种机制,允许指定 Pod 应该与其他 Pod 之间的关系。当希望某些 Pod 被调度到与特定类型的其他 Pod 接近的位置时,可以使用 Pod 亲和性。这通常用于需要低延迟通信的场景,比如应用 Pod 需要与数据库 Pod 进行频繁交互。

2、分类

Pod 亲和性可以根据其约束的严格程度分为两类:硬亲和性和软亲和性。

  • 硬亲和性是一种必须满足的约束,只有当 Pod 满足了这些约束条件时,它才会被调度到集群中的某个节点上。如果 Pod 已经被调度并且之后亲和性条件不再满足(例如,其他 Pod 被删除),那么已经调度的 Pod 不会被重新调度。

  • 软亲和性是一种偏好,它不构成硬性约束。Kubernetes 会在调度 Pod 时尽可能满足这些偏好,但如果无法满足,Pod 仍然会被调度。这意味着即使 Pod 无法完全按照软亲和性偏好进行调度,它也会被调度到集群中的某个节点上。

3、Pod 硬亲和性

3.1、Pod 硬亲和性配置模板

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
  labels:
    app: app-pod
spec:
  containers:
  - name: app-container
    image: nginx:1.14.0
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - db-pod
        topologyKey: kubernetes.io/hostname

3.2、参数解析

  • affinity: 定义亲和性和反亲和性的配置。
    • podAffinity:Pod 亲和性配置。
      • requiredDuringSchedulingIgnoredDuringExecution: 硬性约束,表示 Pod 必须满足亲和性要求才能被调度。
        • labelSelector: 选择具有特定标签的 Pod。
          • matchExpressions: 选择具有指定键值对的 Pod。
            • key: 标签键。
            • operator: 操作符,这里使用 In 表示匹配键值对。
            • values: 标签值。
        • topologyKey: 拓扑键,这里使用 kubernetes.io/hostname 表示 Pod 应该在同一台主机上。

3.3、为 Node 打上不同地区的标签

kubectl label node k8s-node1 zone=beijing
kubectl label node k8s-node2 zone=shanghai
kubectl label node k8s-node3 zone=shenzhen

3.4、创建 Pod 资源清单(使用 Pod 硬亲和性)

cat << EOF > podaffinity-required-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: podaffinity-required-pod
spec:
  containers:
  - name: nginx-containers
    image: nginx:1.14.0
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - proxy
            - web
        topologyKey: zone
EOF
  • labelSelector: 选择具有 app 标签且值为 proxyweb 的 Pod。
  • topologyKey: 指定节点的标签键 zone,这表示 Pod 必须与具有相同 zone 标签的其他 Pod 运行在同一节点上。

3.5、查看调度结果

由于集群中还没有其他带有 app 标签为 proxyweb 的 Pod,因此 podaffinity-required-pod 将保持在 Pending 状态。

kubectl apply -f podaffinity-required-pod.yaml

kubectl get pods -o wide | grep podaffinity-required-pod

3.6、创建带有特定标签的 Pod

cat << EOF > app-proxy-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-proxy-pod
  labels:
    app: proxy
spec:
  containers:
  - name: app-proxy
    image: nginx:1.14.0
EOF

3.7、查看调度结果

kubectl apply -f app-proxy-pod.yaml 

kubectl get pods -o wide | grep app-proxy-pod

kubectl get pods -o wide | grep podaffinity-required-pod

  • app-proxy-pod: 被调度到了 k8s-node1
  • podaffinity-required-pod: 由于硬亲和性要求,该 Pod 也需要被调度到与 app-proxy-pod 相同的节点上,即 k8s-node1

4、Pod 软亲和性

4.1、Pod 软亲和性配置模板

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: nginx:1.14.0
  affinity:
    podAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - my-other-app
          topologyKey: kubernetes.io/hostname

4.2、参数解析

preferredDuringSchedulingIgnoredDuringExecution:软偏好,不构成硬性约束。k8s 会在调度 Pod 时尽可能满足这些偏好

  • weight: 如果满足此亲和性条件时,给 Pod 分配到该节点的优先级分数。取值范围是1-100,数字越大优先级越高。
  • podAffinityTerm:描述了亲和性条件。
    • labelSelector: 指定要匹配的 Pod 的标签。
      • matchExpressions:
        • key: 匹配标签的键名,这里是 app
        • operator: 匹配操作符,这里是 In,表示标签的值必须在 values 列表中。
        • values: 匹配标签的具体值,这里是 my-other-app
    • topologyKey: 指定要匹配的拓扑键,这里是 kubernetes.io/hostname,表示希望 Pod 与具有相同 app=my-other-app 标签的其他 Pod 在同一个节点上运行。

4.3、创建带有 app=nginx 和 app=busybox 标签的 Pod。

cat << EOF > pod-nginx-busybox.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx-container
    image: nginx:1.14.0

---

apiVersion: v1
kind: Pod
metadata:
  name: busybox-pod
  labels:
    app: busybox
spec:
  containers:
  - image: busybox:latest
    name: nodename-containers
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
EOF

4.4、创建 Pod 软亲和性的 Deployment

cat << EOF > soft-pod-affinity-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: soft-pod-affinity-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 81
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - nginx
              topologyKey: zone
          - weight: 80
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - busybox
              topologyKey: zone
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "while true; do sleep 3600; done"]
EOF

4.5、应用标签为 cache 和 db 的 Pod

kubectl apply -f pod-cache-db.yaml

4.6、应用带有 Pod 软亲和性的 Deployment

kubectl apply -f soft-pod-affinity-deployment.yaml

4.7、查看Pod调度情况

  • busybox-pod被调度到k8s-node1,nginx-pod被调度到k8s-node3

  • 定义了两个软亲和性偏好:

    • 第一个偏好权重为 81,表示 Pod 更倾向于与标签为 app=nginx 的 Pod 运行在同一 zone 的节点上。
    • 第二个偏好权重为 80,表示 Pod 更倾向于与标签为 app=busybox 的 Pod 运行在同一 zone 的节点上。
  • 从结果来看还是比较符合我们定义的软亲和度规则

kubectl get pods -o wide

九、Pod Anti-Affinity Pod反亲和性调度

1、概述

Pod 反亲和性(Pod Anti-Affinity)是一种 Kubernetes 调度策略,它确保带有特定标签的 Pod 不会被调度到同一节点上。Pod亲和性是将有密切关联的Pod运行到同一平面、同一个区域或者同一台机器上,而反亲和性是将Pod运行在不同区域、不同机器上,Pod反亲和性调度一般用于分散同一类应用的Pod对象等。

2、分类

  • 硬反亲和性 (Hard Anti-Affinity) :硬反亲和性是一种强制性的要求,如果 Pod 不能满足硬反亲和性条件,则不会被调度到任何节点。硬反亲和性使用 requiredDuringSchedulingIgnoredDuringExecution 字段来定义。

  • 软反亲和性 (Soft Anti-Affinity):软反亲和性则是一种偏好,如果满足条件则更好,但即便不满足条件,Pod 仍会被调度。软反亲和性使用 preferredDuringSchedulingIgnoredDuringExecution 字段来定义。

3、Pod 硬反亲和性

3.1、Pod 硬反亲和性配置模板

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-anti-affinity-hard
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - myapp
            topologyKey: kubernetes.io/hostname
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - myapp
            topologyKey: zone
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "while true; do sleep 3600; done"]

3.2、创建带有硬反亲和性规则的Deployment

cat << EOF > pod-anti-affinity-hard.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-anti-affinity-hard
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values: 
                - myapp
            topologyKey: zone
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "tail -f /etc/passwd" ]
EOF
  • 定义了一个名为 pod-anti-affinity-hard 的 Deployment,它会创建 4 个副本。
  • Pod 的标签为 app=myapp
  • topologyKey 设置为 zone,表示 Pod 不应被调度到具有相同 zone 标签的节点上。

3.3、应用Deployment查看Pod调度情况

kubectl apply -f pod-anti-affinity-hard.yaml

kubectl get pods -o wide --show-labels

  • 由于硬反亲和性配置,带有 app=myapp 标签的 Pod 不会被调度到具有相同 zone 标签的节点上。
  • 由于只有三个 Node 具有不同的 zone 标签,所以只有三个 Pod 被成功调度到了不同的 Node 上。
  • 第四个 Pod 无法找到满足硬反亲和性条件的 Node,因此一直处于 Pending 状态。

4、Pod软反亲和性

4.1、Pod 软反亲和性配置模板

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-anti-affinity-soft
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - myapp
              topologyKey: kubernetes.io/hostname
          - weight: 50
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - myapp
              topologyKey: zone
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "while true; do sleep 3600; done"]

4.2、创建带有 Pod 软反亲和性的 Deployment

cat << EOF > pod-anti-affinity-soft.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-anti-affinity-soft
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 90
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - myapp
              topologyKey: kubernetes.io/hostname
          - weight: 89
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - myapp
              topologyKey: zone
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "while true; do sleep 3600; done"]
EOF

4.3、应用 Deployment 查看调度情况

kubectl apply -f pod-anti-affinity-soft.yaml

kubectl get pods -o wide --show-labels

kubectl get nodes -L zone

根据软反亲和性配置,第一个规则的权重较高(90),因此调度器尽量避免将具有相同 app=myapp 标签的 Pod 调度到同一节点上。

第二个规则的权重较低(89),表示尽量避免将 Pod 调度到具有相同 zone 标签的节点上。

然后由于是软反亲和性,所以即使不满足规则时 Pod 最终还是会被调度。

热门相关:纨绔毒医   弃妃不承欢:腹黑国师别乱撩   我在镇夜司打开地狱之门   赠我深爱如长风   侯门弃女之妖孽丞相赖上门