Kubernetes 运行 HPC 工作负载的挑战

任何使用过 Docker 的人都会体会到容器所带来的巨大效率提升。虽然 Kubernetes 擅长编排容器,但在 Kubernetes 上部署高性能计算 (HPC) 应用程序可能很棘手

在 Kubernetes 中,调度的基本单位是 Pod:调度到集群主机的是一个或多个容器。虽然 Kubernetes 具有Cron 作业和一次性作业的概念,但部署在 Kubernetes 上的应用程序通常是长时间运行的服务,例如 Web 服务器、负载均衡器或数据存储,虽然它们随着 Pod 的进出而高度动态,这些与 HPC 应用程序模式存在很大差异。

传统的 HPC 应用程序通常表现出不同的特征

  • 金融或工程模拟中,一项作业可能由数以万计的短期运行任务组成,需要低延迟和高吞吐量的调度才能在可接受的时间内完成模拟。
  • 计算流体动力学 (CFD) 问题可以使用消息传递库来同步状态,在数百甚至数千个节点上并行执行。这需要专门的调度和作业管理功能来分配和启动此类作业,然后对它们进行检查点、暂停/恢复或回填
  • 其他 HPC 工作负载可能需要 GPU 等专用资源需要访问有限的软件许可证。组织需要围绕哪些类型的资源可以由谁使用来执行政策,以确保项目资源充足并按时完成。

容器和 HPC 之间的界限变得模糊

HPC 用户认为容器很有价值,原因与其他组织相同。将逻辑封装在容器中以使其可移植、不受环境依赖性影响并且易于与其他容器交换,这显然具有价值。然而,转向容器可能很困难。

**HPC 工作负载通常在命令行级别集成。**作业不需要编码,而是通过命令行作为二进制文件或 shell 脚本提交到队列。HPC 站点使用的数百个工程、科学和分析应用程序均采用这种方法,并与流行的工作负载调度程序进行了成熟且经过认证的集成。

虽然将工作负载打包到 Docker 容器中、将其发布到镜像仓库以及提交工作负载的 YAML 描述的概念对于 Kubernetes 用户来说是天然的,但对于大多数 HPC 用户来说却很陌生。在 R、MATLAB 或 Stata 中运行模型的分析师只是希望快速提交仿真、监控其执行情况并尽快获得结果。

HPC 与云原生的融合

为了应对迁移到容器的挑战,运行容器和 HPC 工作负载的组织有多种选择:

方式1:维护独立的基础设施

在集群环境中同时运行 Slurm 和 Kubernetes

  • 有可能需要额外的管理工具来管理节点资源, 管理⼯具需要处理环境之间的资源分配
  • Slurm 和 Kubernetes 都不知道对⽅环境的当前资源和需求

对于在 HPC 方面投入大量资金的组织来说,这可能是首选方法。

在单独的集群上部署新的容器化应用程序并保留 HPC 环境,而不是破坏现有环境,可能会更容易。挑战在于,这是以孤立集群为代价的,增加了基础设施和管理成本。

方案2:在现有 HPC 工作负载管理器下运行容器化工作负载

对于运行传统 HPC 工作负载的站点,另一种方法是使用现有的作业提交机制来启动作业,进而在一个或多个目标主机上实例化 Docker 容器。使用这种方法的站点可以引入容器化工作负载,同时将对环境的干扰降至最低。

Univa Grid Engine Container EditionIBM Spectrum LSF等领先的 HPC 工作负载管理器有对 Docker 容器的本机支持。ShifterSingularity也有支持此类部署的重要开源工具。虽然对于具有简单需求并希望坚持使用 HPC 调度程序的组织来说,这是一个很好的解决方案,但它们将无法访问本机 Kubernetes 功能,这可能会限制 Kubernetes 擅长管理长期运行服务的灵活性。

方案3:在 Kubernetes 环境中运⾏ Slurm 集群

  • Slurm组件通过Kubernetes Pod形态部署,提供Slurm 用户的传统体验
  • 同时允许增强用户之间的隔离,允许更高的吞吐量,并为这些⼯作负载提供全面的 MPI ⽀持
  • 借助自动缩放(通过 Kubernetes Operator 实现) - 可用于将资源移⼊/移出 Slurm 的控制
    • Slurm 22.05+ 中的动态节点功能使这⼀切变得简单
    • 这里的自动缩放也比现有的基于节能的 Slurm 云爆发模型更加细致

