调度器选择策略:
 预选策略(Predicate)
  1. 根据运行Pod的资源限制来排除不符合要求的Node
  2. 根据运行Pod时,是否要求共享宿主机的网络名称空间来判断,如: 某Pod启动要共享宿主机的网络名称空间,启动80端口,而某些Node的80已经被占用,那它就不符合,就也要不排除。

 优选策略(Priority):
  此阶段会经过一系列函数,会把预选出来的节点的相关属性都输入到这些函数中,通过计算来获取每个节点的优先分值,然后在倒序排序,取得分最高者,作为运行Pod的节点,若计算后,发现有多个Node得分相同,此时将随机选取一个Node作为该Pod运行的Node。

 选定(Select):
  当优选选出一个Node后,该Pod将会被绑定到该Node上运行。

  我们在定义一个Pod要运行在那个Node,是有一定偏向性的,比如有些Pod需要运行的有SSD硬盘的Node上,而有些Pod则需要运行在有GPU的节点上等,这时就需要我们给Node上打上不同的标签,来标识整个集群中不同Node的特性,这些特性包括,特有硬件标识,地理位置标识,不同机房标识等。

  我们使用kubectl explain pod.spec 可以看到其中有nodeName 和 nodeSelector 两个属性,第一个是明确指定Node节点名,我就要运行在这个Node上,第二个就是通过匹配Node标签,来选择运行的Node。

调度方式有以下几类:
  1. 节点倾向性(或叫 节点亲和性调度)
   这种调度通常会采用 nodeSelector 来完成调度。

  2. Pod倾向性 和 Pod非倾向性(这也称为Pod亲和性和非亲和性)
   有时候我们需要让某些Pod能运行在同一处时,就需要使用Pod亲和性来定义,如:我们要定义一个NMT(Nginx+Tomcat+MySQL),若这三个Pod分别被调度三个不同地域的机房中,比如北京,上海,深圳等地,那肯定不是我们所希望的,这时我们就需要使用Pod亲和性来定义这三个Pod能运行在相同的Node 或 相邻的Node上。
  而在另外一些情况下,我们又希望某些Pod一定不能运行在相同Node上,这时就需要使用Pod的非亲和性了。

  3. 污点 和 污点容忍调度
   Taints(污点):是定义在Node上的
   Tolerations(容忍): 是定义在Pod上的
    污点:指有时我们希望不将某些Pod调度到某些Node上,这时我们可对某些Node上打上污点,然后,当我们创建Pod时,我们可定义此Pod能容忍的污点范围。
    假如:NodeA上有5个污点: "吃喝嫖赌抽",而此次要调度的Pod比较大度,能容忍5个污点,而NodeA的污点正好是Pod能容忍污点的范围内,于是Pod就能"嫁入"该NodeA。
    假如,Pod运行到NodeA上后,后期我们又给该NodeA打上了新污点如:"坑蒙拐骗偷",此时Pod发现NodeA的恶性原来有这么多,怎么办?通常有两种选择策略:
    1. Pod继续运行在NodeA上"艰苦度日"
    2. Pod被驱逐,或者叫Pod逃离。

默认调度器的调度规则是:根据Node最大资源空闲率来进行均衡调度。

