kube-scheduler简介

调度是容器编排的重要环节,需要经过严格的监控和控制,现实生产通常对调度有各类限制,譬如某些服务必须在业务独享的机器上运行,或者从灾备的角度考虑尽量把服务调度到不同机器,这些需求在Kubernetes集群依靠调度组件kube-scheduler满足。

kube-scheduler是Kubernetes中的关键模块,扮演管家的角色遵从一套机制为Pod提供调度服务,例如基于资源的公平调度、调度Pod到指定节点、或者通信频繁的Pod调度到同一节点等。容器调度本身是一件比较复杂的事,因为要确保以下几个目标:

  1. 公平性:在调度Pod时需要公平的进行决策,每个节点都有被分配资源的机会,调度器需要对不同节点的使用作出平衡决策。

  2. 资源高效利用:最大化群集所有资源的利用率,使有限的CPU、内存等资源服务尽可能更多的Pod。

  3. 效率问题:能快速的完成对大批量Pod的调度工作,在集群规模扩增的情况下,依然保证调度过程的性能。

  4. 灵活性:在实际运作中,用户往往希望Pod的调度策略是可控的,从而处理大量复杂的实际问题。因此平台要允许多个调度器并行工作,同时支持自定义调度器。

为达到上述目标,kube-scheduler通过结合Node资源、负载情况、数据位置等各种因素进行调度判断,确保在满足场景需求的同时将Pod分配到最优节点。显然,kube-scheduler影响着Kubernetes集群的可用性与性能,Pod数量越多集群的调度能力越重要,尤其达到了数千级节点数时,优秀的调度能力将显著提升容器平台性能。

调度流程

kube-scheduler的根本工作任务是根据各种调度算法将Pod绑定(bind)到最合适的工作节点,整个调度流程分为两个阶段:预选策略(Predicates)和优选策略(Priorities)。

  1. 预选(Predicates):输入是所有节点,输出是满足预选条件的节点。kube-scheduler根据预选策略过滤掉不满足策略的Nodes。例如,如果某节点的资源不足或者不满足预选策略的条件如“Node的label必须与Pod的Selector一致”时则无法通过预选。

  2. 优选(Priorities):输入是预选阶段筛选出的节点,优选会根据优先策略为通过预选的Nodes进行打分排名,选择得分最高的Node。例如,资源越富裕、负载越小的Node可能具有越高的排名。

通俗点说,调度的过程就是在回答两个问题:1. 候选有哪些?2. 其中最适合的是哪个?

值得一提的是,如果在预选阶段没有节点满足条件,Pod会一直处在Pending状态直到出现满足的节点,在此期间调度器会不断的进行重试。

到这里我们可以对Pod的整个启动流程进行总结

  1. 资源管控中心Controller Manager创建新的Pod,将该Pod加入待调度的Pod列表。

  2. kube-scheduler通过API Server提供的接口监听Pods,获取待调度pod,经过预选和优选两个阶段对各个Node节点打分排序,为待调度Pod列表中每个对象选择一个最优的Node。

  3. kube-scheduler将Pod与Node的绑定写入etcd(元数据管理服务)。

  4. 节点代理服务kubelet通过API Server监听到kube-scheduler产生的绑定信息,获得Pod列表,下载Image并启动容器,然后由kubelet负责拉起Pod。

到此为止就完成了Pod的调度与启动。

预选和优选算法介绍

kube-scheduler支持多种预选和优选算法,开发者可以根据当前场景下的重要要素进行选择。