也存在一些不足:

  • Kubernetes 工作负载在 Slurm 之外运⾏
  • Slurm 和 Kubernetes ⼯作负载之间的优先级很难确定,Kubernetes 调度不足依然存在

方案4:Slurm管理所有集群资源

Kubernetes 集群是在 Slurm 批处理作业中临时创建的

  • 在作业启动之前,Kubernetes 控制平面不可用,或者需要把控制平面托管在传统集群之外
  • 除了测试/开发环境之外,不是特别有用

image-20231119143055095

两全其美:将 Slurm 集成为 Kubernetes 调度程序

从上文我们知道,在 HPC 和 AI 领域,有两个王者:用于调度和编排大规模并行作业的 Slurm 和用于运行训练/调优/推理等应用程序的 Kubernetes。

Kubernetes 和 Slurm对比

KubernetesSlurm
作业形态1. Kubernetes 旨在管理长时间运行的业务,如:编排微服务;
2. 管理服务的可用性和冗余;
3. 服务默认是容器化的
4. Kubernetes 对批处理⼯作流程的⽀持有限(需要借助Volcano,MPI Operator)
1. Slurm 管理许多相互交织的 HPC 系统管理任务
2. 作业通常是临时脚本,通过命令行提交(Slurm 的 RESTful API 等较新的功能可以支持更多的编程交互,但尚未得到广泛采用);支持容器化作业
3. 控制用户对计算资源的访问(cgroups、pam_slurm_adopt)
4. 支持大规模并发作业启动(MPI、PMIx、nss_slurm、sbcast)
系统资源云原生系统假设**“无穷资源”**可用,工作负载有限采用HPC 系统假设系统规模为固定的,⼯作负载是无穷
任务调度1. 优先级不是云编排的核心
2. 调度API粒度固定在节点级别
3. 调度语义反映云工作负载需求,而不是 HPC,例如,亲和⼒反亲和⼒调度策略Anti-Affinity 用于确保 Pod 实例不共享节点,对于构建冗余系统⾄关重要,但不能转化为传统的 HPC 批量调度
1. Slurm 是⼀个策略引擎
2. 作业排队和优先级

然而,许多公司面临着在它们之间做出选择或需要管理两个独立的集群。

Slurm 工作负载管理器(通常称为 Slurm)由SchedMD开发,是许多 HPC 工作负载事实上的调度程序,被世界各地的领先研究人员、学者和 AI 公司所使用。但是,它是为具有有限生命周期的批处理作业而设计的。

另一方面,Kubernetes 是为长时间运行的工作负载(例如推理)而构建的。因此,用于批量工作负载的 Kubernetes 调度程序不像 Slurm 那样流行,并且缺乏一些功能。其他基于 Kubernetes 的调度程序,例如 Volcano 和 YuniKorn,旨在为 Kubernetes 提供类似 Slurm 的调度功能,但在试图颠覆 Slurm 的庞大知识库方面面临着一场艰苦的战斗

一些公司试图通过同时运行 Slurm 和 Kubernetes 来弥补这一差距**,**但对于当今的公司来说,利用这两者是一个重大挑战。

每个集群都运行自己的计算池来管理, 虽然这有助于公司能够通过 Slurm 或 Kubernetes 分离工作负载,但资源管理是完全独立的。因此,两者之间的任何沟通或协作都需要手动工作,这可能需要大量的时间和精力。

两个流行的平台。两个独立的计算池。需要管理和拥有两个独立的解决方案。

为了真正结合这两种解决方案的优势,CoreWeave 一直致力于在 Kubernetes 上实现 Slurm,以有效同步这两种解决方案。

SUNK 简介(Slurm on Kubernetes)

SUNK 是一个开源项目(将于 2024 年初发布),它将 Kubernetes 容器化部署和 GitOps 引入 Slurm,并将 Slurm 调度程序插件集成到 Kubernetes。

本质上,SUNK 将 Slurm 集成为 Kubernetes 调度程序,并允许 Slurm 作业在 Kubernetes 内运行。这创造了更加无缝的体验,在同一中央平台上支持爆发式和批量工作负载,并允许开发人员利用 Kubernetes 上的 SLURM 资源管理。

单独管理 Slurm 和 Kubernetes 可以降低整体复杂性,但也大大降低了你选择在所有计算上运行的工作负载类型的灵活性。也就是说,最大限度地利用 GPU 资源变得更加困难。

