本文翻译自 https://github.com/kubernetes/community/blob/master/contributors/devel/sig-scheduling/scheduling_code_hierarchy_overview.md
译者:胡云 Troy

调度器代码层次结构概述

介绍

调度器监视新创建的还没有分配节点的 Pod。当发现这样的 Pod 后,调度器将 Pod 调度到最适合它的节点。一般来说,调度是计算机科学中一个相当广泛的领域,它考虑了各种各样的约束和限制。调度器的每个工作负载可能需要不同的方法来实现最佳调度结果。Kubernetes 项目提供的 kube-scheduler 调度器的目标是以简单为代价提供高吞吐量。为了帮助构建调度器(默认或者定制化)和共享调度逻辑,kube-scheduler 实现了 调度框架。该框架没有提供构建新调度器的所有部分。组装一个功能齐全的单元仍然需要队列、缓存、调度算法和其他构建元素。本文档旨在描述所有单独的部分是如何组合在一起,以及它们在整个体系结构中的作用,以便开发人员能够快速了解调度器代码。

调度 Pod

默认的调度器实例运行无限期的循环,该循环(每次有 Pod 时)负责调用调度逻辑,确保 Pod 分配或重新排队以供后续处理。每个循环由一个阻塞调度周期和一个非阻塞绑定周期组成。调度周期负责运行调度算法,选择最合适的节点分配给 Pod。绑定周期确保 kube-apiserver 及时接收分配给 Pod 的节点。一个 Pod 可以立即绑定,或者在群调度中,等所有同级 Pod 分配节点之后再绑定。

图片来源 调度框架

调度周期

每个周期遵循以下步骤:

  1. 获取下一个调度的 Pod
  2. 根据提供的调度算法调度 Pod
  3. 如果调度 Pod 时出现 FitError 错误,调度器将运行 PostFilterPlugin 抢占插件(如果该插件已注册),该插件将指定一个可以运行 Pod 的节点。如果抢占成功,让当前 Pod 知道分配的节点。调度器将处理错误,获取下一个 Pod 并重新开始调度。
  4. 如果调度算法找到了合适的节点,则将 Pod 存储到调度器缓存中(AssumePod 操作),然后按顺序运行 Reserve 和 Permit 扩展点插件。任何插件运行失败将结束当前调度周期,增加相关的指标,调度器的 Error handler 将处理调度错误。
  5. 成功运行所有扩展点后,继续绑定循环。在执行绑定循环的同时,调度周期开始处理下一个调度的 Pod(如果有的话)。

绑定周期

按相同顺序运行以下四个步骤:

  • 从 Permit 扩展点调用插件的 WaitOnPermit (内部 API)。扩展点的一些插件会发送操作请求去等待相应的条件(例如,等待额外的可用资源或者一组中的所有 Pod 被分配)。WaitOnPermit 等待条件满足直到超时。
  • 调用 PreBind 扩展点的插件
  • 调用 Bind 扩展点的插件
  • 调用 PostBind 扩展点的插件

任何扩展点执行失败将调用所有 Reserve 插件的 Unreserve 操作(例如,为一群 Pod 分配空闲资源)。

配置和组装调度器

调度器代码库分散在不同的地方:

初始启动配置

cmd/kube-scheduler/app 下的代码负责收集调度器配置和调度器初始化逻辑,它是 kube-scheduler 作为 Kubernetes 控制面运行的一部分。代码包括:

初始化之后,调度器开始运行。

更详细地说,Setup 函数完成了调度器核心流程的初始化。首先,Setup 验证传递的选项(NewSchedulerCommand() 中添加的 flags 直接设置在此选项结构的字段上)。如果传递的选项没有引发任何错误,那么它将调用 opts.Config(),用于设置最终的内部配置,包括安全服务、领导人选举、客户端,并开始解析与算法源相关的选项(比如,加载配置文件和初始化空 profiles,以及处理不推荐使用的选项像策略配置)。接下来,调用 c.Complete() 填充配置 Config 中的空值。此时,创建一个空 registry 注册 out-of-tree 插件,在 registry 中为每个插件的 New 函数添加条目。Registry 只是插件名称到插件工厂函数的映射。对于默认调度器,注册 registry 这一步什么都不做(因为 cmd/kube-chuler/scheduler.go 中的 main 函数不向 NewSchedulerCommand() 传递任何信息)。这意味着默认的插件在 scheduler.New() 中初始化。