调度器:
 预选策略
  【注意:下面这些预选策略仅是其中一部分,并且k8s在进行预选时,它会是综合评估的,即所有启用的策略规则都要做判断,所得结果越符合就越会被挑选出来,进入优选策略中】:
  CheckNodeCondition: 检查节点是否符合调度条件
  GeneralPredicates:
  HostName: 这种是判断Pod是否定义了pod.spec.hostname属性,若定义了,就在预选时,看看这些Node上是否存在相同主机名的Pod,若有,就排除该Node; 这可能是因为,某些Pod是需要有固定主机名,才会使用。
  PodFitsHostPorts: 此预选策略是判断 pods.spec.containers.ports.hostPort 属性是否定义了,若定义了就表示该Pod要绑定到Node上指定的Port上,这时在进行预选时,就要判断这个端口是否被占用了,若占用就会排除该Node。
  MatchNodeSeletctor:此预选策略会判断 pods.spec.nodeSelector 属性是否定义了,若定义了就根据Pod所定义的NodeSelector来选出匹配指定标签的Node。
  PodFitsResources: 此预选策略会判断 Node上是否符合运行Pod所需的最小空闲资源。

  NoDiskConfict: 用于判断若Pod定义了存储卷,则要检查该存储卷在该Node上是否可用,若Node能满足Pod存储卷的使用需求,则表示此Node可用。
  PodToleratesNodeTaints:检查Pod上的spec.tolerations可容忍的污点是否完全包含Node上定义的污点,若完全包含,则表示Pod能容忍该Node节点的污点,否则该Node不会通过预选。
  PodToleratesNodeNoExecuteTaints:
   #首先,污点并非单一属性,它有三种属性,NoExcute是污点的一种属性。
   #此检查是,若Pod被调度到某Node上时,最初Node上没有Pod不能容忍的污点,但后来Node的污点改变了,多了Pod不能容忍的污点,这时要怎么处理那?默认是继承既定事实,继续在该Node上运行。
   # 第二种是不接受,也就是检查此属性,此时Node会驱逐该Pod,让该Pod重新调度到新Node上。

  CheckNodeLabelPresence:这是定义在选择Node时,检查Node上是否存在指定的标签,若存在则可调度。此规则默认不启用。
  CheckServiceAffinity: 当我们需要创建一些Pod时,这些Pod都使用相同的Serivce,这时,若启用该预选策略,则将这些Pod优先调度到 已存在较多该Service下的Pod的Node上。

  #下面三个默认启用,因为这三个是最大的三个云运营商
  MaxEBSVolumeCount:此预选策略是指若Pod需要使用亚马逊的EBS(弹性块存储服务),则检查该EBS是否已经达到最大运行挂载数39个,若没有,则表示还有存储空间可用。
  MaxGCEPDVolumeCount: GCEPD:是Google的云环境中的持久存储,默认是16个
  MaxAzureDiskVolumeCount: 最大是16个

  CheckVolumeBinding:检查Node上是否存在以绑定 和 未绑定的PVC,若Node上有PVC,并且没有被绑定则能满足Pod运行的需求。
  NoVolumeZoneConflict:它是在给定了区域限制的情况下,Zone在IDC机房是有限制的,他们通常会按房间,机柜,机架来划分范围,假如机架上有20台服务器,每2台一个区域,这个就是一个逻辑区域,此配置项的含义就是检查区域中Node上是否存在逻辑卷冲突。

  CheckNodeMemoryPressure:检查Node上是否存在内存压力,即若某Node上已经出现内存紧张的情况了,那就不要在往它上面调度了。
  CheckNodePIDPressure:检查Node上是否存在PID压力过大的情况,即若某Node上运行的进程过多,导致PID可能紧张,这时在选择调度时,也不会选择它。
  CheckNodeDiskPressure:检查Node上是否存在磁盘使用压力过大的情况

  MatchInterPodAffinity:检查Node上是否满足Pod的亲和性,假如Pod是Nginx,它是要为Tomcat Pod做代理的,那么在调度tomcat Pod时,就会检查Node上是否有Nginx Pod,若有这个非常亲和的Pod则优先调度到该Node上。

优选函数:
  默认启用了7个红色的优选函数,它们的得分相加值越高,该节点越优选被选中。
  LeastRequested: 【与它相反对一个优选函数:MostRequested,它是Node资源占用越高得分越高,有点像是,先将某Node的资源先全部占满的意味,然后空出部分Node.】
     计算得分的算法公式=(cpu((capacity - sum(requested))*10 / capacity) + memory((capacity - sum(requested))*10 / capacity))/2
       此公式的含义:
      假如当前有16个核心,已经用了8个核心,那么总的使用率就是 (16-8)/16 = 0.5 ,而乘以10,是为了计算得分方便,将其转化为0~10之间的数,
      而内存的计算含义一样,假如内存32G,已经用了8G,则使用率为 (32-8)/32 = 0.75 ,乘以10
      将CPU + Memory/2 就可以得到一个平均值。该平均值越高,表示该节点的资源空闲率越高。

  BalanceResourceAllocation:
    它表示CPU和内存资源占用率的相近程度,作为评估标准,越接近也优选选择,比如: Node1的CPU和内存使用率一个48%一个50%,而Node2的CPU和内存使用率都是50%,则Node2胜出。
  #它通常要和LeastRequested 结合来评估Node的资源利用率的。
  #两个组合主要是为了平衡各Node上资源的利用率。

