Kubernetes 调度器详解

article/2025/8/31 10:54:18

kube-scheduler是 kubernetes 系统的核心组件之一,主要负责整个集群资源的调度功能,根据特定的调度算法和策略,将 Pod 调度到最优的工作节点上面去,从而更加合理、更加充分的利用集群的资源,这也是我们选择使用 kubernetes 一个非常重要的理由。如果一门新的技术不能帮助企业节约成本、提供效率,我相信是很难推进的。

调度流程

默认情况下,kube-scheduler 提供的默认调度器能够满足我们绝大多数的要求,我们前面和大家接触的示例也基本上用的默认的策略,都可以保证我们的 Pod 可以被分配到资源充足的节点上运行。但是在实际的线上项目中,可能我们自己会比 kubernetes 更加了解我们自己的应用,比如我们希望一个 Pod 只能运行在特定的几个节点上,或者这几个节点只能用来运行特定类型的应用,这就需要我们的调度器能够可控。

kube-scheduler 是 kubernetes 的调度器,它的主要作用就是根据特定的调度算法和调度策略将 Pod 调度到合适的 Node 节点上去,是一个独立的二进制程序,启动之后会一直监听 API Server,获取到 PodSpec.NodeName 为空的 Pod,对每个 Pod 都会创建一个 binding。

这个过程在我们看来好像比较简单,但在实际的生产环境中,需要考虑的问题就有很多了:

  • 如何保证全部的节点调度的公平性?要知道并不是说有节点资源配置都是一样的
  • 如何保证每个节点都能被分配资源?
  • 集群资源如何能够被高效利用?
  • 集群资源如何才能被最大化使用?
  • 如何保证 Pod 调度的性能和效率?
  • 用户是否可以根据自己的实际需求定制自己的调度策略?

考虑到实际环境中的各种复杂情况,kubernetes 的调度器采用插件化的形式实现,可以方便用户进行定制或者二次开发,我们可以自定义一个调度器并以插件形式和 kubernetes 进行集成。

kubernetes 调度器的源码位于 kubernetes/pkg/scheduler 中,大体的代码目录结构如下所示:(不同的版本目录结构可能不太一样)

kubernetes/pkg/scheduler
-- scheduler.go         //调度相关的具体实现
|-- algorithm
|   |-- predicates      //节点筛选策略
|   |-- priorities      //节点打分策略
|-- algorithmprovider
|   |-- defaults         //定义默认的调度器

其中 Scheduler 创建和运行的核心程序,对应的代码在 pkg/scheduler/scheduler.go,如果要查看kube-scheduler的入口程序,对应的代码在 cmd/kube-scheduler/scheduler.go。

调度主要分为以下几个部分:

  • 首先是预选过程,过滤掉不满足条件的节点,这个过程称为Predicates
  • 然后是优选过程,对通过的节点按照优先级排序,称之为Priorities
  • 最后从中选择优先级最高的节点,如果中间任何一步骤有错误,就直接返回错误

Predicates阶段首先遍历全部节点,过滤掉不满足条件的节点,属于强制性规则,这一阶段输出的所有满足要求的 Node 将被记录并作为第二阶段的输入,如果所有的节点都不满足条件,那么 Pod 将会一直处于 Pending 状态,直到有节点满足条件,在这期间调度器会不断的重试。

所以我们在部署应用的时候,如果发现有 Pod 一直处于 Pending 状态,那么就是没有满足调度条件的节点,这个时候可以去检查下节点资源是否可用。

Priorities阶段即再次对节点进行筛选,如果有多个节点都满足条件的话,那么系统会按照节点的优先级(priorites)大小对节点进行排序,最后选择优先级最高的节点来部署 Pod 应用。

下面是调度过程的简单示意图:

kube-scheduler filter

更详细的流程是这样的:

  • 首先,客户端通过 API Server 的 REST API 或者 kubectl 工具创建 Pod 资源
  • API Server 收到用户请求后,存储相关数据到 etcd 数据库中
  • 调度器监听 API Server 查看为调度(bind)的 Pod 列表,循环遍历地为每个 Pod 尝试分配节点,这个分配过程就是我们上面提到的两个阶段:
    • 预选阶段(Predicates),过滤节点,调度器用一组规则过滤掉不符合要求的 Node 节点,比如 Pod 设置了资源的 request,那么可用资源比 Pod 需要的资源少的主机显然就会被过滤掉
    • 优选阶段(Priorities),为节点的优先级打分,将上一阶段过滤出来的 Node 列表进行打分,调度器会考虑一些整体的优化策略,比如把 Deployment 控制的多个 Pod 副本分布到不同的主机上,使用最低负载的主机等等策略
  • 经过上面的阶段过滤后选择打分最高的 Node 节点和 Pod 进行 binding 操作,然后将结果存储到 etcd 中
  • 最后被选择出来的 Node 节点对应的 kubelet 去执行创建 Pod 的相关操作

