Categories
程式開發

揭秘愛奇藝深度學習平台雲原生遷移實踐


Jarvis深度學習平台介紹

1 平台整體架構

平台支持GPU 和CPU 的訓練/推理,支持S3、HDFS 和NFS 作為訓練數據的存儲、模型的存儲。平台支持Tensorflow、Pytorch、Caffe、Caffe2、MxNet,但主要以Tensorflow 和Pytorch 為主,支持tensorflow 從1.X 到2.X 的各個版本。

支撐公司的廣告、搜索、推薦、NLP 等業務。我們基於mesos + marathon 作為我們的彈性容器平台。這是因為我們容器彈性平台起步比較早,當時k8s 還不是很成熟。因此在很長一段時間內,我們的容器並不是運行在K8s 平台上,這點需要大家注意。

揭秘愛奇藝深度學習平台雲原生遷移實踐 1

2 一站式平台服務

這裡主要是通過4個小平台實現的, 第一個是數據預處理平台 :通過數據預處理平台可以對訓練數據進行可視化分析,幫助用戶合理的調參,並及時發現異常數據。

揭秘愛奇藝深度學習平台雲原生遷移實踐 2

第二個是編寫訓練代碼平台: 用戶可以通過runonce 訓練或者notebook 訓練,得到一個和訓練環境相同的環境編寫訓練代碼,並上傳gitlab。

第三個是執行訓練任務平台: 用戶通過Jarvis 訓練平台執行訓練任務,然後訓練任務將執行用戶的訓練代碼,輸出算法模型。

最後用戶通過 Jarvis 推理平台 創建推理服務,並對外提供服務。

3 平台發展

揭秘愛奇藝深度學習平台雲原生遷移實踐 3

我們是從推理平台入手,首先解決讓用戶訓練好的模型能對外提供服務的能力,而後再逐步的將平台功能擴展到支持訓練、開發及數據預處理,目前我們正處在將容器彈性平台從mesos + marathon 遷移至k8s + volcano 的過程中。

4 使用Volcano之前訓練平台架構

如圖所示為我們在使用Volcano 之前的訓練平台架構:

揭秘愛奇藝深度學習平台雲原生遷移實踐 4

其運行的流程為

(1)用戶編寫訓練代碼並提交到公司內部gitlab。

(2)用戶通過web 頁面、命令行工具去創建訓練任務。創建訓練任務需要用戶填寫的信息有:

  • 所需的資源
  • 使用的鏡像,每一個框架的每一個版本都通過一個鏡像來支持,選擇鏡像就等於選擇了算法框架
  • 我們可能會有多個集群,需要用戶指定在哪個集群上運行任務
  • 需要Gitlab project 的url,這個project 中包含了用戶編寫的訓練代碼

(3)Jarvis cli/web 將請求轉化為grpc 發送給Jarvis core。

(4)Core 中將請求轉換,調用marathon api 創建容器。

(5)容器在對應的集群中啟動,執行訓練任務

5 訓練平台遷移到K8s的挑戰

主要有3個方面的挑戰

  • 原生Pod/Deployment/Job 無法滿足分佈式訓練的要求
  • 無隊列管理、配額管理。
  • 調度能力缺失、如Gang Scheduling

6 引入Volcano

實際上,Volcano 對於我們來說最重要的是這麼幾個概念,一個是vcjob,可以簡單的理解成vcjob 是對K8s job 的一種擴展,或者是對pob 的一種封裝。

第二個對於我們比較重要的是Queue ,也就是隊列,因為在Queue 上可以分配一些配額,可以做一些配額的管理、隊列的管理;

第三個對我們比較重要的是podgroup,可以把它看成是pod 的一個集合,因為有了pod 集合這個概念,所以它才能做一些更高級的上層調度。

揭秘愛奇藝深度學習平台雲原生遷移實踐 5

按照我的理解

  • Volcano 是K8S 原生的batch System,高度符合AI 訓練場景。
  • 不侵入k8s 源碼,符合k8s 的開發規範。 (簡單來說,方便我們二次開發)
  • 項目加入CNCF,成熟度高

火山的力量

Volcano 要如何解決遷移到K8s 上遇到的問題

1 Gang Scheduling 的問題

Gang Scheduling :可以簡單的理解為要么同時被調度,要么同時不被調度,這對於AI 訓練場景來說是非常重要的,因為我們的大部分的訓練都是基於分佈式訓練,分佈式訓練的特點是一次啟動的pod 非常多,可能最多的話是有四五十個pod, 如果一個任務下有個別的pod 被調度,部分pod 沒有被調度,那顯然這個任務是不能正常運行的,那這些運行起來的pod 是沒有任何意義的,會造成資源的浪費,同時可能會引發死鎖的問題。

