开源工具GPU Sharing:支持Kubernetes集群细粒度

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 问题背景 全球主要的容器集群服务厂商的Kubernetes服务都提供了Nvidia GPU容器调度能力,但是通常都是将一个GPU卡分配给一个容器。这可以实现比较好的隔离性,确保使用GPU的应用不会被其他应用影响;对于深度学习模型训练的场景非常适合,但是如果对于模型开发和模型预测的场景就会比较浪费。

问题背景

全球主要的容器集群服务厂商的Kubernetes服务都提供了Nvidia GPU容器调度能力,但是通常都是将一个GPU卡分配给一个容器。这可以实现比较好的隔离性,确保使用GPU的应用不会被其他应用影响;对于深度学习模型训练的场景非常适合,但是如果对于模型开发和模型预测的场景就会比较浪费。 大家的诉求是能够让更多的预测服务共享同一个GPU卡上,进而提高集群中Nvidia GPU的利用率。而这就需要提供GPU资源的划分,而这里GPU资源划分的维度指的就是GPU显存和Cuda Kernel线程的划分。通常在集群级别谈支持共享GPU,通常是两件事情:

1.调度
2.隔离,我们这里主要讨论的是调度,隔离的方案未来会基于Nvidia的MPS来实现。

而对于细粒度的GPU卡调度,目前Kubernetes社区并没有很好的方案,这是由于Kubernetes对于GPU这类扩展资源的定义仅仅支持整数粒度的加加减减,无法支持复杂资源的分配。比如用户希望使用Pod A占用半张GPU卡,这在目前Kubernetes的架构设计中无法实现资源分配的记录和调用。这里挑战是多卡GPU共享是实际矢量资源问题,而Extened Resource是标量资源的描述。

针对此问题,我们设计了一个outoftree的共享GPU调度方案,该方案依赖于Kubernetes的现有工作机制:

  • Extended Resource定义
  • Scheduler Extender机制
  • Device Plugin机制

用户场景

  • 作为集群管理员,我想提高集群的GPU使用率;在开发过程中,多个用户共享模型开发环境
  • 作为应用开发人员,我希望能够同时在Volta GPU上运行多个推理任务

目标

  • 能够让使用者通过API描述对于一个可共享资源的申请, 并能实现该种资源的调度

非目标

  • 不支持该共享资源的隔离
  • 不支持超卖

设计原则

  1. 明确问题简化设计,第一步只负责调度和部署,后续再实现运行时显存管控。
    有很多的客户明确的诉求是首先可以支持多AI应用可以调度到同一个GPU上,他们可以接受从应用级别控制显存的大小,利用类似gpu_options.per_process_gpu_memory_fraction控制应用的显存使用量。那我们要解决的问题就先简化到以显存为调度标尺,并且把显存使用的大小以参数的方式传递给容器内部。
  2. 不做侵入式修改
    本设计中不会修改Kubernetes核心的Extended Resource的设计, Scheduler的实现,Device Plugin的机制以及Kubelet的相关设计。重用Extended Resource描述共享资源的申请API。这样的好处在于提供一个可以移植的方案,用户可以在原生Kubernetes上使用这个方案。
  3. 按显存和按卡调度的方式可以在集群内并存,但是同一个节点内是互斥的,不支持二者并存;要么是按卡数目,要么是按显存分配。

详细设计

前提:

  1. 依旧延用Kubernetes Extended Resource定义,但是衡量维度最小单位从1个GPU卡变为GPU显存的MiB。如果所节点使用的GPU为单卡16GiB显存,它对应的资源就是16276MiB
  2. 由于用户对于共享GPU的诉求在于模型开发和模型预测场景,在此场景下,用户申请的GPU资源上限不会超过一张卡,也就是申请的资源上限为单卡

而我们的工作首先是定义了两个新的Extended Resource: 第一个是gpu-mem, 对应的是GPU显存;第二个是gpu-count,对应的是GPU卡数。 通过两个标量资源描述矢量资源, 并且结合这一资源,提供支持共享GPU的工作机制。下面是基本的架构图:

