助力深度學習!阿里開源可插拔 GPU 共享調度工具

根據 Gartner 對全球 CIO 的調查結果顯示,人工智能將成爲 2019 年組織革命的顛覆性力量。對於人工智能來講,算力即正義,成本即能力,利用 Docker 和 Kubernetes 表明雲原生技術爲 AI 提供了一種新的工做模式,將 GPU 機器放到統一的資源池進行調度和管理,這避免了GPU 資源利用率低下和人工管理的成本。所以,全球主要的容器集羣服務廠商 Kubernetes 都提供了 Nvidia GPU 容器集羣調度能力,可是一般都是將一個 GPU 卡分配給一個容器。這雖然能夠實現比較好的隔離性,確保使用 GPU 的應用不會被其餘應用影響;對於深度學習模型訓練的場景也很是適合,可是,針對模型開發和模型預測的場景仍是會顯得比較浪費。基於此,你們有了共享 GPU 的集羣調度需求。git

Kubernetes 共享 GPU 集羣調度

共享 GPU 的集羣調度就是可以讓更多的模型開發和預測服務共享同一個 GPU 卡,進而提升集羣中 Nvidia GPU 的利用率。而這就須要提供 GPU 資源的劃分,而這裏 GPU 資源劃分的維度指的就是 GPU 顯存和 Cuda Kernel 線程的劃分。一般在集羣級別談支持共享 GPU 是如下兩件事情:
1.調度
2.隔離,咱們這裏主要討論的是調度,隔離的方案目前須要用戶經過應用限制(好比使用 Tensorflow 的per_process_gpu_memory_fraction 來控制),將來會提供基於 Nvidia 的 MPS 的可選項, 也會考慮 GPU 的方案。

而對於細粒度的 GPU 卡調度,目前 Kubernetes 社區並無很好的方案,這是因爲 Kubernetes 對於 GPU 這類擴展資源的定義僅僅支持整數粒度的加加減減,沒法支持複雜資源的分配。好比用戶但願使用 Pod A 佔用半張 GPU卡,這在目前 Kubernetes 的架構設計中沒法實現資源分配的記錄和調用。這裏挑戰是多卡 GPU 共享是實際矢量資源問題,而 Extened Resource 是標量資源的描述。


針對此問題,咱們設計了一個 Out Of Tree 的共享 GPU 調度方案,該方案依賴於 Kubernetes 的現有的工做機制:github

  • Extended Resource 定義
  • Scheduler Extender 機制
  • Device Plugin 機制
  • Kubectl 的擴展機制

這個 GPU 共享調度擴展的好處是:利用 Kubernetes 的擴展和插件機制實現,對於 API Server,Scheduler,Controller Manager 以及 Kubelet 等核心組件沒有侵入性。這就方便了使用者能夠在不一樣 Kubernetes 版本上應用這個方案,無需 rebase 代碼和從新構建 Kubernetes 二進制包。api

用戶場景架構

  • 集羣管理員:「我想提升集羣的 GPU 使用率;在開發過程當中,多個用戶共享模型開發環境。」
  • 應用開發人員:「我但願可以同時在 Volta GPU 上運行多個推理任務。」

