原文連結:http://click.aliyun.com/m/13935/
5K項目是飛天平台的裡程碑,系統在規模、性能和容錯方面都得到了飛躍式的發展,達到世界領先水準。伏羲作為飛天平台的分布式排程系統,能支援單叢集5000節點,并發運作10000作業,30分鐘完成100TB資料Terasort,性能是當時Yahoo ! 在Sort Benchmark上世界紀錄的兩倍。
伏羲介紹
“飛天”是阿裡巴巴的雲計算平台,其中的分布式排程系統被命名為“伏羲”(代碼名稱Fuxi),名字來自我國古代神話人物。伏羲主要負責管理叢集的機器資源和排程并發的計算任務,目前支援離線資料處理(DAG Job)和線上服務(Service),為上層分布式應用如MaxCompute/ OSS / OTS提供穩定、高效、安全的資源管理和任務排程服務,為阿裡巴巴集團打造資料分享第一平台的目标提供了強大的計算引擎。
伏羲系統設計上采用M / S架構(如圖1所示),系統有一個被稱為“伏羲Master”的叢集控制中心,其餘每台機器上會運作一個叫做“伏羲Agent”的守護程序,守護程序除了管理節點上運作的任務外,還負責收集該節點上的資源使用情況,并将之彙報給控制中心。控制中心與伏羲Agent之間使用心跳機制,以監測節點健康狀态。當使用者向伏羲Master送出一個任務時,伏羲Master會排程出一個可用節點在其上啟動任務的主要程序AppMaster,主要程序随後會向伏羲Master提出資源請求,得到伏羲Master配置設定的資源後,AppMaster通知相應節點上的伏羲Agent開始運作任務Worker。伏羲是一個支援多任務并發的排程系統,控制中心伏羲Master負責在多個任務之間仲裁,支援優先級、資源Quota配額和搶占。

