Yarn 實(shí)踐 | LinkedIn 是如何將 Hadoop Yarn 集群擴(kuò)展到超過一萬個(gè)節(jié)點(diǎn)的
在 LinkedIn,我們使用 Hadoop 作為大數(shù)據(jù)分析和機(jī)器學(xué)習(xí)的基礎(chǔ)組件。隨著數(shù)據(jù)量呈指數(shù)級(jí)增長(zhǎng),并且公司在機(jī)器學(xué)習(xí)和數(shù)據(jù)科學(xué)方面進(jìn)行了大量投資,我們的集群規(guī)模每年都在翻倍,以匹配計(jì)算工作負(fù)載的增長(zhǎng)。我們最大的集群現(xiàn)在有大約 10,000 個(gè)節(jié)點(diǎn),是全球最大(如果不是最大的)Hadoop 集群之一。多年來,擴(kuò)展 Hadoop YARN 已成為我們基礎(chǔ)設(shè)施最具挑戰(zhàn)性的任務(wù)之一。
在這篇博文中,我們將首先討論在集群接近 10,000 個(gè)節(jié)點(diǎn)時(shí)觀察到的 YARN 集群性能很慢以及我們?yōu)檫@個(gè)問題開發(fā)的修復(fù)程序。然后,我們將分享我們主動(dòng)監(jiān)控未來性能下降的方法,包括我們編寫的一個(gè)名為 DynoYARN 的開源工具,它可以可靠地預(yù)測(cè)任意大小的 YARN 集群性能。最后,我們將介紹內(nèi)部稱為 Robin 的服務(wù),使我們能夠?qū)⒓核綌U(kuò)展到 10,000 個(gè)以上的節(jié)點(diǎn)。
當(dāng) YARN 集群開始出現(xiàn)問題
與 Hadoop 分布式文件系統(tǒng) (HDFS) 的 NameNode 相比,所有文件系統(tǒng)元數(shù)據(jù)都存儲(chǔ)在一臺(tái)機(jī)器上,YARN 資源管理器則是相當(dāng)輕量級(jí)的,它只維護(hù)少量元數(shù)據(jù)。因此,我們?cè)缦葧r(shí)候遇到了 HDFS 的可擴(kuò)展性問題,并且我們?cè)?2016 年開始解決 HDFS 擴(kuò)展行問題。相比之下,Hadoop 中的計(jì)算組件 YARN 一直是基礎(chǔ)設(shè)施中一個(gè)非常穩(wěn)定的組件。
我們的集群規(guī)模每年都在翻倍,我們知道總有有一天 YARN 的可擴(kuò)展性將成為一個(gè)問題,因?yàn)橘Y源管理器的單線程調(diào)度機(jī)制無法維持集群的無限增長(zhǎng)。盡管如此,我們從未真正投入了解 YARN 的擴(kuò)展限制,并假設(shè)它可能會(huì)在下一個(gè)新技術(shù)出現(xiàn)之前起作用,直到 2019 年初我們的 YARN 集群開始出現(xiàn)了擴(kuò)展性問題。

