走近伏羲,談5000節點集群調度與 性能優化
5K項目是飛天平臺的里程碑,系統在規模、性能和容錯方面都得到了飛躍式的發展,達到世界領先水平。伏羲作為飛天平臺的分布式調度系統,能 支持單集群5000節點,并發運行10000作業,30分鐘完成100TB數據Terasort,性能是當時Yahoo!在SortBenchmark上 世界紀錄的兩倍。
伏羲介紹
“飛天”是阿里巴巴的云計算平臺,其中的分布式調度系統被命名為“伏羲”(代碼名稱Fuxi),名字來自我國古代神話人物。伏羲主要負責管理集群的 機器資源和調度并發的計算任務,目前支持離線數據處理(DAG Job)和在線服務(Service),為上層分布式應用如ODPS/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之間的資源請求通信得到改 善,任務提交后能很快分配到資源開始運行,提高了多并發任務場景下任務的完成速度。例如,經過這個優化,用戶通過ODPS客戶端對海量數據進行Ad hoc的SQL查詢處理速度能得到顯著提升。
關鍵函數優化
在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次左右是 很常見的。
■ 一定要有針對性的測試驗證。
■ 代碼提交時關聯相應的Bug和Review ID,便于后續追溯。
總結
以上和大家分享了5K項目的一些實踐經驗,伏羲系統在5K項目中還做了很多有意義的系統優化和技術探索,參與其中收獲頗豐。性能是功能的一部分,是 系統生死線而非錦上花。5K項目只是阿里云計算平臺技術發展的一個開始,未來會在更大規模和更豐富計算模型等方面進一步發展,為用戶構筑可用可靠的云計算 引擎,進一步降低成本,挖掘數據價值。