前言
人在做自己喜歡的事情時(shí),時(shí)光總是過(guò)得很快骏掀。不知不覺(jué)到公司已經(jīng)兩年多了扫尖,在這兩年的時(shí)間里,我做了很多“有趣又有用”的事情怔匣,其中最讓我有成就感的有兩件事:
- 第一握联,從0到1搭建了一套基于K8s的機(jī)器學(xué)習(xí)平臺(tái)桦沉,高效每瞒、穩(wěn)定、好用纯露,
用戶訪問(wèn)量翻了30多倍
剿骨; - 第二,通過(guò)堅(jiān)持不懈的資源優(yōu)化埠褪,使整體資源利用率(本文統(tǒng)一按內(nèi)存計(jì)算)從30%左右提升到65%+浓利,
為公司節(jié)省了150+臺(tái)高配機(jī)器,經(jīng)濟(jì)價(jià)值超過(guò)500萬(wàn)¥
钞速。
今天就和大家分享一下我在資源優(yōu)化方面的一些心路歷程贷掖。
1. 非技術(shù)方案
1.1 機(jī)器梳理
我剛到公司的時(shí)候,機(jī)器學(xué)習(xí)平臺(tái)的機(jī)器使用非晨视铮混亂苹威。
- 有多少臺(tái)機(jī)器?不知道驾凶!
- 都是哪些機(jī)器牙甫?不知道!
- 這些機(jī)器都在做什么调违?不知道窟哺!
- 這些機(jī)器都是什么配置?不知道技肩!
- 哪些機(jī)器是其他組負(fù)責(zé)的且轨?不知道!
所以亩鬼,我就找運(yùn)維協(xié)助殖告,把我們所負(fù)責(zé)的機(jī)器梳理了一遍,最終梳理出483臺(tái)機(jī)器
雳锋,并確認(rèn)每臺(tái)機(jī)器的配置黄绩,同時(shí)和其它組確認(rèn)好機(jī)器的歸屬問(wèn)題,然后整理成一個(gè)表格玷过,標(biāo)記好機(jī)器的各項(xiàng)重要指標(biāo)爽丹。在這個(gè)過(guò)程中,我找到了19臺(tái)
屬于我們辛蚊,卻一直未被使用粤蝎,未被加入到服務(wù)樹(shù)(我們公司的運(yùn)維平臺(tái))的“野機(jī)器”。
1.2 集群合并
我到公司時(shí)袋马,機(jī)器學(xué)習(xí)平臺(tái)的容器編排工具是Marathon+Mesos初澎,每個(gè)Mesos集群由3個(gè)master節(jié)點(diǎn)
和若干個(gè)slave節(jié)點(diǎn)組成,由于機(jī)器學(xué)習(xí)任務(wù)都比較繁重,為了master節(jié)點(diǎn)的穩(wěn)定碑宴,我們是不往master節(jié)點(diǎn)上調(diào)度任務(wù)的软啼。所以,master節(jié)點(diǎn)幾乎始終處于空閑狀態(tài)延柠。
經(jīng)過(guò)我多次確認(rèn)祸挪,我們的Mesos 集群居然多達(dá)7個(gè)
(還不包括幫其他組搭建的6個(gè) Mesos 集群),也就是說(shuō)有21臺(tái)機(jī)器只作為master節(jié)點(diǎn)贞间,幾乎始終處于空閑狀態(tài)贿条。并且,這7個(gè)Mesos集群給我們帶來(lái)了巨大的運(yùn)維負(fù)擔(dān)增热,消耗了我們大量的工作時(shí)間和非工作時(shí)間整以。
于是,在我接管之后峻仇,就把這些集群做了一系列合并悄蕾,最終合并成一個(gè)集群,并且通過(guò)自動(dòng)化運(yùn)維
础浮,使集群的穩(wěn)定性提升到了99.99%
帆调,這樣就節(jié)省出18臺(tái)
機(jī)器,也使我們從人肉運(yùn)維的泥淖中解脫出來(lái)豆同。
1.3 機(jī)器內(nèi)存擴(kuò)容
機(jī)器學(xué)習(xí)任務(wù)由于要加載大量的樣本和模型數(shù)據(jù)到內(nèi)存中番刊,從而加快訓(xùn)練速度,所以影锈,內(nèi)存一直是機(jī)器學(xué)習(xí)任務(wù)的瓶頸
芹务。于是,我想:是不是可以通過(guò)擴(kuò)容機(jī)器上的內(nèi)存鸭廷,來(lái)減少每組訓(xùn)練任務(wù)所需機(jī)器數(shù)量呢枣抱?
這是一個(gè)顯而易見(jiàn)的道理,為什么之前就沒(méi)有人想到呢辆床?我想佳晶,應(yīng)該是司空見(jiàn)慣了,反而不會(huì)想著去改變
讼载。
于是轿秧,我聯(lián)合算法和運(yùn)維同學(xué),由運(yùn)維同學(xué)負(fù)責(zé)擴(kuò)容內(nèi)存并測(cè)試兼容性咨堤,算法同學(xué)來(lái)驗(yàn)證訓(xùn)練任務(wù)在大內(nèi)存機(jī)器上運(yùn)行是否正常菇篡。最終,我的想法完全可行一喘。
于是驱还,我們通過(guò)一條幾百元的內(nèi)存條,就實(shí)現(xiàn)了一臺(tái)價(jià)值3~4萬(wàn)機(jī)器的效果
,這給我們帶來(lái)了巨大的收益议蟆。
1.4 GPU置換為CPU
之前灼伤,TensorFlow 是我們的主流訓(xùn)練框架,很多排序和召回的算法同學(xué)都喜歡用 GPU 機(jī)器來(lái)提升一下訓(xùn)練速度咪鲜,但由于沒(méi)有GPU指標(biāo)監(jiān)控功能,我們并不知道這86臺(tái) GPU 機(jī)器的顯卡使用率撞鹉。后來(lái)疟丙,我想辦法開(kāi)發(fā)了一套 GPU 監(jiān)控程序,這才發(fā)現(xiàn)原來(lái)80%以上顯卡使用率連 1% 都不到
鸟雏,甚至部分完全空閑享郊。很多算法同學(xué)使用 GPU 只是圖個(gè)心理安慰而已,其實(shí)并沒(méi)有達(dá)到預(yù)期的效果孝鹊,反而造成了很多真正需要 GPU 資源的算法同學(xué)沒(méi)有資源可用炊琉。于是,我先是通過(guò)GPU共享技術(shù)
(詳見(jiàn)2.8)又活,使一張顯卡達(dá)到了之前兩倍的效果
苔咪。
并且,今年以來(lái)柳骄,隨著算法團(tuán)隊(duì)人員發(fā)生了一系列重大調(diào)整团赏,主流訓(xùn)練框架也從 TensorFlow 變成了 PaddlePaddle,而 PaddlePaddle 是不需要使用GPU資源的耐薯。于是舔清,使用 GPU 資源就變得更加沒(méi)有必要。我就開(kāi)始推動(dòng)算法同學(xué)把訓(xùn)練任務(wù)從 GPU 機(jī)器遷移到 CPU機(jī)器曲初,然后把 GPU 機(jī)器交給更依賴 GPU 的圖像識(shí)別体谒、視頻識(shí)別、NLP等訓(xùn)練任務(wù)去使用臼婆,這樣就達(dá)到了雙贏的效果抒痒。
最終,我們?cè)谖丛黾?CPU 機(jī)器的前提下颁褂,將 GPU 機(jī)器數(shù)量從 86 臺(tái)壓縮到 28 臺(tái)
评汰。
1.5 規(guī)范資源申請(qǐng)流程
在此之前,算法同學(xué)對(duì)資源是使用是完全憑個(gè)人感覺(jué)痢虹,想用多少就用多少被去,完全不做限制
。很多實(shí)驗(yàn)動(dòng)輒占用20臺(tái)機(jī)器奖唯,結(jié)果就造成483臺(tái)機(jī)器最多只能同時(shí)支持50組實(shí)驗(yàn)惨缆,而整體資源利用率只有30%左右
。
于是,我在建立起 K8s 集群之后坯墨,就建立了一套完善的資源監(jiān)控反饋體系
(詳見(jiàn)2.5)寂汇,通過(guò)監(jiān)控訓(xùn)練任務(wù)近7天的資源使用情況,來(lái)限制下次重啟時(shí)可用資源量
捣染。如果有特殊需求需要超過(guò)限制的骄瓣,需要走工單
進(jìn)行審批。
功能上線后耍攘,很多同學(xué)就主動(dòng)降低了資源申請(qǐng)量榕栏,于是資源使用率就逐步開(kāi)始提升了。
1.6 任務(wù)分級(jí)
我們的訓(xùn)練任務(wù)大致分為兩類蕾各,一類是探索型實(shí)驗(yàn)扒磁,一類是線上實(shí)驗(yàn),其中線上實(shí)驗(yàn)由根據(jù)所承載流量比例分為小流量實(shí)驗(yàn)和主流量實(shí)驗(yàn)式曲。顧名思義妨托,按重要程度來(lái)排序,主流量實(shí)驗(yàn) > 小流量實(shí)驗(yàn) > 探索型實(shí)驗(yàn)吝羞。
于是兰伤,我就把任務(wù)按照重要程度劃分成三個(gè)等級(jí),優(yōu)先級(jí)越高钧排,可申請(qǐng)資源的buffer越多医清,QoS也越高
。這樣就達(dá)到了節(jié)省資源和保證任務(wù)穩(wěn)定性的雙重目標(biāo)卖氨。
1.7 資源分組
每個(gè)人都想盡可能多啟動(dòng)一些實(shí)驗(yàn)会烙,從而更快地驗(yàn)證模型效果,但資源是有限的筒捺,如果一部分人占用資源太多柏腻,其他人就會(huì)陷入無(wú)資源可以的境地,這就造成經(jīng)常需要我們出面去協(xié)調(diào)資源
系吭。所以五嫂,在各組之間實(shí)行資源隔離就顯得很有必要。
于是肯尺,我們就按照業(yè)務(wù)對(duì)人員進(jìn)行分組
沃缘,并與各組負(fù)責(zé)人開(kāi)會(huì)確定各組所需資源數(shù)量,然后按比例給各組分配資源數(shù)量
则吟。如果某組資源耗盡槐臀,可以通過(guò)組內(nèi)線下協(xié)調(diào)的方式騰挪資源,我們也就獲得了解放氓仲。
2. 技術(shù)方案
2.1 Merge調(diào)度
機(jī)器學(xué)習(xí)訓(xùn)練任務(wù)每10分鐘會(huì)生成一個(gè)增量模型水慨,這個(gè)增量模型(inc)中只包含近10分鐘樣本訓(xùn)練的結(jié)果得糜,模型文件很小。每隔10小時(shí)左右會(huì)把近段時(shí)間的所有增量模型與之前的全量模型(full)進(jìn)行合并(Merge)而生成新的全量模型晰洒。
當(dāng)訓(xùn)練生成full模型時(shí)(Merge)朝抖,資源占用率會(huì)明顯上升,此時(shí)訓(xùn)練出錯(cuò)的風(fēng)險(xiǎn)較高谍珊。如果將Merge單獨(dú)運(yùn)行治宣,則可以明顯降低出錯(cuò)風(fēng)險(xiǎn),并保證資源占用率的平緩和穩(wěn)定砌滞。所以侮邀,需要將所有訓(xùn)練的Merge單獨(dú)運(yùn)行和調(diào)度。
之前的做法是給這些 Merge 任務(wù)單獨(dú)搭建了一個(gè)有40臺(tái)機(jī)器
的 Mesos 集群,然后由訓(xùn)練任務(wù)每隔10小時(shí)自行調(diào)度起其對(duì)應(yīng)的 Merge布持。由于各個(gè)任務(wù)互相獨(dú)立,這就導(dǎo)致經(jīng)常會(huì)有多個(gè) Merge 任務(wù)被同時(shí)啟動(dòng)的情況出現(xiàn)陕悬,因?yàn)闋?zhēng)搶有限的機(jī)器資源而發(fā)生死鎖
题暖,經(jīng)常需要人工去干預(yù)。
于是捉超,我重新設(shè)計(jì)了一套 Merge 調(diào)度方案:
- 首先胧卤,停止訓(xùn)練任務(wù)自行啟動(dòng) Merge,改由 Alpha (我們的機(jī)器學(xué)習(xí)管理平臺(tái))來(lái)統(tǒng)一定時(shí)調(diào)度拼岳。
- 其次枝誊,設(shè)置排隊(duì)機(jī)制,根據(jù)一些策略計(jì)算出每個(gè) Merge 任務(wù)的權(quán)重惜纸,權(quán)重超過(guò)閾值的可以進(jìn)入隊(duì)列叶撒,權(quán)重越高,越靠近隊(duì)列頭部耐版。
- 再者祠够,定時(shí)從隊(duì)列頭部取出 Merge 任務(wù)并嘗試啟動(dòng)。如果當(dāng)前資源不足粪牲,就停止啟動(dòng)并放回隊(duì)列頭部古瓤,如果資源充足,就真正啟動(dòng)Merge任務(wù)腺阳。
- 最后落君,定時(shí)檢查正在運(yùn)行的 Merge 的運(yùn)行狀態(tài),如果 full 模型已經(jīng)生成亭引,或者運(yùn)行超時(shí)绎速,則強(qiáng)行停止 Merge 任務(wù),以防任務(wù)卡死而無(wú)法正常釋放資源焙蚓。
整個(gè)過(guò)程如下圖所示:
這樣就徹底解決了因資源爭(zhēng)搶而造成的死鎖問(wèn)題朝氓。最終魔市,只用18臺(tái)機(jī)器就實(shí)現(xiàn)了 Merge 任務(wù)的正常運(yùn)行
。下圖是優(yōu)化后的效果赵哲,可以看到inc和full模型的延遲都非常穩(wěn)定待德,整個(gè)調(diào)度過(guò)程完全無(wú)需人工干預(yù)
。
2.2 使用K8s替代Mesos
Mesos雖然也是一個(gè)優(yōu)秀的容器編排工具枫夺,但和 K8s 比起來(lái)功能還是太顯單薄将宪,特別是在任務(wù)調(diào)度方面短板很明顯。于是橡庞,我通過(guò)建設(shè) 基于K8s的機(jī)器學(xué)習(xí)平臺(tái)搭建(一) 并逐步通過(guò) “絞殺者模式”實(shí)現(xiàn)任務(wù)從Mesos向K8s遷移 使我們具備了更穩(wěn)定的基礎(chǔ)架構(gòu)和更靈活的任務(wù)調(diào)度能力较坛,這給我們后來(lái)的一系列工作帶來(lái)了巨大的便利。
2.3 真正容器化
在此之前扒最,雖然我們也在使用 Docker 容器化技術(shù)丑勤,也在使用 Mesos 容器編排工具,但使用的方式并不能真正發(fā)揮容器化技術(shù)的優(yōu)勢(shì)吧趣。由于網(wǎng)絡(luò)模式采用的是host模式
(參考 Docker四種網(wǎng)絡(luò)模式)法竞,且所有任務(wù)都使用相同的端口,因端口沖突而導(dǎo)致同一臺(tái)機(jī)器上無(wú)法部署多個(gè)任務(wù)
强挫。
于是岔霸,我在任務(wù)遷移到 K8s 的同時(shí),借助Istio Ingress + 隨機(jī)端口
相結(jié)合的方式(如下圖所示)徹底解決了端口沖突的問(wèn)題俯渤,從而實(shí)現(xiàn)真正的容器化呆细,使一臺(tái)機(jī)器上運(yùn)行多個(gè)任務(wù)變成可能。再結(jié)合我們的一系列資源優(yōu)化手段八匠,使我們的整體資源利用率提升了30%+
絮爷。
2.4 資源限額
上面我介紹過(guò),之前算法同學(xué)對(duì)資源是使用是完全憑個(gè)人感覺(jué)梨树,想用多少就用多少略水,完全不做限制
。這就導(dǎo)致很多實(shí)驗(yàn)資源占用量遠(yuǎn)超實(shí)際使用量劝萤。
為了實(shí)現(xiàn)資源的精確分配渊涝,我們通過(guò) Prometheus
來(lái)監(jiān)控各項(xiàng)任務(wù)的實(shí)際資源使用情況,保留近7天的數(shù)據(jù)床嫌。Alpha定期從 Prometheus
讀取這些數(shù)據(jù)跨释,進(jìn)行聚合后,保存到數(shù)據(jù)庫(kù)中厌处。下次用戶重啟任務(wù)時(shí)鳖谈,我們就根據(jù)此來(lái)限制任務(wù)可申請(qǐng)資源量。當(dāng)然阔涉,為了保證任務(wù)的穩(wěn)定運(yùn)行缆娃,我們會(huì)給任務(wù)預(yù)留出一些buffer捷绒。
2.5 實(shí)驗(yàn)資源利用率排名
安于現(xiàn)狀是很多人的天性,想要算法同學(xué)主動(dòng)去配合節(jié)省資源存在一定的難度贯要。于是暖侨,我們就開(kāi)發(fā)了實(shí)驗(yàn)資源利用率排名功能,并定期在周會(huì)上通報(bào)崇渗,從而使算法同學(xué)逐步養(yǎng)成起了資源優(yōu)化的習(xí)慣字逗。
2.6 個(gè)人成本中心
在對(duì)實(shí)驗(yàn)進(jìn)行資源利用率排名的同時(shí),我們也將資源使用情況細(xì)分到了個(gè)人粒度宅广,對(duì)個(gè)人所有資源的成本折算成現(xiàn)金并排名葫掉,并每周發(fā)送郵件報(bào)表
。這樣跟狱,領(lǐng)導(dǎo)們就能看到每個(gè)人所消耗的成本俭厚,再結(jié)合其所創(chuàng)造的價(jià)值,從而無(wú)形中給了算法同學(xué)優(yōu)化資源的壓力驶臊。
2.7 GPU共享技術(shù)
通過(guò)GPU共享技術(shù)(詳見(jiàn) Kubernetes GPU 共享技術(shù)調(diào)研 和 Kubernetes GPU共享實(shí)踐)我們實(shí)現(xiàn)了顯卡的一卡多用挪挤,GPU使用率提升了80%。
2.8 調(diào)度方案升級(jí)
K8s默認(rèn)的調(diào)度插件是kube-scheduler, kube-scheduler默認(rèn)會(huì)把任務(wù)調(diào)度到資源最空閑的機(jī)器上资铡,而機(jī)器學(xué)習(xí)任務(wù)大都是重型任務(wù)电禀,不能有效地實(shí)現(xiàn)填補(bǔ)“資源碎片”幢码。
于是笤休,我們使用華為開(kāi)源的Volcano替代了kube-scheduler,從而實(shí)現(xiàn)了更合理的任務(wù)調(diào)度方式症副。
Volcano 首先要解決的問(wèn)題就是Gang Scheduling的問(wèn)題店雅,即一組容器要么都成功,要么都別調(diào)度贞铣。這個(gè)是最基本的用來(lái)解決資源死鎖的問(wèn)題闹啦,可以很好的提高集群資源利用率(在高業(yè)務(wù)負(fù)載時(shí))。除此之外辕坝,它還提供了多種調(diào)度算法窍奋,例如priority優(yōu)先級(jí),DRF(dominant resource fairness)酱畅, binpack等琳袄。 我們今天就是挖一挖Volcano內(nèi)部的各種調(diào)度算法實(shí)現(xiàn)。
- Gang Scheduling
這種調(diào)度算法纺酸,首先就是有’組’的概念窖逗,調(diào)度結(jié)果成功與否,只關(guān)注整一’組’容器餐蔬。
具體算法是碎紊,先遍歷各個(gè)容器組(代碼里面稱為Job)佑附,然后模擬調(diào)度這一組容器中的每個(gè)容器(代碼里面稱為Task)。最后判斷這一組容器可調(diào)度容器數(shù)是否大于最小能接受底限仗考,可以的話就真的往節(jié)點(diǎn)調(diào)度(代碼里面稱為Bind節(jié)點(diǎn))音同。
- DRF(dominant resource fairness)
這種調(diào)度算法,主要是Yarn和Mesos都有痴鳄,而K8S沒(méi)有瘟斜,需要補(bǔ)齊。概括而言痪寻,DRF意為:“誰(shuí)要的資源少螺句,誰(shuí)的優(yōu)先級(jí)高”。因?yàn)檫@樣可以滿足更多的作業(yè)橡类,不會(huì)因?yàn)橐粋€(gè)胖業(yè)務(wù)蛇尚,餓死大批小業(yè)務(wù)。注意:這個(gè)算法選的也是容器組(比如一次AI訓(xùn)練顾画,或一次大數(shù)據(jù)計(jì)算)取劫。
- binpack
這種調(diào)度算法,目標(biāo)很簡(jiǎn)單:盡量先把已有節(jié)點(diǎn)填滿(盡量不往空白節(jié)點(diǎn)投)研侣。具體實(shí)現(xiàn)上谱邪,binpack就是給各個(gè)可以投遞的節(jié)點(diǎn)打分:“假如放在當(dāng)前節(jié)點(diǎn)后,誰(shuí)更滿庶诡,誰(shuí)的分?jǐn)?shù)就高”惦银。因?yàn)檫@樣可以盡量將應(yīng)用負(fù)載靠攏至部分節(jié)點(diǎn),非常有利于K8S集群節(jié)點(diǎn)的自動(dòng)擴(kuò)縮容功能末誓。注意:這個(gè)算法是針對(duì)單個(gè)容器的扯俱。
從下圖可以看到,優(yōu)化前資源碎片率高達(dá)43%:
優(yōu)化之后喇澡,資源碎片率降低到了30%迅栅,并且還在持續(xù)自動(dòng)優(yōu)化:
后記
資源優(yōu)化是一個(gè)曲折而艱辛的道路,在這個(gè)過(guò)程中我們付出了很多努力晴玖,得到的成果也是非常豐碩的读存。雖然目前整體65%的資源利用率在很多人看來(lái)好像不是很高,主要是因?yàn)闄C(jī)器學(xué)習(xí)任務(wù)大多是重型任務(wù)呕屎,沒(méi)有足夠的小任務(wù)把資源碎片消除干凈
让簿。后續(xù)我們也在考慮和運(yùn)維同學(xué)合作,調(diào)度一些微服務(wù)到 K8s 集群中榨惰,來(lái)盡可能的填補(bǔ)資源碎片
拜英,那時(shí)候資源利用率提升到80%以上應(yīng)該也不是難事。
最后琅催,希望我們的資源優(yōu)化方案能起到拋磚引玉的作用居凶,當(dāng)大家遇到類似的問(wèn)題時(shí)虫给,能給大家提供一些解題思路我就心滿意足了。