Koordinator 异构资源/任务调度实践

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: Koordinator 异构资源/任务调度实践

作者:李涛(吕风)


前言


Koordinator 是阿里云基于过去我们建设的统一调度系统中积累的技术和实践经验,对外开源了新一代的调度系统。Koordinator 支持 Kubernetes 上多种工作负载的混部调度。它的目标是提高工作负载的运行时效率和可靠性(包括延迟敏感型负载和批处理任务)。Koordinator 不仅擅长混部场景,也同样支持大数据、AI 训练等任务调度场景。本文分享了使用 Koordinator 支持异构资源管理和任务调度场景的实践经验。


AI/LLMs 带来新机遇和新挑战


从 2022 年 11 月 ChatGPT 发布到现在,ChatGPT 所引起的关注、产生的影响可能已经超越了信息技术历史上的几乎所有热点。众多业界专家都被它征服,比如阿里云 CEO 张勇的看法是:“所有行业、应用、软件、服务,都值得基于大模型能力重做一遍。”NVIDIA CEO 黄仁勋称它带来了 AI 的 iPhone 时刻。ChatGPT 开启了新的时代,国内外的企业和科研机构纷纷跟进,几乎每周都有一个甚至多个新模型推出,从自然语言处理、计算机视觉到人工智能驱动的科学研究、生成式 AI 等,应用百花齐放;大模型成为业务提效和打开下一个增长点的关键。同样对于云计算、基础设施、分布式系统的需求也扑面而来。



为支撑百亿级、千亿级别参数量的大模型训练需求,云计算和基础设施需要提供更强大、可扩展的计算和存储资源。大模型训练依赖的的核心技术之一是分布式训练,分布式训练需要在多个计算节点之间传递大量的数据,因此需要一个带宽更高、延迟更低的高性能网络。为了发挥计算、存储和网络资源的最佳效能,保障训练效率,调度和资源管理系统需要设计更合理的策略。在此基础上,基础设施还需要在可靠性上持续增强,具备节点故障治愈和容错能力,确保训练任务的持续运行。


大模型训练离不开异构计算设备,典型的就是我们熟知的 GPU。在 GPU 领域,NVIDIA 仍然占据着主导地位,其他厂商如 AMD 和国内的芯片制造商的机会在努力追赶。以 NVIDIA 为例,其强大的产品设计能力、扎实的技术实力和灵活的市场策略使其能够快速推出更优秀的芯片,但产品间的架构差异较大,例如 NVIDIA A100 型号和 NVIDIA H100 型号的系统架构差异十分明显,使用方式上也存在许多需要注意的细节,这给上层的调度系统和资源管理系统带来了不小的挑战。


Koordinator+KubeDL 的强强联合


我们在阿里云支撑的大模型训练场景中,使用了 Koordinator 来解决基本的任务调度需求和异构设备资源管理需求。同时,使用 KubeDL 管理训练作业生命周期和训练作业排队调度需求。



Koordinator 不仅擅长混部调度场景,还针对大数据、AI 模型训练场景,提供了包括弹性 Quota 调度、Gang 调度等通用的任务调度能力。此外,它还具备精细化的资源调度管理能力,不仅支持中心化分配 GPU,还能感知硬件系统拓扑分配资源,同时支持 GPU&RDMA 的联合分配和设备共享能力。


我们选择使用 KubeDL 来管理训练作业生命周期,是因为它不仅在支撑了内部大量 AI 领域相关场景,而且得益于其优秀的设计和实现都十分优秀,可运维性、可靠性和功能扩展性都非常出色,自身是一个统一的 controller,可以支持多种训练工作负载,如 TensorFlow、PyTorch、Mars 等。此外,它还可以适配不同调度器提供的 Gang 调度能力,可以帮助已经使用 KubeDL 项目的存量场景平滑的切换到 Koordinator;KubeDL 还内置了一个通用的作业排队机制,可以有效解决作业自身的调度需求。


Koordinator 和 KubeDL 的强强联合,可以很好的解决大模型训练的调度需求。


Job 调度


