摘要:本文整理自阿里巴巴高階開發工程師郭暘澤 (天凌) 在 Flink Forward Asia 2021 的演講。主要內容包括:
- 細粒度資源管理與適用場景
- Flink 資源排程框架
- 基於 SlotSharinGroup 的資源配置介面
- 動態資源切割機制
- 資源申請策略
- 總結與未來展望
一、細粒度資源管理與適用場景
在 Flink1.14 之前,使用的是一種粗粒度的資源管理方式,每個運算元 slot request 所需要的資源都是未知的,在 Flink 內部用一個 UNKNOWN 的特殊值來表示,這個值可以和任意資源規格的物理 slot 來匹配。從 TaskManager (以下簡稱 TM) 的角度來說,它擁有的 slot 個數和每個 slot 的資源維度都是根據 Flink 配置靜態決定的。
對於多數簡單作業,現有的粗粒度資源管理已經可以基本滿足對資源效率的要求。比如上圖作業,由 Kafka 讀入資料後經過一些簡單的處理,最終將資料寫入到 Redis 中。對於這種作業,我們很容易將上下游併發保持一致,並將作業的整個 pipeline 放到一個 SlotSharingGroup (以下簡稱 SSG) 中。這種情況下,slot 的資源需求是基本相同的,使用者直接調整預設的slot配置即可達到很高的資源利用效率,同時由於不同的 task 熱點峰值不一定相同,通過削峰填谷效應,將不同的 task 放到一個大的 slot 裡,還可以進一步降低整體的資源開銷。
然而對於一些生產中可能遇到的複雜作業,粗粒度資源管理並不能很好地滿足他們的需求。
比如圖上作業,有兩個 128 併發的 Kafka source 和一個 32 併發的 Redis 維表,上下兩路資料處理路徑。一條是兩個 Kafka source,經過 join 以後再經過一些聚合操作,最終將資料 sink 到第三個 16 併發的 Kafka 中;另一條路徑則是 Kafka 和 Redis 維表進行 join,結果流入一個基於 TensorFlow 的線上推斷模組,最終儲存到 Reids 中。
在這個作業中粗粒度資源管理就可能導致資源利用效率降低。
首先作業上下游併發不一致,如果想把整個作業放到一個 slot 中,只能和最高的 128 併發對齊,對齊的過程對於輕量級的運算元沒有太大問題,但是對於比較重的資源消耗的運算元,會導致很大的資源浪費。比如圖上的 Redis 維表,它將所有資料都快取到記憶體中來提高效能,而聚合運算元則需要比較大的 managed memory 來儲存 state。對於這兩個運算元,本來只需要分別申請 32 和 16 份資源,對齊併發以後則分別需要申請 128 份。
同時,整個作業的 pipeline 可能由於資源過大而無法放到一個 slot 或是 TM 中,比如上述運算元的記憶體,再比如 Tensorflow 模組需要 GPU 來保證計算效率。由於 GPU 是一種非常昂貴的資源,叢集上不一定有足夠的數量,從而導致作業因為對齊併發而無法申請到足夠的資源,最終無法執行。
我們可以將整個作業拆分成多個 SSG。如圖所示,我們將運算元按照併發劃分成 4 個 SSG,保證每個 SSG 內部的併發是對齊的。但是由於每個 slot 只有一種預設規格,依然需要將該 slot 的所有資源維度都對齊到各個 SSG 的最大值,比如記憶體需要和 Redis 維表的需求對齊,managed memory 需要和聚合運算元對齊,甚至擴充套件資源中都需要加入一塊 GPU,這依然不能解決資源浪費的問題。
為了解決這個問題,我們提出了細粒度資源管理,其基本思想是,每個 slot 的資源規格都可以單獨定製,使用者按需申請,最大化資源的利用效率。
綜上,細粒度資源管理就是通過使作業各個模組按需申請和使用資源來提高資源的整體利用效率。它的適用場景包括以下幾種:作業中上下游 task 併發有顯著差異、pipeline 的資源過大或者其中包含比較昂貴的擴充套件資源。這幾種情況都需要將作業拆分成多個 SSG,而不同的 SSG 資源需求存在差異,這時通過細粒度資源管理就能減少資源浪費。此外,對於批任務,作業可能包含一個或多個 stage,不同 stage 之間資源消耗存在顯著差異,同樣需要細粒度資源管理來減少資源開銷。
二、Flink 資源排程框架
Flink 的資源排程框架中主要有三個角色,分別是 JobMaster (以下簡稱 JM),ResourceManager (以下簡稱 RM) 和 TaskManager。使用者寫好的任務首先會被編譯成 JobGraph,注入資源後提交到 JM,JM 的作用就是管理 JobGraph 的資源申請以及執行部署。
JM 中的排程相關的元件是 Scheduler,它會根據 JobGraph 生成一系列 SlotRequest,然後將這些 SlotRequest 進行聚合,生成一個 ResourceRequirement 傳送給 RM,RM 接到資源宣告以後,首先會檢查叢集中現有的資源能否滿足其需求,可以的話就會向 TM 發出請求,讓他給對應的 JM 去 offer slot (這裡 slot 的分配由 SlotManager 元件來完成)。如果現有資源不夠,它會通過內部的 driver 向外部的 K8s 或者 Yarn 申請新的資源,最終 JM 接收足夠多的 slot 之後就會開始部署運算元,作業才能執行起來。
順著這個框架,接下來對細粒度資源管理中的技術實現細節和 design choice 進行分析闡述。
三、基於 SlotSharingGroup 的資源配置介面
在入口處 Flink 需要將資源配置注入 JobGraph 中。這部分是 FLIP-156 中提出的基於 SlotSharingGroup 的資源配置介面,關於資源配置介面的設計選擇,主要問題是資源配置的粒度:
首先是最小的運算元粒度 operator。如果使用者在 operator 上配置資源的話,Filnk 需要根據 chaining 和 slot sharing 進一步將資源聚合成 slot 級別再進行資源排程。
使用這個粒度的好處是,我們可以將資源配置與 chaining 和 slot sharing 的邏輯解耦,使用者只需要考慮當前運算元的需求,而無須考慮它是否和其他運算元嵌在一起或者是否排程到一個 slot 中。其次,它使 Flink 可以更準確地計算每個slot的資源。假如某一個 SSG 中上下游運算元擁有不同的併發,那麼可能 SSG 對應的物理 slot 需要的資源也是有差異的;而如果 Flink 掌握了每個運算元的資源,它就有機會進一步優化資源效率。
當然它也存在一些缺點,首先是使用者配置成本過高,生產中的複雜作業包含了大量運算元,使用者很難一一配置。其次,這種情況下,很難支援粗細粒度混合資源配置。一個 SSG 中如果既存在粗粒度,又存在細粒度的運算元,會導致 Flink 無法判斷其所需要的資源到底是多少。最後,由於使用者對資源的配置或估計會存在一定程度的偏差,這種偏差會不斷累積,運算元的削峰填谷效應也無法被有效利用。
第二種選擇是將運算元 chaining 後形成的 task 作為資源配置的粒度。這種情況下,我們必須向使用者暴露 Flink 內部的 chaining 邏輯,同時 Flink 的 runtime 依然需要根據 task 的 slot sharing 的配置進一步將資源聚合成 slot 級別再進行資源排程。
它的優缺點和運算元粒度大致一樣,只不過相比運算元,它在使用者的配置成本上有了一定程度的降低,但這依然是一個痛點。同時它的代價是無法將資源配置和 chaining 解耦,將 chaining 和 Flink 內部的邏輯暴露給使用者,導致內部潛在的優化受到限制。因為一旦使用者配置了某個 task 的資源,chaining 邏輯的改變可能讓 task 分裂成兩個或者三個,造成使用者配置不相容。
第三種選擇是直接將 SlotSharingGroup 作為資源配置的粒度,這樣對 Flink 來說資源配置所見既所得,省略了前面的資源聚合邏輯。
同時這種選擇還有以下幾個優點:
- 第一,使使用者的配置更靈活。我們將配置粒度的選擇權交給使用者,既可以配置運算元的資源,也可以配置 task 資源,甚至配置子圖的資源,只需要將子圖放到一個 SSG 裡然後配置它的資源即可。
- 第二,可以較為簡單地支援粗細粒度混合配置。所有配置的粒度都是 slot,不用擔心同一個 slot 中既包含粗粒度又包含細粒度的 task。對於粗粒度的 slot,可以簡單地按照 TM 預設的規格計算它的資源大小,這個特性也使得細粒度資源管理的分配邏輯可以相容粗粒度排程的,我們可以把粗粒度看作是細粒度的一種特例。
- 第三,它使得使用者可以利用不同運算元之間的削峰填谷效應,有效減少偏差產生的影響。
當然,也會引入一些限制,它將資源配置的 chaining 以及 Slot Sharing 耦合在了一起。此外如果一個 SSG 裡運算元存在併發差異,那麼為了最大化資源利用效率,可能需要使用者手動拆組。
綜合考慮,我們在 FLIP-156 中,最終選擇了基於 SlotSharingGroup 的資源配置介面。除了上述提到的優點,最重要的是從資源排程框架中可以發現,slot 實際上就是資源排程中最基本的單位,從 Scheduler 到 RM\TM 都是以 slot 為單位進行資源排程申請的,直接使用這個粒度,避免了增加系統的複雜度。
回到示例作業,在支援了細粒度資源管理配置介面後,我們就可以為 4 個 SSG 配置不同的資源,如上圖所示。只要排程框架嚴格按照這個原則進行匹配,我們就可以最大化資源利用效率。
四、動態資源切割機制
解決了資源配置以後,下一步就是為這些資源申請 slot,這一步需要用到 FLIP-56 提出的動態資源切割機制。
簡單回顧一下這幅圖,現在最左側的 JobGraph 已經有資源了,往右走就進入了 JM、RM 和 TM 的資源排程。在粗粒度資源管理下,TM 的 slot 都是固定大小、根據啟動配置來決定的,RM 在這種情況沒法滿足不同規格的 slot 請求的,因此我們需要對 slot 的建立方式進行一定的改造。
先來看現有的靜態 slot 申請機制。實際上 TM 啟動的時候 slot 就已經劃分好了,並且標記了編號。它會將這些 slot 上報給 Slot Manager,slot request 來臨時 Slot Manager 會決定申請 slot1、slot3,最後 slot1 上的 task 執行完以後會釋放 slot。這種情況下,只有 slot3 處於佔用的狀態。我們可以發現,這時雖然 TM 有 0.75 core,3G 的空閒資源,但如果 job 去申請對應資源大小的 slot,TM 也無法滿足它,因為 slot 已經提前劃分好了。
因此我們提出了動態資源切割機制。slot 不再是 TM 啟動後就生成並且不變的,而是根據實際 slot 的請求動態地從 TM 上切割下來。TM 啟動時,我們把能分配給 slot 的資源看作是一整個資源池,比如上圖有 1core,4G 記憶體的資源,現在有一個細粒度的作業,Slot Manager 決定從 TM 上要一個 0.25core,1G 的 slot,TM 會檢查自己的資源池是否能夠切下這個 slot,然後動態生成 slot 並分配對應的資源給 JM,接下來這個作業又申請一個 0.5core,2G 的 slot,Slot Manager 還是可以從同一個 TM 上申請 slot,只要不超過空閒資源就可以。當某個 slot 不再需要時,我們可以將它銷燬,對應的資源會回到空閒資源池。
通過這種機制,我們解決了細粒度資源請求如何滿足的問題。
回到示例作業,我們只需要起 8 個同樣規格的 TM 就能排程作業,每個 TM 上帶一塊 GPU 來滿足 SSG4,之後將 CPU 密集型的 SSG1 和記憶體密集型的 SSG2 和 SSG3 進行混布,對齊 TM 上整體的 CPU 記憶體比即可。
五、資源申請策略
何謂資源申請策略?它包含 RM 與 Resource Provider 還有 TM 互動時的兩個決策,一個是從 Resource Provider 處申請什麼資源規格的 TM 以及各個規格 TM 各需要幾個,另一個是如何將 slot 擺放到各個 TM 中。實際上這兩個決策都是在 Slot Manager 元件內部進行的。
粗粒度的資源申請策略比較簡單,因為只存在一種規格的 TM,並且 slot 規格都是一樣的。在分配策略上只需要考慮是否將 slot 儘量平鋪到各個 TM。但在細粒度資源管理下的策略就需要考慮到不同的需求。
首先我們引入了動態資源切割機制。slot 的排程就可以看作一個多維裝箱問題,既需要考慮如何減少資源碎片,也需要保障資源排程效率。此外還有 slot 是否需要評估,以及叢集可能對 TM 的資源規格有一些要求,比如不能過小,在 K8s 上如果 TM 資源過小,會導致啟動過慢,最後註冊超時,但也不能太大,會影響 K8s 的排程效率。
面對上述複雜性,我們將這個資源申請策略抽象出來,定義了一個 ResourceAllocationStrategy,Slot Manager 會將當前的資源請求和叢集中現有的可用資源告訴 strategy,strategy 負責決策並告訴 Slot Manager 現有資源如何分配、還需要申請多少個新的 TM 以及它們分別的規格,還有是否存在無法滿足的作業。
目前細粒度資源管理還處於 beta 版本,社群內建了一個簡單的預設資源管理策略。在這個策略下 TM 的規格是固定的、根據粗粒度的配置決定的,如果某個 slot 的請求大於資源配置,可能導致無法分配,這是它的侷限性。在資源分配方面,它會順序掃描當前空閒的 TM,只要滿足 slot 的請求就會直接切割,這種策略保障了資源排程即使在大規模的任務上也不會成為瓶頸,但代價是無法避免資源碎片的產生。
六、總結與未來展望
細粒度資源管理目前在 Flink 中還只是 beta 版本。上圖可以看到,對於 runtime 來說,通過 FLIP-56 與 FLIP-156,細粒度資源管理的工作已經基本完成了。而從使用者介面的角度,FLIP-169 已經開放了 Datastream API 上的細粒度配置,具體如何配置,可以參考社群的使用者文件。
未來,我們的發展方向主要是以下幾個方面:
- 第一,定製更多的資源管理策略來滿足不同場景,比如 session 和 OLAP 等;
- 第二,目前我們是把擴充套件資源看作一個 TM 級別的資源,TM 上的每個 slot 都可以看到它的資訊,之後我們會對它的 scope 進行進一步限制;
- 第三,目前細粒度資源管理可以支援粗細粒度混合配置,但是存在一些資源效率上的問題,比如粗粒度的 slot 請求可以被任意大小的 slot 滿足,未來我們會進一步優化匹配邏輯,更好地支援混合配置;
- 第四,我們會考慮適配社群新提出的 Reactive Mode;
- 最後,對 WebUI 進行優化,能夠展示 slot 的切分資訊等。
更多 Flink 相關技術問題,可掃碼加入社群釘釘交流群
第一時間獲取最新技術文章和社群動態,請關注公眾號~