號稱瞭解mesos雙層排程的你,先來回答下面這五個問題!
一提mesos,很多人知道雙層排程,但是大多數理解都在表面,不然試一下下面五個問題。
問題一:如果有兩個framework,一萬個節點,按說應該平均分配給兩個framework,怎麼個分法?一人一臺這樣分,還是前五千給一人,後五千給第二個人,還是隨機分,隨機分怎麼個分法?
問題二:在沒有reserved情況下,每個節點是隻能得給一個framework,還是可以分給多個framework?
問題三:如果兩個framework的權重比例為1比2,是如何保證資源分配是這個比例?
問題四:如果兩個framework的權重比例為1比2,當第二個用完了三分之二,在第一個沒有任務執行的時候,第二個能多用一些麼?如何平衡別人不用多用,別人要用保持比例呢?
問題五:將資源提供給多個framework的時候,是一個節點的資源給第一個framework,第一個framework說我不用,然後再給第二個framework麼?
好了,接下來我們來看Mesos雙層排程的基本原理。
一、入門級理解Mesos雙層排程
Mesos的排程過程如圖所示:
Mesos有Framework, Master, Agent, Executor, Task幾部分組成。這裡面有兩層的Scheduler,一層在Master裡面,allocator會將資源公平的分給每一個Framework,二層在Framework裡面,Framework的scheduler將資源按規則分配給Task。
二、進階級理解Mesos雙層排程
Mesos採用了DRF(主導資源公平演算法 Dominant Resource Fairness),Framework擁有的全部資源型別份額中佔最高百分比的就是Framework的主導份額。DRF演算法會使用所有已註冊的Framework來計算主導份額,以確保每個Framework能接收到其主導資源的公平份額。
舉個例子
考慮一個9CPU,18GBRAM的系統,擁有兩個使用者,其中使用者A執行的任務的需求向量為{1CPU, 4GB},使用者B執行的任務的需求向量為{3CPU,1GB},使用者可以執行儘量多的任務來使用系統的資源。
在上述方案中,A的每個任務消耗總cpu的1/9和總記憶體的2/9,所以A的dominant resource是記憶體;B的每個任務消耗總cpu的1/3和總記憶體的1/18,所以B的dominant resource為CPU。DRF會均衡使用者的dominant shares,執行3個使用者A的任務,執行2個使用者B的任務。三個使用者A的任務總共消耗了{3CPU,12GB},兩個使用者B的任務總共消耗了{6CPU,2GB};在這個分配中,每一個使用者的dominant share是相等的,使用者A獲得了2/3的RAM,而使用者B獲得了2/3的CPU。
以上的這個分配可以用如下方式計算出來:x和y分別是使用者A和使用者B的分配任務的數目,那麼使用者A消耗了{xCPU,4xGB},使用者B消耗了{3yCPU,yGB},在圖三中使用者A和使用者B消耗了同等dominant resource;使用者A的dominant share為4x/18,使用者B的dominant share為3y/9。所以DRF分配可以通過求解以下的優化問題來得到:
max(x,y) #(Maximize allocations)
subject to
x + 3y <= 9 #(CPU constraint)
4x + y <= 18 #(Memory Constraint)
2x/9 = y/3 #(Equalize dominant shares)
最後解出x=3以及y=2,因而使用者A獲得{3CPU,12GB},B得到{6CPU, 2GB}。
三、程式碼級理解Mesos雙層排程
首先理解幾個概念容易混淆:Quota, Reservation, Role, Weight
-
每個Framework可以有Role,既用於許可權,也用於資源分配
-
可以給某個role在offerResources的時候回覆Offer::Operation::RESERVE,來預訂某臺slave上面的資源。Reservation是很具體的,具體到哪臺機器的多少資源屬於哪個Role
-
Quota是每個Role的最小保證量,但是不具體到某個節點,而是在整個叢集中保證有這麼多就行了。
-
Reserved資源也算在Quota裡面。
-
不同的Role之間可以有Weight
Mesos的程式碼實現中,不是用原生的DRF,而是使用HierarchicalDR,也即分層的DRF.
呼叫了三個排序器Sorter(quotaRoleSorter, roleSorter, frameworkSorter),對所有的Framework進行排序,哪個先得到資源,哪個後得到資源。
總的來說分兩大步:先保證有quota的role,呼叫quotaRoleSorter,然後其他的資源沒有quota的再分,呼叫roleSorter。
對於每一個大步分兩個層次排序:一層是按照role排序,第二層是相同的role的不同Framework排序,呼叫frameworkSorter。
每一層的排序都是按照計算的share進行排序來先給誰,再給誰。Share的計算就是按照DRF演算法。
接下來我們具體分析一下這個資源分配的過程。
1. 生成一個數據結構offerable,用於儲存資源分配的結果
hashmap<FrameworkID, hashmap<SlaveID, Resources>> offerable;
這是一個MAP,對於每一個Framework,都會有一個資源的MAP,儲存的是每個slave上都有哪些資源。
2. 對於所有的slave打亂預設排序,從而使得資源分配相對均勻
std::random_shuffle(slaveIds.begin(), slaveIds.end());
3. 進行第一次三層迴圈,對於有quota的Framework進行排序
-
foreach (const SlaveID& slaveId, slaveIds) {
-
foreach (const string& role, quotaRoleSorter->sort()) {
-
foreach (const string& frameworkId_, frameworkSorters[role]->sort()) {
對於每一個slave,首先對role進行排序,對於每一個role,對於Framework進行排序,排序靠前的Framework優先獲得這個slave。
排序的演算法在DRFSorter裡面實現,裡面有一個函式calculateShare,裡面的關鍵點在於進行了一個迴圈,對於每一種資源都計算如下的share值:
share = std::max(share, allocation / _total);
allocation除以total即一種資源佔用的百分比,這裡之所以求max,就是找資源佔用百分比最高的資源,也即主導資源。
但是這個share不是直接進行排序,而是share / weights[name]除以權重進行排序。如果權重越大,這個值越小,這個role會排在前面,分配更多的資源。
排序結束後,對於每一個Framework,將當前slave的資源分配給它。
Resources available = slaves[slaveId].total - slaves[slaveId].allocated;
首先檢視這個slave的可用資源,也即總資源減去已經分配的資源。
Resources resources = (available.unreserved() + available.reserved(role)).nonRevocable();
每個slave上沒有預留的資源和已經預留給這個Framework的資源都會給這個Framework,當然如果上面有預留給其他Framework的資源是不會給當前的Framework的。
offerable[frameworkId][slaveId] += resources;
slaves[slaveId].allocated += resources;
分配的資源會儲存在資料結構offerable中。
4. 進行第二次三層迴圈,對於沒有quota的Framework進行排序
-
foreach (const SlaveID& slaveId, slaveIds) {
-
foreach (const string& role, roleSorter->sort()) {
-
foreach (const string& frameworkId_,frameworkSorters[role]->sort()) {
5. 全部分配結束後,將資源真正提供給各個Framework
-
foreachkey (const FrameworkID& frameworkId, offerable) {
-
offerCallback(frameworkId, offerable[frameworkId]);
-
}
這裡的offerCallback是呼叫Master::Offer,最終呼叫Framework的Scheduler的resourceOffers,讓Framework進行二次排程。
最後,讓我們來解答一下這些問題:
問題一:如果有兩個framework,一萬個節點,按說應該平均分配給兩個framework,怎麼個分法?一人一臺這樣分,還是前五千給一人,後五千給第二個人,還是隨機分,隨機分怎麼個分法?
答:是隨機分,怎麼分呢?是將節點隨機排序,但是排好序之後,就不再隨機分了,而是開始排序,比如隨機後的節點佇列中的第一個節點分給了第一個framework,等下次迴圈再排序的時候,第二個framework由於沒有拿到資源,排在了第一個framework的前面,於是第二個節點就分配給了第二個framework,然後for迴圈到第三個節點的時候(這是外層迴圈),內層迴圈對framework排序的時候,第一個framework又排在了第二個前面,於是第三個節點分給了第一個framework。就這樣你一個,我一個,實現了平均分配。
問題二:在沒有reserved情況下,每個節點是隻能得給一個framework,還是可以分給多個framework?
答:是的,在沒有reserved的情況下,一個節點是隻給一個framework,如果有reserved的情況下,reserved的那部分會給reserve它的那個framework,其他的部分,還是隻能給一個framework,不一定是哪一個,看誰排在前面。
問題三:如果兩個framework的權重比例為1比2,是如何保證資源分配是這個比例?
答:也是通過排序來的,對節點的for迴圈是外層迴圈,對framework的排序和迴圈是內層迴圈,第一次排序的時候,權重為2的framework排在前面,於是第一個節點是它的,第二次排序的時候,還是權重為2的framework排在前面,於是第二個節點也是它的,第三次排序的時候,權重為1的framework由於從來沒拿到過資源,排在了前面,於是第三個節點是它的。就這樣你兩個,我一個,你兩個,我一個,實現了資源按權重分配。
問題四:如果兩個framework的權重比例為1比2,當第二個用完了三分之二,在第一個沒有任務執行的時候,第二個能多用一些麼?如何平衡別人不用多用,別人要用保持比例呢?
答:能的。如果權重為2的framework用完了三分之二,則每次排序,它都會排在權重為1的但是沒有得到資源的framework後面,按說它永遠得不到資源。但是演算法中會有一個filter機制,當一個節點分給某一個framework之後,如果這個framework不用,退回來,則下次再遇到這個framework的時候,先filter掉,這樣另一個framework就有機會得到這個節點了。下次又不會filter掉了。
問題五:將資源提供給多個framework的時候,是一個節點的資源給第一個framework,第一個framework說我不用,然後再給第二個framework麼?
答:不是的。統一執行一遍分配演算法,把資源都全部分配好,才統一發送給framework,如果需要再次分配,是下次統一計算的時候了。
歡迎關注微信公眾號