热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Trimaran:基于实际负载的K8s调度插件

在K8s集群治理过程中,常常会因CPU、内存等高使用率状况而形成热点,既影响了当前节点上Pod的稳定运行,也会导致节点发

在 K8s 集群治理过程中,常常会因 CPU 、内存等高使用率状况而形成热点,既影响了当前节点上 Pod 的稳定运行,也会导致节点发生故障的几率的激增。



编辑|zouyee

接受范围|初级

为了应对集群节点高负载、负载不均衡等问题,需要动态平衡各个节点之间的资源使用率,因此需要基于节点的相关监控指标,构建集群资源视图,从而为下述两种治理方向奠定实现基础:


  • 在 Pod 调度阶段,加入优先将 Pod 调度到资源实际使用率低的节点的节点Score插件

  • 在集群治理阶段,通过实时监控,在观测到节点资源率较高、节点故障、Pod 数量较多等情况时,可以自动干预,迁移节点上的一些 Pod 到利用率低的节点上


针对方向一,可以通过赋予Kubernetes调度器感知集群实际负载的能力,计算资源分配和实际资源利用之间的差距,优化调度策略。

针对方向二,社区给出了Descheduler方案,Descheduler 可以根据一些规则和策略配置来帮助再平衡集群状态,当前项目实现了十余种策略。

注意:Descheduler等方案存在一些与主调度策略不一致的可能性

本文将针对方向一的实现进行详细说明,方向二中Descheduler将在后续文中进行相关介绍。




需求背景


Kubernetes提供了声明式的资源模型,核心组件(调度器、kubelet和控制器)的实现能够满足QoS需求。然而,由于下述一些原因,该模型会导致集群的低利用率:

  • 用户很难准确评估应用程序的资源使用情况,因而对于Pod的资源配置,无从谈起

  • 用户可能不理解资源模型,从而直接使用Kubernetes默认调度插件(Score)(默认插件不考虑实际节点利用率值)。

依托实时资源使用情况,构建调度插件以调度pod,最终的目标是在不破坏Kubernetes资源模型的前提下,降低集群管理的成本,提高集群的利用率,为了实现上述需要,因此提出以下预期条件:

  • 提供可配置的调度插件以提高集群利用率。

  • 每个节点的预期CPU利用率不应超过设定百分比(约束条件)。

  • 尽量避免影响默认score插件逻辑。

  • 该功能以score插件方式实现


使用场景


场景一

作为一家使用公有云的公司,希望通过提升节点实际利用率的方式来降低机器成本。

作为一家拥有的数据中心的公司,希望通过提升节点实际利用率的方式,来减少集群相关的硬件开支和维护。

场景二

尽可能地提高资源利用率的方式可能无法解决所有问题,通过扩大集群的规模以处理突如其来的业务高峰总是需要一些时间的,因此集群管理员希望为突发的高峰留下足够的扩展空间,此时可能就需要有足够的时间向集群添加更多的节点,当然该问题可以通过空闲集群节点、forzen资源方式来缓解,类似函数计算中的热启动等。



设计方案

设计方案由以下组件组成,监控指标整合器(Metrics Provider)、负载监测器(Load Watcher)、数据库及调度插件组成,如下图所示,重点为两个插件算法,即 TargetLoadPacking和 LoadVariationRiskBalancing。这两个插件算法都使用来自负载监控器的指标,用不同的算法对节点进行评分。


监控指标汇聚器


监控指标整合器支持时间序列数据库,如Prometheus、InfluxDB、Kubernetes Metrics Server等。


负载监测器

负载监测和分析器整合在同一进程中,其中监测模块负责通过监控指标整合器(Metrics Provider)获取集群内的资源使用指标,如CPU、内存、网络和IO统计数据等。它将这些数据进行本地缓存,为了实现容错,其在主机数据库中持久化一些聚合数据。分析器负责检测不良监控指标,并实施一些补救措施。坏指标可能是由于监控缺失或可能发生的错误而产生的,在未来方案的扩展中,可以使用ML模型来分析度量。

负载监测器将缓存过去15分钟、10分钟和5分钟窗口的指标,并通过REST API提供查询服务。

存储

这是以文件形式存储的本地数据库,目的是在负载监测器的缓存因崩溃而丢失时,能够快速恢复,并在不影响延迟的情况下执行调度策略。对于HA设置,每个负载监测器进程将在本地维护其相应的文件。

调度插件

这里使用了K8s的调度器框架,注册定制的基于实际负载感知的调度器插件。该插件主要包括以下两个算法:

  • TargetLoadPacking:它是bin pack算法的best fit变体(刷过算法应该知道,背包算法),它通过节点的实际资源利用率给节点打分,使所有被利用的节点都有大约x%的利用率。一旦所有节点达到x%的利用率,它就会转到least fit。

  • LoadVariationRiskBalancing:这是一个节点排序插件,根据节点资源利用率的平均值和标准差对节点进行排序。它不仅是为了平衡负载,也是为了避免负载变化引起的风险。


