關於我們
更多關於雲原生的案例和知識,可關注同名【騰訊雲原生】公眾號~
福利:
①公眾號后台回復【手冊】,可獲得《騰訊雲原生路線圖手冊》&《騰訊雲原生最佳實踐》~
②公眾號后台回復【系列】,可獲得《15個系列100+篇超實用雲原生原創干貨合集》,包含Kubernetes 降本增效、K8s 性能優化實踐、最佳實踐等系列。
③公眾號后台回復【白皮書】,可獲得《騰訊雲容器安全白皮書》&《降本之源-雲原生成本管理白皮書v1.0》
④公眾號后台回復【光速入門】,可獲得騰訊雲專家5萬字精華教程,光速入門Prometheus和Grafana。
作者
徐蓓,騰訊雲容器技術專家,騰訊雲異構計算容器負責人,多年雲計算一線架構設計與研發經驗,長期深耕 Kubernetes、在離線混部與 GPU 容器化領域,Kubernetes KEP Memory QoS 作者,Kubernetes 積極貢獻者。
當前存在問題
GPU 具備大量核心和高速內存,擅長並行計算,非常適合訓練和運行機器學習模型。由於近幾年 AI 技術愈發成熟,落地場景越來越多,對 GPU 的需求呈井噴趨勢。而在資源管理調度平台上,Kubernetes 已成為事實標准。所以很多客戶選擇在 Kubernetes 中使用 GPU 運行 AI 計算任務。
Kubernetes 提供 device plugin 機制,可以讓節點發現和上報設備資源,供 Pod 使用。GPU 資源也是通過該方式提供。以 nvidia GPU 為例,用戶在 Kubernetes 節點部署 nvidia-device-plugin,插件掃描節點 GPU 卡,會以 extended resource 機制將 GPU 資源以類似nvidia.com/gpu: 8
的形式注冊到節點中。用戶創建 Pod 時指定該資源名,經過調度器調度后,Pod 綁定到節點,最終通過 nvidia docker 提供的一系列工具,將所需 GPU 設備掛載到容器里。
Kubernetes device plugin 提供了一種便捷的方式用於集成第三方設備。但應用在 GPU 場景,還是存在以下不足:
-
集群 GPU 資源缺少全局視角。沒有直觀方式可獲取集群層面 GPU 信息,比如 Pod / 容器與 GPU 卡綁定關系、已使用 GPU 卡數 等
-
不能很好支持多 GPU 后端。各種 GPU 技術(nvidia docker、qGPU、vCUDA、gpu share、GPU 池化)均需獨立部署組件,無法統一調度和管理
問題一:缺少 GPU 資源全局視角
現有 Kubernetes 對 GPU 資源的分配調度是通過 extended resource 實現的,它是基於節點上卡數量的加減調度。用戶如果想知道集群中 GPU 卡的分配情況,需要遍歷節點,拿到並計算這些信息。並且由於這個資源是標量的,所以並無法拿到 Pod / 容器 與卡的綁定關系。這些問題在整卡模式下不是那么凸顯,但在細粒度共享模式下,就尤為嚴重了。
由於 GPU 卡相對昂貴,並且某些 AI 負載吃不滿單張 GPU 算力,GPU Sharing 技術應運而生。在 Kubernetes 中,我們會將一些 AI 負載共享同一張 GPU 卡,通過增加業務部署密度,提升 GPU 利用率,從而節省成本。以 TKE qGPU 為例,在 GPU Sharing 方式下,擴展資源從 GPU 卡數量變為百分比的 qGPU Core 與 MB 的 qGPU Memory。也就是說,用戶可通過 qGPU 容器虛擬化技術,申請小於一張卡的 qGPU 虛擬設備。這些設備是在單張物理卡上虛擬出來的,資源之間也是強隔離的。除了 qGPU,vCUDA、gpu share 等技術都支持多個 Pod / 容器 共享同一張 GPU 卡。基於現有 Kubernetes 架構,是無法知道 GPU 卡所包含切片資源(我將之定義為 GPU Core 與 Memory 的組合)的分布情況的。集群資源分布對管理員與用戶都是黑盒。管理員無法知道整個集群 GPU 切片資源的分配情況,用戶也不知道新部署業務有無資源可用。
問題二:無法支持多 GPU 后端
除分配掛載整卡的方式外,TKE qGPU、vCUDA、gpu share、GPU 池化 等 GPU 共享技術越來越被用戶采用。每種方案都有自己獨立的一套 Kubernetes 集成實現方式。比如在 TKE qGPU 中,我們自研了 tke-qgpu-scheduler 用於 GPU 細粒度算力與顯存分配調度,配套的 tke-qgpu-manager,用於節點初始化、注冊上報 qGPU 資源及 qGPU 容器虛擬化。vCUDA、gpu share 也是類似架構,同樣是由調度器 + device plugin 組成。這些方案相互獨立,沒有統一標准,無法共通。這導致用戶在單個集群中很難同時使用多種 GPU 后端技術。比如用戶集群有些業務是在線推理,吃不滿整卡,想申請 TKE qGPU 切片資源。另一部分業務是訓練,需要分配單卡。有些仿真和模型調試業務,為了成本和彈性,想要動態從遠端 GPU 池申請資源。現有方案很難同時滿足以上訴求,這為基於 Kubernetes 構建統一 AI 基礎設施平台增加了很多難度。
以上問題均是 TKE 在基於 Kubernetes 幫助客戶構建 AI 計算平台時遇到的真實困擾。隨着 AI 業務的不斷精進,客戶已不再僅滿足於“能使用 Kubernetes GPU 資源”。對 GPU 成本的關注,對 GPU 資源的整體把控,對 GPU 不同后端的精准使用,都成為了客戶能用好 GPU 算力的前提條件。既然現有體系無法滿足,那我們就需要另辟蹊徑,重新思考 GPU 在 Kubernetes 中的位置。
一種全新的 Kubernetes GPU 方案
PV / PVC 帶來的啟示
在 Kubernetes 中,資源一般是圍繞 Pod 設計和定義。從重要程度上講,集群可用資源包含兩種類型:核心資源和外部資源。核心資源指維持 Pod 正常運行的必不可少的資源,包括 CPU、內存、臨時存儲、網卡 等。這些會通過 kubelet 掃描節點並上報到集群中。另一部分是外部資源,多指外掛存儲和其他設備等,比如數據盤、GPU、FPGA 等。這些設備可能是本地設備掛載、也可能是遠端設備掛載。這類資源的存在可以使得 Pod 更好的運行。比如數據盤增加了 Pod 的存儲容量、GPU / FPGA 加速了 Pod 的計算能力。從這個角度看,存儲與 GPU 有相似之處。
Kubernetes 在存儲上抽象出了一組資源,如 PV / PVC / StorageClass,並為之提供了一組 API 和交互方式,將存儲的供給管理和使用標准化和分離了出來。
- PV:PV 是集群中的一塊實際存儲資源,可以由管理員手動創建,或者通過 StorageClass 方式動態創建。PV 類似節點上的 CPU、內存、網卡 等資源。PV 可以有多種后端供給,如前文描述的公有雲存儲服務、自建共享存儲或本地存儲。
- PVC:PVC 是用戶對 PV 存儲資源的申領。它類似於集群中的 Pod。Pod 申請節點上的 CPU、內存、網絡 等資源,PVC 申請存儲資源,也就是 PV。
- StorageClass:StorageClass 為管理員提供了描述存儲“類”的方法。比如 PV 后端創建的方法、掛載的方法 等。
用戶通過 PV 在指定后端創建實際存儲,用戶通過 PVC 申請已創建的 PV 存儲資源,或者指定 StorageClass 動態從后端創建 PV。
參照 Kubernetes 存儲的設計方式,我們認為 GPU 也可定義和實現類似抽象。
Elastic GPU CRD
我們定義了三種全新的 Kubernetes CRD,用於代表 GPU 資源的不同抽象:
- ElasticGPU:ElasticGPU 是集群中一個實際可使用的 GPU 資源,可以是一塊本地 GPU 物理卡、一個 GPU 切片資源( GPU 算力 / 顯存 的組合)、一個遠端 GPU 設備。
- ElasticGPUClaim:ElasticGPUClaim 是用戶對 ElasticGPU 資源的申領,可以申請整卡數量,申請 GPU 核數 / 顯存,或者申請 TFLOPS 算力。
- EGPUClass:EGPUClass 提供了生產和掛載 ElasticGPU 的方式,可以使用 qGPU 虛擬化、vCUDA、或是 GPU 遠端池化的技術。
type ElasticGPU struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Spec ElasticGPUSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
Status ElasticGPUStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
type ElasticGPUSpec struct {
Capacity v1.ResourceList `json:"capacity,omitempty" protobuf:"bytes,1,rep,name=capacity,casttype=ResourceList,castkey=ResourceName"`
ElasticGPUSource `json:",inline" protobuf:"bytes,2,opt,name=elasticGPUSource"`
ClaimRef v1.ObjectReference `json:"claimRef,omitempty" protobuf:"bytes,3,opt,name=claimRef"`
NodeAffinity GPUNodeAffinity `json:"nodeAffinity,omitempty" protobuf:"bytes,4,opt,name=nodeAffinity"`
NodeName string `json:"nodeName,omitempty" protobuf:"bytes,5,opt,name=nodeName"`
}
type ElasticGPUSource struct {
QGPU *QGPUElasticGPUSource `json:"qGPU,omitempty" protobuf:"bytes,1,opt,name=qGPU"`
PhysicalGPU *PhysicalGPUElasticGPUSource `json:"physicalGPU,omitempty" protobuf:"bytes,2,opt,name=physicalGPU"`
GPUShare *GPUShareElasticGPUSource `json:"gpuShare,omitempty" protobuf:"bytes,3,opt,name=gpuShare"`
}
type ElasticGPUClaim struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Spec ElasticGPUClaimSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
Status ElasticGPUClaimStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
type ElasticGPUClass struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Provisioner string `json:"provisioner" protobuf:"bytes,2,opt,name=provisioner"`
Parameters map[string]string `json:"parameters,omitempty" protobuf:"bytes,3,rep,name=parameters"`
}
下面以 TKE qGPU 為例,描述結合 Elastic GPU 方案的整個資源調度分配流程。
qGPU 資源申請
用戶在集群中創建 ElasticGPUClass,指定 qGPU 作為 GPU 后端。
apiVersion: elasticgpu.io/v1alpha
kind: ElasticGPUClass
metadata:
name: qgpu-class
provisioner: elasticgpu.io/qgpu
reclaimPolicy: Retain
eGPUBindingMode: Immediate
創建 ElasticGPUClaim 描述對 qGPU 資源的申領,tke.cloud.tencent.com/qgpu-core
代表申請 10% 的 GPU 算力,tke.cloud.tencent.com/qgpu-memory
代表申請 4GB 顯存。
apiVersion: elasticgpu.io/v1alpha
kind: ElasticGPUClaim
metadata:
name: qgpu-egpuc
spec:
storageClassName: qgpu-class
resources:
requests:
tke.cloud.tencent.com/qgpu-core: 10
tke.cloud.tencent.com/qgpu-memory: 4GB
用戶在創建 Pod 時指定 ElasticGPUClaim 完成 qGPU 資源申領。
apiVersion: v1
kind: Pod
metadata:
name: qgpu-pod
annotations:
elasticgpu.io/egpuc-<container-name>: qgpu-egpuc
spec:
containers:
- name: test
qGPU 資源調度
考慮到 out-tree 的設計,qGPU 資源發現、上報和調度,還是依賴原有 device plugin 與 extended resource 機制。
我們通過 elastic-gpu-admission-hook 在 Pod 創建時識別 annotations elasticgpu.io/egpuc-<container-name>
,將申請資源正確設置到 containers 中。
apiVersion: v1
kind: Pod
metadata:
name: qgpu-pod
annotations:
elasticgpu.io/egpuc-test: qgpu-egpuc
spec:
containers:
- name: test
resources:
requests:
tke.cloud.tencent.com/qgpu-core: 10
tke.cloud.tencent.com/qgpu-memory: 4GB
limits:
tke.cloud.tencent.com/qgpu-core: 10
tke.cloud.tencent.com/qgpu-memory: 4GB
qgpu-scheduler 擴展調度器用於 qGPU 資源調度,返回符合要求的節點。當 Pod 綁定到節點上后,qgpu-provisioner 會更新ElasticGPU
CRD 中節點、GPU 卡索引 等信息,實現 qGPU 設備的綁定。
qGPU 資源創建
qgpu-manager 會 watch ElastciGPU
CRD 變化,在綁定節點成功后,會執行創建 qGPU 設備的操作。qgpu-manager 會根據 CRD 中包含的申請算力與顯存信息以及調度到的 GPU 卡索引,在底層創建 qGPU 設備。
qGPU 設備掛載
qgpu-manager 是一個 device plugin 插件,kubelet 會在分配 device 時通過標准接口調用插件。在接口 Allocate
和 PreStartContainer
中,我們會掛載必要的 qGPU、nvidia 設備以及設置環境變量。最后,我們依賴 qgpu-container-runtime 進行 qGPU 設備與容器的綁定工作。
下一步發展
隨着 AI 業務的大規模落地,越來越多的用戶在 Kubernetes 中使用 GPU 進行 AI 計算。現有的 extended resource 與 device plugin 機制很難滿足客戶對 GPU 資源的精細控制和分配,新的技術框架勢在必行。Elastic GPU 在 Kubernetes 集群中抽象了一種 native GPU 資源,圍繞三種自定義 CRD,在標准化定義了與其他 GPU 技術交互的前提下,同時提供了集群層面全局 GPU 資源視角,讓用戶可以更好的觀察和管理 GPU 資源。
Elastic GPU 第一步會聚焦在 CRD 定義以及交互流程標准化,並首先適配 TKE qGPU。在這個階段,我們希望參照 PV / PVC / CSI 的設計理念,以 Kubernetes native 的方式提供對 GPU 資源的抽象,標准化資源分配、調度、掛載等流程,並提供靈活的接口,供其他 GPU 技術集成。通過首先在生產環境支持 TKE qGPU,我們會持續打磨框架,發布第一個 alpha 版本。接下來,我們會推動社區實現對主流 GPU 技術的集成支持,包括 nvidia docker、gpu share 以及 vCUDA,橫向擴展框架的適用場景。通過標准框架,統一接口和流程,降低客戶管理成本。通過 GPU Sharing、Remote GPU 等技術提升靈活性、增加利用率,降低客戶用卡成本。我們希望依賴 Elastic GPU 框架,最終可以為客戶提供 Kubernetes 開箱即用使用 GPU 資源的能力。
TKE qGPU:https://cloud.tencent.com/document/product/457/61448
【騰訊雲原生】雲說新品、雲研新術、雲游新活、雲賞資訊,掃碼關注同名公眾號,及時獲取更多干貨!!