NodePreferAvoidPods:
  根据Node节点上是否定义了注解信息"scheduler.alpha.kubernetes.io/preferAvoidPods",若没有则该Node得分为10,而且权重为1万,有此注解则说明该Node是适合运行Pod的。而对于那些有Replicas Controller的控制器则,该Node的得分为0,此时则表示Pod不能运行在该Node上,但这不能决定Pod一定不能运行在该Node上,因为最终是要看总体得分的。
  注解信息:
    # kubectl describe node node1.zcf.com
      Name: node1.zcf.com
      Roles: node
      ..........
      Annotations: node.alpha.kubernetes.io/ttl: 0      #这就是所谓的node上的注解信息。
      volumes.kubernetes.io/controller-managed-attach-detach: true

TaintToleration:
  基于Pod对调度Node上的污点容忍度来评估是否可调度到该Node上。
  简单说:就是取出Pod对象中定义的spec.tolerations列表,查看其中能容忍的污点,然后,一个一个去对比Node上存在的污点,若匹配的越多,则该Node的得分越低,就越不会调度到该Node上。

SelectorSpreading:
  此优化函数是希望将相同标签选择器选取的Pod尽可能的散开到多个Node上。因此假如:ReplicaSet控制器,已经在B上创建了一个Pod,那么它要再创建一个Pod时,此优选函数在计算A,B,C三个Node的得分时,会根据那个Node上拥有此ReplicaSet控制器的标签选择器所匹配的Pod数量,来评分,数量越少,分数越高,反之越低。
  而使用标签选择器的资源对象有:Service,Replication Controller,ReplicaSet,StatefulSet。

InterPodAffinity:
  遍历对象的亲和性条目,并将能够匹配到给定节点的条目的条目数相加结果值越大得分越高。
  简单说:NodeA上运行了3个Pod,NodeB上运行了6个Pod,NodeC上运行了5个Pod,现在要调度一个Pod到其中一个Node上,而该Pod的比较亲和某类Pod,此优选函数就会将这些Node上,所有匹配该Pod亲和条目的数量相加,值越大,则越得分越高。其中条目的理解,我还不是很懂。

NodeAffinity:
  它是根据pod中定义的nodeSeletor来对Node做亲和性检查, 能成功匹配的数量越多,则得分越高。

NodeLabel:
  根据Node是否有某些标签来做评估,然后计算得分的,此优选函数只关注标签,不关注值,只有Node上有这个标签就可以得分。

ImageLocality:
  它评分的标准是,判断Node上是否有运行该Pod的镜像,若有则得分,否则不得分。
  但它评估镜像是根据镜像总的体积大小来计算得分的,例如:NodeA上有1个镜像1G,NodeB上有2镜像,NodeC上有3个镜像,而运行此Pod需要4个镜像,其中一个镜像1G,其它都是比较小,这时NodeA的得分会最高。

#实验:
  1. 定义一个Pod,并设置其nodeSelector,若指定的nodeSeletor标签没有匹配到任何Node,则Pod将处于Pinding状态,只有你给某个Node打上指定的标签和值后,该Pod才会被调度上去。