揭秘愛奇藝深度學習平台雲原生遷移實踐 6

比如,我們整個資源池只有4 張GPU 卡,有訓練任務A 和B,A 任務有4 個pod,每個pod 需要一張卡;另外B任務也是同樣的情況,當A 和B 同時被創建,如果沒有gang scheduling,A 可能拿到了2張卡,而B 拿到了2張卡,那這個時候無論哪個任務都不能順利完成,這個時候系統就產生死鎖了,除非你增加資源,否則就一直保持死鎖的狀態。

如果有了Gang scheduling , 就能避免上述問題。 Volcano 通過podgroup 這個CRD,能夠以podgroup 為單位對job 進行整體調度,實現Gang scheduling的功能。

2 分佈式任務原生支持

我們以Tensorflow 分佈式訓練為例,它主要有這幾個角色,PS、master、worker。 Parameter Server(PS)是用來存儲參數的,master、worker 簡單理解為進行計算梯度的,在每個迭代過程,master、worker 從parameter sever 中獲得參數,然後將計算的梯度更新給parameter server,parameter server 聚合從master、worker 傳回的梯度,然後更新參數,並將新的參數廣播給master、worker。

揭秘愛奇藝深度學習平台雲原生遷移實踐 7

當然我們在這裡不是分析Tensorflow 分佈式訓練的細節。我們只討論它的一個網絡結構。比如master、worker它要和這個Parameter Server去做通信,那麼要做通信的話就存在一個問題,如果我去創建一個pod,在創建的時候我可能並不知道這個pod 的IP 是什麼,如果我在一個Deployment 創建多個pod, pod 之間也互相不知道對方的IP地址或域名是什麼,我們需要通過其他的辦法來做這樣一件事情,對於我們來說相當的複雜。

這裡的每一個角色都要互相知道對方的IP 地址或者域名,最終要組成TF_CONFIG 這樣一個配置文件,這個配置文件裡面其實寫的很清楚,至少包括master、worker、ps 的IP地址或者域名,這些都要寫在這個配置文件裡面,每一個節點都需要知道這個東西,除此之外還需要知道自己所擔任的是什麼角色,Index 是多少,對於K8s 的話是很難實現的,但有了Volcano 後就變得非常簡單了。

揭秘愛奇藝深度學習平台雲原生遷移實踐 8

Volcano 會幫你在一個vcjob 下多個pod 去注入一個文件夾(etc/volcano),這個文件夾下面就會有所有的master、volcano、ps 的域名,都會填在這裡面,這樣的話每個pod都知道整體集群裡都有哪些peer ,非常方便去組成TF_CONFIG 這個文件,只要組成了這個TF_CONFIG 文件,我們就能進行Tensorflow 分佈式訓練。

現在的話Tensorflow 提供一些高層的API,比如說TF estimator ,這個estimator 裡代碼的單機和分佈式代碼是一模一樣的,只不過這個TF_CONFIG 的配置是不一樣的,所有說只要有那樣一個格式的環境變量或者配置文件傳進去的話,就可以做分佈式訓練,對於我們平台方來說,幫助用戶構建TF_CONFIG,用戶拿來直接運行就可以了。 Volcano 通過注入文件,可以方便構建TF_CONFIG,以支持TF 分佈式訓練。

3 Horovod / mpi

Volcano支持Horovod 訓練,Horovod 訓練其實和Tensorflow 分佈式訓練有點類似,因為大家都是分佈式訓練,區別是更新參數的方式不一樣,大家可以注意一下Horovod 訓練簡單來說它更新參數是環形的更新方式。

揭秘愛奇藝深度學習平台雲原生遷移實踐 9

但這個並不重要,因為對我們平台側的話,我們主要想要做的事情是我們要構建好基礎環境,讓上層應用來使用,這種Horovod 網絡架構,它對於我們基礎環境有什麼要求呢?

它的要求很簡單,它除了要保證每一個節點都要知道對方的域名和之前的需求是一模一樣的之外,它還額外的ssh 互信,因為它經過22 端口做ssh 登錄,去做一些工作,所有在這裡面需要做好互性,互性的工作如果讓我們來做,就很麻煩,我們要想一堆辦法來做這個事情,Volcano 可以通過設置ssh plugin 自動完成容器裡的互性,就可以達到Horovod 對於訓練任務的網絡要求。