過去,我們?cè)谝粋€(gè)數(shù)據(jù)中心構(gòu)建了兩個(gè) Hadoop 集群:主集群服務(wù)于主流量業(yè)務(wù),存儲(chǔ)和計(jì)算都是綁定在一起的,而為其他業(yè)務(wù)構(gòu)建的輔助集群存儲(chǔ)是綁定的,而計(jì)算資源是利用閑置的。為了提高資源利用率,我們將輔助 Hadoop 集群的計(jì)算節(jié)點(diǎn)合并到主 Hadoop 集群,并作為一個(gè)單獨(dú)的分區(qū)。
不幸的是,大約兩個(gè)月后,集群開始出現(xiàn)問題。
現(xiàn)象
計(jì)算節(jié)點(diǎn)合并后,集群有兩個(gè)分區(qū),分別有約 4,000 和約 2,000 個(gè)節(jié)點(diǎn)(我們稱它們?yōu)椤爸饕焙汀按我保?。很快,Hadoop 用戶在提交作業(yè)之前遇到了長(zhǎng)達(dá)數(shù)小時(shí)的延遲;然而,集群中有豐富的可用資源。
在尋找延遲的原因時(shí),我們最初認(rèn)為 Hadoop YARN 中處理軟件分區(qū)(software partitioning)的邏輯有問題,但經(jīng)過調(diào)試和調(diào)查后,我們沒有發(fā)現(xiàn)那段代碼有任何問題。我們還懷疑將集群的大小增加 50% 會(huì)使資源管理器過載,導(dǎo)致調(diào)度程序無法跟上。
我們仔細(xì)查看了隊(duì)列的 AggregatedContainerAllocation,它表示容器分配速度。合并前,主集群的平均吞吐量為每秒 500 個(gè) containers,輔助集群的平均吞吐量為每秒 250 個(gè) containers;合并后,AggregatedContainerAllocation 約為每秒 600 個(gè) containers,但分配速度也經(jīng)常在較長(zhǎng)時(shí)間(數(shù)小時(shí))內(nèi)下降至每秒 50 個(gè) containers。
我們做了幾輪分析,發(fā)現(xiàn)一些代價(jià)高昂的操作,比如 DNS 操作,其標(biāo)記了 @synchronized 注釋,這限制了并行性。將這些操作移出同步塊后,我們觀察到吞吐量提高了約 10%,但延遲對(duì)用戶來說仍然很明顯。
通過重新定義公平來減輕壓力
在解析 resource manager 的審計(jì)日志后,我們注意到調(diào)度器經(jīng)常在切換到其他隊(duì)列之前將 containers 調(diào)度在一個(gè)隊(duì)列中很長(zhǎng)一段時(shí)間。即使在性能合理的時(shí)期(每秒 600 個(gè) containers),一些隊(duì)列中的用戶也經(jīng)歷了數(shù)小時(shí)的延遲,而其他隊(duì)列中的用戶幾乎沒有經(jīng)歷過延遲。一些隊(duì)列的 containers 分配速度是正常的,但對(duì)于其他隊(duì)列已經(jīng)下降到幾乎為零。這一觀察使我們重新審視了調(diào)度程序如何決定優(yōu)先調(diào)度哪個(gè)隊(duì)列的 containers。在 LinkedIn,我們使用 Capacity Scheduler,它根據(jù)利用率對(duì)隊(duì)列進(jìn)行排序,并首先將 containers 分配給利用率最低的隊(duì)列。
假設(shè)我們有兩個(gè)隊(duì)列 A 和 B,如果 A 的利用率為 10%,B 的利用率為 20%,那么調(diào)度器將首先為隊(duì)列 A 調(diào)度 containers,然后再移動(dòng)到 B 隊(duì)列,并為提供其服務(wù)。這在大多數(shù)情況下是有效;但是,在容器流失率高的環(huán)境中可能會(huì)出現(xiàn)短暫的死鎖。假設(shè)隊(duì)列 B 中的大多數(shù)正在運(yùn)行的作業(yè)都是相對(duì)較長(zhǎng)的作業(yè),而隊(duì)列 A 中運(yùn)行的作業(yè)是非常短命的作業(yè)。由于 A 的利用率僅為 10%,因此將在隊(duì)列 A 中調(diào)度 containers 而不是隊(duì)列 B。由于隊(duì)列 A 中的 containers 流失率遠(yuǎn)高于隊(duì)列 B,當(dāng)調(diào)度程序完成隊(duì)列 A 中的一次調(diào)度工作負(fù)載迭代時(shí),隊(duì)列 A 的利用率可能保持不變甚至下降,但仍遠(yuǎn)低于隊(duì)列 B,例如 9.5%,而隊(duì)列 B 的利用率略有下降至 19%。在隊(duì)列利用率收斂并且隊(duì)列 A 的利用率超過隊(duì)列 B 之前,調(diào)度程序不會(huì)接收提交到隊(duì)列 B 的工作負(fù)載,但由于隊(duì)列工作負(fù)載的不同特征,這可能需要幾個(gè)小時(shí)。從觀察者的角度來看,調(diào)度程序似乎在調(diào)度隊(duì)列 A 中的工作負(fù)載時(shí)卡住了,而隊(duì)列 B 中的工作負(fù)載卻缺乏資源。
我們問自己為什么這只在合并兩個(gè)集群后才成為問題,并意識(shí)到主分區(qū)隊(duì)列中的工作負(fù)載主要由 AI 實(shí)驗(yàn)和數(shù)據(jù)分析組成,這些工作實(shí)現(xiàn)為更長(zhǎng)時(shí)間運(yùn)行的 Spark 作業(yè),而輔助分區(qū)隊(duì)列中的工作負(fù)載主要是快速運(yùn)行的 MapReduce 作業(yè)。如果 resource manager 可以任意快速地調(diào)度 containers,這將不是問題;然而,由于集群合并顯著降低了調(diào)度速度,分配公平性問題浮出水面。
我們提出的緩解方法是,當(dāng)調(diào)度程序分配 containers 時(shí),以相等的概率挑選隊(duì)列;換句話說,我們隨機(jī)選擇隊(duì)列而不是基于利用率。瞧!我們的問題暫時(shí)得到緩解。我們后來將補(bǔ)丁貢獻(xiàn)給了 Apache Hadoop。
效率低下的根本原因
盡管緩解了隊(duì)列公平性,但仍然沒有解決調(diào)度緩慢的根本原因。我們知道我們的 YARN 集群中仍然存在迫在眉睫的擴(kuò)展性問題。我們不得不深入挖掘!
回顧合并前后的總調(diào)度吞吐量,在最好的情況下,我們達(dá)到了 80% 的性能(每秒約 600 個(gè) containers 與每秒 750 個(gè) containers);在最壞的情況下,我們的性能僅為 7% (每秒約 50 個(gè)容器與每秒 750 個(gè)容器)。這種差距直觀地引導(dǎo)我們重新審視分區(qū)的調(diào)度邏輯,在那里我們發(fā)現(xiàn)了引起我們注意的不規(guī)則性。
默認(rèn)情況下,YARN 資源管理器使用同步調(diào)度,即節(jié)點(diǎn)心跳到資源管理器,這會(huì)觸發(fā)調(diào)度器在該節(jié)點(diǎn)上調(diào)度未完成的 container。未完成的 container 被提交到主分區(qū)或從分區(qū),如果 container 的分區(qū)和節(jié)點(diǎn)的分區(qū)不匹配,則不會(huì)在該節(jié)點(diǎn)上分配 container。
在隊(duì)列中調(diào)度應(yīng)用程序時(shí),調(diào)度程序以先進(jìn)先出 (FIFO) 的順序遍歷它們?,F(xiàn)在假設(shè)主分區(qū)中的一個(gè)節(jié)點(diǎn)向資源管理器發(fā)送心跳;調(diào)度程序選擇隊(duì)列 A 進(jìn)行調(diào)度,隊(duì)列 A 中的前 100 個(gè)未完成的應(yīng)用程序正在從輔助分區(qū)請(qǐng)求資源。我們發(fā)現(xiàn)調(diào)度程序仍然試圖將這些應(yīng)用程序中的 container 匹配到該節(jié)點(diǎn),盡管匹配總是失敗。由于兩個(gè)分區(qū)都處理了大量的工作負(fù)載,這就在每個(gè)心跳上產(chǎn)生了巨大的開銷,從而導(dǎo)致了速度的下降。
為了解決這個(gè)問題,我們優(yōu)化了邏輯,如果主(或次)分區(qū)的節(jié)點(diǎn)向資源管理器心跳,調(diào)度器在調(diào)度時(shí)只考慮提交到主(或次)分區(qū)的應(yīng)用程序。更改后,我們觀察到合并前后總平均吞吐量的大致相同,并且在兩個(gè)分區(qū)都在最壞的情況下也提高了 9 倍!我們將這個(gè)修復(fù)也貢獻(xiàn)給社區(qū)了。
可衡量才可以被修復(fù)
為了應(yīng)對(duì) YARN 可伸縮性的挑戰(zhàn),我們遵循了我們以前工程主管 David Henke 的智慧,“可衡量才可以被修復(fù)(What gets measured gets fixed)?!?緊接著的下一步是構(gòu)建度量和工具來度量和監(jiān)控可擴(kuò)展性。
在我們的集群達(dá)到今天的規(guī)模之前,用戶遇到的任何慢作業(yè)的問題都可以用用戶隊(duì)列中的資源不足來解釋——這個(gè)問題只會(huì)影響在該隊(duì)列中運(yùn)行的團(tuán)隊(duì)。為了找到任何影響性能的根本原因,我們可以簡(jiǎn)單地找出哪些工作負(fù)載消耗了該隊(duì)列中不成比例的資源,并要求他們調(diào)整工作流程。
然而,我們的集群最終達(dá)到了資源管理器級(jí)可伸縮性問題導(dǎo)致用戶作業(yè)速度變慢的地步。因此,我們需要 (1) 一種衡量和反映資源管理器變慢的方法,以及(2)隨著我們繼續(xù)擴(kuò)大集群規(guī)模和工作負(fù)載,一種預(yù)測(cè)未來資源管理器性能的工具。
設(shè)置可擴(kuò)展性指標(biāo)和警報(bào)
我們能夠利用現(xiàn)有的資源管理器指標(biāo)來衡量性能問題。最相關(guān)的是:
1) 待處理的應(yīng)用程序:

