YARN 在字節跳動的優化與實踐

導讀: 

本文從利用率提升、多負載場景優化、穩定性提升、異地多活四個方面介紹了字節跳動在四年來對 Hadoop YARN 進行的一系列的優化,以及生產環境中的實踐經驗。

1.YARN 簡介

1.1 YARN 生態圈

YARN (Yet Another Resource Negotiator) 是 Hadoop 集羣的資源管理系統,是 Hadoop 生態中非常重要的成員項目。

一般來說,離線生態可以分爲五層:

  • 最底層是裸金屬層, 由衆多物理節點組成,每個節點上運行着通用的操作系統。

  • 次底層是集羣資源管理層, YARN 就處在這一層中。

  • 再往上是分佈式計算引擎層, MR/Spark/Flink 等計算引擎處於這層,爲了能讓業務同學更加低成本的寫計算任務, 各個引擎都支持 SQL 功能。

  • 再往上是作業託管層,用來提交 ad-hoc 的作業,管理週期性的批處理作業,管理長時間運行的流式作業。

  • 最上層是用戶邏輯層,如數據日報,數據分析,模型訓練等.

1.2 YARN 架構

上圖中灰色背景區域是 YARN 的主要架構, 主要包含兩種角色:

  • ResourceManager

    • 整個集羣的大腦,負責爲應用調度資源,管理應用生命週期。

    • 對用戶提供接口,包括命令行接口,API, WebUI 接口。

    • 可以同時存在多個 RM,但同一時間只有一個在工作,RM 之間通過 ZK 選主。

  • NodeManager

    • 爲整個集羣提供資源,接受 Container 運行。

    • 管理 Contianer 的運行時生命週期,包括 Localization,資源隔離,日誌聚合等。

YARN 上運行的作業:

  • 在運行時會訪問外部的數據服務,常見的如 HDFS,Kafka 等

  • 會在運行結束後由 YARN 負責將日誌上傳到 HDFS 中

2.字節跳動對 YARN 的定製

字節跳動的 YARN 是在 16 年從社區當時最新的 2.6.0 版本中 fork 出來的,主要承載着公司內的離線作業/流式作業/模型訓練三大場景。由於公司內的 YARN 服務規模巨大、場景複雜,遇到了各種問題,在社區版本沒有提供解決方案之前,內部研發同學定製了許多內容來解決具體問題,經過 4 年來上千次的修改,公司內的版本已經跟社區的版本相差較大。

今天給大家介紹一些比較關鍵的定製,希望能給大家帶來一些啓發。這些關鍵定製主要包括四個方面:

  • 利用率提升: 包括分配率提升和物理使用率提升。

  • 多種負載場景優化: 包括批處理 / 流式 / 模型訓練 三種場景下的體驗提升。

  • 穩定性提升: 包括擺脫對 HDFS 強依賴, Container 分級與驅逐, 非受控 Container 管理。

  • 異地多活: 包括統一的 YARN Client 和 UI 等內容。

2.1 利用率提升

2.1.1 多線程版本的 Fair Scheduler

社區原生版本的 FairScheduler 是單線程的,在節點數量較多時,是整體集羣最大的瓶頸。

我們通過將 FairScheduler 改造爲併發的多線程版本,並將調度器內部的鎖拆分爲更加細粒度的讀鎖和寫鎖,將調度吞吐提升 7 倍以上,在生產環境中達到每秒 3K 個 Container 的速度(未觸及性能瓶頸)。

2.1.2 考慮節點 DRF 的調度

原生的 YARN 在調度時只考慮資源是否滿足,經常會出現一個節點 CPU 被打滿,但是內存還有剩餘的情況。

我們引入節點 DRF(Dominant Resource Fairness)機制,計算每個節點的剩餘資源的主資源,當調度的 Task 的主資源與節點的主資源不匹配時,先延遲此次調度,直到一定次數後再放鬆約束。

通過引入這個機制,集羣資源的碎片化問題大幅降低,生產環境中可以達到 CPU 和內存的 24 小時平均利用率都在 90%以上。

2.1.3 提升單集羣規模

單個集羣的規模越大,就可以有更多的用戶和作業使用這個集羣,這個集羣的利用率也會更高。但是原生的 YARN 在達到 5K 節點規模時開始出現各種問題,比如說一次簡單的切主可能會導致整個集羣雪崩。