arch.jpg

核心功能模块:

  • GPU Share Scheduler Extender: 利用Kubernetes的调度器扩展机制,负责在全局调度器Filter和Bind的时候判断节点上单个GPU卡是否能够提供足够的GPU Mem,并且在Bind的时刻将GPU的分配结果通过annotation记录到Pod Spec以供后续Filter检查分配结果。
  • GPU Share Device Plugin: 利用Device Plugin机制,在节点上被Kubelet调用负责GPU卡的分配,依赖scheduler Extender分配结果执行。

具体流程:

1. 资源上报

GPU Share Device Plugin利用nvml库查询到GPU卡的数量和每张GPU卡的显存, 通过ListAndWatch()将节点的GPU总显存(数量 显存)作为另外Extended Resource汇报给Kubelet; Kubelet进一步汇报给Kubernetes API Server。 举例说明,如果节点含有两块GPU卡,并且每块卡包含16276MiB,从用户的角度来看:该节点的GPU资源为16276 2 = 32552; 同时也会将节点上的GPU卡数量2作为另外一个Extended Resource上报。

2. 扩展调度

GPU Share Scheduler Extender可以在分配gpu-mem给Pod的同时将分配信息以annotation的形式保留在Pod spec中,并且在过滤时刻根据此信息判断每张卡是否包含足够可用的gpu-mem分配。

2.1 Kubernetes默认调度器在进行完所有过滤(filter)行为后会通过http方式调用GPU Share Scheduler Extender的filter方法, 这是由于默认调度器计算Extended Resource时,只能判断资源总量是否有满足需求的空闲资源,无法具体判断单张卡上是否满足需求;所以就需要由GPU Share Scheduler Extender检查单张卡上是否含有可用资源。

以下图为例, 在由3个包含两块GPU卡的节点组成的Kubernetes集群中,当用户申请gpu-mem=8138时,默认调度器会扫描所有节点,发现N1所剩的资源为 (16276 * 2 - 16276 -12207 = 4069 )不满足资源需求,N1节点被过滤掉。
而N2和N3节点所剩资源都为8138MiB,从整体调度的角度看,都符合默认调度器的条件;此时默认调度器会委托GPU Share Scheduler Extender进行二次过滤,在二次过滤中,GPU Share Scheduler Extender需要判断单张卡是否满足调度需求,在查看N2节点时发现该节点虽然有8138MiB可用资源,但是落到每张卡上看,GPU0和分别GPU1只有4069MiB的可用资源,无法满足单卡8138MiB的诉求。而N3节点虽然也是总共有8138MiB可用资源,但是这些可用资源都属于GPU0,满足单卡可调度的需求。由此,通过GPU Share Scheduler Extender的筛选就可以实现精准的条件筛选。

filter.jpg

2.2 当调度器找到满足条件的节点,就会委托GPU Share Scheduler Extender的bind方法进行节点和Pod的绑定,这里Extender需要做的是两件事情

  • 以binpack的规则找到节点中最优选择的GPU卡id,此处的最优含义是对于同一个节点不同的GPU卡,以binpack的原则作为判断条件,优先选择空闲资源满足条件但同时又是所剩资源最少的GPU卡,并且将其作为ALIYUN_COM_GPU_MEM_IDX保存到Pod的annotation中;同时也保存该Pod申请的GPU Memory作为ALIYUN_COM_GPU_MEM_PODALIYUN_COM_GPU_MEM_ASSUME_TIME保存至Pod的annotation中,并且在此时进行Pod和所选节点的绑定。

注意:这时还会保存ALIYUN_COM_GPU_MEM_ASSIGNED的Pod annotation,它被初始化为“false”。它表示该Pod在调度时刻被指定到了某块GPU卡,但是并没有真正在节点上创建该Pod。ALIYUN_COM_GPU_MEM_ASSUME_TIME代表了指定时间。

如果此时发现分配节点上没有GPU资源符合条件,此时不进行绑定,直接不报错退出,默认调度器会在assume超时后重新调度。

  • 调用Kubernetes API执行节点和Pod的绑定

