關於我們
更多關於雲原生的案例和知識,可關注同名【騰訊雲原生】公眾號~
福利:
①公眾號後臺回覆【手冊】,可獲得《騰訊雲原生路線圖手冊》&《騰訊雲原生最佳實踐》~
②公眾號後臺回覆【系列】,可獲得《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/doc...
【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公眾號,及時獲取更多幹貨!!