a. TargetLoadPacking插件

插件将扩展Score的扩展点。K8s调度器框架在调度一个pod时,调用Score方法为每个节点打分。

以下是该算法步骤:

  1. 获取当前节点的利用率,以进行评分,假定该节点为A。

  2. 计算当前pod的CPU总的request和overhead,假定该结果为B。

  3. 计算如果pod被调度到该节点下预期的利用率,通过添加即U=A+B。

  4. 如果U <= X%,返回(100 - X)U/X + X作为分数

  5. 如果X%

  6. 如果U>100%,返回0

举例说明,假设有三个节点X、Y和Z,每个节点有四个cpu,分别使用了1、2和3个cpu。简单起见,假设要调度的pod有0个CPU request和overhead,设定X=50%。

每个节点的利用率:

    Ux → (1/4)*100 = 25
    Uy → (2/4)*100 = 50
    Uz → (3/4)*100 = 75

    节点评分:

      X → (100 - 50)*25/50 + 50 = 75
      Y → (100 - 50)*50/50 + 50 = 100
      Z → 50 * (100 - 75)/(100 - 50) = 25

      根据上述算法,50%是所有节点预设的目标利用率,当然可以把它降低到40%,这样在高峰期或意外负载期间,它超过50%的机会就会少得多。因此,一般来说,为了达到X%的利用率(在实践中建议使用X-10值)。

      在算法的第二步,一个变体的算法是使用当前pod的总CPU limit而不是request,以期获取一个更宽松的利用率的上限预期。

      其中节点利用率的X%阈值通过插件的参数进行配置。


      上图表述了算法概述中函数表现,通过上图可以发现算法表征的多样性。

      1. 当利用率从0到50%时,优先选择调度pod至这些节点。

      2. 当利用率超过50%时,在这些 "热"节点之间调度pod时,针对这些节点线性降权。

      3. 正的斜率从50开始,而不是从0开始,因为分数的范围是0-100,分数越低的节点得分越高,这样分数就越可观,其他插件对调度结果的影响也不会太大。

      4. 图中有一个断点,由于我们的降权,产生了一个下降坡度。

      参数配置

        type PluginArgs struct {
        TargetCPUUtilisation int
        DefaultCPURequests v1.ResourceList
        }


        b. LoadVariationRiskBalancing插件

        插件将扩展Score的扩展点。K8s调度器框架在调度一个pod时,调用Score方法为每个节点打分。

        因为没有考虑到突发性的变化,基于实际平均负载的策略有时是有风险的。LoadVariationRiskBalancing插件不仅可以平衡实际平均负载,还可以降低负载突发性变化引发的风险。假设把所有节点利用率的平均值(M)和标准差(V)绘制成下面的mu-sigma图。在这种情况下,LoadVariationRiskBalancing插件将进行处理,使所有节点的利用率在对角线上对齐,即V + M = c。这里,c是一个常数,表示整个集群的利用率平均值加上标准偏差。总之,考虑到所有节点上的负载随时间动态变化,LoadVariationRiskBalancing插件倾向于选择低风险节点,即负载超出节点可分配总量的节点。

        以下是该算法步骤:

        1. 获取待调度的Pod 的request的资源,设为r 。

        2. 获取当前节点所有类型的资源(CPU、Memory、GPU等)的利用率的百分比(0到1),并根据计算的滑动窗口的平均数(V)和标准差(M),进行打分。

        3. 计算当前节点的每一类资源的得分:

        4. 为每种类型的资源获取一个分数,并将其映射到[0,1]区间:

        5. 计算每个资源的节点优先级分数为:

        6. 得到最终的节点分数为:

        举例说明,假设有三个节点N1、N2和N3,要安排的pod的CPU和内存请求为500 milicores和1 GB。所有节点都有4个cpu和8GB的内存。

        Pod的资源请求比例可以计算为

        然后根据步骤2~4,可以计算出CPU和内存部分利用率的平均值和标准偏差,如下所示。

        根据步骤5~6,每类资源和每个节点的得分如下。

        根据我计算的分数,节点N3将被选中,分数如下。

        如果把这些分数画在mu-sigma图中,经过线性拟合可以把节点的利用率推到对角线sigma = 1 - mu。这里的1表示100%的利用率。这里可以配置的是系数ita,表示mu + ita x sigma <= 100 %,这里选择ita = 1。ita在这里模拟使用率不超过节点容量,假设实际使用率遵循高斯分布,并遵循68-96-99.5规则。

        因此,当ita设定为不同的值时,可以得到不同的但不超过容量的线性图。

        • ita=1,有16%的风险几率,使用量超过节点容量。

        • ita = 2,有2.5%的风险几率,实际使用量超过节点容量。

        • ita = 3,有0.15%的风险几率,实际使用量超过节点容量。默认情况下,选择ita=1,因为希望提高整体利用率。ita参数可以通过插件的SafeVarianceMargin配置。

        参数配置

          type PluginArgs struct {
          SafeVarianceMargin int
          }


          API设计

          a. REST API

          GET watcher说明:返回集群中所有节点的指标

          GET watcher/{hostname}说明:返回给定主机名的指标

          注意:如果没有指标,则返回404

          b. 返回结构

            {
            "$schema": "http://json-schema.org/draft-04/schema#",
            "type": "object",
            "properties": {
            "timestamp": {
            "type": "integer"
            },
            "window": {
            "type": "object",
            "properties": {
            "duration": {
            "type": "string"
            },
            "start": {
            "type": "integer"
            },
            "end": {
            "type": "integer"
            }
            },
            "required": [
            "duration",
            "start",
            "end"
            ]
            },
            "source": {
            "type": "string"
            },
            "data": {
            "type": "object",
            "patternProperties": {
            "^[0-9]+$": {
            "type": "object",
            "properties": {
            "metrics": {
            "type": "array",
            "items": [
            {
            "type": "object",
            "properties": {
            "name": {
            "type": "string"
            },
            "type": {
            "type": "string"
            },
            "rollup": {
            "type": "string"
            },
            "value": {
            "type": "integer"
            }
            },
            "required": [
            "name",
            "type",
            "rollup",
            "value"
            ]
            },
            {
            "type": "object",
            "properties": {
            "name": {
            "type": "string"
            },
            "type": {
            "type": "string"
            },
            "rollup": {
            "type": "string"
            },
            "value": {
            "type": "integer"
            }
            },
            "required": [
            "name",
            "type",
            "rollup",
            "value"
            ]
            }
            ]
            },
            "tags": {
            "type": "object"
            },
            "metadata": {
            "type": "object",
            "properties": {
            "dataCenter": {
            "type": "string"
            },
            "pool": {
            "type": "string"
            }
            }
            }
            },
            "required": [
            "metrics"
            ]
            }
            }
            }
            },
            "required": [
            "timestamp",
            "window",
            "source",
            "data"
            ]
            }


            注意事项


            控制器以goroutine方式监听.spec.nodeName事件,其保持一个节点→pod映射的时间顺序状态,用于存储过去5分钟内成功调度的pod。在不同的调度周期中维护该状态,并供TargetLoadPacking/LoadVariationRiskBalancing Score插件使用。它将有助于在指标异常的时候根据前期实际分配情况来预测利用率。



            小结

            局限性


            启用上述插件将会与调度器中的2个默认评分插件的产生冲突:"NodeResourcesLeastAllocated "和 "NodeResourcesBalancedAllocation "插件。因此,建议在启用上述插件时禁用默认两个插件。

            如果利用率的指标在很长一段时间内不可用,将退回到基于分配的best fit的bin pack算法,而无需用户干预。为了达到X%的利用率,建议在实践中将该值设置为X - 10,以下为其他注意项与缺陷:

            • 将上述约束条件2作为Filter插件。

            • 在最初的设计中没有解决不服预期的调度结果(热节点,碎片等)被取消的问题。

            • 在最初的设计中没有考虑内存、网络和磁盘等利用率。



            由于笔者时间、视野、认知有限,本文难免出现错误、疏漏等问题,期待各位读者朋友、业界专家指正交流。


            参考文献


            1.https://github.com/kubernetes-sigs/scheduler-plugins/tree/master/kep/61-Trimaran-real-load-aware-scheduling



            真诚推荐你关注



            推荐阅读
            • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
            • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
            • XML介绍与使用的概述及标签规则
              本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
            • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
            • Python瓦片图下载、合并、绘图、标记的代码示例
              本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
            • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
            • SpringBoot uri统一权限管理的实现方法及步骤详解
              本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
            • CSS3选择器的使用方法详解,提高Web开发效率和精准度
              本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
            • 如何用UE4制作2D游戏文档——计算篇
              篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
            • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
            • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
              本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
            • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
            • Java学习笔记之面向对象编程(OOP)
              本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
            • Linux如何安装Mongodb的详细步骤和注意事项
              本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
            • CentOS 7部署KVM虚拟化环境之一架构介绍
              本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
            author-avatar
            海伦国际官2502862377
            这个家伙很懒,什么也没留下!
            PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
            Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有