作者:Koordinator 团队
Koordinator 从 2022 年 4 月发布以来,迄今一共迭代发布了 8 个版本。项目经历的大半年发展过程中,Koordinator 社区吸纳了包括阿里巴巴、小米、小红书、爱奇艺、360 在内的大量优秀工程师,贡献了众多的想法、代码和场景,一起推动 Koordinator 项目的成熟。
11 月 3 日,在 2022 杭州 · 云栖大会上,阿里云智能云原生应用平台负责人丁宇宣布,Koordinator 1.0 正式发布。
如果你对混部、调度领域不太关注,可能对 Koordinator 还没有做过太深入的了解。本文就借着 v1.0 发布时机,为你详细的梳理一次 Koordinator 项目的发展脉络,解读它的核心思想和愿景,掌握这个正在快速发展的云原生混部系统的技术理念。
项目的前生今世
Koordinator,名字取自 coordinator,K for Kubernetes,发音相同。语意上契合项目要解决的问题,即协调编排 kubernetes 集群中不同类型的工作负载,使得他们以最优的布局、最佳的姿态在一个集群、一个节点上运行。
容器混部技术最早起源于 Google 内部的一个 Borg 系统,在其论文公开发表(2015)之前在行业上一直是非常神秘的存在。在业界,基于 Hadoop 的大数据任务调度技术先行,在比较早期很好的解决了企业对于大数据计算的诉求。熟悉历史的同学应该知道,云原生容器调度编排系统 Kubernetes 开源于 2014 年, 而 Kubernetes 正是受 Borg 设计思想启发,由 Borg 系统的设计者结合云时代应用编排的需求重新设计而来。Kubernetes 良好的扩展性使其能适应多样的工作负载,帮助用户很好的解决工作负载日常运维效率。随着 Kubernetes 的蓬勃发展,逐步成为行业的事实标准,越来越多的工作负载开始运行在 Kubernetes 之上,特别是在这两年,大数据相关的负载逐步迁移到 Kubernetes 之上。
阿里巴巴早在 2016 年就启动了混部技术研发,到今年,内混部系统经历了三轮大的架构升级,并经历多年“双11”峰值流量的锤炼。目前,阿里巴巴实现全业务规模超千万核的云原生混部,混部 CPU 利用率超 50%,混部技术帮助 2022 年“双11”计算成本大幅下降。在这个过程中,阿里巴巴混部也走过了一些弯路,积累了大量的生产实践经验。
为了帮助企业少走弯路,更快速的拿到云原生混部带来的资源效率红利,阿里巴巴在 2022 年 4 月份正式对外发布的 Koordinator 开源项目。通过建立一个中立的开源社区,帮助企业实现在标准 Kubernetes 之上的多种类型负载混部的调度解决方案,以达到云上、云下一致的云原生混部架构,降低系统运维成本,保持长期可持续发展的健康形态。
Koordinator 自发布以来,在开源制度与规范上不断向 Kubernetes 等前辈学习,逐渐形成了较为完善的社区运作机制。在这里,我们会淡化每一位社区成员的雇主、职务,以用户、开发者的身份参与到社区建设中。同样,Koordinator 未来的 roadmap 与发展规划,也将源于所有成员与用户的反馈、需求与共识,而非阿里巴巴自身制定。因此,对于每一位云原生爱好者,不管你是初识调度领域或是混部场景的亲身经历者,都欢迎你关注和参与到 Koordinator 项目中,共同打造一套面向未来的开源混部体系。
标准、通用的混部解决方案
混部需要一套完整、自闭环的调度回路,但在企业应用混部的过程中,将要面临的两大挑战是:
- 应用如何接入到混部平台
- 应用如何在平台上能够运行稳定、高效
Koordinator 吸取了阿里巴巴内部多年的生产实践经验教训,针对这两大挑战针对性的设计了解决方案,旨在帮助企业真正意义上的用上混部,用好 Kubernetes,而不是秀技术秀肌肉。
Koordinator 1.0 的整体架构如下图所示,为了用户提供了完整的混部工作负载编排、混部资源调度、混部资源隔离及性能调优解决方案,帮助用户提高延迟敏感服务的运行性能,挖掘空闲节点资源并分配给真正有需要的计算任务,从而提高全局的资源利用效率。
大规模生产实践锤炼
2021 双 11 之后阿里对外宣布了“首次!统一调度系统规模化落地,全面支撑阿里巴巴双 11 全业务”:
作为阿里巴巴的核心项目,阿里云(容器团队和大数据团队)联合阿里巴巴资源效能团队、蚂蚁容器编排团队,历时一年多研发和技术攻坚,实现了从“混部技术”到今天“统一调度技术”的全面升级。
今天,统一调度已实现阿里巴巴电商、搜推广、MaxCompute 大数据的调度全面统一,实现了 Pod 调度和 task 高性能调度的统一,实现了完整的资源视图统一和调度协同,实现了多种复杂业务形态的混部和利用率提升,全面支撑了全球数十个数据中心、数百万容器、数千万核的大规模资源调度。
作为云原生混部的践行者,阿里巴巴是真刀真枪的在生产环境中推进混部技术理念,并在去年双 11 完成了超过千万核的混部规模,通过混部技术帮助阿里巴巴双 11 节约超过 50% 的大促资源成本,在大促快上快下链路上提速 100%,助力大促实现丝滑的用户体验。
正是在双 11 这样的峰值场景驱动之下,阿里的混部调度技术持续演进,积累了大量的生产实践经验,到今天已经是第三代即云原生全业务混部系统。Koordinator 的架构基于阿里巴巴内部的超大规模生产实践,结合阿里云看到的企业容器客户的真实诉求,在标准化、通用化上做出了更多的突破,实现首个基于标准 Kubernetes 的生产可用的开源混部系统。
支持丰富的负载类型
混部是一套针对延迟敏感服务的精细化编排+大数据计算工作负载混合部署的资源调度解决方案,核心技术在于:
- 精细的资源编排,以满足性能及长尾时延的要求,关键点是精细化的资源调度编排策略及 QoS 感知策略;
- 智能的资源超卖,以更低成本满足计算任务对计算资源的需求,并保证计算效率的同时不影响延迟敏感服务的响应时间。
上图是 Koordinator 混部资源超卖模型,也是混部最关键最核心的地方。其中超卖的基本思想是去利用那些已分配但未使用的资源来运行低优先级的任务,如图所示的四条线分别是资源使用量(红线),短生命周期可超卖量(蓝线),长生命周期可超卖量(浅蓝),以及资源总量(黑线)。
该资源模型足够精炼的同时也具备很强的灵活性,支持丰富的在线资源编排类别,支持短生命周期的批处理任务(MapReduce 类别),以及实时计算类的生命周期任务。Koordinator 整个混部资源调度的大厦构建在这样一个资源模型的基础之上,配合上优先级抢占、负载感知、干扰识别和 QoS 保障技术,构建出混部资源调度底层核心系统。Koordinator 社区将围绕这个思路投入建设,持续将混部场景的调度能力展开,解决企业面临的真实业务场景问题。
零侵入,低接入成本
企业接入混部最大的挑战是如何让应用跑在混部平台之上,这第一步的门槛往往是最大的拦路虎。Koordinator 针对这一问题,结合内部生产实践经验,设计了“零侵入”的混部调度系统:
- 对 Kubernetes 平台的零侵入:无需修改任何 Kubernetes 原生组件,而是以插件的方式增强混部需要的各种能力
- 对工作负载编排系统的零侵入:无需修改计算任务的管理引擎(operator),而是以配置化的方式管理混部策略参数,简化应用接入的难度
- 支持系统的平滑升级:已有的 Kubernetes 集群的调度器可以平滑升级到 Koordinator,存量的 Pod 的标准调度能力可以无损接管到 Koordinator 中
通过在这三方面的努力,降低用户在生产环境中引入 Koordinator 的难度,只在帮助用户解决生产环境应用混部的第一道拦路虎。
版本特性深入解读
自 2022 年 4 月份 Koordinator 发布以来,社区围绕在混部资源的一层调度而构建,解决任务混部最核心的资源编排、资源隔离的问题。在版本迭代过程中,Koordinator 社区始终围绕三大能力而构建,即任务调度、差异化 SLO 以及 QoS 感知调度能力。
任务调度
- Enhanced Coscheduling
Koordinator 在启动之初,期望支持 Kubernetes 多种工作负载的混部调度,提高工作负载的运行时效率和可靠性,其中就包括了机器学习和大数据领域中广泛存在的具备 All-or-Nothing 需求的作业负载。例如当提交一个Job 时会产生多个任务,这些任务期望要么全部调度成功,要么全部失败。这种需求称为 All-or-Nothing,对应的实现被称作 Gang Scheduling(or Coscheduling) 。为了解决 All-or-Nothing 调度需求,Koordinator 基于社区已有的 Coscheduling 实现了 Enhanced Coscheduling:
- 支持 Strict/NonStrict 模式,解决大作业场景长时间得不到资源问题
- 支持 AI 场景多角色联合的 coscheduling 策略,例如一个 TF 训练 Job 中包含 PS 和 Worker 两种角色,并且两种角色都需要单独定义 MinMember,但又期望两种角色的 All-or-Nothing 都满足后才能继续调度
- Enhanced ElasticQuota Scheduling
企业的 Kubernetes 一般由多个产品和研发团队共用一个规模比较大的 Kubernetes 集群,由资源运维团队统一管理集群内大量 CPU/Memory/Disk 等资源。
Koordinator 为帮助用户管理好资源额度,提升资源额度的使用效率,实现降本增效,Koordinator 基于基于社区 ElasticQuota CRD 实现了 Enhanced ElasticQuota Scheduling ,具备如下增强特性:
- 兼容社区的 ElasticQuota CRD,用户可以无缝升级到 Koordinator
- 支持树形结构管理 Quota,契合企业的组织架构
- 支持按照共享权重(shared weight)保障公平性
- 允许用户设置是否允许借用 Quota 给其他消费对象
Koordinator ElasticQuota Scheduling 通过额度借用机制和公平性保障机制,Koordinator 把空闲的额度复用给更需要的业务使用。当额度的所有者需要额度时,Koordinator 又可以保障有额度可用。通过树形管理机制管理 Quota,可以方便的与大多数公司的组织结构映射,解决同部门内或者不同部门间的额度管理需求。
- Fine-grained Device Scheduling
在 AI 领域,GPU、RDMA、FPGA 等异构资源被广泛的应用,Koordinator 针对异构资源调度场景,提供了精细化的设备调度管理机制,包括:
- 支持 GPU 共享,GPU 独占,GPU 超卖
- 支持百分比的 GPU 调度
- 支持 GPU 多卡调度
- NVLink 拓扑感知调度(doing)
差异化 SLO
差异化 SLO 是 Koordinator 提供的核心混部能力,保障资源超卖之后的 Pod 的运行稳定性。Koordinator 定了一一组 Priority & QoS,用户按照这一最佳实践的方式接入应用,配合完善的资源隔离策略,最终保障不同应用类型的服务质量。
- CPU Supress
Koordinator 的单机组件 koordlet 会根据节点的负载水位情况,调整 BestEffort 类型 Pod 的 CPU 资源额度。这种机制称为 CPU Suppress。当节点的在线服务类应用的负载较低时,koordlet 会把更多空闲的资源分配给 BestEffort 类型的 Pod 使用;当在线服务类应用的负载上升时,koordlet 又会把分配给 BestEffort 类型的 Pod 使用的 CPU 还给在线服务类应用。
- CPU Burst
CPU Burst 是一种服务级别目标 (SLO) 感知的资源调度功能。用户可以使用 CPU Burst 来提高对延迟敏感的应用程序的性能。内核的调度器会因为容器设置的 CPU Limit 压制容器的 CPU,这个过程称为 CPU Throttle。该过程会降低应用程序的性能。
Koordinator 自动检测 CPU Throttle 事件,并自动将 CPU Limit 调整为适当的值。通过 CPU Burst 机制能够极大地提高延迟敏感的应用程序的性能。
- 基于内存安全阈值的主动驱逐机制
当延迟敏感的应用程序对外提供服务时,内存使用量可能会由于突发流量而增加。类似地,BestEffort 类型的工作负载可能存在类似的场景,例如,当前计算负载超过预期的资源请求/限制。这些场景会增加节点整体内存使用量,对节点侧的运行时稳定性产生不可预知的影响。例如,它会降低延迟敏感的应用程序的服务质量,甚至变得不可用。尤其是在混部场景下,这个问题更具挑战性。
我们在 Koordinator 中实现了基于内存安全阈值的主动驱逐机制。koordlet 会定期检查 Node 和 Pods 最近的内存使用情况,检查是否超过了安全阈值。如果超过,它将驱逐一些 BestEffort 类型的 Pod 释放内存。在驱逐前根据 Pod 指定的优先级排序,优先级越低,越优先被驱逐。相同的优先级会根据内存使用率(RSS)进行排序,内存使用率越高越优先被驱逐。
- 基于资源满足的驱逐机制
CPU Suppress 在线应用的负载升高时可能会频繁的压制离线任务,这虽然可以很好的保障在线应用的运行时质量,但是对离线任务还是有一些影响的。虽然离线任务是低优先级的,但频繁压制会导致离线任务的性能得不到满足,严重的也会影响到离线的服务质量。而且频繁的压制还存在一些极端的情况,如果离线任务在被压制时持有内核全局锁等特殊资源,那么频繁的压制可能会导致优先级反转之类的问题,反而会影响在线应用。虽然这种情况并不经常发生。
为了解决这个问题,Koordinator 提出了一种基于资源满足度的驱逐机制。我们把实际分配的 CPU 总量与期望分配的 CPU 总量的比值成为 CPU 满足度。当离线任务组的 CPU 满足度低于阈值,而且离线任务组的 CPU 利用率超过 90% 时,koordlet 会驱逐一些低优先级的离线任务,释放出一些资源给更高优先级的离线任务使用。通过这种机制能够改善离线任务的资源需求。
- L3 Cache 和内存带宽分配(MBA) 隔离
混部场景下,同一台机器上部署不同类型的工作负载,这些工作负载会在硬件更底层的维度发生频繁的资源竞争。因此如果竞争冲突严重时,是无法保障工作负载的服务质量的。
Koordinator 基于 Resource Director Technology (RDT, 资源导向技术) ,控制由不同优先级的工作负载可以使用的末级缓存(服务器上通常为 L3 缓存)。RDT 还使用内存带宽分配 (MBA) 功能来控制工作负载可以使用的内存带宽。这样可以隔离工作负载使用的 L3 缓存和内存带宽,确保高优先级工作负载的服务质量,并提高整体资源利用率。
QoS 感知调度、重调度
Koordinator 差异化 SLO 能力在节点侧提供了诸多 QoS 保障能力能够很好的解决运行时的质量问题。同时 Koordinator Scheduler 也在集群维度提供了增强的调度能力,保障在调度阶段为不同优先级和类型的 Pod 分配合适的节点。
- 负载感知调度
超发资源可以极大的提升集群的资源利用率,但也会凸显集群内节点之间资源利用率不均匀的现象。这个现象在非混部环境下也是存在的,只是因为 Kubernetes 原生是不支持资源超发机制,节点上的利用率往往不是很高,一定程度上掩盖了这个问题。但当混部时,资源利用率会上升到比较高的水位时就暴露了这个问题。
利用率不均匀一般是节点之间不均匀以及出现局部的负载热点,局部的负载热点会可能影响工作负载的整体运行效果。另一个是在负载高的节点上,在线应用和离线任务之间可能会存在的严重的资源冲突,影响到在线应用的运行时质量。
为了解决这个问题, Koordinator 的调度器提供了一个可配置的调度插件控制集群的利用率。该调度能力主要依赖于 koordlet 上报的节点指标数据,在调度时会过滤掉负载高于某个阈值的节点,防止 Pod 在这种负载较高的节点上无法获得很好的资源保障,另一方面是避免负载已经较高的节点继续恶化。在打分阶段选择利用率更低的节点。该插件会基于时间窗口和预估机制规避因瞬间调度太多的 Pod 到冷节点机器出现一段时间后冷节点过热的情况。
- 精细化 CPU 调度
随着资源利用率的提升进入到混部的深水区,需要对资源运行时的性能做更深入的调优,更精细的资源编排可以更好的保障运行时质量,从而通过混部将利用率推向更高的水平。
我们把 Koordinator QoS 在线应用 LS 类型做了更细致的划分,分为 LSE、LSR 和 LS 三种类型。拆分后的 QoS 类型具备更高的隔离性和运行时质量。通过这样的拆分,整个 Koordinator QoS 语义更加精确和完整,并且兼容 Kubernetes 已有的 QoS 语义。
而且我们针对 Koordinator QoS,设计了一套丰富灵活的 CPU 编排策略,如下表所示。
不同的 QoS 类型的工作负载具备不同的隔离性:
Koordinator Scheduler 会针对 LSE/LSR 类型的 Pod 分配具体的 CPU 逻辑核,并更新到 Pod Annotation 中,由单机侧 koordlet 配合,把调度器分配的 CPU 更新到 cgroup 中。
调度器在分配 CPU 时,会根据 CPU 拓扑结构分配,默认尽可能的保障分配的 CPU 属于同一个 NUMA Node 以获得更好的性能。并且 CPU 调度时,支持 Pod 根据需要设置不同的互斥策略,例如一个系统中多个核心的服务部署在相同的物理核上,性能表现比较差,但分开就完全没问题,此时就可以配置互斥策略,调度器会尽可能的把这种互斥属性的 Pod 分配使用不同的物理核。并且在打分阶段,调度器会参考集群的整体情况,选择符合 CPU 编排要求的节点。
- 资源预留(Reservation)
Koordinator 支持在不侵入 Kubernetes 已有的机制和代码前提下,实现了资源预留的原子能力(Reservation)。Koordinator Reservation API 允许用户不修改 Pod Spec 或者存量的 Workload(例如 Deployment, StatefulSet)即可以预留资源。资源预留在容量管理、碎片优化、调度成功率和重调度等场景有重要作用:
- 当有重要的工作负载在未来某段时间需要资源时,可以提前预留资源满足需求。
- 用户在 PaaS 上发起扩容时,可以通过资源预留能力尝试预留,预留成功后发起扩容,保障 PaaS 的 SLA。
- 用户在 PaaS 上发布时,如果应用采用了滚动发布的能力,可以通过资源预留保留即将销毁的 Pod 持有的资源,在滚动发布时,新建的 Pod 可以复用预留下来的原有资源,能够有效提高滚动发布的成功率。
- 碎片优化场景中,可以通过资源预留占住空闲的碎片资源,并把可被整理的 Pod 迁移到这些节点。
- 重调度时在发起驱逐前,先尝试预留资源,预留成功后发起驱逐,避免驱逐后无资源可用影响应用的可用性。
- 灵活可扩展且安全的重调度器
调度器调度时是根据当时集群内的情况和配置,做出综合的判断,选择出一个最合适的节点分配给 Pod 使用。但随着时间和工作负载的变化,原本最合适的节点也会变差,差异化 SLO 和调度器都提供了丰富的能力帮助改善这些问题,但差异化 SLO 更多还是关注在自身单机的情况,无法感知全局的变化。
从控制的角度看,我们也需要根据集群内的情况做出决策,把异常的 Pod 驱逐迁移到更合适的节点,让这些 Pod 有机会可以更好的对外服务。因此 Koordinator Descheduler 应运而生。
我们认为 Pod 迁移是一个复杂的过程,涉及到审计、资源分配、应用启动等步骤,还夹杂着应用的发布升级、扩容/缩容场景和集群管理员的资源运维操作。因此,如何管理 Pod 迁移过程的稳定性风险,保证应用不会因为 Pod 的迁移影响可用性,是一个非常关键的必须解决的问题。
为了让大家更好的理解,举几个场景:
- 社区的重调度器内置的多个重调度策略根据自身逻辑判断某个 Pod 是否要被迁移,需要迁移时调用 Kubernetes Eviction API 发起驱逐。但是这个过程并不关注被驱逐的 Pod 在将来是否可以分配到资源。因此存在大量 Pod 被驱逐后因为没有资源而处于 Pending 状态的情况。如果应用此时有大量请求进来,又因为没有足够的可用的 Pod 导致可用性异常。
- 另外,社区重调度器调用的 Kubernetes Evcition API 虽然会检查 PDB 确保在安全范围内驱逐,但是众所周知,众多的 workload Controller 在发布和缩容场景都是通过直接调用 Delete API 的形式销毁 Pod,此时并不会被 PDB 限制。这就导致重调度时如果遇到上述场景,是很可能引发严重的稳定性问题。
- 我们认为 Pod 腾挪不是一个简单的后台自动化逻辑,有相当多的场景和用户期望由人工介入手工迁移 Pod,甚至期望重调度时发起的自动迁移请求应该被拦截掉,经过审批决定是否执行。
Koordinator 基于 CRD 定义了一个名为 PodMigrationJob API。重调度器或者其他自动化自愈组件通过 PodMigrationJob 可以安全的迁移 Pod。PodMigrationJob Controller 在处理 PodMigrationJob 时会先尝试通过 Koordinator Reservation 机制预留资源,预留失败则迁移失败;资源预留成功后发起驱逐操作并等待预留的资源被消费。中间的过程都会记录到 PodMigrationJobStatus 中,并产生相关的 Event。
我们在 Koordinator 中实现了一个全新的 Descheduler Framework。我们认为重调度场景:
- 需要一个插件化机制实现自定义的重调度策略,但又不希望这个抽象过于复杂;
- 需要具备基本的插件管理能力,通过配置启用和禁用插件;
- 具备统一的插件配置下发机制,方便插件自定义参数;
- 并能够方便的扩展和使用统一的 Evictor 机制;
- 另外期望用户能够基于 controller-runtime 实现 controller 并纳入统一的插件管理机制。
Koordinator descheduler framework 提供了插件配置管理(例如启用、禁用,统一配置等)、插件初始化、插件执行周期管理等机制。并且该框架内置了基于 PodMigrationJob 实现的 Controller,并作为 Evictor Plugin 方便被各种重调度插件使用,帮助重调度插件安全的迁移 Pod。
基于 Koordinator descheduler framework,用户可以非常容易的扩展实现自定义重调度策略,就像基于 Kubernetes scheduling framework 的实现自定义的调度插件一样简单。并且用户也可以以插件的形式实现 controller,支持基于 Event 触发重调度的场景。
Koordinator 的未来规划
Koordinator 社区将不断丰富大数据计算任务混部的形态,拓展包括 Hadoop YARN 等计算框架混部支持,丰富任务混部解决方案,项目上持续的完善干扰检测、问题诊断体系,推进更多的负载类型融入 Koordinator 生态,并取得更好的资源运行效率。
Koordinator 社区将持续的保持中立的发展趋势,联合各厂商持续的推进混部能力的标准化,也欢迎大家加入社区共同推进混部的标准化进程。