初始化是在调度框架之外执行的,使用框架的用户可以以不同的方式初始化环境来满足自身的需求。例如,模拟器可以通过 informer 注入自身需要的对象。或者自定义的插件可以替换默认的插件。调度框架的已知使用者:

组装调度器

默认调度器实现的目录在 pkg/scheduler,调度器的各种元素在这里初始化并组合在一起:

  • 默认调度选项,例如 node percentage, 初始化和最大 backoff, profiles
  • 调度器缓存和队列
  • 实例化调度的 profiles 以定制框架,每个 profile 可以更好的安置 Pod(每个 profile 定义自身使用的插件集合)
  • Handler 函数用于获取下一个调度的 Pod(NextPod)和处理错误(Error)

在创建调度器实例的过程中,将执行以下步骤:

  • 初始化调度器 缓存
  • 合并 带插件的 in-tree 和 out-of-tree 注册表
  • Metrics 已注册
  • 配置器 构建调度器实例(连接缓存,插件注册表,调度算法和其它元素)
  • 注册 Event handlers 以允许调度器对 PV、PVC、服务和其它与调度相关的资源的更新做出反应(最终,每个插件都将定义一组事件,并对其作出反应,更详细的可参考 kubernetes/kubernetes#100347)。

下图表明了初始化后各个元素是如何连接在一起的。Event handlers 确保 Pod 在 调度队列中排队,缓存随 Pod 和节点的更新而更新(提供最新的快照 snapshot)。调度框架有对应的调度算法和绑定周期(每个框架实例有自己的 profile)。

调度框架

调度器的框架代码目前位于 pkg/scheduler/framework 下。它包含 各种插件,负责过滤和评分节点(以及其他)。常常用作调度算法的构建模块。

当 插件初始化 后,它会传递一个 框架 handler,该框架 handler 提供访问和/或操作 pod、节点、clientset、事件记录器和每个插件实现其功能所需的其他 handler 的接口。

调度缓存

缓存负责记录集群的最新状态。保存节点和 assumed Pod 以及 Pod 和 images 的状态。缓存提供了协调 Pod 和节点对象(调用 event handlers)的方法,使集群的状态保持最新。允许在每个调度周期开始时使用最新状态(在运行调度算法时固定集群状态)更新集群的快照。

缓存还允许运行假定的操作,该操作将 Pod 临时存储在缓存中,使得 Pod 看起来像已经在快照的所有消费者的指定节点上运行那样。假定操作忽视了 kube-apiserver 和 Pod 实际更新的时间,从而增加调度器的吞吐量。

以下操作使用假定的 Pod 进行操作:

  • AssumePod:用于通知调度算法找到可行的节点,以便在当前 Pod 进入绑定周期时可以调度下一个 Pod
  • FinishBinding:用于发出绑定完成的信号,以便可以将 Pod 从假定 Pod 列表中删除
  • ForgetPod:从假定的 Pod 列表中删除 Pod,用于绑定周期中未能成功处理 Pod 的情况(例如,Reserve,Permit,PreBind 或者 Bind 评估)

缓存跟踪以下三个指标:

  • scheduler_cache_size_assumed_pods:在假定 Pod 列表中的 Pod 数量
  • scheduler_cache_size_pods:在缓存中的 Pod 数量
  • scheduler_cache_size_nodes:在缓存中的节点数量

快照

快照 捕获集群的状态,其中包含集群中所有节点和每个节点上对象的信息。即节点对象、分配在每个节点上的 Pod、每个节点上所有 Pod 的请求资源、节点的可分配资源、拉取的镜像以及做出调度决策所需的其他信息。每次调度 Pod 时,都会捕获集群当前状态的快照。这样是为了避免在处理插件时更改 Pod 或节点时导致的数据不一致,因为一些插件可能会获得不同的集群状态。

配置器

配置器 通过将插件、缓存、队列、handlers 和其他元素连接在一起来构建调度器实例。每个 profile 都使用自己的框架(所有框架共享 informers,event recorders 等)进行 初始化。

也可以让配置器根据 策略文件 创建实例。不过,这种方法已被弃用,最终将从配置中删除。只保留调度器配置作为提供给配置器配置的唯一方式。

默认调度算法

代码库定义了 ScheduleAlgorithm 接口。任何该接口的实现都可以用作调度算法。这里有两种方法:

  • Schedule:负责使用从 PreFilter 到 NormalizeScore 扩展点的插件来调度 Pod,提供包含调度决策(最合适的节点)和附带信息的 ScheduleResult,其中附带信息包括评估了多少节点以及发现有多少节点可用于调度。
  • Extenders: 当前仅用于测试

默认算法实现的每个周期包括:

  1. 从调度缓存中获取 当前快照
  2. 过滤掉所有无法调度 Pod 的节点
    • 运行 PreFilter 插件(预处理阶段,例如计算 Pod 亲和性关系)
    • 并行运算 Filter 插件:过滤掉不满足 Pod 限制条件(例如资源,节点亲和性等)的节点,包括运行 Filter 扩展器
    • 运行 PostFilter 插件 如果没有节点满足要调度的 Pod
  3. 在 Pod 至少有两个可行节点可以调度的情况下,运行 scoring 插件:
    • 运行 PreScore 插件(预处理阶段)
    • 并行运行 Score 插件:每个节点都有一个分数向量(每个坐标对应一个插件)
    • 运行 NormalizeScore 插件:给所有插件打分,间隔为 [0, 100]
    • 计算每个节点的 权重分数 (每个分数插件都可以分配一个权重,指示其分数在多大程度上优于其他插件)
    • 运行 打分扩展器,并且将分数计入每个节点的总分
  4. 选择 并 返回 得分最高的节点。如果只有一个可供调度的节点则跳过 Prescore,Score 和 NormalizeScore 扩展点,并且立即返回调度的节点。如果没有可供调度的节点,将结果返回给调度器。

值得注意的是:

  • 如果插件提供 score normalization,当调用 ScoreExtensions() 时,插件需要返回非 nil

[译] kubernetes:kube-scheduler 调度器代码结构概述的更多相关文章

  1. Kubernetes集群调度器原理剖析及思考

    简述 云环境或者计算仓库级别(将整个数据中心当做单个计算池)的集群管理系统通常会定义出工作负载的规范,并使用调度器将工作负载放置到集群恰当的位置.好的调度器可以让集群的工作处理更高效,同时提高资源利用 ...

  2. quartz2.3.0(十二)通过RMI协议向Scheduler调度器远程添加job任务

    此代码示例通过RMI协议向Scheduler调度器远程添加job任务. 代码文件包括:job任务类(SimpleJob.java).RMI服务端server类(RemoteServerExample. ...

  3. Kubernetes K8S之调度器kube-scheduler详解

    Kubernetes K8S之调度器kube-scheduler概述与详解 kube-scheduler调度概述 在 Kubernetes 中,调度是指将 Pod 放置到合适的 Node 节点上,然后 ...

  4. scrapy 源码解析 (四):启动流程源码分析(四) Scheduler调度器

    Scheduler调度器 对ExecutionEngine执行引擎篇出现的Scheduler进行展开.Scheduler用于控制Request对象的存储和获取,并提供了过滤重复Request的功能. ...

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

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

  6. kubernetes机理之调度器以及控制器

    一 了解调度器 1.1  调度器是如何将一个pod调度到节点上的 我们都已然知晓了,API服务器不会主动的去创建pod,只是拉起系统组件,这些组件订阅资源状态的通知,之后创建相应的资源,而负责调度po ...

  7. 图解kubernetes调度器SchedulerExtender扩展

    在kubernetes的scheduler调度器的设计中为用户预留了两种扩展机制SchdulerExtender与Framework,本文主要浅谈一下SchdulerExtender的实现, 因为还有 ...

  8. 图解kubernetes调度器抢占流程与算法设计

    抢占调度是分布式调度中一种常见的设计,其核心目标是当不能为高优先级的任务分配资源的时候,会通过抢占低优先级的任务来进行高优先级的调度,本文主要学习k8s的抢占调度以及里面的一些有趣的算法 1. 抢占调 ...

  9. Kubernetes 调度器实现初探

    Kubernetes 调度器 Kubernetes 是一个基于容器的分布式调度器,实现了自己的调度模块.在Kubernetes集群中,调度器作为一个独立模块通过pod运行.从几个方面介绍Kuberne ...

  10. 图解kubernetes调度器SchedulerCache核心源码实现

    SchedulerCache是kubernetes scheduler中负责本地数据缓存的核心数据结构, 其实现了Cache接口,负责存储从apiserver获取的数据,提供给Scheduler调度器 ...

随机推荐

  1. Mybatis-Flex之增、删、改

    方法全解 (1) INSERT BaseMapper 的接口提供了 insert 和 insertBatch 方法,用于新增数据: insert(entity):插入实体类数据,不忽略 null 值. ...

  2. 华企盾DSC手机app登录不上常见处理方法

    1.DSC服务器是否正常运行. 2.telnet外网是否通.(需要在程序与功能中添加telnet功能才能在cmd窗口用telnet命令 举例:telnet 172.17.2.20 5558) 3.其它 ...

  3. Postgres服务器操作方法

    切换用户 sudo su postgres psql postgres 查询当前postgres下的DB列表 \l 查看postgres下角色的详细信息(权限) \du 删除odoo角色 drop r ...

  4. springboot去除内嵌tomcat

    springboot去除内嵌tomcat步骤 在pom文件中加入以下代码 点击查看代码 <!-- 多模块排除内置tomcat --> <dependency> <grou ...

  5. 【玩转腾讯混元大模型】怎么说?我用混元AI大模型开发了个IDEA插件

    前言 halo 我是杨不易呀,在混元大模型内测阶段就已经体验了一番当时打开页面的时候灵感模块让我大吃一惊这么多角色模型真的太屌了,随后我立马进行了代码处理水平和上下文的效果结果一般般但是到如今混元大模 ...

  6. Pikachu漏洞靶场 Unsafe Filedownload(不安全的文件下载)

    不安全的文件下载 概述 文件下载功能在很多web系统上都会出现,一般我们当点击下载链接,便会向后台发送一个下载请求,一般这个请求会包含一个需要下载的文件名称,后台在收到请求后会开始执行下载代码,将该文 ...

  7. 微信小程序实时噪声分贝

    为了做一个能够检测实时噪声分贝的小程序,网上找了很多例子,基本没有完整的代码,手写了一个,话不多说直接上效果图 实现实时录音的功能,并且能够根据声音分贝大小转动仪表盘显示实时的分贝,运用echart实 ...

  8. Numpy计算近邻表时间对比

    技术背景 所谓的近邻表求解,就是给定N个原子的体系,找出满足cutoff要求的每一对原子.在前面的几篇博客中,我们分别介绍过CUDA近邻表计算与JAX-MD关于格点法求解近邻表的实现.虽然我们从理论上 ...

  9. 2023-09-13:用go语言,给定一个整数数组 nums 和一个正整数 k, 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。 输入: nums = [4, 3, 2, 3, 5,

    2023-09-13:用go语言,给定一个整数数组 nums 和一个正整数 k, 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等. 输入: nums = [4, 3, 2, 3, 5, 2 ...

  10. 让当前元素的width或者height达到父元素的尺寸

    double.infifinity 和double.maxFinite可以让当前元素的width或者height达到父元素的尺寸: 区别: 我想成为我的父母所允许的最大的(double.infinit ...