通过在 Kubernetes (SUNK) 之上部署 Slurm 集群,在同一计算池之上,你可以灵活地从 Kubernetes 或 Slurm 端无缝使用该计算。

**两种解决方案。一个平台。一个计算池。⼀个编排层来统治它们。 **

SUNK 简介

为什么创建SUNK ?

答案很简单:客户效率。当你运行非常大且昂贵的 HPC 集群时,接近 100% 的利用率非常重要。任何时候,当你可能不使用你所付费的计算时,成本都可能非常高。

SUNK 完全构建在 Kubernetes 之上,每个客户端都对其集群有一个单一的入口和管理点。但是,我们意识到许多喜欢 Slurm 的客户会单独管理它,或者询问我们是否有 Slurm 集成。

SUNK 的核心是效率——这就是专为 GPU 密集型用例而构建的原因。

我们希望使客户能够利用 Slurm 的优势,同时保持系统的完整性和易用性(也称为无需管理单独的集群)。由于该解决方案不存在,我们决定构建它。

SUNK 的特点

SUNK 特点
**配置和部署:**通过将 Slurm 集群部署为各种 Kubernetes 资源,我们能够使用高度可定制的 Helm Chart 来部署它。这解锁了基于 Kubernetes 的 GitOps 工作流程的大型生态系统及其附带的所有功能。其他好处包括:

  • 轻松跟踪和配置prolog 和epilog脚本
  • 快速部署测试集群
  • 支持 s6 脚本和服务
  • 可配置的身份验证方案,包括通过配套的 OpenLdap helm chart或第三方解决方案(Authentik、GAuth 等)

‍ **Kubernetes集成:**部署 SUNK 后,你将获得在 Kubernetes 上运行的所有正常好处,例如:

  • 快速调度
  • 集装箱化
  • 控制平面服务的高可用性
  • 动态节点扩展
  • 具有请求和限制的资源管理
  • 通过 PersistentVolumeClaim 资源共享文件系统

它还包括自定义 Slurm Kubernetes 调度程序(用于通过 Slurm 调度程序调度本机 Kubernetes 工作负载),使你能够在 Slurm 作业(突发工作负载)和 Kubernetes 工作负载(无服务器工作负载)之间动态转移单个计算池。

**状态管理:**通过在 Kubernetes 之上运行,你还可以更好地控制状态管理,包括:

  • 在 k8s 和 Slurm 之间具有双向状态同步的动态节点
  • 自动拓扑生成
  • 支持 Pyxis 容器执行
  • GRES 支持和自动识别

SUNK 的工作原理

要了解SUNK如何有效集成Slurm和Kubernetes,我们先来说一下底层结构。

下图展示了SUNK的高层架构图。在更详细地讨论一些重要部分之前,让我们将其逐个分解。

SUNK 的工作原理

SUNK 的工作原理以及节点集。

首先要指出的是,所有这些服务都在 Kubernetes 中容器化。在顶部,你拥有在集群范围内部署的资源,在底部,你拥有为单个 Slurm 集群部署的组件。

**A 部分:**所有典型的 Slurm 组件都部署在一个 pod 内,每个组件都有自己的可配置资源请求。此处未显示,但这还包括用户为了与 Slurm 集群交互而连接的登录节点。一旦连接到这些登录节点,Kubernetes 就会被抽象出来,你将获得正常 Slurm 集群的体验。

B部分:其中许多组件都需要 Slurm 配置。通过将它们部署为 k8s ConfigMap 和 Secret,Slurm 配置文件可以在一个位置进行管理,并安装在需要它们的任何地方。这包括 Slurm 配置、拓扑、prolog /epilog脚本以及数据库密码等敏感信息。

‍ C部分:HPC 集群最重要的方面是计算节点,它们在中间显示为裸机 Kubernetes 节点。slurmd 在上面以 1:1 映射显示的计算 Pod 中运行。计算 Pod 的部署由称为 Nodeset 的 CRD 进行管理,它具有一些特殊功能,我们将在本文后面介绍。

**D 部分:**我们讨论的许多功能都要求 Slurm 和 Kubernetes 端的计算状态保持同步。Slurm 同步器充当两端之间的中间人,通过 Slurm 的 REST API 发送和拉取信息。