我們爲提升單集羣規模做了一系列的優化。

  • 首先,通過對 YARN 內部事件梳理調整,精準的修改了一些事件處理邏輯。

  • 然後,將 NodeManager 節點的心跳機制改爲根據 ResourceManager 的壓力動態調整。

  • 之後,修改內存單位(int->long)突破單個集羣 21 億 MB 的限制

  • 再之後,通過對切主過程進行深度優化, 將切主時間控制在秒級

當然還有很多其它的細節優化不再一一列舉,最終的效果是讓單個生產集羣達到了 2 萬節點的規模。

2.1.4 與流式&在線服務混部

公司內離線的資源全天都比較緊張,而流式作業和在線服務的資源使用量隨着用戶的行爲,在時間上有明顯的波峯波谷,在凌晨時通過混部的方式將流式和在線富餘的資源提供給離線可以全面的提升利用率。

我們通過將 NodeManager 改造爲可以根據宿主機的富餘資源動態的調整的 NM',來達到與流式作業和在線服務的混部,爲離線提供更多資源的目的。

目前生產環境中已有數萬臺節點進行了混部,混部後將原機器的 CPU 利用率絕對值提升了 20%以上。

2.1.5 Smart Resource : 在運行時/重啓時調整資源

原生的 YARN 中,用戶申請的資源和實際使用的資源經常會出現比較大的偏差, 導致出現大量的資源浪費的情況,爲此我們開發了一整套的資源動態調整方案,可以將申請的資源調整到接近於實際使用資源的數值。

並且,在實際使用中發現,如果資源調整必須以一個核爲最小粒度的話,還是會出現很嚴重的浪費,比如用戶真實的需求可能是 0.001 個核*1000,原生的 YARN 只能分配 1000 個核,就白白浪費了 999 個核。我們開發了以千分之一核爲最小粒度的功能,可以有效的減少資源的浪費。並且千分之一核與資源動態調整結合,可以更加精細化的調整資源。

2.2 多種負載場景優化

字節跳動的 YARN 承載了公司內的 批處理 / 流式 / 模型訓練 三大場景,由於 YARN 天生是爲批處理而設計的,很多地方與流式 / 模型訓練場景並不匹配,爲了給這些場景更好的體驗,需要做一些定製工作。

2.2.1 YARN Gang Scheduler 調度器

流式作業和訓練作業的調度需求與批處理有很大的不同:批處理強調的是高吞吐,而流式/訓練類型的作業更加強調低延遲和全局視角。爲了彌補原生 YARN 在低延遲和全局視角上的缺陷,我們開發了一個全新的調度器 Gang Scheduler。

Gang Scheduler 提供了一個 All-or-Nothing (一次全交付或不交付)的語義,如作業申請 1000 個 container,那麼要麼直接返回 1000 個 container,要麼就返回失敗,並提示失敗的原因。這樣可以有效的避免兩個作業都只拿到一半的資源,誰也無法啓動的互鎖局面。

除此之外,Gang Scheduler 還有個特性是超低延遲, 它可以在毫秒級給出 All-or-Nothing 的結論,這樣可以大大緩解流式作業在重啓時的 lag 積壓問題。

最重要的是,Gang Scheduler 爲流式作業和訓練作業提供了全局視角,每個作業可以通過配置自己定製的強約束和弱約束來達到全局最優的放置策略。其中,強約束是指必須要滿足的條件;弱約束是指儘量滿足,但確實無法滿足時可以接受降級的約束。目前支持的強約束包括節點屬性, 高負載等;支持的弱約束包括:節點屬性,高負載,Container 打散,Quota 平均,GPU 親和性等。

2.2.2 更加精細化的 CPU 使用策略

除了開啓 YANR 原生默認支持的 CGroup 限制之外,我們還配置了更加豐富的 CGroup 管理策略,比如在 share 模式下支持自定義的最大值限制,支持綁核,支持綁 NUMA 節點等. 通過這些措施,給流式作業和訓練作業更加靈活的管控策略,滿足不同場景下的隔離或共享需求。

2.2.3 訓練場景下的其它定製

對於訓練場景,我們還定製了更豐富的內容。包括:

  • 爲了更好的隔離性,定製了支持 GPU 和 Ceph 的 Docker

  • 爲了更靈活的資源申請,定製了帶範圍的資源值 (傳統的 YARN 資源只有個數, 沒有範圍,比如多少個 CPU,多少 GB 內存,但在訓練場景下,有時希望有範圍,比如當需要兩個 GPU 卡時,不止希望隨意的兩張卡,而是希望要一臺機器上兩個連號的 GPU 卡,比如卡 0 和卡 1 是連號的,而卡 0 和卡 2 不是連號的。這個場景同樣也適用於端口號。)

  • 爲了更高效的同時使用 CPU 和 GPU 機器,定製了節點屬性功能。