以下图为例,当GPU Share Scheduler Extender要把gpu-mem:8138的Pod和经过筛选出来的节点N1绑定,首先会比较不同GPU的可用资源,分别为GPU0(12207),GPU1(8138),GPU2(4069),GPU3(16276),其中GPU2所剩资源不满足需求,被舍弃掉;而另外三个满足条件的GPU中, GPU1恰恰是符合空闲资源满足条件但同时又是所剩资源最少的GPU卡,因此GPU1被选出。

bind.jpg

3. 节点上运行

当Pod和节点绑定的事件被Kubelet接收到后,Kubelet就会在节点上创建真正的Pod实体,在这个过程中, Kubelet会调用GPU Share Device Plugin的Allocate方法, Allocate方法的参数是Pod申请的gpu-mem。而在Allocate方法中,会根据GPU Share Scheduler Extender的调度决策运行对应的Pod

3.1 会列出该节点中所有状态为Pending并且ALIYUN_COM_GPU_MEM_ASSIGNEDfalse的GPU Share Pod

3.2 选择出其中Pod Annotation的ALIYUN_COM_GPU_MEM_POD的数量与Allocate申请数量一致的Pod。如果有多个符合这种条件的Pod,就会选择其中ALIYUN_COM_GPU_MEM_ASSUME_TIME最早的Pod。

3.3 将该Pod的annotation ALIYUN_COM_GPU_MEM_ASSIGNED设置为true,并且将Pod annotation中的GPU信息转化为环境变量返回给Kubelet用以真正的创建Pod。

sequence.jpg

相关项目

目前项目已经开源到github.com上

gpushare-scheduler-extender

gpushare-device-plugin

部署

请参照部署文档

测试样例

1. 首先创建一个使用aliyun.com/gpu-mem的应用

apiVersion: apps/v1
kind: Deployment

metadata:
  name: binpack-1
  labels:
    app: binpack-1

spec:
  replicas: 1

  selector: # define how the deployment finds the pods it manages
    matchLabels:
      app: binpack-1

  template: # define the pods specifications
    metadata:
      labels:
        app: binpack-1

    spec:
      containers:
      - name: binpack-1
        image: cheyang/gpu-player:v2
        resources:
          limits:
            # GiB
            aliyun.com/gpu-mem: 2

使用

请参照使用文档

构建

请参照如何构建

视频Demo

Demo 1: 部署多个GPU Share的Pod,发现他们以binpack的方式被放置到同一个GPU卡上



Demo 2: 避免错误调度申请资源超过单个GPU可用资源的Pod



Roadmap

  • 利用nvidia MPS实现隔离
  • 支持该方案可以在由kubeadm初始化的Kubernetes集群自动化部署
  • Scheduler Extener的高可用性
  • 为GPU, RDMA 和弹性网卡提供通用方案
