位元組跳動 YARN 雲原生化演進實踐

陶然陶然發表於2022-12-19

   1. 演進背景

  位元組跳動(以下簡稱位元組)內部離線業務具有龐大的規模,線上每天有數十萬節點執行,每天的任務數達到百萬量級,每天使用的資源量達到千萬核量級。在如此龐大的計算規模下,為了能夠高效地處理任務,提高資源流轉效率,排程系統發揮了非常重要的作用。  

  如上圖所示,我們可以清楚地看到,位元組內部排程架構分為兩大塊 —— 離線排程系統和線上排程系統,離線排程系統主要負責離線資源管理和離線任務排程,線上排程系統主要負責線上資源管理和線上任務排程。

  離線排程系統基於 YARN 實現,主要包括 Resource Manager(RM) 和 Node Manager(NM) 兩個元件,負責資源排程和容器執行時管理。位元組內部在 YARN 的基礎上進行了很多功能豐富和最佳化工作,針對不同場景實現了不同的排程器,例如:Batch Scheduler,Gang Scheduler 等。

  線上排程系統基於 Kubernetes 生態,進行了很多最佳化,支援位元組內部多樣化的線上服務。

  為了提高位元組內部整體的資源利用率,我們也進行了混部技術的探索 。 主要思路是在線上的節點上同時部署 Kubelet 和 NM 服務,當線上節點比較空閒時可以及時將空閒資源出讓給離線業務使用,以此使得整個資料中心的資源利用率能夠得到比較大的提升。

  但隨著公司內業務規模的持續發展 ,這一套系統也暴露出了一些短板 :

  首先,在離線屬於兩套系統,一些重大活動場景需要透過運維方式進行在離線資源轉換,運維負擔繁重,轉換週期長;

  其次,現在的混部架構只是在部分節點上同時部署了 NM 和 Kubelet 兩個 Agent,資源利用率仍有很大的提高空間;

  最後,在離線是兩套割裂的系統,Quota 平臺、機器運維等都不能複用,大資料作業無法享受到雲原生的各種好處,例如:資源池化、更好的單機隔離特性等。

  綜上所述, 位元組內部有三個核心訴求:

  重大活動場景(春節 / 雙 11 等),在離線資源需要能夠高效、靈活地相互轉換;

  整個資料中心的利用率需要得到更全面、充分的提升,進一步降本增效;

  在離線資源共池,Quota 管控、排程、執行、機器運維統一。

  為了實現上述訴求,我們進行了一些思考和探索。其中一種解決方案是:能不能讓離線作業直接遷移到 Kubernetes? 即:大資料生態下的各個計算引擎(包括:Spark、Flink 等)進行深度改造去適配 Kubernetes。在探索過程中發現這種方式有比較大的缺陷,主要有以下三點:

  對計算引擎侵入較深,計算引擎側需要做大量改造才能支援原先在 YARN 的各種特性;

  生產環境的作業(百萬級)非常多,如何從 YARN 平滑遷移到 Kubernetes 也是個比較大的問題;

  特別地,部分比較古老的計算引擎,比如 MapReduce,目前處於 Maintain 狀態,已經無法進行大的改造來遷移。

  基於以上思考,我們提出了一種全新的解決方案——Yodel。Yodel 的全稱是 YARN on Gödel(Gödel 是公司內部增強版 Kubernetes,它對 API Server、Gödel 排程器以及底層執行時都進行了增強),是位元組跳動提出的 Hadoop YARN 雲原生化演進實踐方案。透過 Yodel 我們將公司內的大資料業務(Spark、Flink 等)、訓練業務(Primus)平滑遷移到了 Kubernetes ,實現了在離線資源池統一,提升了整體資源利用率。

   2. 解決方案

  下面將從 Yodel 整體架構、Remote Godel Scheduler(RGS) 服務、Remote Kubelete Service(RKS) 服務、持久化服務、平滑遷移及重要最佳化六個方面來詳細介紹我們的解決方案。

  2.1 Yodel 整體架構

  Yodel 基於 YARN 實現,新增 ZK / ETCD / KV State Store、Remote Godel Scheduler 、Remote Kubelet Service 服務。ZK / ETCD / KV State Store 主要用於持久化儲存、Remote Godel Scheduler 維護資源請求並與 API Server 互動,將排程能力統一到 Godel Scheduler;Remote Kubelet Service 實現了 YARN NM 所有介面,對使用者和作業透明的前提下,把 NM 的 Container 管理能力平滑下沉到 Kubelet。  

  Yodel 整體架構圖

  從上面的架構圖可以清楚看到 Yodel 的整體架構, 圖中藍色元件是進行了適配改造的元件,藍色中標紅的元件是新增元件,黃色元件是 Gödel 生態下的元件,關於新增元件:

  ZK / ETCD / KV State Store:支援將叢集後設資料資訊持久化到 ZK、 ETCD 和 KV 等持久化儲存,可以透過 API Server 方便地進行相關資料查詢和更新;

  Remote Godel Scheduler:維護叢集所有任務的資源請求,透過該服務將任務的資源請求轉化為 Pod 寫入 API Server,同時與 API Server 互動獲取已排程的 Pod,最終將排程能力下沉到底層的 Godel Scheduler;

  Remote Kubelet Service:實現了原來 YARN 中 NM 的所有介面,例如:啟動容器、停止容器、獲取容器狀態的介面。透過這個服務容器啟動從 NM 切換到 Kubelet,最終將容器執行時的管理下沉到底層的 Kubelet。

  下面介紹在 Yodel 架構下一個離線任務的提交和執行流程 :

  使用者從開發機或任務託管平臺向叢集提交一個任務;

  當任務經過校驗後,Yodel RM 會新建一個 App 物件並持久化至 API Server;

  Yodel RM 建立 AM Pod 並寫入 API Server,等待底層排程器排程;

  Yodel RM 收到已經排程完成的 AM Pod 並進行相關轉化操作;

  Yodel RM 將相關啟動資訊豐富至 AM Pod 中並 Patch 至 API Server 由 Kubelet 拉起相關程式;

  AM 啟動成功後,隨心跳主動向 Yodel RM 申請資源;

  Yodel RM 收到任務的資源請求後,透過 RGS 服務將資源請求轉化為 Pod 物件或 PodGroup 物件並寫入到 API Server;

  底層排程器 Watch 到相關物件後,按照一定策略進行排程,同時 Yodel RM 也會及時地 Watch 到已經排程的 Pod;

  Yodel RM 會將已經排程的 Pod 轉化為 Container,隨心跳返回給對應的 AM;

  AM 收到已經排程的 Container 後,會再跟 Yodel RM 進行互動,來啟動對應的容器;

  Yodel RM 收到容器啟動請求後,透過 RKS 服務將容器啟動所需要的資訊豐富到 Pod 物件裡並 Patch 到 API Server。Kubelet Watch 到待啟動的 Pod 後,會進行這個 Pod 的啟動。  

  Yodel 架構 Pod 生命週期

  上面講了 Yodel 架構下任務的啟動流程,下面我們來看一下對於一個 Pod 來說,它的生命週期是怎麼樣的 ,核心流程如上圖所示:

  首先,AM 啟動起來後會隨心跳申請資源;

  Yodel RM 收到資源請求後,會基於該資源請求的資源量、優先順序等建立一個 Pod 物件寫入 API Server。建立完成後,該 Pod 物件處於 Pending-Unscheduled 狀態,等待底層排程器進行排程;

  底層排程器 Watch 到新建立的 Pod 後,根據一定策略進行排程,排程完成後會將排程結果寫入 API Server。寫入完成後,該 Pod 物件的狀態會變為 Pending-Scheduled 狀態;

  Yodel RM Watch 到已經排程完成的 Pod 後會轉化為 Container,該 Pod 物件的狀態會變為 Allocated 狀態;

  新分配的 Container 會隨心跳返回給 AM,Container 被對應 AM 拿走後,該 Pod 物件的狀態會變為 Acquired 狀態;

  AM 獲取到容器後會與 Yodel RM 互動進行啟動操作;

  Yodel RM 收到容器拉起請求後,會把容器啟動所需的資訊填充到 Pod 物件中並 Patch 到 API Server ;

  Kubelet Watch 到需要啟動的 Pod 後,會啟動相關程式,容器執行時由 Kubelet 維護。

  2.2 Remote Godel Scheduler  

  下面來介紹排程模組比較重要的一個服務 —— RGS 服務。由上圖可以看到,RGS 服務主要分為三大部分:

  最上層是 Quota Manager 負責進行 Quota 管理: Quota Manager 部分在 YARN 基礎上進行了增強。在 YARN 中有佇列的概念,但佇列只支援一種資源型別。在 Yodel 中對此進行了擴充套件,一個佇列可以同時支援兩種型別的資源 —— Guaranteed Resource 和 Best-effort Resource。單佇列支援兩種資源型別後可以顯著簡化使用者的佇列管理成本,對使用者使用更友好。

  Guaranteed Resource :穩定資源,使用 Guaranteed Resource 的容器一般情況下不會被搶佔也不會被驅逐;

  Best-effort Resource:混部資源, 是線上節點出讓的暫時空閒不用的資源,資源會隨著節點負載情況動態波動,使用 Best-effort Resource 的容器可能會被搶佔或驅逐;

  中間層是 Allocate Service 負責進行請求轉換和狀態維護: 主要包括四個子服務,Convert Reqeust To Pod Service 負責將任務的資源請求轉化為 Pod 物件並寫入 API Server;Convert Pod To Container Service 負責將已經排程的 Pod 轉化為 Container 並返回給 AM;Update Pod Status Service 負責及時更新 Pod 狀態並持久化至 API Server ;Delete Pod Service 負責在容器或任務結束時,及時刪除 API Server 中的相關物件。

  最下層是 Remote Scheduler 負責進行排程和關鍵資訊持久化。

  透過上述各個服務的協調配合, Yodel 能夠實現:

  100% 相容 Hadoop 協議,使用者無需要做任何改動,可以像原來使用 YARN 一樣來使用 Yodel;

  支援 GT 和 BE 兩種資源型別,方便上層使用者對平臺的使用。

  2.3 Remote Kubelet Service  

  接下來介紹 RKS 服務。RKS 部署在 Yodel RM 內部,實現了 YARN NM 的所有介面,把 NM 的 Container 管理能力平滑下沉到 Kubelet。它主要由兩部分組成:

  Patch Pod Service : 主要負責收到 AM 拉起請求後,將容器啟動所需的資訊豐富到 Pod 物件中,這些資訊包括:容器的 ENV 、 HDFS 自研列表、啟動命令等;

  Pod Status Update Service : 該服務會及時從 API Server Watch Pod 的最新狀態,並將狀態返回給對應 AM。

  此外,為了補齊 NM 上的執行體驗,底層以 daemonset 方式部署了一些其他服務。這些 daemonset 補齊了 NM 的能力,使得離線作業只需要升級 hadoop 依賴,不用做太多改動,就能讓容器執行在 Kubelet 上。這些服務包括:

  LocalizationService : 用於下載 Pod 所需的 HDFS 資源;

  Log Serving : 用於方便使用者檢視 Pod 日誌;

  Shuffle Service : 主要有 Spark Shuffle Service 及 MR Shuffle Service,這些 Shuffle Service 是從 NM 的程式解耦出來的,單獨部署用於提供計算框架的 Shuffle 服務;

  Metrics Collector : 用於收集離線 Pod 執行時的各維度監控資訊;

  Webshell : 方便使用者透過 Web 端進入到容器的 Shell,方便排查問題。

  下面看一個容器是怎麼執行在 Kubelet 上的:

  改造了 NM Client SDK,使 AM 呼叫 startContainer 時能直連 RKS;

  RKS 收到啟動請求後,會把 containerLaunch 上下文等資訊寫入到 Pod 並 Patch 到 API Server;

  Kubelet Watch 到離線 Pod 後,會透過本機的 LocalizationService 下載 Pod 對應的 HDFS 資源;

  下載完成後, Kubelet 透過 Containerd 把對應的 HDFS 資源掛載到容器的 Pod 裡,之後透過 Containerd 啟動 Pod;

  啟動完成後,Kubelet 會把 Pod 的狀態更新回 API Server;

  RKS watch 到 Pod 狀態變化後,同步更新記憶體中的 Container 狀態,之後等待 AM 心跳時同步 Container 最新狀態。

  2.4 持久化服務  

  YARN 架構是透過 ZKRMStateStore 將後設資料資訊持久化到 ZooKeeper,而 Yodel 架構,我們自己實現了一個 KVStateStore 儲存後設資料到 API Server,儲存的後設資料包括 MetaData,Queue,Application,Appattempt 和 Pod。現線上上的一個 API Server 可以支援儲存 300 queue,2w 個 application,10w 個 app attempt,以及支援 30W 離線 Pod 同時執行。

  MetaData:叢集後設資料資訊、叢集預設配置等;

  Queue (~300 / cluster) :佇列 Quota、ACL 資訊等;

  Application(~2W / cluster) :Name、User、State 等;

  AppAttempt(~10W / cluster) :Name、User、State 等;

  Pod (~30W / cluster) :State、Annotation、ENV、HDFS 自研列表、啟動命令等。

  2.5 平滑遷移  

  位元組內每天執行著百萬量級的任務,如何平滑地把作業從 YARN 架構遷移到 Yodel 架構是一個很大的挑戰,整體上我們是透過 ResLake 來完成的,首先介紹幾個關鍵元件:

  WorkFlow Hosting:作業託管平臺,負責進行作業提交;

  ResLake:是 RM Proxy,可以根據一定策略把作業路由到不同叢集;

  Quota Platform:用於同步佇列的 Quota 資訊;

  AutoMigration:負責從 YARN 叢集下線節點,搬遷到 Yodel 叢集上。

  ResLake 在 YARN 叢集和 Yodel 叢集上有同名的佇列,如上圖中的 root.queueA 和 root.queueB,這兩個叢集上的佇列有著相同的後設資料資訊。AutoMigration 服務會不斷地從 YARN 搬遷節點到 Yodel 叢集,搬遷資訊會同步給 Quota Platform,Quota Platform 會進一步將佇列 Quota 資訊同步給 Reslake。作業託管平臺提交作業到 ResLake 時,ResLake 會根據 YARN/Yodel 上佇列的 Quota 資訊,決定作業是提交到 YARN 叢集還是 Yodel 叢集。隨著機器不斷地往 Yodel 搬遷,最終作業也平滑遷移到了 Yodel 叢集上。

  2.6 重要最佳化

  在 Yodel 架構升級上線過程中,也遇到了很多問題,我們也做了非常多的最佳化,主要包括效能最佳化和執行最佳化。

  2.6.1 效能最佳化

  Recover 階段非同步恢復 Pod 狀態降低切主時間(秒級):起初為了確保切主後叢集、佇列和任務的各維度統計資訊準確,採用同步方式恢復 Pod,但上線時發現恢復過程非常耗時。為此透過最佳化,在確保各維度資訊統計準確的前提下非同步恢復 Pod 狀態,將切主時間縮短到秒級;

  非同步多執行緒操作 Pod 以提高排程吞吐(~2K / s):透過非同步多執行緒方式將已經排程 Pod 轉化為 Container 後,排程吞吐得到顯著提升,目前排程吞吐可以達到每秒 2000 個 Pod;

  PodName 雜湊最佳化助力底層儲存寫延遲降低為原來的 1 / 100(百分之一) : 因 API Server 底層採用基於 range 的 KV 儲存,若 PodName 有序會頻繁產生分割槽裂變,導致 API Server 的相關處理延遲顯著增加。透過將 PodName 進行雜湊最佳化,將 Pod 打散儲存在不同的分割槽中,底層儲存寫延遲下降 100 倍;

  與 API Server 互動增強,Java Fabric8 Kubernetes Client 最佳化:

  支援指數退讓重試,增強 API Server 故障容錯;

  List 操作預設新增 ResourceVersion 引數,避免擊穿到底層儲存;

  將 Informer Resync 設定為 0,避免頻繁記憶體複製造成 OOM。

  2.6.2 執行最佳化

  AM 容器執行在單獨資源池,獨立優先順序不可搶佔:對於使用 BE 資源的容器有被搶佔或驅逐的風險,而 AM 作為任務的 Master 一旦失敗就會導致整個任務失敗。為了避免此問題,將 AM 容器執行在單獨的資源池,確保 AM 可以穩定執行避免任務失敗;

  支援映象本地化約束,平均拉起速度提升約 1000 倍:一些離線任務的映象比較大,在容器啟動時拉取映象會花費較多時間,進而導致啟動時間變長。為了解決該問題,支援了映象本地化約束,讓容器可以儘量排程到有映象的節點,該功能上線後容器平均拉起速度提升 1000 倍;

  支援雙棧節點和 v6 only 節點在單叢集混跑:透過將雙棧節點和 v6 only 節點混跑在同一個叢集中顯著降低了運維成本,同時也有利於資源利用率提升;

  Shuffle 資料寫遠端,避免打爆本地磁碟:shuffle 資料通常較大很容易將本地磁碟打滿,將 shuffle 資料寫遠端後,可以避免因本地磁碟打滿而導致任務執行異常;

  大量引入 SSD 和 Nvme 磁碟,加速作業執行。

   3. 上線收益

  Yodel 架構已經在位元組內部上線,上線後帶來了如下收益:

  高效資源切換:實現了 2022 元旦/春節 約 50 萬核離線資源分鐘級出讓,顯著提高了在離線資源轉化效率,為重大活動場景下的資源切換提供了堅實的技術支撐;

  利用率提升:NM 和周邊單機元件下線,降低 Overhead,帶來單機 2% 利用率提升;

  在離線統一:在離線資源全量共池,Quota 管控、排程、執行、機器運維統一。

   4. 未來規劃

  RGS & RKS 部署雲原生化

  接入服務發現

  支援容器化部署

  可彈性擴充套件

  開源 Yodel 回饋社群

來自 “ 位元組跳動技術團隊 ”, 原文作者:邵凱陽 林友權;原文連結:http://server.it168.com/a2022/1219/6781/000006781402.shtml,如有侵權,請聯絡管理員刪除。

相關文章