2.2.4 跳過高 Load 節點

離線批處理場景經常會遇到"Fetch Failed"的問題,主要來源是本地的磁盤 IOPS 不足,導致 Shuffle Service 卡住,爲了緩解這個問題,我們在資源調度的過程中加入目標主機 LoadAvg 的考慮因素,如果一臺機器的 LoadAvg 過高,則暫時跳過對其分配新任務. 通過這個機制,將"Fetch Failed"問題降低了約 40%。

2.3 穩定性優化

字節跳動的 YARN 服務規模巨大,在穩定性方面遇到了很多挑戰,有很多細節方面的優化, 在這裏由於時間有限,挑選幾個比較有代表性的優化點跟大家分享一下:

  • 將 HDFS 做成弱依賴

    • 對於一般的離線批處理來說,如果 HDFS 服務不可用了,那麼 YARN 也沒必要繼續運行了。但是在字節跳動內部由於 YARN 還同時承載流式作業和模型訓練,因此不能容忍 HDFS 故障影響到 YARN。爲此,我們通過將 NodeLabel 存儲到 ZK 中,將 Container Log 在 HDFS 的目錄初始化和上傳都改爲異步的方式,擺脫了對 HDFS 的強依賴。

  • Container 分級與驅逐

    • 某些 Container 的磁盤空間佔用過高,或者將單機 Load 打得非常高,會比較嚴重的影響到其它 Container 的正常運行,爲此,我們爲 YARN 定製了 Container 分級與驅逐機制。對於可能會嚴重影響到其它 Container 的 Container 會進行主動驅逐。對於被驅逐的作業,可申請到獨立的 Label 中運行。

  • 非受控 Container 的清理機制

    • 由於種種原因,線上總是會出現一些 Container 明明還在運行,但是已經不受 YARN 的管控。通常是由於不正常的運維操作產生,或者機器本身出現故障導致。對於這些 Container 如果不加管制,不僅會讓單機的實際資源緊張,有時還會造成 Kafka Topic 的重複消費導致線上事故。爲此我們在 YARN 的 NodeManager 中增加了非受控 Container 的清理機制。

2.4 異地多活

隨着公司發展迅猛,YARN 也迎來了異地多機房的場景,原生的 YARN 只支持單集羣使用,對用戶的使用造成不便,如果每個集羣都孤立的提供給用戶的話,會讓用戶使用起來很困難,爲此,我們對異地多活做了一些定製工作:

  • 全球統一的 YARN UI 界面

    • 爲所有的 YARN 用戶統一定製了一個 YARN UI 界面,該界面包含全球的所有隊列和用戶的作業。

  • 放棄數據本地性調度延遲等待

    • 當有多個集羣時,很難與 HDFS 的數據進行本地性對齊。

    • 機房內網絡資源富餘,數據本地性對性能提升不明顯,還會導致集羣吞吐下降。

  • YARN 安全模式

    • 爲了配合多機房容災,有時需要主動將部分 YARN 集羣設置爲不調度新任務的安全模式。

3. 未來工作

未來我們會持續的優化與流式和在線服務的混部工作,包括:

  • 物理利用率提升

  • 更好的隔離

  • 更加可控的殺死率

  • GPU 資源的混部

同時,我們也會繼續完善 YARN Gang Scheduler,包括:

  • 更加豐富的調度謂詞

  • 更加低延遲

4. 團隊介紹

基礎架構 YARN 團隊負責字節跳動公司內部離線/流式/模型訓練三大場景的資源管理和調度, 支撐了推薦/數倉/搜索/廣告等衆多核心業務,管理着在集羣規模、調度吞吐能力、資源利用率、業務複雜性等多個方向上都在業界領先的超大規模集羣。

針對公司內的抖音、今日頭條等產品重度依賴推薦的特點, 團隊對調度器進行了深度定製以支持流式(Flink)訓練和 GPU 訓練等場景, 擁有幾十項專利技術。同時爲了進一步提升集羣資源利用率,調度團隊已經開啓在離線大規模混部,並且預期在不久後會進一步融合 YARN / K8S 等調度系統。

猜你喜歡

1、從行存儲到 RCFile,Facebook 爲什麼要設計出 RCFile?

2、Apache Spark 3.0.0 正式版終於發佈了,重要特性全面解析

3、Kafka架構原理,也就這麼回事!

4、來自 Facebook 的 Spark 大作業調優經驗

過往記憶大數據微信羣,請添加微信:fangzhen0219,備註【進羣】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章