预选策略(Predicates)

  1. 基于存储卷数量的判断

    • MaxEBSVolumeCount:确保已挂载的EBS存储卷数量不超过设置的最大值(默认39),调度器会检查直接或及间接使用这种类型存储的PVC,累加总数,如果卷数目超过设最大值限制,则不能调度新Pod到这个节点上。

    • MaxGCEPDVolumeCount:同上,确保已挂载的GCE存储卷数量不超过预设的最大值(默认16)。

    • MaxAzureDiskVolumeCount:同上,确保已挂载的Azure存储卷不超过设置的最大值(默认16)。

  2. 基于资源压力状态的判断

    • CheckNodeMemoryPressure:判断节点是否已经进入到内存压力状态,如果是则只允许调度内存为0标记的Pod。

    • CheckNodeDiskPressure:判断节点是否已经进入到磁盘压力状态,如果是,则不能调度新的Pod。

  3. 基于卷冲突的判断

    • NoDiskConflict:卷冲突判断,即如果该节点已经挂载了某个卷,其它同样使用相同卷的Pod将不能再调度到该节点。

    • NoVolumeZoneConflict:对于给定的某块区域,判断如果在此区域的节点上部署Pod是否存在卷冲突。

    • NoVolumeNodeConflict:对于某个指定节点,检查如果在此节点上部署Pod是否存在卷冲突。

  4. 基于约束关系的判断

    • MatchNodeSelector:检查节点标签(label)是否匹配Pod指定的nodeSelector,是则通过预选。

    • MatchInterPodAffinity:根据Pod之间的亲和性做判断。

    • PodToleratesNodeTaints:排斥性关系,即判断Pod不允许被调度到哪些节点。这里涉及到两个概念Taints(污点)和Toleration(容忍)。Node可以定义一或多个Taint,Pod可以定义一或多个Toleration,对于具有某个Taint的节点,只有遇到能容忍它的(即带有对应Toleration的)Pod,才允许Pod被调度到此节点,从而避免Pod被分配到不合适的节点。

  5. 基于适合性的判断

    • PodFitsResources:检查节点是否有足够资源(如CPU、内存、GPU等)满足Pod的运行需求。

    • PodFitsHostPorts:检查Pod容器所需的HostPort是否已被节点上其它容器或服务占用。如果已被占用,则禁止Pod调度到该节点。

    • PodFitsHost:检查Pod指定的NodeName是否匹配当前节点。

优选策略(Priorities)

优选过程会根据优选策略对每个候选节点进行打分,最终把Pod调度到分值最高的节点。kube-scheduler用一组优先级函数处理每个通过预选的节点,每个函数返回0-10的分数,各个函数有不同权重,最终得分是所有优先级函数的加权和,即节点得分的计算公式为:

优选的优先级函数包括:

  • LeastRequestedPriority(默认权重1):尽量将Pod调度到计算资源占用比较小的Node上,这里涉及两种计算资源:内存和CPU。计算公式如下:其中,capacity表示该节点的现有容量,requested表示Pod所请求的容量。

  • BalancedResourceAllocation(默认权重1):CPU和内存使用率越接近的节点权重越高。该策略均衡了节点CPU和内存的配比,尽量选择在部署Pod后各项资源更均衡的机器。该函数不能单独使用,必须和LeastRequestedPriority同时使用,因为如果请求的资源(CPU或者内存)大于节点的capacity,那么该节点永远不会被调度到。计算公式如下:

  • SelectorSpreadPriority(默认权重1):把属于同一个Service或者ReplicationController的Pod,尽量分散在不同的节点上,如果指定了区域,则尽量把Pod分散在该区域的不同节点。通常来说节点上已运行的Pod越少,节点分数越高。计算公式如下,是基于节点的计算和基于区域的计算的加权和,其中,maxPriority代表系数,默认为10,maxCount为节点最多允许运行的Pod数量,nodeCount为该节点已经存在的Pod数量,maxCountByZone为该区域最多允许的Pod数量,zoneCount为区域内已经运行的Pod数量。

  • NodeAffinityPriority(默认权重1):尽量调度到标签匹配Pod属性要求的节点,判断行为与预选中的MatchNodeSelector相似,未来可能会完全将其取代。

    该函数提供两种选择器:requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution。前者是强要求,指定将Pod调度到节点上必须满足的所有规则;后者是弱要求,即尽可能调度到满足特定限制的节点,但允许不满足。计算公式如下,在节点满足requiredDuringSchedulingIgnoredDuringExecution后,该节点会累加preferredDuringSchedulingIgnoredDuringExecution中所有满足条件的规则的权值weight_i,CountWeight为preferredDuringSchedulingIgnoredDuringExecution中所有规则的权值总和:

    InterPodAffinityPriority(默认权重1):叠加该节点已调度的每个Pod的权重,权重可配置文件里设定,通常亲和性越强的Pod权重越高,如果Pod满足要求则将加到权重和,具有最高权重和的节点是最优的。提供两种选择器:requiredDuringSchedulingIgnoredDuringExecution(保证所选的主机必须满足所有Pod对主机的规则要求)、preferredDuringSchedulingIgnoredDuringExecution(调度器会尽量但不保证满足NodeSelector的所有要求)。

  • 计算公式如下,其中,weight_i为节点上符合亲和性的每个Pod的权重,sumCount为节点上符合亲和性的Pod权重和,maxCount为节点上所有Pod的权重和,minCount为节点上最小的Pod权重,maxPriority是系数,默认为10。

  • NodePreferAvoidPodsPriority(默认权重10000):避免将ReplicationController或ReplicaSet调度到节点。如果Pod由RC或者RS的Controller调度,则得分为0,不对最终加权得分产生影响;如果不是,则总分为100000,意味着覆盖其他策略,直接决定最优节点。

  • TaintTolerationPriority(默认权重1):Pod与Node的排斥性判断。通过Pod的tolerationList与节点Taint进行匹配,配对失败的项越少得分越高,类似于Predicates策略中的PodToleratesNodeTaints。计算公式如下:其中,totalTaints表示Taint总个数,intolerableTaints表示配对不成功的个数。

  • ImageLocalityPriority(默认权重1):尽量调度到Pod所需镜像的节点。检查Node是否存在Pod所需镜像:如果不存在,返回0分;如果存在,则镜像越大得分越高。计算公式如下:其中,sumSize表示该节点上存在的Pod所需镜像大小总和,maxImgSize表示Pod所需镜像总大小,minImgSize表示Pod所需最小镜像的尺寸。

  • EqualPriority(默认权重1):给予所有节点相等权重,一般仅用于测试。

  • MostRequestedPriority(默认权重1):适用于动态伸缩集群环境,会优先调度Pod到使用率最高的节点,方便在伸缩集群时,先腾出空闲机器,从而进行停机处理。