Job 是一种更高层次的抽象,通常具有特定的计算任务或操作。它可以分割成多个子任务并行完成,也可以拆分成多个子任务协作完成。通常 Job 不会依赖其他的工作负载,可以独立的运行。而且 Job 比较灵活,在时间维度、空间维度、或者资源方面的约束都比较少。



Job 排队

Job 同样需要经过调度程序调度,这也就意味着 Job 同样在调度时需要排队。那为什么需要排队呢?或者说我们可以通过排队解决哪些问题?


是因为系统中的资源有限的,我们的预算也是有限的,而 Job 的数量和计算需求往往是无限的。如果不进行排队和调度,那些计算需求较高或者执行时间较长的 Job 就会占用大量的资源,导致其他 Job 无法获取到足够的资源进行计算,甚至可能导致集群系统崩溃。


因此,为保证各个 Job 能够公平的获得资源,避免资源争夺和冲突,就需要对 Job 进行排队和调度。


我们使用 KubeDL提供的通用的 Job 排队和调度机制解决这个问题。KubeDL 因为本身就内置支持了多种训练工作负载,因此它天然支持按照 Job 粒度进行调度;并且它具备多租户间的公平性保障机制,减少 Job 间的资源争夺和冲突,排队和调度的过程中,KubeDL 根据 Job 的计算需求、优先级、资源需求等因素进行评估和分配,确保每个 Job 都能够得到合适的资源进行计算。KubeDL 支持多种扩展插件,如 Filter 插件,Score 插件等,可以进一步扩展其功能和特性满足不同场景的需求。



弹性 Quota

Job 排队要解决的核心问题之一是资源供给的公平性,一般在调度系统中都是通过弹性 Quota 机制来解决。


弹性 Quota 机制要解决的几个核心问题:首先是保障公平性,不能让某一些任务的资源需求过高导致其他任务被饿死,应尽量让大部分任务都能得到资源;其次需要有一定的弹性能力,能够把空闲的额度共享给当下更需要资源的任务,同样还要能够在需要资源时,把共享出去的资源拿回来,这意味还需要提供具备灵活的策略满足不同场景的需求。



Koordinator 实现了弹性 Quota 调度能力,可以保障租户间的公平性。我们在设计之初就考虑兼容 scheduler-plugins 社区项目中定义的 ElaticQuota CRD,这样方便存量的集群和用户可以平滑的过度到 Koordinator。


另外,我们不仅是兼容 ElasticQuota 原有按照 Namespace 管理 Quota 的能力,还支持按照支持按照树形结构进行管理,可以跨 Namespace。这样的方式可以很好的支持一个复杂的组织的额度管理需求,比如一家公司里多个产品线,每个产品线的预算和使用情况都不一样,都可以转为 Quota 进行管理,并借助弹性 Quota,把暂时没有用到的空闲资源通过额度的形式临时共享给其他部门使用。



Coscheduling

当一个 Job 经过排队被调度后,Job Controller 会创建出一批子任务,对应到 K8s,就是一批 Pod。这些 Pod 往往需要协调一致的启动运行。这也就要求调度器在调度时一定要按照一组 Pod 分配资源,这一组 Pod 一定都可以那可以申请到资源或者一旦有一个 Pod 拿不到资源都认为是调度失败。这也就是调度器需要提供的  All-or-Nothing 调度语义。


如果我们不这样按照一组调度,会出现因为多个作业在资源调度层面出现争抢,是有可能出现资源维度的死锁,即至少两个 Job 会出现拿不到资源的情况,即使原本空闲资源足够其中一个 Job 运行的,也会拿不到。


比如下图中,Job A 和 Job B 同时创建一批 Pod,如果不在中间的 Scheduling Queue 进行排序而是随意的调度,就会出现 Job A 和 Job B 的 Pod 各持有了一部分节点的部分资源,如果此时集群资源紧张,很有可能 Job A 和 Job B 都可能拿不到资源。但如果排序后,我们尝试先让其中一个 Job 的 Pod 先一起尝试优先分配资源,那么至少保障一个 Job 可以运行。



