vivo 在離線混部探索與實踐

作者:來自 vivo 互聯網服務器團隊

本文根據甘青、黃榮傑老師在“2023 vivo開發者大會"現場演講內容整理而成。

伴隨 vivo 互聯網業務的高速發展,數據中心的規模不斷擴大,成本問題日益突出。在離線混部技術可以在保證服務質量的同時,極大的提升數據中心資源利用率,降低成本。混部技術涉及任務調度、資源隔離、運維觀測等一系列技術難題,本文將介紹 vivo 在混部技術方面的實踐和探索,爲讀者提供借鑑和參考

一、在離線混部技術背景

1.1 爲什麼混部

圖片

數據中心運行的服務可以分爲在線服務和離線任務兩大類,它們具有不同的資源使用特徵。

在線服務是指那些長時間運行、對時延非常敏感的服務,如電商、遊戲等,在線服務的資源利用率存在明顯的波峯波谷現象,平均利用率較低。離線任務是指那些運行週期短,有容錯性,對實時性要求低的服務,如數據轉換、模型訓練等,離線任務在執行過程中資源利用率很高。

在混部之前,在線和離線都是分開獨立部署,機器不共享,無法形成有效的資源互補,這導致數據中心整體資源利用率不高,卻要不斷購買新機器,造成了資源浪費。

1.2 混部技術定義

圖片

通過混部技術,我們可以將在線和離線部署到同一臺物理機上,形成資源互補,提升物理機的資源利用率,降低成本。混部技術最早由谷歌在2015年提出,經過多年的發展,混部技術已經趨於成熟,目前業內利用混部技術可以將數據中心的CPU利用率提升至40%左右 。

vivo在2020年開始調研混部技術,2023年混部平臺投入生產,目前我們已經將部分混部集羣的CPU利用率提升至25%(最新已達30%)左右。相較業界標杆這還有一定的差距,但隨着混部規模的擴大,我們將挑戰更高的目標。

二、在離線混部平臺實踐

2.1 混部平臺產品能力

圖片

混部平臺必須具備兩個產品能力:

  • 第一、強大的調度、隔離能力

  • 第二、完善的監控、運維能力

強大的調度能力解決了,我們如何將離線任務高效、合理的調度到在線服務所在的物理機上。而強大的隔離能力保障了在線服務的質量不受離線任務干擾。完善的監控和運維能力則可以讓我們洞悉整個混部平臺的運行情況,及時發現潛在風險,幫助運維人員更高效的完成系統和業務的運維工作,保障集羣的高穩定性。

2.2 混部差異化資源視圖

圖片

混部首先要解決的一個問題是離線使用哪一部分資源。

在vivo混部系統中在線和離線看到的資源視圖是不同的:

  • 在線可用資源爲 整機資源

  • 離線可用資源爲 整機資源減去 在線實際使用的資源

同時爲了避免整機負載太高影響系統的穩定性,我們設置一個安全水位線,用於調節離線可用資源大小。

2.3 混部QoS等級

圖片

爲了保障混部系統的slo,我們將服務分爲三個等級:高、中,低

不同等級的服務對物理資源如:CPU、內存 使用時有不同的優先級。高優先級服務支持綁定CPU模式,適用對延時非常敏感的在線服務。一般的在線服務可設置爲中優先級。離線任務設置爲低優先級,通過這樣的等級劃分,我們很好的實現在線對離線的資源壓制和隔離,保障了在線服務質量。

2.4 混部核心組件架構

圖片

我們所有的混部組件都是以插件方式獨立運行,對原生K8s無侵入。我們實現了一個混部調度器,在線和離線統一使用這個調度器,避免了多調度器資源賬本衝突的問題。

每臺物理機上都會部署一個混部agent,它可以實時採集容器資源使用數據,並根據安全水位線對離線任務進行壓制、驅逐等操作。

內核層面我們使用了龍蜥OS,它具備強大的資源隔離能力,可以幫助我們更好的隔離在線、離線資源使用,保障在線服務質量。

2.5 混部組件功能

圖片

我們把混部組件分爲管控組件和單機組件兩大類。

管控組件主要負責調度和控制,根據vivo業務使用場景,我們對調度器做了一些增強,提供了numa感知、負載感知,熱點打散,批量調度等能力。

混部控制器主要提供了一些配置管理能力:如資源畫像統計、node slo配置、node擴展資源變更等。

2.6 混部可視化監控

圖片

我們爲混部建立一套完整的可視化監控體系。

