本文通過分析學習兩篇文章來看目前工業界可能偏好的解決方案,也順便探尋下yarn的未來發展方向。
[業界方案] yarn的業界解決方案和未來方向
目錄
3.1 實體使用率提升
3.2 多叢集
3.3 流式和線上服務的混合部署
2.1 綜述
2.2 關鍵定制
2.3 具體定制
2.1.1 yarn在快手的應用特點
2.1.2 位元組跳動 yarn 應用特點
2.2.1 yarn在快手的應用特點
2.2.2 位元組跳動 yarn 應用特點
2.3.1 事件處理
2.3.2 排程方面
2.3.3 資源方面
2.3.4 穩定性
2.3.5 單點問題
2.3.6 與流式&線上服務混部
2.3.7 訓練場景
2.3.8 單叢集規模
1.1 參考文章
0x00 摘要
0x01 yarn
0x02 分析
0x03 yarn的未來規劃
yarn是叢集資源管理層,分成了三個子產品:
resourcemanager 管理整個叢集的資源;
整個叢集的大腦,負責為應用排程資源,管理應用生命周期。
對使用者提供接口,包括指令行接口,api, webui 接口。
可以同時存在多個 rm,但同一時間隻有一個在工作,rm 之間通過 zk 選主。
nodemanager 管理整個機器資源情況;
為整個叢集提供資源,接受 container 運作。
管理 contianer 的運作時生命周期,包括 localization,資源隔離,日志聚合等。
applicationmaster管理整個app的資源資訊。mr/spark/flink實作了自己的am邏輯在yarn上運作。
主要是參考下面兩篇文章,個人覺得有代表性,可以管中窺豹。下面對此兩篇文章的内容一律以引用格式。
yarn 在快手的應用實踐與技術演進之路
yarn 在位元組跳動的優化與實踐
可以看出來兩個公司應用領域都差不多:離線作業/流式作業/模型訓練三大場景。
快手主要是:根據不同領域做不同定制。
位元組跳動是:沒有針對領域做不同定制(當然也可能是沒有披露)。
yarn上面服務了一些經典的離線計算,像hivesql,最終回變成一個mr/spark作業或者pesto查詢在yarn上運作。 對于流式的實時資料處理需求,我們上層有一個青藤平台來托管flink在yarn上運作。 對于模型訓練的場景,我們是用xlearning作為排程器,排程tensorflow,xgboost,mpi等訓練學習引擎。 基于spark和xlearning,我們打造了亞瑟機器學習平台,可以把資料處理流程和一些模型的訓練、預測流程做一個打通,友善使用者使用。
位元組跳動的 yarn 是在 16 年從社群當時最新的 2.6.0 版本中 fork 出來的,主要承載着公司内的離線作業/流式作業/模型訓練三大場景。由于公司内的 yarn 服務規模巨大、場景複雜,遇到了各種問題,在社群版本沒有提供解決方案之前,内部研發同學定制了許多内容來解決具體問題,經過 4 年來上千次的修改,公司内的版本已經跟社群的版本相差較大。
可以看到:穩定性,調用性能和使用率是兩個公司都注重的。
此外快手提到了 “小io優化”。位元組跳動提到了“異地多活”。
對yarn的一些改動,主要分成四個方面: (1)叢集穩定性方面的優化。 (2)對yarn的搶占機制做了優化。 (3)yarn的排程性能提升。 (4)計算叢集小io優化。
這些關鍵定制主要包括四個方面: 穩定性提升: 包括擺脫對 hdfs 強依賴, container 分級與驅逐, 非受控 container 管理。 使用率提升: 包括配置設定率提升和實體使用率提升。 多種負載場景優化: 包括批處理 / 流式 / 模型訓練 三種場景下的體驗提升。 異地多活: 包括統一的 yarn client 和 ui 等内容。
下面把兩個公司具體優化點整合起來介紹。
yarn中幾個子產品之間有大量的事件傳遞和處理,其中勢必有缺陷和可優化之處,這就是業界優化之處,比如優化備援,解耦合。具體展現在速度提升,對io操作優化,叢集啟動上。
事件處理是單線程的,會有各種各樣的問題,整個事件處理壓力非常大。針對這些問題我們做了一些針對性的優化。 通過對 yarn 内部事件梳理調整,精準的修改了一些事件處理邏輯。 把處理nm節點資訊的彙報等操作、抽離出來,放在一個額外的線程上。 将 nodemanager 節點的心跳機制改為根據 resourcemanager 的壓力動态調整。 rm和nm互動有一些備援事件的,我們對備援事件進行了一些優化。對于nm,我們設計了一個慢啟動的政策,如果nm剛啟動沒有必要維持每秒彙報一次,開始可以20秒彙報一次,下次10秒,下次5秒,最終恢複到正常,這樣會把整個rm的事件處理壓力降下來。 優化事件處理的耗時操作: rm對hdfs的操作主要集中在失敗app的處理,不是非常核心的邏輯,解決方案是把hdfs的操作從同步改成異步。 把像dns的操作這種比較重io的操作進行相應的優化,確定事件處理邏輯中都是快速的cpu操作,保證事件處理的高效。
yarn有三種排程器,都不能滿足兩個公司的需求,兩個公司都對排程做了優化,或者幹脆重新寫了排程器。
yarn的排程模型各種排序要耗費很大資源。減少排序時間從三方面着手。 減少排序規模。實際場景中大部分是不需要資源和參與排序的,這樣把整個排序規模減小了。 減少單次排序時間, 優化排序算法。在yarn裡面每一次compare的時候有很多可以可以優化的地方,比如計算一個隊列使用的資源量,有一些臨時對象可以cache住,最終縮小整個單次排序的時間。collection.sort底層使用歸并排序,我們改成堆排序。 為了提高排程的擴充性,我們重寫排程邏輯,開發了kwaischeduler。 底層實作主要分成兩部分。一部分是叢集資源的預配置設定過程,把一些資源配置設定到每個app。第二部分是app怎麼去每台機器上競争資源。 一個單獨線程定期會對叢集情況做snapshot,基于snapshot來做一個上帝視角的資源配置設定。我們為每個app配置設定出資源之後,就可以把app丢到線程池裡面并發搶資源,對相應的節點排序,選出分數最高的節點,最終你會有一個commit的過程,真正拿到資源。整個排程配置設定結束後,會把整個配置設定結果寫回到整個原生的yarn架構。 處理很多的小io問題。很多的小io,導緻整個叢集磁盤util非常高,但是磁盤讀寫速度非常慢。針對這個問題,我們對mr的shuffle過程做一個cache。在shuffle過程中,當一個請求來的時候,我們分析一下這次shuffle過程有沒有可能産生比較多小io,可以按需把shuffle資料放到cache裡,隻需要一次大的io把資料搬到cache裡面,後面的shuffle請求可以直接從cache裡面讀,消滅了後面多次小io,通過這個我們優化,提升了整個的叢集io性能。
社群原生版本的 fairscheduler 是單線程的,在節點數量較多時,是整體叢集最大的瓶頸。我們通過将 fairscheduler 改造為并發的多線程版本,并将排程器内部的鎖拆分為更加細粒度的讀鎖和寫鎖,将排程吞吐提升 7 倍以上。
從現實看,資源的隔離和利用都有缺陷,是以兩個公司都做了細化、優化。
現在社群主要是用cgroup做一些記憶體和cpu的隔離,其他方面的隔離非常弱的,我們當時碰到一些場景比如磁盤打滿了,fd洩露、線程洩露的問題。解決方案就是對container的線程數目,磁盤大小定期檢查,如果超過阙值,直接kill掉。 除了開啟 yanr 原生預設支援的 cgroup 限制之外,我們還配置了更加豐富的 cgroup 管理政策,比如在 share 模式下支援自定義的最大值限制,支援綁核,支援綁 numa 節點等. 通過這些措施,給流式作業和訓練作業更加靈活的管控政策,滿足不同場景下的隔離或共享需求。 原生的 yarn 中,使用者申請的資源和實際使用的資源經常會出現比較大的偏差, 導緻出現大量的資源浪費的情況,為此我們開發了一整套的資源動态調整方案,可以将申請的資源調整到接近于實際使用資源的數值。 在實際使用中發現,如果資源調整必須以一個核為最小粒度的話,還是會出現很嚴重的浪費,比如使用者真實的需求可能是 0.001 個核*1000,原生的 yarn 隻能配置設定 1000 個核,就白白浪費了 999 個核。我們開發了以千分之一核為最小粒度的功能,可以有效的減少資源的浪費。并且千分之一核與資源動态調整結合,可以更加精細化的調整資源。 原生的 yarn 在排程時隻考慮資源是否滿足,經常會出現一個節點 cpu 被打滿,但是記憶體還有剩餘的情況。我們引入節點 drf(dominant resource fairness)機制,計算每個節點的剩餘資源的主資源,當排程的 task 的主資源與節點的主資源不比對時,先延遲此次排程,直到一定次數後再放松限制。
這點和下面一點屬于穩定性方面。
将 hdfs 做成弱依賴 對于一般的離線批處理來說,如果 hdfs 服務不可用了,那麼 yarn 也沒必要繼續運作了。但是在位元組跳動内部由于 yarn 還同時承載流式作業和模型訓練,是以不能容忍 hdfs 故障影響到 yarn。為此,我們通過将 nodelabel 存儲到 zk 中,将 container log 在 hdfs 的目錄初始化和上傳都改為異步的方式,擺脫了對 hdfs 的強依賴。 container 分級與驅逐 某些 container 的磁盤空間占用過高,或者将單機 load 打得非常高,會比較嚴重的影響到其它 container 的正常運作,為此,我們為 yarn 定制了 container 分級與驅逐機制。對于可能會嚴重影響到其它 container 的 container 會進行主動驅逐。對于被驅逐的作業,可申請到獨立的 label 中運作。 非受控 container 的清理機制 由于種種原因,線上總是會出現一些 container 明明還在運作,但是已經不受 yarn 的管控。為此我們在 yarn 的 nodemanager 中增加了非受控 container 的清理機制。
擴充了nm磁盤的黑名單功能,通過container的失敗資訊做一些規則比對,這樣可以定向發現一些磁盤問題,把疑似有問題的磁盤放在黑名單裡面,不再向這個磁盤排程作業。 社群提出了am的黑名單機制,主要來解決am的失敗問題,如果am大量失敗,不往這台機器上排程am,app内部依賴自己的黑名單機制,發現這些問題機器。我們覺得這樣可能會造成很多無效的container失敗,是以我們的解決思路是建立整個叢集的黑名單,而不單獨是am的黑名單。當我們通過一些規則發現有大量的container在某一台機器失敗,或者這台機器的container排程速度非常異常,我們會把這台機器放到我們叢集的黑名單裡面,不再向這台機器排程資源。 離線批處理場景經常會遇到"fetch failed"的問題,主要來源是本地的磁盤 iops 不足,導緻 shuffle service 卡住,為了緩解這個問題,我們在資源排程的過程中加入目标主機 loadavg 的考慮因素,如果一台機器的 loadavg 過高,則暫時跳過對其配置設定新任務. 通過這個機制,将"fetch failed"問題降低了約 40%。
本部分和下一部分都是為了特殊場景做改造。
通過将 nodemanager 改造為可以根據主控端的富餘資源動态的調整的 nm',來達到與流式作業和線上服務的混部,為離線提供更多資源的目的。 為了彌補原生 yarn 在低延遲和全局視角上的缺陷,我們開發了一個全新的排程器 gang scheduler。 gang scheduler 提供了一個 all-or-nothing (一次全傳遞或不傳遞)的語義,如作業申請 1000 個 container,那麼要麼直接傳回 1000 個 container,要麼就傳回失敗,并提示失敗的原因。這樣可以有效的避免兩個作業都隻拿到一半的資源,誰也無法啟動的互鎖局面。 gang scheduler 還有個特性是超低延遲, 它可以在毫秒級給出 all-or-nothing 的結論,這樣可以大大緩解流式作業在重新開機時的 lag 積壓問題。 gang scheduler 為流式作業和訓練作業提供了全局視角,每個作業可以通過配置自己定制的強限制和弱限制來達到全局最優的放置政策。其中,強限制是指必須要滿足的條件;弱限制是指盡量滿足,但确實無法滿足時可以接受降級的限制。目前支援的強限制包括節點屬性, 高負載等;支援的弱限制包括:節點屬性,高負載,container 打散,quota 平均,gpu 親和性等。
為了更好的隔離性,定制了支援 gpu 和 ceph 的 docker 為了更靈活的資源申請,定制了帶範圍的資源值 (傳統的 yarn 資源隻有個數, 沒有範圍,比如多少個 cpu,多少 gb 記憶體,但在訓練場景下,有時希望有範圍,比如當需要兩個 gpu 卡時,不止希望随意的兩張卡,而是希望要一台機器上兩個連号的 gpu 卡,比如卡 0 和卡 1 是連号的,而卡 0 和卡 2 不是連号的。這個場景同樣也适用于端口号。) 為了更高效的同時使用 cpu 和 gpu 機器,定制了節點屬性功能。
修改記憶體機關(int->long)突破單個叢集 21 億 mb 的限制 通過對切主過程進行深度優化, 将切主時間控制在秒級
綜合兩個公司對具體未來規劃,總結如下,基本能看出來主要思路就是:提高使用率,豐富功能,擴大應用領域。
yarn現在主要托管的是一些離線計算的資源,公司還有很多空閑資源沒有使用,怎麼來使用這些空閑資源,怎麼做到把一些合适的任務調入到一些比較空閑的機器上,當這個機器需要的時候,及時把任務遷移走,怎麼減少業務互相的影響,底層這方面需要做什麼支撐,這都需要探索。 建構作業分級保障,現在我們yarn的叢集規模比較大,大家使用的資源都非常多,但是這些資源有沒有用到真正比較重要的業務上,其實我們是有些疑問的,有多少無效的計算在裡面,當然這個涉及到業務層的優化。為作業打一些作業的标簽,基于這些任務的标簽,以及優先級的特性,刻劃整個叢集資源的使用情況,為預算或者其他的技術方案提供一些技術的底層支援。 更加豐富的排程謂詞 更加低延遲
我們現在單個yarn叢集規模在國内是top級的,但是單叢集畢竟是容量有限,我們後面會考慮多叢集建設的方案,社群的federation方案在跨idc方面有些問題,如何在業務透明的前提下,建設跨idc叢集有非常多問題需要解決。
實體使用率提升 更好的隔離 更加可控的殺死率 gpu 資源的混部