案例演示

下面将通过四个案例来介绍如何在实战中对以上的调度策略进行选择。

    • 场景一:调度到基于SSD的节点

      某架构使用分布式缓存Redis,对磁盘读写敏感,开发者希望调度到配置SSD的节点,可用预选策略MatchNodeSelector解决,对目标Node设置label,Redis的spec添加nodeSelector。

假设Redis的YAML文件如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-master
  labels:
    name: redis
  namespace: default
spec:
  replicas: 4
  template:
    metadata:
      labels:
      name: redis
  spec:
    containers:
    - name: master
      image: 172.16.1.41:5000/redis:3.0.5
      resources:
        requests:
          cpu: 100m
          memory: 100Mi

节点transwarp配置了SSD,因此添加“disk=ssd”标签。

kubectl label node transwarp disk=ssd

通过更新Redis服务的YAML文件字段添加nodeSelector标签“disk=ssd”来匹配节点transwarp。

kubectl patch deploy redis-master -p  '{"spec":{"template":{"spec":{"nodeSelector:"{"disk":"ssd"}}}}}'
    • 场景二:只允许调度到CentOS节点

      某系统中有各种异构系统机器,比如SLES、RHEL、CentOS7.2、CentOS7.3等,现在有一个线上任务只能运行在CentOS系统,但是不区分CentOS系统版本。当然我们可以使用MatchNodeSelector来实现匹配,然而如果以这种方式实现需要备注多个标签,因此建议用NodeAffinityPriority解决。

我们在该服务的YAML文件中将NodeAffinityPriority选择器的指定为requiredDuringSchedulingIgnoredDuringExecution,表示强要求,指定Pod必须被调度到key为“operation system”,值为“centos 7.2”或“centos 7.3”的节点。

requiredDuringSchedulingIgnoredDuringExecution:
  nodeSelectorTerms:
  - matchExpressions:
    - key: operation system
      operator: In
      values:
      -  centos 7.2
      -  centos 7.3

如果需要优先调度到CentOS 7.2的节点,还可以增加一个由preferredDuringSchedulingIgnoredDuringExecution决定的调度策略,优先调度到具有“another-node-label-key-system”标签,且值为“another-node-label-value-centos7.2”的节点。

preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
  preference:
   matchExpressions:
   - key: another-node-label-key-system
     operator: In
     values:
     - another-node-label-value-centos7.2