針對在線服務我們提供了:容器資源使用指標,受離線干擾指標、業務混部收益指標等監控能力。

針對離線任務,我們提供了離線可用資源、任務異常狀態等監控能力。

在平臺層面上我們提供了節點、內核,核心組件的監控,通過這些監控可及時發現平臺潛在的風險。

2.7 混部平臺運維

圖片

爲了簡化運維操作,提升運維效率,我們對混部集羣搭建和節點擴縮容操作進行了白屏化改造,開發了資源池管理功能,簡化了物理機接入流程,運維效率大幅提升。

在運維平臺上運維人員可以快速調整混部隔離、水位線等參數,如果發現在線服務受到干擾,運維人員可以一鍵關閉混部,驅逐離線任務,保障在線服務質量。

2.8 問題與挑戰

2.8.1 apiServer拆分

圖片

通過混部產品能力的建設,我們很好的實現了容器混部能力,但是在實踐中我們還是遇到一些新的挑戰:相對於普通K8s集羣,混部集羣中運行着更多的容器,而且離線任務由於生命週期短,容器創建銷燬更爲頻繁,這對K8s apiServer 產生了很大的壓力。

所以我們拆分了apiServer ,離線任務使用獨立的apiServer ,保障了集羣apiServer 負載一直處於一個安全水平。

2.8.2 監控架構優化

圖片

同樣混部之後由於採集了更多的監控指標,導致Prometheus內存消耗過多,無法滿足平臺指標 採集需求。針對這個問題,我們優化了監控架構,將在線和離線監控組件分開部署,離線改用性能更好的vmagent,通過這個優化,監控組件內存消耗減少到原來的十分之一。

2.9 利用率提升

圖片

混部初期雖然集羣CPU利用率有所提升,但是還是沒有達到我們的預期,主要原因有:

  • 一、部分低配置機器資源本身較少。

  • 二、Java 類應用堆會固定佔用大量內存,導致可提供給離線使用內存資源不足。

針對這些問題,我們開發了定時調整安全水位線功能,在業務低峯期上調安全水位線,釋放更多的資源給離線使用。通過一系列的優化手段,我們將其中一個混部集羣的CPU利用率由13%提升到了25%左右,幾乎翻倍,混部效果得到了有效的驗證。

三、Spark on K8s 彈性調度實踐

3.1 方案選型

圖片

在大方向的技術選型上,我們選擇了 Spark on K8s,在業內,也有一些公司採用了 YARN on K8s的方案。我們也對這兩種方案進行過對比。

從業務適用性來說,YARN on K8s 是通用的,可以兼容Hive、Spark、Flink這些引擎,它不需要頻繁創建Nodemanager pod,對K8s的壓力比較小。這些都是它的優點,但另一方面,Nodemanager ESS服務是對磁盤有容量和讀寫性能要求的,混部機器磁盤一般難以滿足。所以我們要支持不同引擎的remote shuffle service。

如果計算引擎有不同的版本,那麼RSS也要支持不同版本,比如Spark2,Spark3。如果你有不同的引擎,不同的版本,很可能一種RSS還滿足不了需求。另外Nodemanager需要根據K8s混部節點的剩餘資源,動態調整可用的vcore和內存,所以還需要一個額外的組件來做這個事情,這需要較高的改造成本。在資源利用上,NM的資源粒度相對大,自身也會佔用一些資源,存在一定的浪費。在資源緊張的情況下,Nodemanager作爲整體被驅逐,會影響多個任務。這些是YARN on K8s的劣勢。

作爲對比,Spark on K8s 劣勢有哪些?

首先這個特性在Spark 3.1以上版本才正式可用。Spark on K8s由於會頻繁的創建、查詢、銷燬大量的executor pod,對K8s的調度能力以及master節點會造成比較大的壓力。另一方面,它的優勢在於只需要能支持spark3.X的RSS,這有較多的開源產品可選擇。而且改造成本比較低,不需要額外的組件。資源粒度小,更有利於充分利用集羣資源。在資源緊張時,會逐個pod進行驅逐,任務的穩定性會更高。

兩方案各有優劣勢,爲什麼我們選擇了Spark on K8s?一方面因爲Spark3.X是vivo當前及未來2~3年的主流離線引擎,另一方面vivo內部對K8s研發比較深入,能有力支持我們。基於以上原因,我們最終決定使用spark on K8s

3.2 三步走戰略

圖片