当一个 Job 切分的一组 Pod 非常大时,而集群内的资源又不是十分充足,或者  Quota 不是很多时,可以把这样的一组 Pod 切分成更多个子组,这个切割的大小以能运行任务为基础,假设一个 Job 要求最小切割粒度是每组 3 个 Pod,那么这个最小粒度,一般在调度域中称为 min available。


具体到 AI 模型训练领域,一些特殊的 Job 比如 TFJob,它的子任务有两种角色,这两种角色在生产环境中,也是需要设置不同的 min available 的。这种不同角色的区分的场景还有可能要求每个角色的 min available 都满足时才可以认为符合 All-or-Nothing 语义。


Koordinator 内置了 Coscheduling 调度能力,它兼容社区的 scheduler-plugins/coscheduling 定义 PodGroup CRD,还支持把多个 PodGroup 联合调度,这样就可以支持按角色设置 min available 场景。Koordinator 实现了一个 KubeDL Gang Scheduler 插件,这样就可以和 KubeDL 做集成一起支撑这类调度场景。


精细化设备管理


K8s 设备管理的局限性

K8s 是通过 kubelet 负责设备管理和分配,并和 device plugin 交互实现整套机制,这套机制在 K8s 早期还是够用的,其他厂商如 AMD 和国内的芯片制造商也抓住机会努力追赶。


kubelet 与 device plugin 协作流程


首先 K8s 只允许通过 kubelet 来分配设备,这样就导致无法获得全局最优的资源编排,也就是从根本上无法发挥资源效能。比如一个集群内有两台节点,都有相同的设备,剩余的可分配设备数量相等,但是实际上两个节点上的设备所在的硬件拓扑会导致 Pod 的运行时效果差异较大,没有调度器介入的情况下,是可能无法抵消这种差异的。


其次是不支持类似 GPU 和 RDMA 联合分配的能力。大模型训练依赖高性能网络,而高性能网络的节点间通信需要用到 RDMA 协议和支持 RDMA 协议的网络设备,而这些设备又和 GPU 在节点上的系统拓扑层面是紧密协作的,比如下面这张图是 NVIDIA 的 A100 型号机型的硬件拓扑图,我们可以看到,PCIe Switch 下挂了 GPU 和高性能网卡,我们需要就近分配这两个设备,才能做到节点间通信的低延迟。而且这里比较有意思的是,当如果需要分配多个 GPU 时,如果涉及到了多个 PCIe Switch,就意味着需要分配多个网卡,这就和 K8s 的另一个限制有关系,即声明的资源协议是定量的,而不是随意变化的,也就是说用户实际上也不知道这个 Pod 需要多少支持 RDMA 的网卡,用户只知道要多少个 GPU 设备,并期望就近分配 RDMA 的网卡而已。



而且 kubelet 也不支持设备的初始化和清理功能,更不支持设备的共享机制,后者在训练场景一般用不到,但在线推理服务会用到。在线推理服务本身也有很明显的峰谷特征,很多时刻并不需要占用完整的 GPU 资源。


K8s 这种节点的设备管理能力一定程度上已经落后时代了,虽然现在最新的版本里支持了 DRA 分配机制(类似于已有的 PVC 调度机制),但是这个机制首先只在最新版本的 K8s 才支持,但实际情况是还有大量存量集群在使用,并且升级到 K8s 最新版本也并不是一个小事情,所以我们得想其他办法。


Koordinator 精细化设备管理机制

我们在 Koordinator 中提出了一种方案,可以解决这些问题,做到精细化的资源调度。


Koordinator 精细化设备管理机制


从上面的图中可以看到,用户创建的一个 Pod,由 koord-scheduler 调度器根据 koordlet 上报的 Device CRD 分配设备,并写入到 Pod Annotation 中,再经 kubelet 拉起 Sandbox 和 Container,这中间 kubelet 会发起 CRI 请求到 containerd/docker,但在 Koordinator 方案中,CRI 请求会被 koord-runtime-proxy 拦截并转发到 koordlet 内的 GPU 插件,感知 Pod Annotation 上的设备分配结果并生成必要的设备环境变量等信息返回给 koord-runtime-proxy,再最终把修改后的 CRI 请求转给 containerd/docker,最终再返回给 kubelet。这样就可以无缝的介入整个容器的生命周期实现自定义的逻辑。