其中Predicates过滤有一系列的算法可以使用,我们这里简单列举几个:

  • PodFitsResources:节点上剩余的资源是否大于 Pod 请求的资源
  • PodFitsHost:如果 Pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
  • PodFitsHostPorts:节点上已经使用的 port 是否和 Pod 申请的 port 冲突
  • PodSelectorMatches:过滤掉和 Pod 指定的 label 不匹配的节点
  • NoDiskConflict:已经 mount 的 volume 和 Pod 指定的 volume 不冲突,除非它们都是只读的
  • CheckNodeDiskPressure:检查节点磁盘空间是否符合要求
  • CheckNodeMemoryPressure:检查节点内存是否够用

除了这些过滤算法之外,还有一些其他的算法,更多更详细的我们可以查看源码文件:k8s.io/kubernetes/pkg/scheduler/algorithm/predicates/predicates.go。

Priorities优先级是由一系列键值对组成的,键是该优先级的名称,值是它的权重值,同样,我们这里给大家列举几个具有代表性的选项:

  • LeastRequestedPriority:通过计算 CPU 和内存的使用率来决定权重,使用率越低权重越高,当然正常肯定也是资源是使用率越低权重越高,能给别的 Pod 运行的可能性就越大
  • SelectorSpreadPriority:为了更好的高可用,对同属于一个 Deployment 或者 RC 下面的多个 Pod 副本,尽量调度到多个不同的节点上,当一个 Pod 被调度的时候,会先去查找该 Pod 对应的 controller,然后查看该 controller 下面的已存在的 Pod,运行 Pod 越少的节点权重越高
  • ImageLocalityPriority:就是如果在某个节点上已经有要使用的镜像节点了,镜像总大小值越大,权重就越高
  • NodeAffinityPriority:这个就是根据节点的亲和性来计算一个权重值,后面我们会详细讲解亲和性的使用方法

除了这些策略之外,还有很多其他的策略,同样我们可以查看源码文件:k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/ 了解更多信息。每一个优先级函数会返回一个0-10的分数,分数越高表示节点越优,同时每一个函数也会对应一个表示权重的值。最终主机的得分用以下公式计算得出:

finalScoreNode = (weight1 * priorityFunc1) + (weight2 * priorityFunc2) + … + (weightn * priorityFuncn)

自定义调度

上面就是 kube-scheduler 默认调度的基本流程,除了使用默认的调度器之外,我们也可以自定义调度策略。

调度器扩展

kube-scheduler在启动的时候可以通过 --policy-config-file参数来指定调度策略文件,我们可以根据我们自己的需要来组装PredicatesPriority函数。选择不同的过滤函数和优先级函数、控制优先级函数的权重、调整过滤函数的顺序都会影响调度过程。

下面是官方的 Policy 文件示例:

{"kind" : "Policy","apiVersion" : "v1","predicates" : [{"name" : "PodFitsHostPorts"},{"name" : "PodFitsResources"},{"name" : "NoDiskConflict"},{"name" : "NoVolumeZoneConflict"},{"name" : "MatchNodeSelector"},{"name" : "HostName"}],"priorities" : [{"name" : "LeastRequestedPriority", "weight" : 1},{"name" : "BalancedResourceAllocation", "weight" : 1},{"name" : "ServiceSpreadingPriority", "weight" : 1},{"name" : "EqualPriority", "weight" : 1}]
}

多调度器

如果默认的调度器不满足要求,还可以部署自定义的调度器。并且,在整个集群中还可以同时运行多个调度器实例,通过 podSpec.schedulerName 来选择使用哪一个调度器(默认使用内置的调度器)。

apiVersion: v1
kind: Pod
metadata:name: nginxlabels:app: nginx
spec:schedulerName: my-scheduler  # 选择使用自定义调度器 my-schedulercontainers:- name: nginximage: nginx:1.10