#实验2:
  kubectl explain pods.spec.affinity
    nodeAffinity: 定义Node的亲和性
    podAffinity: 定义Pod的亲和性
    podAntiAffinity:定义Pod非亲和性

  nodeAffinity:
   # 软亲和性,即若能满足则一定运行在满足条件的Node上,否则运行在其它Node上也不是不可以。
   preferredDuringSchedulingIgnoredDuringExecution

   #硬亲和性,若不能满足运行条件,则不运行Pod。
   requiredDuringSchedulingIgnoredDuringExecution
   nodeSelectorTerms:
    matchExpressions :     这是更强大的一种方式,它是配表达式的。

    matchFields:
      key: 要对那个label key做匹配操作
      operator:指定你是做什么比较操作,支持: In/NotIn(包含/不包含), Exists/DoesNotExist(存在/不存在) , Gt/Lt(大于/小于)
      values:若操作符为Exists/DoesNotExists则,values必须为空。

vim  pod-node-required-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values:
- foo
- bar #默认node上没有打上zone=foo 或 zone=bar的标签,此时是硬亲和,若不能满足条件,则无法调动. vim pod-node-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: zone
operator: In
values:
- foo
- bar
weight: # 此为软亲和,若node上有zone=foo 或 zone=bar,则优先调度上去,若全部都没有,勉强选一个调度上去也不是不可以

为啥有了节点亲和性,还要有Pod亲和性?
  假如我们有一个分布式跨地域的大集群,现在想构建一个NMT的架构,为了实现这个目的,我完全可以给Node上打标签,然后,让NMT这些Pod都匹配这些标签,然后被调度到这些Node上去运行,这不是也可以实现我们的目的吗?
  看上去没有问题,但问题是,假如你跨地域的分布式K8s集群,分别分布在北京,上海,深圳,杭州等地,你就不得不精心规划不同地域,不同IDC,不同机房,不同机柜的Node标签规划,否则你很可能在将来实现这个目的是变得困难不堪,你可以想象一下,你希望让一组NMT运行在北京某IDC的某机房中,这样一个NMT架构在工作时,才能更加高效的通信,你想实现这样的控制,你不规划Node的标签,你让预选策略如何按照你的想法去调度那?这还仅是一方面,你为了NMT架构能分离开,北京一套,上海一套.....你也要配置Pod的在选择Node标签时,设定它亲和哪些标签,所以工作量你可自己评估。

  Pod亲和性的逻辑时,我要部署NMT环境,我的MySQL Pod第一个被调用,我不管调度器会把MySQL Pod调度到哪里,反正只要调度完成,并且运行了,我后续的Nginx,Tomcat的Pod是亲和MySQL Pod的,MySQL Pod在哪个Node上,那在调度NT时,就更加优先调度到那个Node上。
  这样说看上去很简单,但仔细想想,MySQL被调度到某Node上,要是将NT也调度到那个Node上合适吗?若那个Node没有这么多资源运行它们怎么办?能不能将NT调度到M所在Node旁边的Node上,或同机房的Node中?那这算亲和吗? 其实也算,但怎么知道运行MySQL Pod的Node ,它旁边的Node是否那个主机?这其实还是需要借助于Node标签来实现,因此为集群标签的规划是在所难免的,因为你必须给预选策略一种判断标准,哪些是相同机柜,哪些是不同机房对吧,否则鬼知道集群中那么多Node那个和那个是邻居对吧,当然若你靠主机名来判断也不是不可以,你就要定义根据主机名的判断标准了。所以,比较通用的方法是,给每个Node打上不同的标签,如:北京机房1 机柜20 机架号等等来定义,这样根据这些标签值,就可以判断Node和Node之间的临近关系了。
  打个比方: MySQL Pod被调度到 rack=bjYZ1 ,那后续调度NT时,就会优选rack=bjYZ1的Node,只要有这个标签值得Node会被优选调度,这样NMT它们就都可以运行在北京亦庄的机房中了。

  kubectl pods.spec.affinity.podAffinity.
  requiredDuringSchedulingIgnoredDuringExecution : 硬限制
  labelSelector: 指定亲和Pod的标签
  namespaces: 你要指定亲和Pod,你就需要指定你亲和的Pod属于哪个名称空间,否则默认是Pod创建在那个名称空间,就匹配那个名称空间的Pod