Koordinator Device CRD 用来描述节点的设备信息,包括 Device 的拓扑信息,这些信息可以指导调度器实现精细化的分配逻辑。


image.png

Koordinator Device 对象


Future: NRI 模式

前面提到了 Koordinator 单机侧依靠 koord-runtime-proxy 协作完成设备信息注入,我们自己也意识到,koord-runtime-proxy 这种方式其实不太好在大家的集群内落地。这涉及到修改 kubelet 的启动参数问题。


所以 Koordinator 社区后续会引入 NRI/CDI 等机制解决这个场景的问题。这块工作正在和 Intel 相关团队共建。


NRI/CDI 是 containerd 支持的一种插件化机制。其部署方式有点类似于大家熟悉的 CNI,支持在启动 Sandbox/Container 前后获得机会修改参数或者实现一些定制逻辑。这相当于是 containerd 内置的 runtimeproxy 机制。



GPU&RDMA 按照硬件拓扑联合分配

前面也提到,大模型训练不仅仅只用到了 GPU,还依赖 RDMA 网络设备。要确保 GPU 和 RDMA 之间的延迟尽可能的低,否则会因为设备间的延迟放大到整个分布式训练网络中,拖慢整体的训练效率。


这就要求在分配 GPU 和 RDMA 时需要感知硬件拓扑,尽可能就近分配这种设备。尝试按照同 PCIe,同 NUMA Node,同 NUMA Socket 和 跨 NUMA 的顺序分配,延迟依次升高。


而且我们还发现同一个硬件厂商的不同型号的 GPU,它们的硬件系统拓扑是不一样的,这就要求我们的调度器需要感知这些差异。比如下图是 NVIDIA A100 型号的 System Topology 和 NVIDIA H100 的一个简单的设备连接图。


NVIDIA A100 System Topology


NVIDIA A100 GPU 之间的 NVLINK 联通方式和 NVIDIA H100 型号就不一样,NVSwitch 的数量也不一样,这种差异就会给使用方式带来很大的差异。


NVIDIA H100


NVIDIA-based system 在多租模式下的差异

NVIDIA H100 GPU 在多租 VM 场景下的特殊之处,多个 GPU 之间联通需要操作 NVSwitch 才可以实现。


在多租场景中,NVIDIA 为保障安全,会通过 NVSwitch 管理 NVLink 的隔离状态,并且要求只能由授信的软件操作 NVSwitch。这个授信软件是可以自定义的。


NVIDIA 支持多种模式,一种是 Full Passthrough 模式,这种模式把 GPU 和 NVSwitch 都直通到 VM 的 Guest OS,这样做的好处是使用起来很简单,但代价是当 GPU VM 多了,NVLINK 的带宽会减少(原文:Reduced NVLink bandwidth for two and four GPU VMs)。


另一种称为 Shared NVSwitch 多租户模式,它只要求把 GPU 直通到 Guest OS,然后通过一个特殊的 VM,称为 Service VM 管理 NVSwitch,并通过 ServiceVM 调用 NVIDIA Fabric Manager 激活 NVSwitch 实现 GPU 间通信。这种模式就不会出现因为 Full Passthrough 模式的弊端,但使用方式明显要更复杂一些。这种特殊的硬件架构和使用方式,还导致在分配 GPU 时有一些额外的要求。NVIDIA 定义了哪些 GPU 设备实例可以组合一起分配,比如用户申请分配 4个 GPU,那必须是按照规定的 1,2,3,4 号一起或者 5,6,7,8 一起分,否则就会导致 Pod 无法运行。


这种特殊的分配方式的背后原因我们不得而知,但分析这些分配约束可以发现,厂商规定的这种组合关系正好符合硬件系统拓扑结构,也就是可以满足前面讲到的 GPU&RDMA 联合分配期望的分配结果。


NVIDIA H100 System Topology


关于 Koordinator


Koordinator 开源到现在也有一年多的时间了,吸引到国内众多企业或者团队参与到项目中,并在生产中应用;我们在阿里云内部环境中也应用广泛,支撑了内部丰富场景的调度需求和混部需求。