4 配額系統、排隊系統

Volcano 其實是通過Queue 這麼一個CRD 來支持的,在圖中這個是表示資源池裡的一個資源,我們假設有兩個Queue,一個是Queue1,一個是Queue2,一個的配額有20個GPU,一個有10個GPU,當Queue 已經使用的資源不太多時,新任務來了之後是可以被調度的,但是如果Queue2 已經被使用了非常多,就已經使用完了,下面的新任務來了之後那就不可以被調度,只能繼續排隊,你podgroup 的狀態就處於pending 的狀態,就一直處於排隊中。

揭秘愛奇藝深度學習平台雲原生遷移實踐 10

Volcano 通過Queue 支持排隊及配額系統,正好與平台中的team 對應,因為我們的結構也是一個組有一個獨立的配額,這個配額與配額之間是相互獨立的,你只能用這麼多,使用量超過配額後,任務就將排隊,當然排隊的任務支持優先級策略,高優先級的任務將在有資源後首先被執行,正好和我們系統的設計是一致的,所以就非常的好對接。

5 與volcano 集成

新增了volcano_plugin,其封裝了vcjob、queue、podgroup 的restful api,將grpc 請求的內容轉成k8s api 規範的yaml 配置,並調用k8s api 創建容器。

揭秘愛奇藝深度學習平台雲原生遷移實踐 11

Jarvis Core 根據傳入的集群信息,決定使用哪個backend。

實際使用中遇到問題

1 問題一

現象: 當升級volcano 版本的時候,直接修改https://github.com/volcano_x0002_sh/volcano/blob/master/installer/volcano-development.yaml 中的image,然後執行kubectl apply -f ,會導致已經存在的queue/vcjob 等全部消失。

原因: yaml 中的volcano-admission-init 會重複執行,導致volcano 整體被reset 現象

解決辦法: 升級的時候想清楚升級對應的組件就可以了

揭秘愛奇藝深度學習平台雲原生遷移實踐 12

2 問題二

現象: 通過list_and_watch 監控vcjob 狀態變化的程序進行遇到watch 連接無故斷開的問題,即如果沒有新的events 產生,大約80~90s 就會斷開一次,每次時間還不固定,但是同樣的代碼watch pod 就沒有問題。

原因

通過閱讀k8s 源碼,K8s 對於CRD 資源,默認的http timeout 的時間是time.Duration(float64(minRequestTimeout) * (rand.Float64() + 1.0)),其minRequestTimeout 為1 分鐘,因此會出現上述的問題。可以通過客戶端指定timeoutSecond 來避免該問題。

揭秘愛奇藝深度學習平台雲原生遷移實踐 13

3 問題三

現象: Jarvis 訓練平台中容器入口地址是一個bash 腳本,在k8s 下運行時,會出現stop 命令下發後,等約30s 才退出。

原因

bash 不會把signal 傳遞給子進程中。當graceful stop timeout 到了之後,守護進程發現容器還沒有退出,會再發SIGKILL,此時會將bash 腳本殺掉,容器退出,但是容器中的其他進程將無法主動完成清理工作。

解決方案

使用dumb-init,比如入口腳本:

#!/ usr / bin / dumb-init / bin / bash

my-web-server&#在後台啟動進程

my-other-server#在前台啟動另一個進程

4 對Volcano 的修改

  • SVC plugin 支持傳入參數,參數為nodeport 的端口號,當創建vcjob 並傳入SVC 參數時,將創建對應的nodeport,這是因為我們的tensorboard 及其他服務需要讓外部訪問。
  • ssh plugin 的名字超過63 字節則會創建失敗,我們自己修復了這個bug。
  • Queue 的capability 存在bug,用戶可以突破capability 來使用資源,目前官方已經修復了這個問題。https://github.com/volcano-sh/volcano/issues/921
  • 給vcjob annotation 後,某個pod 失敗時,無法觸發刪除vcjob,https://github.com/volcano_x0002_sh/volcano/issues/805

總結

(1)Volcano 彌補了kubernetes 深度學習場景下的基本能力的缺失

  • 幫派調度程序
  • 隊列管理

(2)Volcano 代碼遵循kubernetes 的標準,採用非侵入式方案

  • 減少開發者的開發對接成本
  • 便於二次開發

(3) 基於Volcano 的Jarvis 訓練平台目前已經上線並運行良好

作者介紹

本文轉載自微信號容器魔方(ID:K8S-Huawei)。

原文鏈接

揭秘愛奇藝深度學習平台雲原生遷移實踐