確定了方案選型,那在vivo我們是如何推進spark on K8s大規模的應用落地呢?回顧總結我們走過的路,可以大致歸納爲三步走的戰略。

  • 第一,是任務跑通跑順的初期階段

  • 第二,是任務跑穩、跑穩的中期階段

  • 最後,是任務跑得智能的成熟階段

接下來的內容,我們將對每個階段展開細說。

3.2.1 任務跑通跑順

圖片

在任務跑通、跑順的第一階段,我們要解決的是怎麼將任務提交K8s集羣,同時要求易用性、便利性方面能夠達到與on YARN 一致的用戶體驗。將我們最後採用的方案架構簡化一下,就如同這張圖所示。

首先,爲了降低任務提交的複雜性、避免用戶改造任務的成本。我們在任務調度管理平臺做到了對原有Spark任務的兼容,通過vivo內部的容器開放API-這個代理層,我們不需要維護額外的K8s client環境,就可以輕鬆實現任務提交,提交後也能近實時獲取任務的狀態和日誌信息。

另外一個關鍵點是,我們選用了Spark Operator作爲Spark任務容器化的方案。Spark Operator是谷歌基於K8s Operator模式開發的一款的工具,用於通過聲明式的方式向K8s集羣提交Spark作業。

Spark Operator的方式還有其他優點:

  • Operator方式對K8s更友好,支持更靈活、更全面的配置項

  • 使用上更簡單易用

  • 內置Metrics,有利於我們做集中管理

要達到階段一的目標,讓任務跑通、跑順。我們主要克服了哪些關鍵問題和挑戰

圖片

第一個是日誌查看,因爲Spark Operator方式並沒有提供已結束作業的日誌查看方式,包括driver和executor日誌。在Driver側,我們通過定期請求容器開放API,能準實時地獲取Driver Pod狀態與日誌。在Executor側,我們參考了on yarn的方式,Executor Pod結束後,日誌上傳HDFS,與YARN日誌聚合類似。

另一方面,我們也在Spark HistoryServer做了二次開發工作,增加了on K8s方式的日誌查看接口。用戶查看已完成的Executor日誌時,不再請求JobHistory Server,而是請求Spark HistoryServer接口。在體驗上做到了基本與yarn一致。

在混部K8s集羣,我們也做了三方面能力的加強

  • 一是,確保分配能力能支持離線任務頻繁建刪pod的需求,在優化後我們離線Pod分配能力達到數百pod/秒。

  • 二是,在K8s側提升了spark內部的Driver優先級,確保了在驅逐時Driver穩定性高於Executor。

  • 最後一個是,發現並修復了spark-operator的一個bug,這個bu是Operator在多副本部署時,slave副本webhook處理有一點概率出現pod 找不到的問題。

3.2.2 任務跑穩跑準

圖片

在第二階段,我們要保障的是任務跑穩,數據跑準,因此,我們有兩個關鍵的舉措

  • 大規模雙跑,目的是確保Spark任務遷移到K8s集羣后是兼容的,任務成功率有保障;任務執行時長是穩定的,不會明顯變慢;數據是準確的,跟on YARN保持一致性。爲此,我們需要對任務進行on YARN和on K8s兩種模式下的雙跑測試,我們分批次總共進行了7輪雙跑,覆蓋了2萬+的線上正式任務。最終也取得了我們想要的結果:我們雙跑最終達成的任務成功率超過了99.5%,絕大部分的任務在兩種模式下的時長波動在25%以內,數據一致性是100%。

  • 混部集羣的壓力聯調,目的是確保混部集羣的承載容量能夠支撐大規模的離線任務調度,通過模擬未來1年的任務量來給混部集羣做壓力測試,充分發現和檢測K8s集羣可能存在的性能問題。最終,通過我們多輪壓測和問題解決,我們在單個K8s集羣能夠支撐150+同時運行的Spark任務,1萬+同時在運行的Pod數量。

圖片

第二階段,我們主要面臨三個方面的問題和挑戰

首先是我們需要爲Spark選擇一個外部的shuffle服務,經過技術選型和比較,我們最終選擇開源的celeborn作爲我們的remote shuffle service組件。我們通過對機型和參數的測試調優,使celeborn的性能達到我們的預期需求。在大規模的應用場景中,我們還發現了會存在大任務會阻塞小任務,導致shufle read變慢的問題,我們對這種情況做了參數和代碼上的優化,當前社區也針對shuffle read的問題有一些待優化的改進。另外celeborn進行了容器化部署,在提升自動化運維能力的同時,也可以爲混部集羣提供額外的計算資源。