我们十分期待大家能参与到这个项目中一起做点好玩的有意思的事情,我们鼓励所有贡献者成为成员,我们的目标是发展一个活跃、健康的社区;我们非常期待您使用 Koordinator,并反馈遇到的任何问题。


我们每隔两周会有一次周会,周会上会交流一些最新的进展,和讨论或者分享一些技术细节。



点击此处,立即了解 Koordinator 项目!

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
资源调度 Kubernetes Cloud Native
资源调度的最佳实践|学习笔记
快速学习资源调度的最佳实践
671 0
资源调度的最佳实践|学习笔记
|
资源调度 分布式计算 安全
伏羲—阿里云分布式调度系统
在12月12日的云栖社区在线培训上,“飞天”分布式系统核心开发人员陶阳宇分享了《伏羲-阿里云分布式调度系统》。他主要从伏羲系统架构、任务调度、资源调度、容错机制、规模挑战、安全与性能隔离方面介绍了伏羲分布式系统架构和设计理念。
22109 0
|
6月前
|
分布式计算 资源调度 监控
分布式资源管理和调度架构
分布式资源管理和调度架构
|
资源调度 分布式计算 Kubernetes
Koordinator 支持 K8s 与 YARN 混部,小红书在离线混部实践分享
Koordinator 支持 K8s 与 YARN 混部,小红书在离线混部实践分享
|
资源调度 分布式计算 Kubernetes
给 K8s 装上大数据调度引擎:伏羲架构升级 K8s 统一调度
飞天伏羲作为有着十多年历史的调度团队,在服务好 MaxCompute 大数据平台的过程中,一直在不断通过自我革新赶超业界先进水平,我们经历了 Fuxi 2.0 的这样的大规模升级,今天通过 K8s 统一调度项目又再次实现了系统架构的蜕变,将大数据平台强大的调度能力赋予 K8s 系统,同时去拥抱 K8s 周边丰富的生态。除了集团弹内集群,将来我们在公共云、专有云等多个场景,也会以 K8s 统一调度的方式进行输出,以更好地服务云上的用户,敬请期待!
1793 13
给 K8s 装上大数据调度引擎:伏羲架构升级 K8s 统一调度
|
7月前
|
消息中间件 Kubernetes Serverless
Serverless Kubernetes 开发实践:异构资源,按需使用
Kubernetes 作为当今云原生业界标准,具备良好的生态以及跨云厂商能力。Kubernetes 很好的抽象了 IaaS 资源交付标准,使得云资源交付变的越来越简单,与此同时用户期望更多的聚焦于业务自身,做到面向应用交付,Serverless 理念也因此而生。 那么如何通过原生 k8s 提供Serverless 能力?如何实现GPU等异构资源按需使用?这里给大家介绍一下我们在Serverless Kubernetes 开发实践:异构资源,按需使用。
298 0
Serverless Kubernetes 开发实践:异构资源,按需使用
|
Kubernetes Cloud Native 应用服务中间件
Koordinator 最佳实践系列:精细化 CPU 编排
Koordinator 最佳实践系列:精细化 CPU 编排
|
资源调度 调度 开发者
资源调度|学习笔记
快速学习资源调度
资源调度|学习笔记
|
Kubernetes Cloud Native 安全
Koordinator 0.6:企业级容器调度系统解决方案,引入 CPU 精细编排、资源预留与全新的重调度框架
经过社区多位成员的贡献,Koordinator 0.6 版本正式发布。相较于上一个版本 0.5,新版本进一步完善了 CPU 精细化编排能力,更好的兼容原生用法;支持了资源预留的能力(Reservation),补齐了调度原子语意缺失;发布了全新的重调度框架,支持用户灵活的扩展自定义插件。这些特性源自于阿里巴巴内部的生产实践,并结合上游社区规划思考,为用户带来标准、强大、灵活的调度解决方案。
1081 0
Koordinator 0.6:企业级容器调度系统解决方案,引入 CPU 精细编排、资源预留与全新的重调度框架
|
资源调度 分布式计算 并行计算
分布式调度系统现状|学习笔记
快速学习分布式调度系统现状
分布式调度系统现状|学习笔记