Pod亲和性示例:
vim pod-required-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: web1-first
labels:
app: web1
tier: frontend
spec:
containers:
- name: myapp
image: harbor.zcf.com/k8s/myapp:v1 ---
apiVersion: v1
kind: Pod
metadata:
name: db1-second
labels:
app: db1
tier: db1
spec:
containers:
- name: busybox
image: busybox: latest
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "sleep 3600"]
affinity:
podAffinity: #若需要测试Pod的反亲和性,只需要修改podAffinity为 podAntiAffinity 即可测试
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["web1","web2"]}
topologyKey: kubernetes.io/hostname #通过默认Node上主机名标签来临时代表地区标签,即:只有主机名相同,就认为是在相同位置的。 #Pod反亲和测试:
Pod亲和性示例:
vim pod-required-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: web1-first
labels:
app: web1
tier: frontend
spec:
containers:
- name: myapp
image: harbor.zcf.com/k8s/myapp:v1 ---
apiVersion: v1
kind: Pod
metadata:
name: db1-second
labels:
app: db1
tier: db1
spec:
containers:
- name: busybox
image: busybox: latest
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "sleep 3600"]
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["web1","web2"]}
topologyKey: kubernetes.io/hostname
#第一次测试: 使用存在的标签来测试,web1会运行在一个节点上,db1一定不会运行在web1所在的Node上。
topologyKey: zone
#第二次测试: 使用此key,因为两个Node上都没有此标签key,因此预选策略将认为,没有找到不同区域的Node,因此db1将处于Pinding状态,无法启动。
vim  myapp-toleration.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-toler1
spec:
replicas:
selector:
matchLabels:
app: myapp
release: canary
template:
metadata:
labels:
app: myapp
release: canary
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v2
ports:
- name: http
containerPort:
tolerations:
- key: "node-type"
operator: "Equal"
value: "production"
effect: "NoSchedule"
#说明: 在定义Pod的容忍度时,若指定为NoSchedule,则不能定义tolerationSeconds,即容忍不了时,可宽限多久被驱逐。
# 若定义为NoExecute 则可以定义tolerationSeconds。
# Equal:做等值比较,即node-type标签 和 其值必须完全匹配。
# Exists :做存在性比较,即只要node-type标签存在,即可匹配,不看其标签值是否相同。 #测试NoExecute影响
#目前node2.zcf.com 上定义了node-type污点的影响为: NoExecute
............
tolerations:
- key: "node-type"
operator: "Exists"
value: "" #设置node-type的值为空,因为Exists是做标签判断,只要node-type标签存在,即可.
effect: "NoSchedule"
#定义污点影响度为NoSchedule,即若Node上没有这个node-type污点标签,就不调度到其上.
#若Node上污点影响(effect)为NoExecute,它是不包含NoSchedule的,即Node是不允许不能容忍NoExecute的Pod调度到自己上面的。 #若Pod能容忍node-type标签的污点,无论它是什么值,什么影响,都可以容忍
...............
tolerations:
- key: "node-type"
operator: "Exists"
value: ""
effect: "" #删除节点上的污点:
kubectl taint node node01.zcf.com node-type-
#注意:node-type是:node01上的标签,最后的“-”是删除该标签的意思。