[](https://www.aatech.org/articles/132268#2)目標app

  • 可以讓使用者經過 API 描述對於一個可共享資源的申請, 並能實現該種資源的調度

[](https://www.atatech.org/articles/132268#3)非目標

  • 不支持該共享資源的隔離
  • 不支持超賣

[](https://www.atatech.org/articles/132268#4)設計原則

  1. 明確問題簡化設計,第一步只負責調度和部署,後續再實現運行時顯存管控。
    有不少的客戶明確的訴求是首先能夠支持多AI應用能夠調度到同一個 GPU 上,他們能夠接受從應用級別控制顯存的大小,利用相似gpu_options.per_process_gpu_memory_fraction控制應用的顯存使用量。那咱們要解決的問題就先簡化到以顯存爲調度標尺,而且把顯存使用的大小以參數的方式傳遞給容器內部。
  2. 不作侵入式修改
    本設計中不會修改 Kubernetes 核心的 Extended Resource 的設計, Scheduler 的實現,Device Plugin 的機制以及 Kubelet 的相關設計。重用 Extended Resource 描述共享資源的申請 API。這樣的好處在於提供一個能夠移植的方案,用戶能夠在原生 Kubernetes 上使用這個方案。
  3. 按顯存和按卡調度的方式能夠在集羣內並存,可是同一個節點內是互斥的,不支持兩者並存;要麼是按卡數目,要麼是按顯存分配。

詳細設計ide

[](https://www.atatech.org/articles/132268#6)前提:

  1. 依舊延用 Kubernetes Extended Resource 定義,可是衡量維度最小單位從 1 個 GPU 卡變爲 GPU 顯存的 MiB。若是所節點使用的 GPU 爲單卡 16GiB 顯存,它對應的資源就是 16276MiB;
  2. 因爲用戶對於共享GPU的訴求在於模型開發和模型預測場景,在此場景下,用戶申請的GPU資源上限不會超過一張卡,也就是申請的資源上限爲單卡。


而咱們的工做首先是定義了兩個新的 Extended Resource: 第一個是 gpu-mem, 對應的是 GPU 顯存;第二個是 gpu-count,對應的是 GPU 卡數。 經過兩個標量資源描述矢量資源, 而且結合這一資源,提供支持共享 GPU 的工做機制。下面是基本的架構圖:學習

[](https://www.atatech.org/articles/132268#7)核心功能模塊:

  • 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 分配結果執行。

[](https://www.atatech.org/articles/132268#8)具體流程:

  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 的篩選就能夠實現精準的條件篩選。測試

 


2.2 當調度器找到知足條件的節點,就會委託 GPU Share Scheduler Extender 的 bind 方法進行節點和 Pod 的綁定,這裏 Extender 須要作的是兩件事情ui

  • 以 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 被選出。


3. 節點上運行
當 Pod 和節點綁定的事件被 Kubelet 接收到後,Kubelet 就會在節點上建立真正的 Pod 實體,在這個過程當中, Kubelet 會調用 GPU Share Device Plugin 的Allocate方法, Allocate方法的參數是 Pod 申請的 gpu-mem。而在Allocate方法中,會根據 GPU Share Scheduler Extender 的調度決策運行對應的 Pod

  • 會列出該節點中全部狀態爲 Pending 而且ALIYUN_COM_GPU_MEM_ASSIGNEDfalse的 GPU Share Pod
  • 選擇出其中 Pod Annotation 的ALIYUN_COM_GPU_MEM_POD的數量與 Allocate 申請數量一致的 Pod。若是有多個符合這種條件的 Pod,就會選擇其中ALIYUN_COM_GPU_MEM_ASSUME_TIME最先的 Pod。
  • 將該 Pod 的 annotation ALIYUN_COM_GPU_MEM_ASSIGNED設置爲true,而且將 Pod annotation 中的 GPU 信息轉化爲環境變量返回給 Kubelet 用以真正的建立 Pod。

 

[](https://www.atatech.org/articles/132268#9)相關項目

目前項目已經開源到 http://github.com 上
gpushare-scheduler-extender
gpushare-device-plugin

部署

請參照部署文檔

[](https://www.atatech.org/articles/132268#11)測試樣例

  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:
            # MiB
            aliyun.com/gpu-mem: 1024

使用

請參照使用文檔

構建

請參照如何構建

視頻 Demo

[](https://www.atatech.org/articles/132268#15)Demo 1: 部署多個 GPU Share 的 Pod,發現他們以 binpack 的方式被放置到同一個 GPU 卡上

[](https://www.atatech.org/articles/132268#16)Demo 2: 避免錯誤調度申請資源超過單個 GPU 可用資源的 Pod

Roamap

  • 在 Device Plugin 中提供 Nvidia MPS 的可選支持;
  • 支持該方案能夠在由 kubeadm 初始化的 Kubernetes 集羣自動化部署;
  • 提高 Scheduler Extener 的高可用性;
  • 爲 GPU, RDMA 和彈性網卡提供通用方案。

 

原文連接

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索