做者 | 王慶璨(阿里雲技術專家)、張凱(阿里雲高級技術專家)python
導讀:阿里雲容器服務團隊結合多年 Kubernetes 產品與客戶支持經驗,對 Kube-scheduler 進行了大量優化和擴展,逐步使其在不一樣場景下依然能穩定、高效地調度各類類型的複雜工做負載。《進擊的 Kubernetes 調度系統》系列文章將把咱們的經驗、技術思考和實現細節全面地展示給 Kubernetes 用戶和開發者,指望幫助你們更好地瞭解 Kubernetes 調度系統的強大能力和將來發展方向。本文爲該系列文章的第二篇。git
什麼是 Coscheduling 和 Gang scheduling。Wikipedia 對 Coscheduling 的定義是「在併發系統中將多個相關聯的進程調度到不一樣處理器上同時運行的策略」。在 Coscheduling 的場景中,最主要的原則是保證全部相關聯的進程可以同時啓動。防止部分進程的異常,致使整個關聯進程組的阻塞。這種致使阻塞的部分異常進程,稱之爲「碎片(fragement)」。github
在 Coscheduling 的具體實現過程當中,根據是否容許「碎片」存在,能夠細分爲 Explicit Coscheduling,Local Coscheduling 和 Implicit Coscheduling。 其中 Explicit Coscheduling 就是你們常聽到的 Gang Scheduling。Gang Scheduling 要求徹底不容許有「碎片」存在, 也就是「All or Nothing」。api
咱們將上述定義的概念對應到 Kubernetes 中,就能夠理解 Kubernetes 調度系統支持批任務 Coscheduling 的含義了。 一個批任務(關聯進程組)包括了 N 個 Pod(進程),Kubernetes 調度器負責將這 N 個 Pod 調度到 M 個節點(處理器)上同時運行。若是這個批任務須要部分 Pod 同時啓動便可運行,咱們稱需啓動 Pod 的最小數量爲 min-available。特別地,當 min-available=N 時,批任務要求知足 Gang Scheduling。架構
Kubernetes 目前已經普遍的應用於在線服務編排,爲了提高集羣的的利用率和運行效率,咱們但願將 Kubernetes 做爲一個統一的管理平臺來管理在線服務和離線做業。默認的調度器是以 Pod 爲調度單元進行依次調度,不會考慮 Pod 之間的相互關係。可是不少數據計算類的離線做業具備組合調度的特色,即要求全部的子任務都可以成功建立後,整個做業才能正常運行。若是隻有部分子任務啓動的話,啓動的子任務將持續等待剩餘的子任務被調度。這正是 Gang Scheduling 的場景。併發
以下圖所示,JobA 須要 4 個 Pod 同時啓動,才能正常運行。Kube-scheduler 依次調度 3 個 Pod 並建立成功。到第 4 個 Pod 時,集羣資源不足,則 JobA 的 3 個 Pod 處於空等的狀態。可是它們已經佔用了部分資源,若是第 4 個 Pod 不能及時啓動的話,整個 JobA 沒法成功運行,更糟糕的是致使集羣資源浪費。less
若是出現更壞的狀況的話,以下圖所示,集羣其餘的資源恰好被 JobB 的 3 個 Pod 所佔用,同時在等待 JobB 的第 4 個 Pod 建立,此時整個集羣就出現了死鎖。運維
社區目前有 Kube-batch 以及基於 Kube-batch 衍生的 Volcano 2 個項目來解決上文中提到的痛點。實現的方式是經過開發新的調度器將 Scheduler 中的調度單元從 Pod 修改成 PodGroup,以組的形式進行調度。使用方式是若是須要 Coscheduling 功能的 Pod 走新的調度器,其餘的例如在線服務的 Pod 走 Kube-scheduler 進行調度。機器學習
這些方案雖然可以解決 Coscheduling 的問題,可是一樣引入了新的問題。如你們所知,對於同一集羣資源,調度器須要中心化。但若是同時存在兩個調度器的話,有可能會出現決策衝突,例如分別將同一塊資源分配給兩個不一樣的 Pod,致使某個 Pod 調度到節點後由於資源不足,致使沒法建立的問題。解決的方式只能是經過標籤的形式將節點強行的劃分開來,或者部署多個集羣。這種方式經過同一個 Kubernetes 集羣來同時運行在線服務和離線做業,勢必會致使總體集羣資源的浪費以及運維成本的增長。再者,Volcano 運行須要啓動定製的 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook。這些 Webhooks 自己存在單點風險,一旦出現故障,將影響集羣內全部 pod 的建立。另外,多運行一套調度器,自己也會帶來維護上的複雜性,以及與上游 Kube-scheduler 接口兼容上的不肯定性。分佈式
《進擊的 Kubernetes 調度系統 (一):Scheduling Framework 》介紹了 Kubernetes Scheduling Framework 的架構原理和開發方法。在此基礎上,咱們擴展實現了 Coscheduling 調度插件,幫助 Kubernetes 原生調度器支持批做業調度,同時避免上述方案存在的問題。Scheduling framework 的內容在前一篇文章詳細介紹,歡迎你們翻閱。
Kubernetes 負責 Kube-scheduler 的小組 sig-scheduler 爲了更好的管理調度相關的 Plugin,新建了項目 scheduler-plugins 管理不一樣場景的 Plugin。咱們基於 scheduling framework 實現的 Coscheduling Plugin 成爲該項目的第一個官方插件,下面我將詳細的介紹 Coscheduling Plugin 的實現和使用方式。
咱們經過 label 的形式來定義 PodGroup 的概念,擁有一樣 label 的 Pod 同屬於一個 PodGroup。min-available 是用來標識該 PodGroup 的做業可以正式運行時所須要的最小副本數。
labels: pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpu pod-group.scheduling.sigs.k8s.io/min-available: "2"
備註: 要求屬於同一個 PodGroup 的 Pod 必須保持相同的優先級
Framework 的 Permit 插件提供了延遲綁定的功能,即 Pod 進入到 Permit 階段時,用戶能夠自定義條件來容許 Pod 經過、拒絕 Pod 經過以及讓 Pod 等待狀態 (可設置超時時間)。Permit 的延遲綁定的功能,恰好可讓屬於同一個 PodGruop 的 Pod 調度到這個節點時,進行等待,等待積累的 Pod 數目知足足夠的數目時,再統一運行同一個 PodGruop 的全部 Pod 進行綁定並建立。
舉個實際的例子,當 JobA 調度時,須要 4 個 Pod 同時啓動,才能正常運行。但此時集羣僅能知足 3 個 Pod 建立,此時與 Default Scheduler 不一樣的是,並非直接將 3 個 Pod 調度並建立。而是經過 Framework 的 Permit 機制進行等待。
此時當集羣中有空閒資源被釋放後,JobA 的中 Pod 所須要的資源都可以知足。
則 JobA 的 4 個 Pod 被一塊兒調度建立出來,正常運行任務。
因爲 Default Scheduler 的隊列並不能感知 PodGroup 的信息,因此 Pod 在出隊時處於無序性 (針對 PodGroup 而言)。以下圖所示,a 和 b 表示兩個不一樣的 PodGroup,兩個 PodGroup 的 Pod 在進入隊列時,因爲建立的時間交錯致使在隊列中以交錯的順序排列。
當一個新的 Pod 建立後,入隊後,沒法跟與其相同的 PodGroup 的 Pod 排列在一塊兒,只能繼續以混亂的形式交錯排列。
這種無序性就會致使若是 PodGroupA 在 Permit 階段處於等待狀態,此時 PodGroupB 的 Pod 調度完成後也處於等待狀態,相互佔有資源使得 PodGroupA 和 PodGroupB 均沒法正常調度。這種狀況便是把死鎖現象出現的位置從 Node 節點移動到 Permit 階段,沒法解決前文提到的問題。
針對如上所示的問題,咱們經過實現 QueueSort 插件, 保證在隊列中屬於同一個 PodGroup 的 Pod 可以排列在一塊兒。咱們經過定義 QueueSort 所用的 Less 方法,做用於 Pod 在入隊後排隊的順序:
func Less(podA *PodInfo, podB *PodInfo) bool
首先,繼承了默認的基於優先級的比較方式,高優先級的 Pod 會排在低優先級的 Pod 以前。
而後,若是兩個 Pod 的優先級相同,咱們定義了新的排隊邏輯來支持 PodGroup 的排序。
經過如上的排隊策略,咱們實現屬於同一個 PodGroup 的 Pod 可以同一個 PodGroup 的 Pod 可以排列在一塊兒。
當一個新的 Pod 建立後,入隊後,會跟與其相同的 PodGroup 的 Pod 排列在一塊兒。
爲了減小無效的調度操做,提高調度的性能,咱們在 Prefilter 階段增長一個過濾條件,當一個 Pod 調度時,會計算該 Pod 所屬 PodGroup 的 Pod 的 Sum(包括 Running 狀態的),若是 Sum 小於 min-available 時,則確定沒法知足 min-available 的要求,則直接在 Prefilter 階段拒絕掉,再也不進入調度的主流程。
若是某個 Pod 在 Permit 階段等待超時了,則會進入到 UnReserve 階段,咱們會直接拒絕掉全部跟 Pod 屬於同一個 PodGroup 的 Pod,避免剩餘的 Pod 進行長時間的無效等待。
用戶既能夠在本身搭建的 Kubernetes 集羣中,也能夠在任一個公有云提供的標準 Kubernetes 服務中來試用 Coscheduling。須要注意的是集羣版本 1.16+, 以及擁有更新集羣 master 的權限。
本文將使用 阿里雲容器服務 ACK 提供的 Kubernetes 集羣來進行測試。
咱們已經將 Coscheduling 插件和原生調度器代碼統一構建成新的容器鏡像。並提供了一個 helm chart 包 ack-coscheduling 來自動安裝。它會啓動一個任務,自動用 Coscheduling scheduler 替換集羣默認安裝的原生 scheduler,而且會修改 scheduler 的相關 Config 文件,使 scheduling framework 正確地加載 Coscheduling 插件。完成試用後,用戶可經過下文提示的卸載功能恢復集羣默認 scheduler 及相關配置。
下載 helm chart 包,執行命令安裝:
$ wget http://kubeflow.oss-cn-beijing.aliyuncs.com/ack-coscheduling.tar.gz $ tar zxvf ack-coscheduling.tar.gz $ helm install ack-coscheduling -n kube-system ./ack-coscheduling NAME: ack-coscheduling LAST DEPLOYED: Mon Apr 13 16:03:57 2020 NAMESPACE: kube-system STATUS: deployed REVISION: 1 TEST SUITE: None
在 Master 節點上,使用 helm 命令驗證是否安裝成功。
$ helm get manifest ack-coscheduling -n kube-system | kubectl get -n kube-system -f - NAME COMPLETIONS DURATION AGE scheduler-update-clusterrole 1/1 8s 35s scheduler-update 3/1 of 3 8s 35s
經過 helm 卸載,將 kube-scheduler 的版本及配置回滾到集羣默認的狀態。
$ helm uninstall ack-coscheduling -n kube-system
使用 Coscheduling 時,只須要在建立任務的 yaml 描述中配置 pod-group.scheduling.sigs.k8s.io/name 和 pod-group.scheduling.sigs.k8s.io/min-available 這兩個 label 便可。
labels: pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpu pod-group.scheduling.sigs.k8s.io/min-available: "3"
備註: 屬於同一個 PodGroup 的 Pod 必須保持相同的優先級。
接下來咱們經過運行 Tensorflow 的分佈式訓練做業來演示 Coscheduling 的效果。當前測試集羣有 4 個 GPU 卡
Arena 是基於 Kubernetes 的機器學習系統開源社區 Kubeflow 中的子項目之一。Arena 用命令行和 SDK 的形式支持了機器學習任務的主要生命週期管理(包括環境安裝,數據準備,到模型開發,模型訓練,模型預測等),有效提高了數據科學家工做效率。
git clone https://github.com/kubeflow/arena.git kubectl create ns arena-system kubectl create -f arena/kubernetes-artifacts/jobmon/jobmon-role.yaml kubectl create -f arena/kubernetes-artifacts/tf-operator/tf-crd.yaml kubectl create -f arena/kubernetes-artifacts/tf-operator/tf-operator.yaml
檢查是否部署成功:
$ kubectl get pods -n arena-system NAME READY STATUS RESTARTS AGE tf-job-dashboard-56cf48874f-gwlhv 1/1 Running 0 54s tf-job-operator-66494d88fd-snm9m 1/1 Running 0 54s
apiVersion: "kubeflow.org/v1" kind: "TFJob" metadata: name: "tf-smoke-gpu" spec: tfReplicaSpecs: PS: replicas: 1 template: metadata: creationTimestamp: null labels: pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpu pod-group.scheduling.sigs.k8s.io/min-available: "5" spec: containers: - args: - python - tf_cnn_benchmarks.py - --batch_size=32 - --model=resnet50 - --variable_update=parameter_server - --flush_stdout=true - --num_gpus=1 - --local_parameter_device=cpu - --device=cpu - --data_format=NHWC image: registry.cn-hangzhou.aliyuncs.com/kubeflow-images-public/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3 name: tensorflow ports: - containerPort: 2222 name: tfjob-port resources: limits: cpu: '1' workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarks restartPolicy: OnFailure Worker: replicas: 4 template: metadata: creationTimestamp: null labels: pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpu pod-group.scheduling.sigs.k8s.io/min-available: "5" spec: containers: - args: - python - tf_cnn_benchmarks.py - --batch_size=32 - --model=resnet50 - --variable_update=parameter_server - --flush_stdout=true - --num_gpus=1 - --local_parameter_device=cpu - --device=gpu - --data_format=NHWC image: registry.cn-hangzhou.aliyuncs.com/kubeflow-images-public/tf-benchmarks-gpu:v20171202-bdab599-dirty-284af3 name: tensorflow ports: - containerPort: 2222 name: tfjob-port resources: limits: nvidia.com/gpu: 2 workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarks restartPolicy: OnFailure
刪除上述 TFJob yaml 中的 pod-group.scheduling.sigs.k8s.io/name 和 pod-group.scheduling.sigs.k8s.io/min-available 標籤,表示該任務不使用 Coscheduling。建立任務後,集羣資源只能知足 2 個 Worker 啓動,剩餘兩個處於 Pending 狀態。
$ kubectl get pods NAME READY STATUS RESTARTS AGE tf-smoke-gpu-ps-0 1/1 Running 0 6m43s tf-smoke-gpu-worker-0 1/1 Running 0 6m43s tf-smoke-gpu-worker-1 1/1 Running 0 6m43s tf-smoke-gpu-worker-2 0/1 Pending 0 6m43s tf-smoke-gpu-worker-3 0/1 Pending 0 6m43s
查看其中正在運行的 Worker 的日誌,都處於等待剩餘那兩個 Worker 啓動的狀態。此時,4 個 GPU 都被佔用卻沒有實際執行任務。
$ kubectl logs -f tf-smoke-gpu-worker-0 INFO|2020-05-19T07:02:18|/opt/launcher.py|27| 2020-05-19 07:02:18.199696: I tensorflow/core/distributed_runtime/master.cc:221] CreateSession still waiting for response from worker: /job:worker/replica:0/task:3 INFO|2020-05-19T07:02:28|/opt/launcher.py|27| 2020-05-19 07:02:28.199798: I tensorflow/core/distributed_runtime/master.cc:221] CreateSession still waiting for response from worker: /job:worker/replica:0/task:2
添加 pod-group 相關標籤後建立任務,由於集羣的資源沒法知足用戶設定的 min-available 要求,則 PodGroup 沒法正常調度,全部的 Pod 一直處於 Pending 狀態。
$ kubectl get pods NAME READY STATUS RESTARTS AGE tf-smoke-gpu-ps-0 0/1 Pending 0 43s tf-smoke-gpu-worker-0 0/1 Pending 0 43s tf-smoke-gpu-worker-1 0/1 Pending 0 43s tf-smoke-gpu-worker-2 0/1 Pending 0 43s tf-smoke-gpu-worker-3 0/1 Pending 0 43s
此時,若是經過集羣擴容,新增 4 個 GPU 卡,資源能知足用戶設定的 min-available 要求,則 PodGroup 正常調度,4 個 Worker 開始運行。
$ kubectl get pods NAME READY STATUS RESTARTS AGE tf-smoke-gpu-ps-0 1/1 Running 0 3m16s tf-smoke-gpu-worker-0 1/1 Running 0 3m16s tf-smoke-gpu-worker-1 1/1 Running 0 3m16s tf-smoke-gpu-worker-2 1/1 Running 0 3m16s tf-smoke-gpu-worker-3 1/1 Running 0 3m16s
查看其中一個 Worker 的日誌,顯示訓練任務已經開始:
$ kubectl logs -f tf-smoke-gpu-worker-0 INFO|2020-05-19T07:15:24|/opt/launcher.py|27| Running warm up INFO|2020-05-19T07:21:04|/opt/launcher.py|27| Done warm up INFO|2020-05-19T07:21:04|/opt/launcher.py|27| Step Img/sec loss INFO|2020-05-19T07:21:05|/opt/launcher.py|27| 1 images/sec: 31.6 +/- 0.0 (jitter = 0.0) 8.318 INFO|2020-05-19T07:21:15|/opt/launcher.py|27| 10 images/sec: 31.1 +/- 0.4 (jitter = 0.7) 8.343 INFO|2020-05-19T07:21:25|/opt/launcher.py|27| 20 images/sec: 31.5 +/- 0.3 (jitter = 0.7) 8.142
利用 Kubernetes Scheduling Framework 的機制實現了 Coscheduling,解決了 AI、數據計算類的批任務須要組合調度,同時減小資源浪費的問題。從而提高集羣總體資源利用率。
做者介紹:
王慶璨,阿里雲技術專家,專一於大規模集羣資源管理和調度。Kubernetes 社區成員,主要參與 Kube-scheduler 社區開發。目前負責阿里雲容器服務 ACK 資源調度和雲原生 AI 相關工做。
張凱,阿里雲高級技術專家,從事容器服務 ACK 和雲原生 AI 解決方案的研發和客戶支持,以及相關領域的開源建設。擁有 10 餘年大規模深度學習平臺,雲計算,SOA 等領域經驗。
「阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的公衆號。」