其次,在任務穩定性方面,我們也解決了一系列的問題。

  1. 在雙跑的時候,我們發現有不少任務在on K8s模式下很容易OOM,這是因爲在on YARN模式下申請的container內存大小,不止是由Spark任務本身的內存參數決定,還會被YARN的資源粒度參數所影響。所以這塊要做一些適配對標工作。

  2. 在任務量比較大的情況下,Spark operator的吞吐能力會遇到瓶頸,需要我們將併發worker數量、隊列的相關參數調大。

  3. CoreDNS因爲Spark任務頻繁的域名解釋請求,導致壓力增大,甚至可能影響在線服務。這個可以通過訪問ip而不是域名的方式來規避,比如namenode節點、driver和executor。

  4. 橫向擴展namespace,這樣可以避免單namespace的瓶頸,也防止etcd出現問題。

  5. 我們K8s apiserver的壓力隨着任務量增長壓力也會逐漸增大,這會影響整個集羣的穩定性。我們主要通過優化Spark driver list pod接口、使用hostnetwork方式兩個優化手段,有效降低了apiserver的壓力。

最後要說的是數據一致性,關鍵點是要做到行級記錄的MD5校驗,發現有不一致的Case,我們做到100%的分析覆蓋。排除了因爲時間戳隨機函數等一些預期內的不一致,我們發現並修復兩種case會偶發導致不一致的問題:

  • celeborn Bug導致不一致,具體可參考CELEBORN-383解決

  • Java版本不一致導致的問題

3.2.3 任務跑得智能

圖片

第三階段,我們需要解決的問題是讓任務跑得智能,怎麼定義智能,我想用三個詞來概括彈性、健壯、業務需求。這是我們彈性調度的架構圖,細節我就不講了,這裏我介紹下我們的調度系統重點支持的功能。

圖片

在彈性方面,我們需要做到實時根據混部集羣資源閒忙,智能提交至混部集羣或者Hadoop集羣。在前期我們K8s集羣的資源相對Hadoop是小頭,通過合理的水位線控制,防止大量任務同時調度到K8s導致餓死。

健壯,就是要保證任務的高可用。

我們建設的能力包括:

  • 任務雙跑成功後再混部

  • 支持離線任務失敗自動回滾到Hadoop集羣執行

  • 支持用戶自主決定任務是否可調度至K8s集羣

  • 初期剔除重要核心任務、剔除不可重試任務

目的是在用戶任務遷移時做到讓用戶無感。

在滿足業務需求方面,我們支持優先調度本業務的離線任務, 優先滿足業務部門的離線任務資源需求;支持只在指定時間段裏調度離線任務,支持在出現異常情況下一鍵終止調度K8s。這些是爲了確保在線服務的高可用性,免除在線業務的後顧之憂。

3.3 混部效果

圖片

克服了三步走過程中的磕磕碰碰,我們終於可以將離線任務大規模混布到K8s混部集羣了。但是我們很快發現,混部集羣的整體利用率並沒有達到我們的預期,主要有三方面的原因

  1. 初期的Spark任務不足,這個我們通過加快雙跑,遷移低版本的Spark任務,遷移Hive SQL任務來解決。

  2. 在混部的時候,我們也觀察到,離線任務的pod cpu利用率其實沒那麼高。比如我們申請一個核,通常只能利用0.6個核,存在浪費的情況。我們上線了CPU資源超分的能力,目前是靜態的固定比例超分,通過這個措施,我們能將pod的實際cpu利用率打到80%以上。

  3. 另外就是混部集羣中的機器配置不均,部分機器cpu資源充足,但是內存不夠。我們通過在調度側控制可調度任務的資源粒度,儘量調度對內存資源需求較小的任務。

通過我們在任務調度側,以及之前甘青提到過的其他措施。混部集羣利用率得到了進一步的提升。

圖片

最後,我向大家同步下,當前我們達成的混部效果

我們目前可供調度的任務接近2萬個,這些任務每天調度的次數已經超過了4萬次。在凌晨的高峯期,我們通過混部,能爲離線任務額外增加2萬核、50TB內存的計算資源。這個收益是相當可觀的,我們也希望在未來的2到3年,將可調度的任務規模提升到6萬個,彈性資源能夠爲離線計算總資源貢獻20%的份額。

通過繼續深度推進在離線混部技術,我們期望能夠爲vivo增效降本工作持續地貢獻力量。

以上就是本次分享的全部內容。

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