**E部分:**一旦同步器从 Slurm 获取状态信息到 Kubernetes,信息需要在许多不同的地方保持一致。集群范围的Operator监视不同的资源并在适当的时候进行更改,无论更改来自 Kubernetes 还是 Slurm。

由于具有同步状态的能力,我们能够使用自定义 Kubernetes 调度程序,该调度程序允许你根据 Slurm 状态将工作负载调度到计算 Kubernetes 节点上。

最后,由于所有这些组件都在 Kubernetes 上运行,我们可以通过 Prometheus 公开指标,这些指标可通过多种不同的方式使用。

节点集、同步器和调度器

为了使这种集成成为可能,我们的团队必须定制 SUNK 的三个方面:节点集、同步器和调度程序。

节点集
Nodeset

首先是 Nodeset(节点集)(如上图和第一个图表的 F 部分所示)。如前所述,这是我们开发的 CRD,它在 Kubernetes 环境上下文中定义了 Slurm 节点。它的定义与Statefulset或Deployment类似,但与节点的一对一映射更类似于Daemonset。

Nodeset 维护着一系列表示 Slurm 中状态的状态字段。这提供了基于 kubernetes 和 Slurm 中的状态来保护 Slurm 节点更新和扩展的机制。

Nodeset pod 运行 slurmd 和 munged,安装在共享配置映射中,用于 Slurm 配置、prolog 和epilog,以及作为 PVC 的共享文件系统卷。

下图显示了大部分状态字段。正如你所看到的,Nodeset pod 可以主动了解有多少可能的节点与关联性匹配、其中有多少节点当前被分配为 Slurm 节点、跟踪准备情况、运行和Drain条件等。

同步器

Nodesets 的许多功能都依赖于 Kubernetes 端了解 Slurm 的状态,反之亦然。Syncer 通过两部分完成此任务:Slurm 客户端和 pod 控制器。

Slurm 客户端与 Slurm REST API 通信以推送和拉取信息。随着 Slurm 集群的规模增长到数百甚至数千个节点,这种通信将增长到大量流量。客户端有效地缓存结果以处理大规模集群。

Pod 控制器接收来自客户端的事件,并根据任何差异来协调 Pod 状态。如果更改来自 Kubernetes 端,pod 控制器会将事件推送到客户端,客户端会根据需要将该更改传递给 Slurm。

因此,信息流有两个方向。

举例来说,Slurm 中的一个节点进入Drain状态。Syncer 将检测状态更改并在 pod 上添加注释。此注释可以在 Kubernetes 端进行操作,例如仅当 Slurm 节点处于Drain状态时才开始更新。

另一方面,改变可以从 Kubernetes 端开始。假设你检测到硬件问题并封锁 Kubernetes 内部的一个节点。Syncer 会将相应的 Slurm 节点放入Drain中,并添加一个很好的理由来解释为什么它被驱逐。

同步器

调度程序

现在真正的魔力是:调度程序是在命名空间中运行的另一个服务,它允许一些非常有趣的功能。

假设你想要在 SUNK上获取一个预留实例池,并确保除了从按需池中使用的实例之外,你还能获得这些实例的最大利用率。又名:同时运行推理和训练。

当在 k8s 中使用此 Scheduler 调度 pod 时,就像由 Knative 等无服务器解决方案驱动的推理 pod 一样,会创建一个 Slurm 作业,该作业能够抢占低优先级任务。当 Slurm 节点最终分配给 Slurm 中的作业时,调度程序会将 k8s pod 放置到该节点上并在那里运行。

简而言之,你可以无缝地使用 Kubernetes 中 Slurm 集群的计算,而无需主动维护两个独立的预留计算池(这些池不是动态分配的)。

调度程序
公司可以利用 SUNK 实现两全其美,SUNK 即将集成 Slurm 作为 Kubernetes 调度程序。通过允许 Slurm 作业在 Kubernetes 内运行(在一些自定义功能的帮助下),SUNK 使你能够使用 Slurm,同时保持 Kubernetes 的灵活性和性能优势。

参考链接

  1. Kubernetes 遇见高性能计算:https://kubernetes.io/blog/2017/08/kubernetes-meets-high-performance/
  2. SUNK 是 Kubernetes 上 Slurm 的实现:https://www.coreweave.com/blog/sunk-slurm-on-kubernetes-implementations?utm_content=271747701&utm_medium=social&utm_source=linkedin&hss_channel=lcp-36121341
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