使用伏羲,使用者可以運作常見的MapReduce任務,還可以托管線上服務,滿足不同應用場景的需求。多使用者可以共享叢集,伏羲支援配置分組的資源配額,限定每個使用者組可以使用的計算資源。緊急任務如重要資料報表可以提高任務優先級來優先使用計算資源。
5K帶來的挑戰
在5K項目攻堅過程中,我們看到大型雲計算平台從設計到實作每一步都可能存在性能“陷阱”,原因主要在三個方面:規模放大效應,當系統擴充到數千節點時,原本非瓶頸與規模成正比的環節,其影響會被放大;木桶效應,很多時候,系統中99 % 的地方都被優化過,完成剩下1 % 的優化看起來也隻是“錦上添花”,然而那1 % 很可能就會成為影響系統性能的緻命的瓶頸;長路徑子產品依賴,有些請求處理過程可能需要跨越多個子產品(包括外部子產品),而外部子產品性能的不穩定性最終可能會影響到這個請求的處理性能和穩定性。5K項目是一場全方位戰役,給伏羲系統帶來規模、性能、穩定、運維等多方面的技術挑戰,例如下面的性能“陷阱”:
- 通信消息DDoS:在5000規模的叢集中,不同程序之間的RPC請求數量會随規模猛增,網絡中總請求數可達10000 QPS,極易造成系統中單點程序的消息擁塞,進而導緻請求處理嚴重逾時。另外消息處理還存在隊頭阻塞(HoL)問題。
- 關鍵函數OPS:伏羲Master是資源排程的中心節點,内部關鍵排程函數的OPS必須達到極高的标準,否則就可能因為木桶效應影響到叢集整體的排程性能。
- 故障恢複對外部子產品依賴:伏羲Master具有對使用者透明的故障恢複功能(Failover),其恢複過程依賴寫在Nuwa上的Checkpoint(注:Nuwa是飛天平台的協同系統,如名字服務)。是以,整體恢複速度會受到Nuwa通路速度的影響。
我們做了大量伏羲優化工作來規避上述的性能“陷阱”,涉及到架構設計、實作細節和子產品依賴,透過現象看本質,從最底層性能分析入手一步步找到瓶頸。下面結合具體的實戰例子來分享優化過程。
伏羲優化實戰
通信性能優化
在5K項目初期階段,我們測試大規模并發作業時發現,當作業數量超過1000時就容易出現運作時間變長的現象。分析監控曲線和日志,我們發現AppMaster發給伏羲Master的資源請求出現大量消息逾時,AppMaster遲遲拿不到資源,資源請求處理的延時很高。
消息從到達伏羲Master程序到最終被處理傳回的總時間主要包括在隊列中等待時間和實際處理的時間,是以延時高無非是兩個原因:消息處理本身的OPS下降;消息堆積在待處理隊列中未被及時處理。順着這一思路,在通過Profiling發現伏羲Master資源排程關鍵函數并沒有占到整個消息處理延時的大部分後,罪魁禍首就隻剩下消息堆積了。在繪出了伏羲Master中資源排程消息隊列中消息堆積的曲線之後,果然發現當作業數量增加時,堆積的請求數量劇增(如圖2所示),每一條請求的處理時間也較小規模時高出很多。
為什麼在伏羲Master隊列中會堆積如此多的消息?在伏羲系統中,守護程序伏羲Agent和AppMaster都需要向負責資源排程的伏羲Master查詢資源狀态,在通信政策上采用了定期Polling的方式,預設是每秒查詢一次。采用Polling通信方式主要基于其簡單性,能比較魯棒地應對網絡故障,消息傳遞發送過程比較自然有規律。然而在5000規模叢集中,這個政策必須進行調整優化,否則會造成伏羲Master被大量請求“DDoS攻擊”而無法服務。
定位到消息堆積的問題後,我們立即對消息通信政策進行了流控,算法簡單有效:發送端檢查如果上次詢問的請求結果已經傳回,表明目前伏羲Master請求處理較為順暢,則間隔一個較短的時間後進行下一次詢問。反之,如果上次詢問的請求逾時,說明伏羲Master較忙(例如有任務釋放大批資源待處理等),發送端則等待較長時間後再發送請求。通過這種自适應流控的通信政策調整,伏羲Master消息堆積問題得到了有效解決。
此外,我們還解決了伏羲Master消息的隊頭阻塞(HoL)問題。AppMaster需要與伏羲Master通信獲得資源排程結果,同時也與伏羲Agent通信進行Worker的啟停。由于伏羲Agent數量遠大于伏羲Master,在極端情況下,如果AppMaster采用同一個線程池來處理這些消息,那麼伏羲Master消息會被前面大量的伏羲Agent消息阻塞。我們将消息處理的全路徑包括從發送到處理完畢等各個時間段進行了Profling,結果印證了隊頭阻塞現象。當一個任務的Worker較多時,AppMaster需要與之通信的伏羲Agent也會增多,觀察到AppMaster拿到資源的時間明顯變長。針對隊頭阻塞問題,我們通信元件中加入了獨立線程功能達到QoS的效果,并應用在AppMaster處理伏羲Master消息的通信中。如圖3所示,伏羲Master的消息單獨使用一個線程池,其餘消息則共用另一個線程池。
通過上面的兩項性能優化,伏羲系統内部的通信壓力得到顯著降低,提高了通信效率。AppMaster與伏羲Master之間的資源請求通信得到改善,任務送出後能很快配置設定到資源開始運作,提高了多并發任務場景下任務的完成速度。例如,經過這個優化,使用者通過MaxCompute用戶端對海量資料進行Ad hoc的SQL查詢處理速度能得到顯著提升。阿裡雲數加大資料計算服務MaxCompute産品位址:https://www.aliyun.com/product/odps
關鍵函數優化
在5K項目中我們還重點關注系統中的關鍵函數性能,那裡也可能藏着“陷阱”。伏羲Master在排程資源時的一個關鍵操作是:比較一個節點的空閑資源能否滿足該節點上排隊等待的所有資源請求,進而決定該資源配置設定給哪個任務。這個函數的調用次數會與機器規模和請求數量成正比,是以其速度對伏羲Master的排程OPS有決定性影響。
伏羲在排程資源時支援多個次元,如記憶體、CPU、網絡、磁盤等,所有的資源和請求都用一個多元的鍵值對表示,例如 {Mem: 10, CPU: 50,net: 40,disk: 60}。是以,判斷一個空閑資源能否滿足一個資源請求的問題可以簡單地抽象成多元向量的比較問題,例如R: [r1, r2, r3, r4] > Q: [q1, q2, q3, q4],其中1、2、3、4等數字表示各個次元,當且僅當R各個次元均大于Q時才判斷R > Q。比較次數決定了這個操作的時間複雜度。最好情況下隻需比較1次即可得出結果,如判斷 [1, 10, 10, 10]大于 [2, 1, 1, 1]失敗;最差需要D次(D為次元數),如判斷 [10, 10, 10, 1]大于 [1, 1, 1, 10]需比較4次。在資源排程高頻發生時,必須對這裡的比較進行優化。
我們通過Profiling分析了系統運作時資源空閑與請求情況,在資源充足時通常值最大的次元最難滿足,是以在資源排程場景我們采用基于主鍵的優化算法:對每個資源請求的最大值所在次元定義為該向量的主鍵,當有空閑資源時首先比較主鍵次元是否滿足請求,如果在主鍵上滿足再比較其他次元。此外,對一個節點上排隊等待所有請求的主鍵值再求一個最小值,空閑資源如果小于該最小值則無需再比較其他請求。通過主鍵算法,我們大大減少了資源排程時向量比較次數,伏羲Master一次排程時間優化到幾個毫秒。注意到資源請求送出後不會改變,是以計算主鍵的系統開銷可以忽略不計。
伏羲Master關鍵排程性能的優化增強了系統的規模擴充能力,使用者利用飛天平台能管理更大規模的叢集,容納更多的計算任務,發揮出雲計算平台的成本優勢。
子產品依賴性能優化
伏羲Master支援故障恢複,在重新開機後進行故障恢複時需要從Nuwa讀取所有任務的描述檔案(Checkpoint)以繼續運作使用者任務。考慮到之前Nuwa服務在伺服器端對檔案内容沒有做持久化,伏羲Master在讀取了Checkpoint後還會再寫一次Nuwa,這個回寫操作性能依賴于Nuwa子產品。在5000節點的叢集上,名字解析壓力的顯著增加導緻Nuwa在Server的回寫操作上也出現了性能下降問題,最終通過子產品依賴傳遞到了伏羲Master,進而影響了故障恢複的性能。經測試觀察,一次Checkpoint回寫就消耗70秒,這大大降低了伏羲系統的可用性。
我們對伏羲Master故障恢複進行了優化。首先,從伏羲Master的角度,在故障恢複時剛剛讀取的Checkpoint内容在Nuwa伺服器端是不會發生改變的,是以讀取Checkpoint後沒有必要回寫到伺服器端,隻需要通知本地的Nuwa Agent讓其代理即可,Agent會負責伺服器當機重新開機時向伺服器推送本地緩存的檔案内容。于是與Nuwa團隊的同學合作,在Nuwa API中新增加一個隻寫本地的接口,這樣伏羲Master規避了在故障恢複時回寫Checkpoint的性能風險。優化後,在5000節點叢集和并發5000任務的測試規模下,一次故障恢複中處理Checkpoint操作僅需18秒(主要時間在一次讀取)。可見在分布式系統中,對外部子產品的依賴哪怕隻是一個RPC請求也可能是“性能陷阱”,在設計和實作時盡量避免出現在關鍵路徑上。
故障恢複是分布式系統保證可用性必須具備的功能,經過優化,伏羲Master的快速故障恢複增強了飛天計算平台的可用性和穩定性,屏蔽了硬體故障,使使用者的使用過程不受影響。
工程經驗
高品質代碼沒有捷徑可走,也不能隻靠制度流程,唯有認真二字:作者認真、Reviewer認真、測試認真。
- 任何一個Item,無論是解決Bug還是新增Feature,都必須在動手寫代碼前讨論清楚方案,Code Review不能代替方案讨論。在讨論時作者需要回答兩個問題:這個解決方法真的可行嗎?副作用是什麼?這些讨論需要記錄在Wiki或者BugFree等工具上進行跟蹤。
- 小步快跑,盡早送出Code Review,很多問題在這個階段就能發現,不必等到測試中發現,代價大。
- 代碼Reviewer對Item有一半的責任,是以Review時不是簡單過一遍字面完事的。我采用的Checklist有:是否準确反映了之前讨論好的方案;是否存在死鎖、“性能陷阱”;子產品化封裝是否足夠;函數名變量名是否規範,日志格式是否規範;注釋是否足夠。一段代碼Review疊代10次左右是很常見的。
- 一定要有針對性的測試驗證。