2)容器分配吞吐量(AggregateContainersAllocated ):

如果想及時(shí)了解Spark、Hadoop或者HBase相關(guān)的文章,歡迎關(guān)注微信公共帳號(hào):過往記憶大數(shù)據(jù)
3)NodeManager 心跳處理速率:


待處理的應(yīng)用程序指標(biāo)讓全面了解用戶所看到的性能。有許多應(yīng)用程序待處理意味著隊(duì)列已滿,許多用戶的應(yīng)用程序還沒有運(yùn)行。
在資源管理器方面,容器分配吞吐量指標(biāo)告訴我們資源管理器是否不能足夠快地調(diào)度容器;長(zhǎng)時(shí)間(例如 30 分鐘)持續(xù)的低吞吐量表明可能出現(xiàn)問題。然而,單獨(dú)的低容器分配吞吐量并不表示資源管理器性能問題。例如,如果集群被充分利用并且容器流失率低,我們可能會(huì)看到低吞吐量,但這是因?yàn)榧嘿Y源不夠。
在 capacity scheduler 中,我們?cè)?NodeManager 發(fā)送心跳的時(shí)候分配 container,也就是 yarn.scheduler.capacity.per-node-heartbeat.multiple-assignments-enabled 為 true。NodeManager 心跳處理速率指標(biāo)告訴我們這個(gè)關(guān)鍵代碼路徑是否有任何緩慢。例如,在一次事件中,我們注意到資源管理器在上線新功能后花費(fèi)了更多的 CPU 周期。使用此指標(biāo)幫助我們確定該功能對(duì)節(jié)點(diǎn)心跳處理邏輯進(jìn)行了更改,優(yōu)化此代碼路徑后,節(jié)點(diǎn)更新吞吐量大幅增加,資源管理器 CPU 使用率恢復(fù)到以前的水平。
DynoYARN 用于解決 YARN 可擴(kuò)展性問題
評(píng)估 YARN 可擴(kuò)展性的另一個(gè)重要目標(biāo)是能夠預(yù)測(cè)未來 resource manager 的性能。雖然我們從歷史容量分析中知道我們的工作負(fù)載和集群規(guī)模同比增長(zhǎng)了 2 倍,但我們不知道 resource manager 將如何應(yīng)對(duì)這種增長(zhǎng),也不知道在什么時(shí)候 resource manager 的性能將不再能夠維持這些增長(zhǎng)。與我們編寫的用于評(píng)估未來 HDFS NameNode 性能的規(guī)模測(cè)試工具 Dynamometer 類似,我們編寫了 DynoYARN,一種用于啟動(dòng)任意大小的模擬 YARN 集群,然后在這些集群上運(yùn)行任意工作負(fù)載的工具。
DynoYARN 由兩個(gè)組件組成:一個(gè)用于啟動(dòng)模擬 YARN 集群的“driver”,以及一個(gè)用于在該集群上運(yùn)行模擬工作負(fù)載的“workload”。兩者都是作為 YARN 應(yīng)用程序?qū)崿F(xiàn)的;我們實(shí)際上是在一個(gè) YARN 集群中運(yùn)行一個(gè) YARN 集群,但是資源約束要低得多。例如,要啟動(dòng)一個(gè)1200個(gè)節(jié)點(diǎn)模擬集群,驅(qū)動(dòng)程序?qū)⒎峙湟粋€(gè) container 來運(yùn)行模擬的 resource manager,并分配 container 來運(yùn)行模擬的 node managers。后一種 container 可以運(yùn)行多個(gè)模擬的 node managers 。在我們的設(shè)置中,我們可以在單個(gè)50GB container 中運(yùn)行30個(gè)模擬 node managers。因此,在256GB主機(jī)上,我們可以運(yùn)行5個(gè)容器,每個(gè)容器帶有30個(gè)模擬 node managers,或者在每個(gè)真實(shí)的256GB主機(jī)上運(yùn)行150個(gè)模擬 node managers。因此,模擬的1200節(jié)點(diǎn)集群只需要1200/150 = 8臺(tái)真實(shí)主機(jī)。
為了評(píng)估 resource manager 的性能,工作負(fù)載(workload)應(yīng)用程序解析來自生產(chǎn)集群的審計(jì)日志,并將它們提供給驅(qū)動(dòng)程序(driver)應(yīng)用程序的模擬集群。在模擬的 resource manager 上如實(shí)地重跑生產(chǎn)工作負(fù)載。從審計(jì)日志中,我們提取每個(gè)應(yīng)用程序請(qǐng)求的容器數(shù)量、每個(gè)容器的內(nèi)存/vcore需求、應(yīng)用程序提交的時(shí)間,以及其他元數(shù)據(jù),如哪個(gè)用戶提交了應(yīng)用程序(以模擬用戶限制約束)和應(yīng)用程序提交到哪個(gè)隊(duì)列。結(jié)果模擬的性能非常接近我們?cè)谏a(chǎn)中看到的性能,因?yàn)楣ぷ髫?fù)載幾乎是完全相同的。
為了預(yù)測(cè)未來的可擴(kuò)展性,我們?cè)诠ぷ髫?fù)載應(yīng)用程序中實(shí)施了一項(xiàng)功能,允許我們修改解析的審計(jì)日志以模擬預(yù)計(jì)的工作負(fù)載。例如,我們經(jīng)常模擬的一個(gè)用例是將生產(chǎn)工作負(fù)載“乘以”一個(gè)固定數(shù)字,例如 1.5 倍或 2 倍。在 1.5x 的情況下,每個(gè)申請(qǐng)有 50% 的機(jī)會(huì)被提交兩次;在 2x 的情況下,每個(gè)申請(qǐng)都有 100% 的機(jī)會(huì)被提交兩次。使用這種策略,我們保留了生產(chǎn)中的優(yōu)先級(jí)高的工作負(fù)載模式(例如,Spark 應(yīng)用程序的比例、長(zhǎng)時(shí)間運(yùn)行與短期運(yùn)行的應(yīng)用程序的比例等),同時(shí)將它們擴(kuò)展以預(yù)測(cè)未來的性能。
通過在許多細(xì)粒度乘數(shù)(例如,1.5x、1.6x、1.7x、1.8x、1.9x、2x)上重新運(yùn)行模擬,我們可以得到 resource manager 的性能如何隨著我們逐步擴(kuò)大生產(chǎn)集群而變化的準(zhǔn)確趨勢(shì)。以下是這些模擬的結(jié)果:

| 乘數(shù) | Node Manager 的數(shù)量 | 每天 Applications 的數(shù)量 | p95 application delay (minutes) |
| 1 | 7152 | 237472 | 4.633 |
| 1.5 | 10728 | 354600 | 8.769 |
| 1.6 | 11443 | 377962 | 10.278 |
| 1.7 | 12158 | 401440 | 19.653 |
| 1.8 | 12873 | 424540 | 22.815 |
| 1.9 | 13588 | 441090 | 43.029 |
可擴(kuò)展性結(jié)果
我們的目標(biāo)是將 p95 應(yīng)用程序延遲保持在 10 分鐘或以下。根據(jù)我們的模擬,我們發(fā)現(xiàn) 11,000 個(gè)節(jié)點(diǎn)的集群可以將應(yīng)用程序延遲大致保持在 10 分鐘的時(shí)間窗口內(nèi)(11,443 個(gè)節(jié)點(diǎn)的集群為我們提供 10.278 分鐘的延遲,僅略高于我們的 10 分鐘目標(biāo)),但 12,000 個(gè)節(jié)點(diǎn)的集群應(yīng)用程序延遲為 19.653 分鐘,遠(yuǎn)遠(yuǎn)超出我們的 SLA。
根據(jù)這一預(yù)測(cè),我們推斷(基于我們 2 倍的同比增長(zhǎng))何時(shí)達(dá)到這一里程碑,因此我們有多少時(shí)間來處理因擴(kuò)展而開始遇到嚴(yán)重的資源管理器性能問題。
DynoYARN 開源
除了確定未來的擴(kuò)展性能外,在 LinkedIn,我們還使用 DynoYARN 來評(píng)估大型功能在將它們推向生產(chǎn)之前的影響,并確保將我們的集群升級(jí)到更高的社區(qū)版本時(shí)的性能情況。例如,當(dāng)我們將 Hadoop 集群從 Hadoop 2.7 升級(jí)到 2.10 時(shí),我們使用 DynoYARN 來比較資源管理器的性能。我們還使用此工具對(duì)前面討論的資源管理器優(yōu)化進(jìn)行了 A/B 測(cè)試。它是我們確定 YARN 性能路線圖以及自信地推出大型資源管理器功能和升級(jí)的有用工具。我們認(rèn)為 YARN 社區(qū)也可以從中受益,因此我們很高興地宣布,我們正在 GitHub 上開源 DynoYARN。該 repo 可在 https://github.com/linkedin/dynoyarn 上獲得。歡迎評(píng)論和貢獻(xiàn)!
使用 Robin 實(shí)現(xiàn)水平擴(kuò)展
雖然我們能夠快速推出多項(xiàng)優(yōu)化以緩解我們?cè)谫Y源管理器中發(fā)現(xiàn)的瓶頸,但很明顯單個(gè) YARN 集群很快將不再能夠維持 LinkedIn 當(dāng)前的計(jì)算增長(zhǎng)(主要是由于 單線程調(diào)度程序)。因此,我們踏上了尋找未來幾年可以依賴的長(zhǎng)期解決方案的旅程。
我們首先評(píng)估了 Hadoop 開源社區(qū)的兩個(gè)潛在解決方案,即 Global Scheduling 和 YARN Federation。
Global Scheduling 的主要目標(biāo)是解決默認(rèn)心跳驅(qū)動(dòng)調(diào)度程序(heartbeat-driven scheduler)無法滿足的復(fù)雜資源放置要求。它還引入了多線程調(diào)度,結(jié)合樂觀并發(fā)控制,提高集群整體調(diào)度吞吐量。但是,我們?cè)谏a(chǎn)跟蹤中沒有觀察到 DynoYARN 模擬中默認(rèn)單線程調(diào)度程序的顯著改進(jìn)(可能是由于調(diào)度線程之間的過度鎖爭(zhēng)用或最終提交步驟中容器分配的高拒絕率)。鑒于我們只能通過使用 YARN 進(jìn)行調(diào)度優(yōu)化來實(shí)現(xiàn)有限的(相對(duì)于我們的增長(zhǎng)率)改進(jìn),所以我們沒有進(jìn)一步朝這個(gè)方向發(fā)展。
另一方面,專門為解決單個(gè) YARN 集群的可擴(kuò)展性限制而設(shè)計(jì)的 YARN Federation 對(duì)我們來說似乎是一個(gè)更有前途的長(zhǎng)期計(jì)劃。它允許應(yīng)用程序跨越數(shù)萬個(gè)節(jié)點(diǎn)的多個(gè)集群,同時(shí)呈現(xiàn)單個(gè) YARN 集群的視圖,這非常適合在我們添加更多集群以適應(yīng)未來計(jì)算增長(zhǎng),而且這個(gè)可以做到對(duì)用戶透明。但是,出于一些原因,我們決定不在 LinkedIn 上使用它。
?控制平面(Global Policy Generator)的當(dāng)前實(shí)現(xiàn)是一個(gè)基于 CLI 的手動(dòng)過程,它不能處理 Hadoop 集群中的動(dòng)態(tài)變化。?它引入了新的依賴項(xiàng)(用于策略存儲(chǔ)的 MySQL,用于注冊(cè) YARN 的 Zookeeper)并需要許多我們從未測(cè)試或使用過的功能,例如 YARN Reservation, unmanaged AM 以及 AMRMProxy。這些復(fù)雜性對(duì)于我們這樣規(guī)模的團(tuán)隊(duì)來說意義重大。
請(qǐng)注意,設(shè)計(jì)的大部分復(fù)雜性來自允許 YARN 應(yīng)用程序跨越多個(gè)集群,如果應(yīng)用程序可以保持在單個(gè) YARN 集群的邊界內(nèi),我們可以避免大部分復(fù)雜性并為 YARN 構(gòu)建特定于域(domain-specific)的負(fù)載均衡器,非常像規(guī)范的 L7 負(fù)載平衡器。
Robin
我們?cè)O(shè)想我們的 Hadoop 集群由每個(gè)包含約 5,000 個(gè)節(jié)點(diǎn)的子集群組成,因此所有應(yīng)用程序都可以保持在一個(gè)子集群的邊界內(nèi)。有了這個(gè)假設(shè),我們可以構(gòu)建一個(gè)集群協(xié)調(diào)器來協(xié)調(diào)底層 YARN 集群之間的工作。這就是 Robin:一個(gè)負(fù)載均衡器,用于將 YARN 應(yīng)用程序動(dòng)態(tài)分發(fā)到多個(gè) Hadoop 集群,我們?cè)?LinkedIn 開發(fā)它以擴(kuò)展我們的 YARN 集群。