要开发我们自己的调度器也是比较容易的,比如我们这里的 my-scheduler:

  • 首先需要通过指定的 API 获取节点和 Pod
  • 然后选择phase=PendingschedulerName=my-scheduler的pod
  • 计算每个 Pod 需要放置的位置之后,调度程序将创建一个Binding对象
  • 然后根据我们自定义的调度器的算法计算出最适合的目标节点

优先级调度

与前面所讲的调度优选策略中的优先级(Priorities)不同,前面所讲的优先级指的是节点优先级,而我们这里所说的优先级 pod priority 指的是 Pod 的优先级,高优先级的 Pod 会优先被调度,或者在资源不足低情况牺牲低优先级的 Pod,以便于重要的 Pod 能够得到资源部署。

要定义 Pod 优先级,就需要先定义PriorityClass对象,该对象没有 Namespace 的限制:

apiVersion: v1
kind: PriorityClass
metadata:name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

其中:

  • value为 32 位整数的优先级,该值越大,优先级越高
  • globalDefault用于未配置 PriorityClassName 的 Pod,整个集群中应该只有一个PriorityClass将其设置为 true

然后通过在 Pod 的spec.priorityClassName中指定已定义的PriorityClass名称即可:

apiVersion: v1
kind: Pod
metadata:name: nginxlabels:app: nginx
spec:containers:- name: nginximage: nginximagePullPolicy: IfNotPresentpriorityClassName: high-priority

另外一个值得注意的是当节点没有足够的资源供调度器调度 Pod,导致 Pod 处于 pending 时,抢占(preemption)逻辑就会被触发。Preemption会尝试从一个节点删除低优先级的 Pod,从而释放资源使高优先级的 Pod 得到节点资源进行部署。

现在我们通过下面的图再去回顾下 kubernetes 的调度过程是不是就清晰很多了:

kube-scheduler


http://chatgpt.dhexx.cn/article/Nc27ThdL.shtml

相关文章

操作系统之调度

目录 什么是调度 进程调度的时机、切换、过程与方式 调度器/调度程序 调度算法 先来先服务算法 短作业优先算法 高响应比优先算法 时间片轮转算法 优先级调度算法 多级反馈队列调度算法 什么是调度 调度的三个层次 高级调度 中级调度 低级调度 总结如下: …

操作系统——线程调度

0.关注博主有更多知识 操作系统入门知识合集 目录 6.1线程调度概念 思考题: 6.2典型调度算法 思考题: 6.3Linux线程调度 6.1线程调度概念 在第四章曾经介绍过,线程是操作系统调度的基本单位,那么本篇就不再以进程的视角去…

【分布式任务调度】(四)XXL-JOB的任务调度执行流程及实现原理

文章目录 1.概述2.调度中心流程2.1.任务配置扫描流程2.2.计算任务触发时机2.2.1.已超时5秒以上2.2.2.超时未超过5秒2.2.3.还未到触发时间 2.3.任务触发流程2.3.1.任务触发线程池2.3.2.参数处理2.3.3.任务触发2.3.4.分片广播策略(补充) 3.执行器流程3.1.任…

分布式任务调度

一、概述 1、定义 业务场景 - 某电商系统需要在每天上午10点,下午3点,晚上8点发放一批优惠券。 - 某银行系统需要在信用卡到期还款日的前三天进行短信提醒。 - 某财务系统需要在每天凌晨0:10结算前一天的财务数据,统计汇总。 - 12306会根据…

【操作系统篇】第五篇——调度(概念,层次,调度时机,切换与过程,方式,评价指标)

​基本概念 ​三个层次 ​高级调度(作业调度) ​中级调度(内存调度) ​低级调度(进程调度) ​三层调度的联系,对比 ​补充知识 ​进程的"挂起态"与七状态模型 ​时机 ​什么时候需要进程调度 ​什么时候不能进行进程调度 ​切换与过程 ​&qu…

操作系统(五)调度

文章目录 一、调度的概念,层次1.1 调度的概念1.2 调度的三个层次1.2.1 高级调度(作业调度)1.2.2 中级调度(内存调度)1.2.3 低级调度(进程调度) 1.3 三层调度的对比1.4 总结 二、进程调度时机&am…

【操作系统】 2.2 调度概念以及调度算法

