在 Kubernetes 上進行機器學習訓練,需要有效管理資源和最佳化分散式訓練架構。本文將探討如何組態 GPU 等特殊硬體資源,並利用 Kubernetes 的資源管理機制,例如裝置外掛,來滿足機器學習任務的需求。同時,文章也將分析分散式 TensorFlow 的架構,以及如何透過引數伺服器和工作節點的協同運作,提升訓練效率。此外,針對 Kubernetes 的排程特性,文章提出了一些最佳化建議,例如使用汙點和容忍度來控制 Pod 的排程,以及避免資源碎片化問題。最後,本文還將介紹一些機器學習平台工具,例如 Kubeflow 和 Polyaxon,以簡化在 Kubernetes 上的機器學習佈署流程。
Kubernetes上的機器學習訓練與資源管理最佳實踐
在Kubernetes上執行機器學習(ML)工作負載,需要深入瞭解資源管理、分散式訓練和硬體最佳化等關鍵議題。本文將探討如何在Kubernetes叢集上高效執行機器學習任務,並提供實用的最佳實踐。
機器學習訓練作業的 Kubernetes 實作
首先,讓我們觀察一個典型的機器學習訓練作業在 Kubernetes 上的執行過程。以下是一段記錄訓練過程中準確率的輸出範例:
Accuracy at step 10: 0.7094 Accuracy at step 20: 0.8354 … Accuracy at step 490: 0.9516
內容解密:
這段輸出展示了機器學習模型在不同訓練步驟中的準確率變化。從中我們可以觀察到模型的收斂過程以及訓練的穩定性。值得注意的是,準確率隨著訓練步驟的增加而不斷提升,顯示模型正在有效地學習資料特徵。
當訓練完成後,可以透過以下命令檢查作業狀態:
$ kubectl get jobs
NAME COMPLETIONS DURATION AGE
mnist-demo 1/1 31s 2m19s
清理訓練作業同樣簡單:
$ kubectl delete -f mnist-demo.yaml
job.batch "mnist-demo" deleted
分散式訓練的架構考量
分散式訓練是機器學習領域的一個重要議題。雖然在某些情況下,分散式訓練是必要的,但它通常比單機訓練更難最佳化。下圖展示了分散式 TensorFlow 的架構:
圖表翻譯: 此圖示展示了分散式 TensorFlow 的基本架構。其中引數伺服器負責儲存和更新模型引數,而多個工作節點平行執行計算任務並將梯度更新回傳至引數伺服器,最終實作模型的同步更新。
關鍵架構特點:
- 引數伺服器架構:集中管理模型引數
- 工作節點擴充套件:支援多個計算節點平行處理
- 高效同步機制:確保各節點間的模型一致性
資源限制與特殊硬體需求
機器學習工作負載對資源有著特殊的需求,特別是在訓練階段。為了滿足這些需求,Kubernetes 提供了多種資源管理機制:
GPU 資源管理範例
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
containers:
- name: digits-container
image: nvidia/digits:6.0
resources:
limits:
nvidia.com/gpu: 2 # 請求2個GPU
內容解密:
這段組態定義了一個需要2個GPU的Pod。在 Kubernetes 中,透過裝置外掛(device plugin)機制,可以將 GPU 等特殊硬體資源暴露給排程器,從而實作對這些資源的有效管理和分配。
排程特性與最佳化建議
在 Kubernetes 上執行機器學習任務時,需要注意以下排程相關的特性:
- 資源可見性:Kubernetes 只能排程它所瞭解的資源
- GPU 利用率最佳化:目前 Kubernetes 無法感知 GPU 的細粒度資源(如核心利用率)
- 資源碎片化問題:無法請求分數GPU資源可能導致資源浪費
針對這些挑戰,可以考慮使用裝置外掛來擴充套件 Kubernetes 的資源管理能力,或是最佳化應用程式的資源使用模式來提高整體利用率。
機器學習工作負載在Kubernetes上的挑戰
特殊硬體的存取限制
在Kubernetes環境中執行機器學習工作負載時,特殊硬體(如GPU)的存取是一個重要的考量因素。雖然某些GPU支援平行執行多個執行緒,但若未正確組態,可能無法充分利用此一能力。
函式庫、驅動程式和核心模組的組態
要存取特殊硬體,通常需要專門的函式庫、驅動程式和核心模組。這些元件必須掛載到容器執行環境中,以便在容器內執行的工具可以使用它們。直接將這些元件加入容器映像檔並不是最佳做法,因為這些工具需要與底層主機的版本相符,並且需要針對特定系統進行適當組態。
NVIDIA Docker的解決方案
像NVIDIA Docker這樣的容器執行環境可以簡化將主機磁碟區對應到每個容器的過程。如果沒有專門的容器執行環境,可以考慮建立一個Admission Webhook來提供相同的功能。然而,使用特殊硬體可能會需要特權容器,這會影響叢集的安全性設定檔。Kubernetes裝置外掛程式可以促進相關函式庫、驅動程式和核心模組的安裝。
儲存需求
儲存是機器學習工作流程中最關鍵的方面之一。它直接影響以下幾個方面:
- 資料集在訓練期間在節點間的儲存和分佈
- 檢查點和模型儲存
資料集的儲存和分佈
在訓練期間,每個節點都必須能夠存取資料集。儲存需求主要是唯讀的,通常情況下,磁碟存取速度越快越好。適當的磁碟型別取決於資料集的大小。數百MB或GB的資料集可能適合區塊儲存,而數TB的資料集則可能更適合物件儲存。
檢查點和模型儲存
檢查點是在模型訓練過程中建立的,而儲存模型則允許將其用於服務。在這兩種情況下,都需要在每個節點上附加儲存裝置來儲存資料。通常,這些資料儲存在單一目錄下,每個節點寫入特定的檢查點或儲存檔案。大多數工具要求檢查點和儲存資料位於單一位置,並且需要ReadWriteMany(即可由多個節點以讀寫方式掛載)。在使用Kubernetes PersistentVolumes時,需要確定最適合需求的儲存平台。
網路需求
訓練階段對網路有很大影響,特別是在執行分散式訓練時。以TensorFlow的分散式架構為例,有兩個離散階段會產生大量網路流量:引數伺服器到每個節點的變數分佈,以及從每個節點回傳到引數伺服器的梯度應用。這種交換所需的時間直接影響模型訓練的時間。因此,更快的網路連線(在合理的範圍內)對於提高訓練效率至關重要。
網路頻寬考量
大多數公有雲和伺服器都支援1-Gbps、10-Gbps甚至40-Gbps的網路介面卡,因此網路頻寬通常只在較低頻寬的情況下成為瓶頸。可以考慮使用InfiniBand來提高網路頻寬。
遠端直接記憶體存取(RDMA)
在某些情況下,問題不在於網路頻寬,而在於如何將資料從核心傳輸到網路上。一些開源專案利用RDMA來進一步加速網路流量,而無需修改節點或應用程式碼。RDMA允許網路中的電腦直接交換主記憶體中的資料,而無需使用處理器、快取或作業系統。
專門的協定
在Kubernetes上使用機器學習時,可以考慮其他專門的協定來解決分散式訓練的可擴充套件性問題。例如,某些協定允許GPU之間的直接資訊交換,而無需涉及節點CPU和OS。這類別協定包括:
- 訊息傳遞介面(MPI):一個標準化的可攜式API,用於在分散式程式之間傳輸資料。
Kubernetes上的機器學習工作負載架構
圖表翻譯: 此圖示呈現了在Kubernetes上執行機器學習工作負載所需的各個元件之間的關係,包括特殊硬體存取、儲存需求和網路需求。圖中顯示了從特殊硬體存取到網路需求的多個層面,以及相關技術如NVIDIA Docker、Admission Webhook、InfiniBand和RDMA等如何支援這些需求。
圖表詳細解析
- 特殊硬體存取 是基礎,需要透過 函式庫、驅動程式和核心模組 來實作。
- NVIDIA Docker 和 Admission Webhook 提供瞭解決方案來簡化組態過程。
- 特權容器 和 Kubernetes裝置外掛程式 與安全性和管理相關。
- 儲存需求 包括 資料集儲存和分佈 和 檢查點及模型儲存,並且需要 ReadWriteMany 的支援。
- 網路需求 強調了 分散式訓練 的重要性,並且提到了 TensorFlow分散式架構 和相關技術如 InfiniBand 和 RDMA。
- 最後,圖表還提到了 MPI 這種專門的協定,用於最佳化分散式訓練過程。
在Kubernetes上建立更高層級的應用模式
眾所周知,Kubernetes是一個複雜的系統。雖然它簡化了分散式應用的佈署和維運,但對於開發者來說,它並沒有使開發過程變得簡單。事實上,當引入新的概念和工件供開發者互動時,它在簡化維運的同時,也增加了一層複雜性。因此,在許多環境中,開發更高層級的抽象以在Kubernetes之上提供更友好的開發者介面是有意義的。此外,在許多大公司中,標準化應用組態和佈署方式以使所有人遵循相同的維運最佳實踐也是有意義的。這可以透過開發更高層級的抽象來實作,使開發者自動遵循這些原則。然而,開發這些抽象可能會隱藏重要的細節,並可能引入「圍牆花園」效應,從而限制或複雜化某些應用的開發或現有解決方案的整合。在雲的發展過程中,基礎設施的靈活性和平台的強大功能之間的緊張關係一直存在。設計合適的更高層級抽象使我們能夠在這兩者之間找到理想的平衡點。
開發更高層級抽象的方法
在考慮如何在Kubernetes之上開發更高層級的原始模型時,有兩種基本方法。第一種是將Kubernetes作為實作細節進行包裝。透過這種方法,使用您提供的平台的開發者應該基本上不知道他們正在Kubernetes上執行;相反,他們應該認為自己是您提供的平台的消費者,因此Kubernetes是一個實作細節。
Kubernetes上的機器學習最佳實踐
為了實作機器學習工作負載的最佳效能,請考慮以下最佳實踐:
智慧排程和自動擴充套件:由於機器學習工作流程的大多數階段本質上是批次處理,因此建議使用叢集自動擴充套件器(Cluster Autoscaler)。GPU硬體成本高昂,您當然不希望在未使用時支付費用。建議使用汙點和容忍度(taints and tolerations)或根據時間的叢集自動擴充套件器來批次執行任務。這樣,叢集就可以根據需要擴充套件以滿足機器學習工作負載的需求,而不是提前擴充套件。
關於汙點和容忍度,上游慣例是使用擴充套件資源作為鍵來汙染節點。例如,具有NVIDIA GPU的節點應該被汙染如下:鍵:nvidia.com/gpu,效果:NoSchedule。
使用這種方法意味著您還可以利用ExtendedResourceToleration准入控制器,它將自動為請求擴充套件資源的Pod新增適當的容忍度,這樣使用者就不需要手動新增它們。
混合工作負載叢集:用於執行日常業務服務的叢集也可能用於機器學習。鑑於機器學習工作負載的高效能需求,建議使用單獨的節點池,並對其進行汙染,以僅接受機器學習工作負載。這將有助於保護叢集的其他部分免受在機器學習節點池上執行的機器學習工作負載的影響。此外,您應該考慮多個具有不同效能特徵的GPU啟用節點池,以適應不同的工作負載型別。還建議在機器學習節點池上啟用節點自動擴充套件。只有在您對機器學習工作負載對叢集的效能影響有深入瞭解後,才使用混合模式叢集。
透過分散式訓練實作線性擴充套件:這是分散式模型訓練的聖杯。大多數函式庫不幸的是,在分散式時不能線性擴充套件。許多工作正在進行中,以使擴充套件性更好,但瞭解成本非常重要,因為這不是簡單地增加硬體就能解決的問題。根據我們的經驗,幾乎總是模型本身而不是支援它的基礎設施是瓶頸的來源。然而,在指責模型之前,檢視GPU、CPU、網路和儲存的使用情況非常重要。開源工具如Horovod試圖改進分散式訓練框架並提供更好的模型擴充套件性。
資料科學家的關切
在本章的前面,我們分享了在Kubernetes叢集上執行機器學習工作負載所需的考慮因素。但是,資料科學家怎麼辦?在這裡,我們介紹了一些流行的工具,使資料科學家可以輕鬆地利用Kubernetes進行機器學習,而無需成為Kubernetes專家:
Kubeflow:一個適用於Kubernetes的機器學習工具包,它原生於Kubernetes,並提供了完成機器學習工作流程所需的幾種工具。Jupyter Notebooks、管道和Kubernetes原生控制器等工具使資料科學家能夠簡單、輕鬆地充分利用Kubernetes作為機器學習平台。
Polyaxon:一種用於管理機器學習工作流程的工具,支援許多流行的函式庫,並可在任何Kubernetes叢集上執行。Polyaxon提供商業和開源版本。
Pachyderm:一個企業級的資料科學平台,具有豐富的資料集準備、生命週期和版本控制工具,以及構建機器學習管道的能力。Pachyderm提供商業版本,可以佈署到任何Kubernetes叢集。
在Kubernetes上建立更高層級的應用模式
在Kubernetes上建立更高層級的應用模式有兩種主要方法。第一種是建立一個完全獨立的抽象層,隱藏底層的Kubernetes複雜性。第二種是利用Kubernetes本身的擴充套件能力,動態新增新的資源到Kubernetes API。
建立獨立抽象層
這種方法需要建立一個完整的抽象層,隱藏Kubernetes的複雜性,使用者無需瞭解Kubernetes即可使用。這種方法的優點是可以讓使用者更專注於自己的領域,而不是分散式系統。例如,建立一個機器學習管道,可以讓資料科學家快速完成工作,而無需瞭解Kubernetes。
擴充套件Kubernetes
另一種方法是擴充套件Kubernetes本身,新增新的資源到Kubernetes API。這種方法的優點是可以繼續利用Kubernetes生態系統中的工具和新工具。例如,建立一個簡單的Java應用程式佈署方式,可以擴充套件Kubernetes而不是替換它。
選擇合適的方法
選擇合適的方法取決於抽象層的目標。如果是要建立一個完全獨立、整合的環境,使用者不需要“打破玻璃”並逃脫,那麼第一種方法是個好選擇。但如果是要建立一個更高層級的開發者抽象,例如簡單的Java應用程式佈署方式,那麼擴充套件Kubernetes是更好的選擇。
擴充套件Kubernetes叢集
擴充套件Kubernetes叢集涉及瞭解Kubernetes中資源的接觸點。有三種相關的技術解決方案。第一種是Sidecar容器,Sidecar容器與主應用程式容器一起執行,提供額外的功能。第二種是Admission Controller,Admission Controller可以攔截Kubernetes API請求,並在請求被儲存之前對其進行驗證或修改。
Sidecar設計
Sidecar容器已經在服務網格的背景下流行起來。這些容器與主應用程式容器一起執行,提供額外的功能。例如,在服務網格中,Sidecar可以提供透明的相互傳輸層安全性(mTLS)身份驗證給容器化應用程式。
此圖示
graph LR
A[主應用程式容器] -->| 執行在相同的Pod中 |> B[Sidecar容器]
B -->| 提供額外的功能 |> C[例如:mTLS身份驗證]
**圖表翻譯:**此圖示顯示了Sidecar設計,主應用程式容器和Sidecar容器執行在相同的Pod中,Sidecar容器提供額外的功能,例如mTLS身份驗證。
Admission Controller
Admission Controller可以攔截Kubernetes API請求,並在請求被儲存之前對其進行驗證或修改。在Sidecar的背景下,Admission Controller可以用來自動將Sidecar新增到叢集中建立的所有Pod中,以便開發人員無需瞭解Sidecar即可獲得其好處。
此圖示
graph LR
A[Kubernetes API請求] -->| 被Admission Controller攔截 |> B[驗證或修改API物件]
B -->| 自動新增Sidecar |> C[建立Pod]
**圖表翻譯:**此圖示顯示了Admission Controller的工作流程,Kubernetes API請求被Admission Controller攔截,並在請求被儲存之前對其進行驗證或修改,然後自動將Sidecar新增到建立的Pod中。
擴充套件 Kubernetes 的功能與使用者經驗
Kubernetes 的准入控制器(Admission Controllers)不僅可用於新增 sidecar,還能驗證開發者提交的物件。例如,實作一個 Kubernetes linter(程式碼分析工具),確保開發者提交的 Pod 和其他資源遵循最佳實踐。常見的錯誤是開發者未為其應用程式保留資源。在這種情況下,根據准入控制器的 linter 可以攔截請求並拒絕它們。當然,也應該提供逃生門(escape hatch),例如特殊的註解(annotation),以便高階使用者可以選擇離開 lint 規則。
自定義資源定義(CRDs)
CRDs 是一種動態新增新資源到現有 Kubernetes 叢集的方法。例如,使用 CRDs 可以新增新的 ReplicatedService 資源到 Kubernetes 叢集。當開發者建立 ReplicatedService 的例項時,它會轉而建立對應的 Deployment 和 Service 資源。因此,ReplicatedService 是開發者的一種方便的抽象概念,用於常見的模式。CRDs 通常由佈署到叢集本身的控制迴路(control loop)實作,以管理這些新的資源型別。
擴充套件 Kubernetes 使用者經驗
新增資源到叢集是提供新功能的好方法,但要充分利用它們,擴充套件 Kubernetes 使用者經驗(UX)也很有用。預設情況下,Kubernetes 工具不知道自定義資源和其他擴充套件,因此以非常通用和不友好的方式處理它們。擴充套件 Kubernetes 命令列可以提供更好的使用者經驗。
kubectl 外掛
kubectl 外掛是具有名稱如 kubectl-foo 的二進位制檔案,其中 foo 是外掛的名稱。當您在命令列上呼叫 kubectl foo ... 時,呼叫會被路由到外掛二進位制檔案的呼叫。使用 kubectl 外掛,您可以定義新的體驗,深入瞭解您新增到叢集的新資源。您可以自由實作任何型別的體驗,同時利用熟悉的 kubectl 工具。
圖形介面工具
如果您希望為根據 Kubernetes 的平台構建圖形介面,有多種工具可以幫助。特別是開源的 Headlamp 專案是一個函式庫,能夠輕鬆構建根據 Web、移動或桌面的應用程式,用於與 Kubernetes 基礎架構互動。使用像 Headlamp 這樣的工具,您可以快速建立自定義的開發者體驗,完美地適應您的平台及其需求。
簡化容器化開發
在將應用程式佈署到 Kubernetes 之前,開發者必須首先容器化該應用程式。雖然對於熟悉雲原生生態系統的人來說,構建容器是第二天性,但對於許多人來說,這是一項艱鉅的任務,甚至阻礙了現代應用程式開發的開始。
自動生成 Dockerfile
幸運的是,有多個開源工具可以幫助啟動您的開發。例如,Draft 和 Skaffold 可以自動為特定的語言或開發環境生成 Dockerfile。
Paketo
如果開發者熟悉來自 Cloud Foundry 或其他平台的 buildpack 概念,還有像 Paketo 這樣的工具,提供易於使用和驗證的容器映像,用於在流行的語言中構建應用程式,以及命令列工具,以便輕鬆入門。
開發“Push-to-Deploy”體驗
許多 PaaS 產品最受歡迎的功能之一是“push to deploy”,這意味著單次推播程式碼到 Git 儲存函式庫會導致應用程式佈署到雲環境。雖然這以前是大型託管 PaaS 解決方案的領域,但現在使用 CI/CD 解決方案(如 GitHub Actions、Azure DevOps 或其他持續構建工具)構建類別似的體驗非常容易。
自動化 CI/CD 管道
透過正確設計的管道,一旦開發者將程式碼推播到其 Git 儲存函式庫,它就會自動測試、構建、封裝到容器映像中,並推播到容器登入檔中。
GitOps
一旦容器登入檔中存在新版本的容器映像,就可以使用另一個 Git 提交結合 GitOps 將該映像推播到正在執行的應用程式。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Kubernetes機器學習訓練資源管理最佳實踐
package "Kubernetes Cluster" {
package "Control Plane" {
component [API Server] as api
component [Controller Manager] as cm
component [Scheduler] as sched
database [etcd] as etcd
}
package "Worker Nodes" {
component [Kubelet] as kubelet
component [Kube-proxy] as proxy
package "Pods" {
component [Container 1] as c1
component [Container 2] as c2
}
}
}
api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2
note right of api
核心 API 入口
所有操作經由此處
end note
@enduml
圖表翻譯: 此圖示展示了從開發者推播程式碼到最終佈署到 Kubernetes 的整個自動化流程。首先,程式碼推播觸發 CI/CD 管道,接著進行自動測試與構建,然後將應用程式封裝成容器映像並推播到容器登入檔。最後,利用 GitOps 將新的容器映像佈署到 Kubernetes 中,更新執行中的應用程式。
#### 內容解密:
上述 Plantuml 圖表展示了一個完整的 CI/CD 流程,從開發者提交程式碼到最終佈署至 Kubernetes。此流程自動化了測試、構建、封裝及佈署等步驟,大幅提升了開發效率與佈署速度。其中,每一步驟都可依實際需求進行客製化調整,以滿足不同專案的需求。