在較高的層次上,Robin 提供了一個(gè)簡(jiǎn)單的 REST API,它為給定的作業(yè)返回一個(gè) YARN 集群。在提交作業(yè)之前,YARN 客戶端會(huì)與 Robin 核對(duì)以確定應(yīng)將作業(yè)路由到哪個(gè)集群,然后將作業(yè)發(fā)送到正確的集群。一旦作業(yè)提交后,作業(yè)就一直保留在那個(gè)集群中。雖然應(yīng)用程序最多只能使用單個(gè)集群的容量,但我們沒有發(fā)現(xiàn)這對(duì)我們的工作負(fù)載構(gòu)成限制。
在 LinkedIn,大部分工作負(fù)載由 Azkaban 管理,Azkaban 是我們的工作流編排引擎,代表最終用戶充當(dāng) YARN 客戶端。它原生版本只支持單個(gè)物理 Hadoop 集群;因此,我們必須改造 Azkaban 以支持動(dòng)態(tài)作業(yè)提交并添加 Robin 集成,以便在我們擴(kuò)展邏輯集群并在其下添加物理集群時(shí)向最終用戶呈現(xiàn)單個(gè)邏輯集群的視圖。因此,大多數(shù)最終用戶對(duì) Robin 是無感知的。

雖然 Robin 的整體理念及其設(shè)計(jì)很簡(jiǎn)單,但我們必須解決一些其他值得一提的問題。
高可用性:Azkaban 是我們 Hadoop 最終用戶的核心接口。Azkaban 中的每一項(xiàng)工作執(zhí)行都依賴于 Robin,因此 Robin 始終保持高可用是至關(guān)重要的。
?Robin 會(huì)定期在后臺(tái)檢查每個(gè) YARN 集群的可用資源,并僅根據(jù)最新快照做出路由決策,因此即使 Robin 到 YARN 連接間歇性失敗,作業(yè)也可以路由;?Robin 被設(shè)計(jì)為無狀態(tài)的,因此我們可以通過添加或刪除副本來擴(kuò)大和縮小規(guī)模。它部署在 Kubernetes (K8s) 上,以提供動(dòng)態(tài)擴(kuò)縮容。
負(fù)載均衡策略:選擇正確的負(fù)載均衡策略對(duì)于保持每個(gè)集群的工作負(fù)載平衡以及減少資源爭(zhēng)用和作業(yè)延遲至關(guān)重要。我們已經(jīng)嘗試了一些策略,例如:
?使用絕對(duì)占優(yōu)資源公平分享算法(Dominant Resource Fairness,簡(jiǎn)稱 DRF),即,將作業(yè)路由到具有最多可用資源的集群。例如,集群 A 總內(nèi)存為 100 TB,其中的 20 TB 可用;而集群 B 總內(nèi)存為 200 TB ,其中的 30 TB 可用,則將作業(yè)路由到集群 B。?使用相對(duì)占優(yōu)資源公平分享算法,即,將作業(yè)路由到可用資源百分比最高的集群。例如,如果集群 A 總內(nèi)存為 100 TB,其中 20 TB 可用(20% 凈空);而集群 B 總內(nèi)存為 200 TB ,其中的 30 TB 可用(15% 凈空),則將作業(yè)路由到集群 A。?隊(duì)列級(jí)別的絕對(duì)可用資源。即將作業(yè)路由到該作業(yè)將運(yùn)行的隊(duì)列中可用資源最多的集群,(我們所有的集群具有相同的隊(duì)列結(jié)構(gòu))。
我們?cè)?DynoYARN 中模擬了帶有生產(chǎn)工作負(fù)載跟蹤的每個(gè)策略,并發(fā)現(xiàn)第一個(gè)策略最小化了應(yīng)用程序延遲,使其成為我們的最佳策略。
數(shù)據(jù)本地性:如今,LinkedIn 的工作負(fù)載性能在很大程度上依賴于數(shù)據(jù)本地性。作為將 Robin 部署到我們最大的生產(chǎn)集群的一部分,我們將現(xiàn)有的 YARN 集群拆分為兩個(gè)相同大小的 YARN 子集群,這兩個(gè) YARN 子集群和 HDFS 集群共享相同的節(jié)點(diǎn)。因?yàn)楝F(xiàn)在每個(gè)作業(yè)只能訪問 50% 的 HDFS 數(shù)據(jù),與拆分前可以訪問 100% 的數(shù)據(jù)相比,數(shù)據(jù)局部性有所損失。為了緩解這種情況,我們必須在兩個(gè)子集群之間平均分配每個(gè)機(jī)架上的機(jī)器,以便無論作業(yè)在哪個(gè)集群上運(yùn)行,仍然可以從同一機(jī)架訪問數(shù)據(jù)。事實(shí)證明,這個(gè)方法是很有效的。
下一步
LinkedIn 正在積極遷移到 Azure。下一步,我們正在研究在云上管理和擴(kuò)展 YARN 集群的最佳方式。將我們的本地 Hadoop YARN 集群的 10,000 多個(gè)節(jié)點(diǎn)提升到云端有很多令人興奮的挑戰(zhàn);與此同時(shí),Azure 中也有令人興奮的探索機(jī)會(huì),比如 Spot 實(shí)例、自動(dòng)縮放等。我們還計(jì)劃擴(kuò)展 Robin 以支持跨本地和云集群的路由,以及后期會(huì)開源 Robin。
本文翻譯自《Scaling LinkedIn's Hadoop YARN cluster beyond 10,000 nodes》:https://engineering.linkedin.com/blog/2021/scaling-linkedin-s-hadoop-yarn-cluster-beyond-10-000-nodes