该线上服务的YAML文件具体内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: operation system
            operator: In
            values:
            -  centos 7.2
            -  centos 7.3
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key-system
            operator: In
            values:
            - another-node-label-value-centos7.2
  containers:
  - name: with-node-affinity
    image: gcr.io/google_containers/pause:2.0

注意:如果同时指定了nodeSelector和nodeAffinity,目标节点必须同时满足这两个规则;nodeSelectorTerms可以指定多个,满足任意一个即可满足;matchExpressions也可以指定多个,要求必须都满足。

    • 场景三:调度到同一分区

      现有公有云组件API服务,需要与认证服务器频繁通信,因此要尽可能的协同部署在云提供商的同一个分区中。

      该场景的需求是使API服务和认证服务运行在同一分区,转化到更通用的视角就是把Pod优先调度到更具Pod亲和性的节点,故可用优选策略InterPodAffinityPriority解决。

首先我们对API服务添加标签“security=cloud”,它的其中一段配置如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-flag
  labels:
    security: "cloud"
spec:
  containers:
  - name: nginx
    image: nginx

为了使认证服务分配到相同分区,需要在其YAML文件中添加requiredDuringSchedulingIgnoredDuringExecution字段去亲和具有“security=cloud”标签的Pod,同时添加topologyKey: failure-domain.beta.kubernetes.io/zone字段来限制拓扑域范围,表示把部署限制在同一个分区。

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - cloud
        topologyKey: failure-domain.beta.kubernetes.io/zone
    • 场景四:

      私有云服务中,某业务使用GPU进行大规模并行计算。为保证性能,希望确保该业务对服务器的专属性,避免将普通业务调度到部署GPU的服务器。

      该场景要求Pod避免被调度到指定类型的节点,符合排斥关系,故可用策略PodToleratesNodeTaints解决。

首先先在配有GPU的node1中添加Taints污点(gpu = true),NoSchedule表示只要没有对应Toleration的Pod就不允许调度到该节点。

kubectl taint nodes nodes1 gpu=true:NoSchedule

同时,通过更新服务的YAML文件,为需要GPU资源业务的Pods添加Toleration(key: "gpu",value: "true"),表示容忍GPU服务器的Taints,这样就可以把指定业务调度到GPU服务器,同时把普通服务调度到其他节点。

apiVersion: v1
kind: Pod
metadata:
  generateName: redis-
  labels:
    app: redis
  namespace: default
spec:
  containers:
  - image: 172.16.1.41:5000/redis
    imagePullPolicy: Always
    name: redis
  schedulerName: default-scheduler
  tolerations:
  - effect:  NoSchedule
    key: gpu
    operator:  Equal
    value: true

这里需要提一下,tolerations里的effect字段表示处理排斥的策略,支持三种调度规则:NoSchedule表示不允许调度,已调度的不影响;PreferNoSchedule表示尽量不调度;NoExecute表示不允许调度,已运行的Pod将在之后删除。

总结与展望

目前kube-scheduler已经提供了丰富的调度策略可供使用,一般情况下,使用kube-scheduler的默认调度策略就能满足大部分需求,并且其插件化的形式也方便于用户进行定制与二次开发。未来,我们会在此基础上对其进一步优化:包括增加cache以减少predict和prioritize阶段的重复计算,已在TOS 1.9中实现;通过scheduler-extender扩展来针对local Volume、GPU等资源实现更合理调度;适配custom-metrics-server的API来实现实时调度,使得每个节点的资源能更好的被利用。