相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
2月前
|
弹性计算 人工智能 Serverless
阿里云ACK One:注册集群云上节点池(CPU/GPU)自动弹性伸缩,助力企业业务高效扩展
在当今数字化时代,企业业务的快速增长对IT基础设施提出了更高要求。然而,传统IDC数据中心却在业务存在扩容慢、缩容难等问题。为此,阿里云推出ACK One注册集群架构,通过云上节点池(CPU/GPU)自动弹性伸缩等特性,为企业带来全新突破。
|
2天前
|
人工智能 文字识别 异构计算
NVIDIA-Ingest:英伟达开源智能文档提取及结构化工具,支持 GPU 加速和并行处理
NVIDIA-Ingest 是英伟达开源的智能文档提取工具,支持 PDF、Word、PPT 等多种格式,提供并行处理和 GPU 加速,适用于企业内容管理和生成式应用。
43 18
NVIDIA-Ingest:英伟达开源智能文档提取及结构化工具,支持 GPU 加速和并行处理
|
1天前
|
存储 运维 Kubernetes
正式开源,Doris Operator 支持高效 Kubernetes 容器化部署方案
飞轮科技推出了 Doris 的 Kubernetes Operator 开源项目(简称:Doris Operator),并捐赠给 Apache 基金会。该工具集成了原生 Kubernetes 资源的复杂管理能力,并融合了 Doris 组件间的分布式协同、用户集群形态的按需定制等经验,为用户提供了一个更简洁、高效、易用的容器化部署方案。
正式开源,Doris Operator 支持高效 Kubernetes 容器化部署方案
|
9天前
|
缓存 容灾 网络协议
ACK One多集群网关:实现高效容灾方案
ACK One多集群网关可以帮助您快速构建同城跨AZ多活容灾系统、混合云同城跨AZ多活容灾系统,以及异地容灾系统。
|
20天前
|
Kubernetes Ubuntu 网络安全
ubuntu使用kubeadm搭建k8s集群
通过以上步骤,您可以在 Ubuntu 系统上使用 kubeadm 成功搭建一个 Kubernetes 集群。本文详细介绍了从环境准备、安装 Kubernetes 组件、初始化集群到管理和使用集群的完整过程,希望对您有所帮助。在实际应用中,您可以根据具体需求调整配置,进一步优化集群性能和安全性。
83 12
|
22天前
|
Prometheus Kubernetes 监控
OpenAI故障复盘 - 阿里云容器服务与可观测产品如何保障大规模K8s集群稳定性
聚焦近日OpenAI的大规模K8s集群故障,介绍阿里云容器服务与可观测团队在大规模K8s场景下我们的建设与沉淀。以及分享对类似故障问题的应对方案:包括在K8s和Prometheus的高可用架构设计方面、事前事后的稳定性保障体系方面。
|
24天前
|
Kubernetes 网络协议 应用服务中间件
Kubernetes Ingress:灵活的集群外部网络访问的利器
《Kubernetes Ingress:集群外部访问的利器-打造灵活的集群网络》介绍了如何通过Ingress实现Kubernetes集群的外部访问。前提条件是已拥有Kubernetes集群并安装了kubectl工具。文章详细讲解了Ingress的基本组成(Ingress Controller和资源对象),选择合适的版本,以及具体的安装步骤,如下载配置文件、部署Nginx Ingress Controller等。此外,还提供了常见问题的解决方案,例如镜像下载失败的应对措施。最后,通过部署示例应用展示了Ingress的实际使用方法。
54 2
|
1月前
|
存储 Kubernetes 关系型数据库
阿里云ACK备份中心,K8s集群业务应用数据的一站式灾备方案
本文源自2024云栖大会苏雅诗的演讲,探讨了K8s集群业务为何需要灾备及其重要性。文中强调了集群与业务高可用配置对稳定性的重要性,并指出人为误操作等风险,建议实施周期性和特定情况下的灾备措施。针对容器化业务,提出了灾备的新特性与需求,包括工作负载为核心、云资源信息的备份,以及有状态应用的数据保护。介绍了ACK推出的备份中心解决方案,支持命名空间、标签、资源类型等维度的备份,并具备存储卷数据保护功能,能够满足GitOps流程企业的特定需求。此外,还详细描述了备份中心的使用流程、控制台展示、灾备难点及解决方案等内容,展示了备份中心如何有效应对K8s集群资源和存储卷数据的灾备挑战。
|
2月前
|
Kubernetes 监控 Cloud Native
Kubernetes集群的高可用性与伸缩性实践
Kubernetes集群的高可用性与伸缩性实践
86 1
|
2月前
|
Ubuntu 网络安全 容器
KubeSphere 是一个开源的容器平台,提供丰富的功能和便捷的操作界面,适用于企业容器化部署和管理
KubeSphere 是一个开源的容器平台,提供丰富的功能和便捷的操作界面,适用于企业容器化部署和管理。本文详细介绍了如何在 Ubuntu 22.04 上安装 KubeSphere,包括系统要求、安装依赖项、设置防火墙、下载安装脚本、选择安装选项、验证安装结果等步骤,并提供了常见问题的解决方法。希望本文能为读者提供实用的参考和帮助。
53 3

相关产品

  • 容器服务Kubernetes版