K8s预选策略和优选函数简介的更多相关文章

  1. 05-k8s调度器、预选策略、优选函数

    目录 k8s调度器.预选策略.优选函数 节点选择过程 调度器 预选策略 优选函数 高级调度设置机制 node选择器/node亲和调度 pod亲和性 污点调度 Taints 与 Tolerations ...

  2. k8s调度的预选策略及优选函数

    scheduler调度过程:    Predicate(预选)-->Priority(优选)-->Select(选定)调度方式:    1.节点亲和性调度(NodeAffinity)使用n ...

  3. k8s之调度器、预选策略及优选函数

    1.调度器(scheduler) 调度器的功能是调度Pod在哪个Node上运行,这些调度信息存储在master上的etcd里面,能够和etcd打交道的只有apiserver; kubelet运行在no ...

  4. K8S 调度器,预选策略,优选函数

    Kubernetes Scheduler 提供的调度流程分三步: 预选策略(predicate) 遍历nodelist,选择出符合要求的候选节点,Kubernetes内置了多种预选规则供用户选择. 优 ...

  5. k8s-调度器、预选策略及优选函数-二十

    一.简介 master上运行着三个最核心的组件,apiserver.scheduler.controller manager.此外,master还依赖于ectd存储节点,最好ectd是有冗余能力的集群 ...

  6. Kubernetes 学习20调度器,预选策略及优选函数

    一.概述 1.k8s集群中能运行pod资源的其实就是我们所谓的节点,也称为工作节点.master从本质上来讲,他其实是运行整个集群的控制平面组件的比如apiserver,scheal,controlm ...

  7. k8s调度器、预选策略及调度方式

    一.k8s调度流程 1.(预选)先排除完全不符合pod运行要求的节点2.(优先)根据一系列算法,算出node的得分,最高没有相同的,就直接选择3.上一步有相同的话,就随机选一个 二.调度方式 1.no ...

  8. linux进程编程:子进程创建及执行函数简介

    linux进程编程:子进程创建及执行函数简介 子进程创建及执行函数有三个: (1)fork();(2)exec();(3)system();    下面分别做详细介绍.(1)fork()    函数定 ...

  9. Linux Shell系列教程之(十五) Shell函数简介

    本文是Linux Shell系列教程的第(十五)篇,更多Linux Shell教程请看:Linux Shell系列教程 函数可以将一个复杂功能划分成若干模块,从而使程序结构更加清晰,代码重复利用率更高 ...

随机推荐

  1. SpringCloud与Dubbo区别对比

    1:SpringCloud与Dubbo区别对比 (1):活跃度 目前SpringCloud的活跃度明显远高于Dubbo(参考github) (2):主要区别   Dubbo Spring Cloud ...

  2. Turbo码基本框架

    1. 基本原理 1.1 定义 1.2 分量码设计 2. 编码方法 2.1 交织器的设计 2.2 分量编码器 2.3 删余矩阵及复用 3. 译码方法和算法 3.1 迭代译码 3.2 MAP类算法 3.3 ...

  3. 关于 Task.Run 简单的示例

    1. 关于 Task.Run 简单的示例01 直接贴代码了: public static class TaskDemo01 { public static void Run() { Console.W ...

  4. 三、hexo+github搭建个人博客的主题配置

    更换博客主题 主题可参考:https://hexo.io/themes/ hexo默认主题:Landscape 示例主题:Next 下载Next主题 进入Blog所在目录,输入下载命令 #进入Blog ...

  5. DNS:从零搭建公司内网DNS服务器

    写在前面的话 网上关于 DNS 的文章其实一搜索一大把,但是看别人的文档一般都会有个问题,乱,不讲究,全是 ctrl c + ctrl v,我个人是看不下去的.头皮发麻.所以决定自己来写写这方面的东西 ...

  6. file 从InputStream读取byte[]示例

    file 从InputStream读取byte[]示例 分类专栏: java基础   public static byte[] getStreamBytes(InputStream is) throw ...

  7. 关于 Java 关键字 volatile 的总结

    1 什么是 volatile volatile 是 Java 的一个关键字,它提供了一种轻量级的同步机制.相比于重量级锁 synchronized,volatile 更为轻量级,因为它不会引起线程上下 ...

  8. python列表推导式及其简单应用

    列表推导式(又称列表解析式)提供了一种简明扼要的方法来创建列表 一个简单平方 普通for循环 for i in range(1,5): print(i*i,end='') 列表推导式 res = [x ...

  9. 本机安装mysql步骤

    原文:https://www.cnblogs.com/dukeShi/p/6546024.html 本机安装mysql服务,步骤教程(另附SQLyog和Navicat工具) 因为这段时间不是装系统就是 ...

  10. Django Template语法中 OneToOne、ForeignKey 外键查询

    主表的Models的结构 class A(models.Model): username = models.CharField(max_length=32, verbose_name='用户名称') ...