k8s调度器kube-scheduler的更多相关文章

  1. 7.k8s.调度器scheduler 亲和性、污点

    #k8s. 调度器scheduler 亲和性.污点 默认调度过程:预选 Predicates (过滤节点) --> 优选 Priorities(优先级排序) --> 优先级最高节点 实际使 ...

  2. MySQL事件调度器Event Scheduler

    我们都知道windows的计划任务和linux的crontab都是用来实现一些周期性的任务和固定时间须要运行的任务. 在mysql5.1之前我们完毕数据库的周期性操作都必须借助这些操作系统实现. 在m ...

  3. 【技术干货】华为云FusionInsight MRS的自研超级调度器Superior Scheduler

    Superior Scheduler是一个专门为Hadoop YARN分布式资源管理系统设计的调度引擎,是针对企业客户融合资源池,多租户的业务诉求而设计的高性能企业级调度器. Superior Sch ...

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

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

  5. 泡面不好吃,我用了这篇k8s调度器,征服了他

    1.1 调度器简介 来个小刘一起 装逼吧 ,今天我们来学习 K8的调度器 Scheduler是 Kubernetes的调度器,主要的任务是把定义的 pod分配到集群的节点上,需要考虑以下问题: 公平: ...

  6. k8s调度器介绍(调度框架版本)

    从一个pod的创建开始 由kubectl解析创建pod的yaml,发送创建pod请求到APIServer. APIServer首先做权限认证,然后检查信息并把数据存储到ETCD里,创建deployme ...

  7. RxJS——调度器(Scheduler)

    调度器 什么是调度器?调度器是当开始订阅时,控制通知推送的.它由三个部分组成. 调度是数据结构.它知道怎样在优先级或其他标准去存储和排队运行的任务 调度器是一个执行上下文.它表示任务在何时何地执行(例 ...

  8. Hadoop YARN 调度器(scheduler) —— 资源调度策略

    本文通过MetaWeblog自动发布,原文及更新链接:https://extendswind.top/posts/technical/hadoop_yarn_resource_scheduler 搜了 ...

  9. MySQL计划任务(事件调度器)(Event Scheduler)

    http://www.cnblogs.com/c840136/articles/2388512.html https://dev.mysql.com/doc/refman/5.7/en/events- ...

  10. MySQL计划任务(事件调度器)(Event Scheduler)[转]

    原文链接: http://www.cnblogs.com/c840136/articles/2388512.html MySQL5.1.x版本中引入了一项新特性EVENT,顾名思义就是事件.定时任务机 ...

随机推荐

  1. 性能测试-详细的 TPS 调优笔记

    概述 在本地针对项目的登录接口做了一次简单的压力测试.200并发持续120s,观察吞吐量 运行结束之后,吞吐量是这样的 如图所示,吞吐量波动巨大,完全不正常.现在我们需要去观察一下服务器了 mpsta ...

  2. MTV

    M:模型 models.py T:模板 html C:控制 urls.py 和 views.py 与MVC类似

  3. (转)宽字节编码类型的XSS

    今晚又看了一遍PKAV-心上的瘦子写的xss腾讯系列的例子,收获挺大的,其中对宽字节注入有了更深的了解,又查找了一些相关的资料,整理一下,以备以后查阅 参考文档: http://book.2cto.c ...

  4. 使用Razor表达式 举数组和集合 精通ASP-NET-MVC-5-弗瑞曼

  5. 20191211 HNOI2017模拟赛 C题

    题目: 分析: 开始觉得是神仙题... 然后发现n最多有2个质因子 这说明sm呢... 学过物理的小朋友们知道,当一个物体受多个不同方向相同的力时,只有相邻力的夹角相等,受力就会平衡 于是拆扇叶相当于 ...

  6. 19_07_07校内训练[xor]

    题意 长度为n的数组,上面有k个位置是1,现在有l种长度的连续全1串,要求用最少的次数将这个数组异或成全0的数组.n<=1E5,k<=10,l<=100. 思考 先将数组进行异或的差 ...

  7. Android教程2020 - RecyclerView响应点击

    本文介绍RecyclerView设置点击的方法.这里给出比较常见的使用方式. Android教程2020 - 系列总览 本文链接 前面我们已经知道如何用RecyclerView显示一列数据. 用户点击 ...

  8. Web自动化测试项目(五)测试结果通知

    一.邮件通知 使用第三方邮件发送库yagmail github地址:https://github.com/kootenpv/yagmail 安装 pip3 install yagmail demo.p ...

  9. Spring实战:第一个spring mvc项目

    我自己看的pdf书中有几个小错误导致项目一直起不来,具体错误是: 此处的名称不一致导致的,此外对于映射@RequestMapping("/"),需要删除创建框架时自带的index. ...

  10. java8 stream按对象多个属性对集合进行分组,并进行组装数据

    如图,数据库查出来的数据: 需求是按menu_id和menu_name分组,stream实现最简单, stream里面只有按一个属性分组的,但是可以利用string简单变换一下: List<Js ...