文章目录 1.调度的概念2.调度的三个层次3.七状态模型4.三层调度的联系和对比5. 进程调度的时机6.进程调度的方式7.进程的切换与过程8.调度算法的评价指标 调度算法先来先服务(FCFS)短作业优先(SJF)最短剩余时间优先(SR…

G1与ZGC

目录 前言JDK7和JDK8的GCG1RegionGC模式Young GCMixed GCFull GC ZGCRegionGC模式 一些感悟一些图文末彩蛋 前言 Java发展至今,最新版本是JDK16,最新的LTS长期支持版本是JDK11,今年9月即将推出JDK17,将是最新一代LTS。 但是&…

Java垃圾收集器之G1

在之前的文章中介绍了JVM的常见垃圾收集器,这边文章我想单独介绍一下G1垃圾收集器。G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。 G1收集器之内存模型…

【JVM】G1垃圾回收器

1.概述 G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一。早在JDK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术。同优秀的CMS垃圾回收器一样,G1也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方也推荐使用G1来代…

jvm之G1 GC

写在前面 jdk9以及之后的版本已经将默认的垃圾收集器parallel更换为G1.本文就一起来看下。 1:G1介绍 parallel GC的设计目标是高吞吐量,CMS GC的设计目标是低延迟,而G1的设计目标不是这二者中的任何一个,其设计目标是让GC的STW…

G1 GC详解及设置

一、概述 G1 GC,全称Garbage-First Garbage Collector,在JDK1.7中引入了G1 GC,从JAVA 9开始,G1 GC是默认的GC算法。通过-XX:UseG1GC参数来启用。G1收集器是工作在堆内不同分区上的收集器,分区既可以是年轻代也可以是老…

java 并g1_JVM G1详解

java程序性能 当我们调优java程序时,通常的目标有两个: 响应能力 或者 吞吐量 响应能力 响应能力指一个程序或者系统对请求的是否能够及时响应。 比如: 一个桌面UI能多快的响应一个事件; 一个网站能够多快返回一个页面请求&#x…

垃圾回收之G1收集过程

G1 中提供了 Young GC、Mixed GC 两种垃圾回收模式,这两种垃圾回收模式,都是 Stop The World(STW) 的。 G1 没有 fullGC 概念,需要 fullGC 时,调用 serialOldGC 进行全堆扫描(包括 eden、survivor、o、perm&#xff0…

G1调优分析

目录 1、畅想GC的目标 2、jvm调优的目标 3、GC调优时机 4、垃圾收集器的选择 5、G1调优策略 6、G1垃圾收集实践 6.1、JVM自动选择垃圾收集器 6.2、G1垃圾收集 6.3、GC日志分析 7、小结 前言 c和java之间有一堵由内存动态分配和垃圾收集技术所围成的墙,墙外面的人想进…

JVM垃圾回收器G1详解

1、概述 在我们应用程序所应对的业务越来越庞大、复杂,用户越来越多,没有GC就不能保证应用程序正常进行,而经常造成STW的GC又跟不上实际的需求,我们需要不断地尝试对GC进行优化。G1(Garbage-First)垃圾回收…

G1垃圾回收器

1、最大堆大小 G1管理的最大堆大小为64G。每个Region的大小通过 -XX:G1HeapRegionSize 来设置,大小为 1~32MB ,默认最多可以有2048个Region,G1能管理的最大堆内存是 32MB*204864G 。 使用G1垃圾回收器最小堆内存应为 1MB*20482GB &#xff…

ZGC都出来了,你还不懂G1?

概念 G1(Garbage-First Collector)是一种垃圾回收算法,最早在JDK 6 Update 14中作为实验性功能加入,并在JDK 7 Update 4正式JDK,之后在JDK 9 中成为默认垃圾回收算法,在JDK 10中优化了Full GC性能。 G1是一…

G1详解

一 G1收集器 g1收集器是一个面向服务端的垃圾收集器适用于多核处理器、大内存容量的服务端系统。 它满足短时间gc停顿的同时达到一个较高的吞吐量。 JDK7以上版本适用 “ 先介绍两个概念:吞吐量和响应能力,响应能力和吞吐量是评价一个系统的两个重要指标…

G1垃圾回收器详解

文章目录 前言一、思考问题二、官方文档三、基本介绍四、G1的内存模型五、G1的标记过程六、G1的垃圾回收1、G1过程梳理2、Young GC3、Mixed GC4、Full GC 七、参数介绍八、分析各阶段触发时机根据GC日志分析Young GC的触发时机根据GC日志分析并发标记的触发时机根据GC日志分析M…