當前位置:
首頁 > 最新 > DockOne微信分享:Juice-一種基於MesosFramework的任務雲框架

DockOne微信分享:Juice-一種基於MesosFramework的任務雲框架

【編者的話】在介紹Juice之前,我想先聊一聊Mesos,Mesos被稱為2層調度框架,是因為Master通過內部的Allocator完成Master->Framework的第一層調度,再由Framework通過調度器完成對於資源->任務的分配,這個過程稱為第二層調度。

About MesosFramework

先來看一看Mesos&Framework的整體架構圖:

Mesos的Framework分為2部分組成,分別為調度器和執行器。

調度器被稱為Scheduler,從Mesos1.0版本開始,官方提供了基於HTTP的RestAPI供外部調用並進行二次開發。

Scheduler用於處理Master端發起的回調事件(資源列表並載入任務、任務狀態通知等),進行相應處理。Agent接收到Master分配的任務時,會根據任務的container-type進行不同的處理,當處理默認container-type= Mesos 時,先檢查Framework所對應的Executor進程是否啟動,如果沒有啟動則會先啟動Executor進程,然後再提交任務到該Executor去執行,當運行一個container-type= Docker 的任務時,則啟動Docker Executor進行處理,程序的運行狀態完全取決於Docker內部的處理及返回值。

MesosFramework交互API

並需要設置 subscribe.framework_info.id ,該ID由Scheduler生成,在一個Mesos集群中必須保證唯一,Mesos以此FrameworkID來區分各個Framework所提交的任務,發送完畢後,Scheduler端等待Master的 SUBSCRIBE 回調事件,Master的返回事件被定義在event對象中,event.type為"SUBSCRIBE (注意:"SUBSCRIBE 請求發起後,Scheduler與Master端會保持會話連接(keep-alive),Master端主動發起的事件回調都會通過該連接通知到Scheduler)。(scheduler-http-api中介面"SUBSCRIBE )http://master-ip:5050/api/v1/schedule

2.Master主動發起 OFFERS 事件回調,通知Scheduler目前集群可分配使用資源,事件的event.type為"OFFERS 。(scheduler-http-api中介面"OFFERS )

3.Scheduler調用resourcesOffer為Offers安排Tasks。當完成任務分配後,主動發起 ACCEPT 事件請求到Master端告知Offers-Tasks列表。(scheduler-http-api中介面"ACCEPT )

4.Master接收到Scheduler的任務請求後,將任務發送到OfferId對應的Agent中去執行任務。

5.Agent接收到任務,檢查任務對應的Executor是否啟動,如啟動,則調用該Executor執行任務,如未啟動,則調用lauchExecutor()創建Executor對象並執行initialize()初始化Executor,Executor初始化過程中會調用RegisterExecutorMessage在Agent上註冊,之後便接受任務開始執行。(Executor-http-api中介面"LAUNCH )

6.Executor執行完畢或錯誤時通知Agent任務的task_status。(Executor-http-api中介面"UPDATE )

7.Agent再同步task_status給Master,Master則調用"UPDATE 事件回調,通知Scheduler更新任務狀態。(scheduler-http-api中介面"UPDATE )

8.Scheduler確認後發送 ACKNOWLEDGE 請求告知Master任務狀態已確認。(scheduler-http-api中介面"ACKNOWLEDGE )

任務狀態標示及Agent宕機處理

對於一個任務的運行狀態,Mesos定義了13種TASK_STATUS來標示,常用的有以下幾種:

TASK_STAGING-任務準備狀態,該任務已有Master分配給Slave,但Slave還未運行時的狀態。 TASK_RUNNING-任務已在Agent上運行。 TASK_FINISHED-任務已運行完畢。 TASK_KILLED-任務被主動終止,調用scheduler-http-api中"KILL 介面。 TASK_FAILED-任務執行失敗。 TASK_LOST-任務丟失,通常發生在Slave宕機。

當Agent宕機導致TASK_LOST時,Mesos又是怎麼來處理的呢?

在Master和Agent之間,一般都是由Master主動向每一個Agent發送Ping消息,如果在設定時間內(flag.slave_ping_timeout,默認15s)沒有收到Agent的回復,並且達到一定次數(flag.max_slave_ping_timeouts,默認次數為5),那麼Master會操作以下幾個步驟:

1.將該Agent從Master中刪除,此時該Agent的資源將不會再分配給Scheduler。

2.遍歷該Agent上運行的所有任務,向對應的Framework發送任務的Task_Lost狀態更新,同時把這些任務從Master中刪除。

3.遍歷該Agent上的所有Executor,並刪除。

4.觸發Recind Offer,把這個Agent上已經分配給Scheduler的Offer撤銷。

使用Marathon可以方便的發布及部署應用

目前有很多基於MesosFramework的開源框架,例如Marathon。我們在生產環境中已經使用了Marathon框架,依靠marathon來管理應用服務,它支持應用服務自動/手動起停、水平擴展、健康檢查等。我們依靠jenkins+docker+marathon完成服務的自動化發布及部署。

Why Juice

下面來講下我基於MesosFramework所開發的一套框架-Juicehttps://github.com/HujiangTechnology/Juice.gi

在開發Juice之前,我公司所有的音視頻轉碼切片任務都是基於一個叫TaskCenter的隊列分配框架,該框架並不具備分布式調度的功能(資源分配),所以集群的資源利用率一直是個問題,所以,我們想開發一套基於以下三點的新框架來替代老的TaskCenter。

1.一個任務調度型的框架,需要對資源(硬體)儘可能的做到最大的利用率。 2.框架必須可運行各種類型的任務。 3.平台必須是穩定的。

憑藉對Marathon的使用經驗,以及對於Mesos相關文檔的查閱,我們決定基於MesosFramework來開發一套任務調度型的框架,Mesos與Framework的特性剛才已經說過了,而我們將所需要執行的任務封在Docker中去執行,那麼對於框架本身來說他就不用關心任務的類型了,這樣業務的邊界和框架的邊界就變得很清晰,對於Framework來說,運行一個Docker任務也很方便,剛才說過Mesos內置了DockerExecutor可以完美的啟動Docker任務,這樣,我們的框架在Agent端所需要的開發就非常的少。

Juice框架在這樣的背景下開始了開發的歷程,我們對於它的定位是一套分布式任務雲系統,這裡為什麼要稱為任務雲系統呢?因為對於調用者來說,使用Juice,只要做2件事情:把要做的任務打成Docker鏡像並Push到docker倉庫中,然後向Juice提交一個Docker類型的任務。其它的,交給Juice去完成就可以了,調用者不用關心任務會在哪台物理機上被執行,只需要關心任務本身的執行狀況。

Juice架構

Juice-Rest參數設置

處理外界發起的對任務CURD操作,當提交一個任務到Juice-Rest時,需要設置一些參數,比如:https://github.com/HujiangTech ... ent.m

"taskName":"demo-task", "env":{"name":"environment","value":"dev"}, "args":["this is a test"], "container":{ "docker":{ "image":"dockerhub.XXXX.com/demo-slice" }, "type":"DOCKER" } }

其中Container中的type目前僅支持 Docker ,我們沒有加入"Mesos 類型的Container模式是因為目前項目組內部的服務已經都基於Docker化,但是預留了 Mesos 類型,在未來可以支持 Mesos 類型的任務。

commands模式支持運行Linux命令行命令和Shell腳本,比如:

這裡支持Commands模式的原因有2點

1.有時調用方可能只是想在某台制定的Agent上運行一個腳本。

2.公司內部其他有些項目組還在使用Jar包啟動的模式,預留一個Shell腳本的入口可以對這些項目產生支持。

env設置示例,設置運行的任務環境為dev:

args設置示例,設置文件路徑:

PS:使用Commands模式時不支持args選項。

此外,Juice-Rest支持用戶自定義資源大小(目前版本僅支持自定義CPU、內存),如需要指定資源,需在請求介面中配置resources對象,否則,將會使用默認的資源大小運行任務。Juice-Rest支持資源約束(constrains),即滿足在特定Host或Rack_id標籤的Agent上運行某任務,設置介面中constrains對象欄位即可。

Juice所使用的中間件(MQ、DB等)

下面講一下Rest層的處理模型,當外界發起一個任務請求時,Juice-Rest接收到任務後,並不是直接提交到Juice-Service層,而是做了以下2件事情:

1.將任務放入MQ中。(目前Juice使用Redis-List來作為默認的Queue,採用LPUSH、RPOP的模式,先進先出,為什麼選擇使用Redis中的List作為Queue而沒有選擇其他諸如rabbitmq、kafka這些呢,首先,Redis相對來說是一個比較輕量級的中間件,而且HA方案比較成熟,同時,在我看來,隊列中的最佳任務wait數量是應該<10000的,否則,任務的執行周期將會被拉得很長,以我公司的Juice系統來舉例,由於處理的都是耗時的音視頻轉碼切片任務,通常情況下10000個任務的排隊等候時間會在幾個小時以上,所以當任務數量很大時,考慮擴大集群的處理能力而不是把過多的任務積壓在隊列中,基於此,選擇Redis-List相對其他的傳統MQ來說沒有什麼劣勢。考慮到一些特殊情況,Juice也允許用戶實現CacheUtils介面使用其他MQ替換Redis-List)。

2.紀錄Tasks信息到Juice-Tasks表中,相當於數據落地。後續版本會基於此實現任務重試機制(目前的1.1.0內部開發版本已實現),或者在failover切換後完成任務恢復,此功能在後續1.2.0版本中考慮加入。(目前資料庫使用MySql)。

當Juice-Rest接受並完成任務提交後會返回給調用方一個Long型18位數字(JuiceID,全局唯一)作為憑證號。當任務完成後,Juice-Rest會主動發起回調請求,通知調用方該任務的運行結果(以此JuiceID作為業務憑證),前提是調用方必須設置callbackUrl。同時,調用方可以使用該JuiceID對進行任務查詢、終止等操作。

另外,在Juice-Rest層單獨維護一個線程池來處理由Juice-service端返回的任務狀態信息Task_status。

Juice-Service內部處理流程

Juice-Service可以看作是一個MesosFramework,與Master之間通訊協議採用ProtoBuf,每一種事件請求都通過對應類型的Call產生,這裡Juice-Service啟動時會發出Subscribe請求,由SubscribeCall()方法產生requestBody,採用OKHTTP發送,並維持與Master之間的長連接,如圖:

之後便進入while循環,當Master端的通知事件發生時,調用onEvent()方法執行。

Mesos的回調事件中,需要特別處理的主要事件由以下幾種:

1.SUBSCRIBED:Juice框架在接收到此事件後將註冊到Master中的FrameworkID紀錄到資料庫juice_framework表中。

2.OFFERS:當Juice-Service接收到該類型事件時,分配任務資源並提交到MesosMaster。

3.UPDATE:當Agent處理完任務時,任務會由Executor->Agent->Master->Juice-Service來完成任務的狀態通知。Juice-Service會將結果塞入result-list中。

4.ERROR:框架產生問題,通常這樣的問題分兩種,一種是比較嚴重的,例如Juice-Service使用了一個已經被Master端移除的FrameworkID,則Master會返回"framework has been removed"的錯誤信息,Juice-Service此時會拋出UnrecoverException錯誤:

Juice-Service在處理UnrecoverException類的錯誤時會Reset服務,當第二個參數為True時,會重新生成一個新的FrameworkID。

而當其他類型的錯誤,比如Master和Juice-Service之間的長鏈接中斷,僅僅Reset服務。

下面我想詳細來說說第二步,我們先來看下 OFFERS 請求處理代碼段:

該段代碼是分配Offer-tasks的核心代碼,來看幾個方法:

1.SchedulerService.filterAndAddAttrSys(),該方法作用是過濾不符合的OFFER,我們知道在Mesos的Agent中是可以通過配置Attr來使一些機器跑特殊的任務,而這裡的過濾正是基於該特性,比如我們設置了該Juice-Service只使用包含以下Attr屬性的資源時(在配置文件application.properties中)

經過了SchedulerService.filterAndAddAttrSys()方法的過濾,符合以上attr的資源會被選取執行任務。同時不符合的Offer會加入declines List,通過AuxiliaryServic.declineOffer()一次性發送給Master告知忽略。

```

bz:xx;

env:xx;

size:xx;

rack_id:xx;

dc:xx

2.SchedulerService.handleOffers(),該方法實現了原先MesosFramework中的resourceOffer的功能,對Offer進行Tasks分配,最後產生TaskInfo List,由AuxiliaryService.acceptOffer()發送給Master通知處理任務。

注意:Master在發送完Offer事件通知後會一直處於wait狀態,直到Framework端調用Accept call(AuxiliaryService.acceptOffer())或Decline call(AuxiliaryServic.declineOffer())來告知Master資源是否使用後才會通知下一個Framework去分配資源。(默認Master會一直等待,如果沒有通知,則Mesos集群中的資源利用率將可能達到100%,可以通過在Master端設置Timeout來避免這個問題。)

在Juice-Service內部,當SchedulerDriver與Master產生交互後,Juice-Service的處理邏輯由SchedulerService以及AuxiliaryService來實現。

SchedulerService處理Juice的主要邏輯,比如資源分配演算法、任務優先順序演算法,所有Master回調事件處理方法都定義在SchedulerService中。

AuxiliaryService維護幾組線程池,完成各自任務,剛才看到的AuxiliaryService.acceptOffer()和AuxiliaryServic.declineOffer(),都是通過調用AuxiliaryServic中的send-pool去完成call的發送,另外還有一些管理類的任務(比如實時查詢任務狀態、終止正在運行的任務等等)通過auxiliary-pool去完成。所以,AuxiliaryServic的調用都是非同步的。

Juice中各種隊列的功能介紹

juice.task.retry.queue:Juice-Service在取任務時是按照每一個Offer輪詢分配的,當一個Offer在分配資源時,假如從MQ中R-POP出來的任務不滿足該Offer時(比如need-resources大於該Offer的max offer value時,或者存在constrains,當前的offer和指定執行任務的offer不match時),這時,Juice-Service的做法是將當前任務放入juice.task.retry.queue中,等待下一次Offer分配時,優先從juice.task.retry.queue獲取任務並分配,這裡涉及到Juice內部獲取任務Queue的優先順序,我用了一個比較簡單的方式,即每次分配一個新的Offer資源時,先從juice.task.retry.queue中取出一定數目的任務(CACHE_TRIES = 5),當還有剩餘資源時,則從juice.task.queue中取任務,直到撐滿這個Offer。另外,處於juice.task.retry.queue會有淘汰機制,目前的任務淘汰機制遵循2點,當先觸發以下某一項時,則該任務會認為失敗,任務的Task_status被設置為Task_Failed,放入juice.task.result.queue,任務的淘汰演算法如下:

通過SDK提交一個任務

目前開源的Juice版本,已經提供了完整的SDK來完成對於Juice-Rest之間的交互,以下是提交一個docker任務的示例:

SDK採用流式的寫法,調用者可以簡單的對Juice-Rest進行操作請求。

總結及未來

目前Juice 1.1.0開源版本已經處於測試階段,新版本除修復一些Bug之外,還增加了2個新功能:

1.增加了任務插隊功能,可以通過在傳入參數中設置priority=1來提高一個任務的執行優先順序,該任務會被置於處理隊列的最前端。 2.任務失敗自動重試功能,設置傳入參數retry=1,任務失敗會自動重試,最多重試3次。

面對複雜的業務需求,對於此,最好的方式是請大家Fork這個項目的Git,或直接聯繫本人,大家一起來把Juice做好。

Q&A

Q.juice與elastic job的差異

A.我本身對於elastic job並不算太熟悉,就隨便說幾點,如果有錯還請各位糾正:

首先juice與elastic-job-cloud都基於mesos,資源-任務分配這塊elastic-job用了Fenzo(netflix),而juice是自己開發的調度演算法。

juice在作業調用時不需要作業註冊,只要上傳任務的鏡像(Docker)到倉庫及任務觸發。而elastic-job需要註冊作業。

juice在Rest-Api介面上近乎完全和marathon一致,方便一些使用慣marathon部署service的用戶。

juice目前版本並不支持作業分片。

Q.能詳細介紹下任務資源分配這一塊的演算法嗎?

A.之前已經簡單介紹過了,通過接收"OFFERS 事件觸發相關任務-資源分配的代碼塊。

Q.所有的任務都存檔在docker裡面對於一些臨時的任務如何處理?

A.臨時的任務確實會產生一些垃圾的鏡像,需要定期對Docker倉庫進行清理,一般設置清理周期為1個月。

Q.任務系統是是否有幫助用戶完成docker封裝的操作?

A.目前沒有,所以使用者必需會一些Docker的基本操作,至少要會打鏡像,提交鏡像等。當然,像一些Docker的設置,比如掛載volume,網路(bridge、host)等可以在提交任務時通過參數設置。

Q.Mesos和kubernetes的優劣勢是什麼?

A.其實我主要使用Mesos,Mesos相對K8S應該是一套更重的系統,Mesos更像是個分布式操作系統,而K8S在容器編排方面更有優勢(Pod之類)。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 推酷 的精彩文章:

賈躍亭在美有豪宅?樂視汽車:是公司接待處 非私
Pycharm:代碼跳轉如何回退
一圖看懂易到的前世今生
Scala 類型的類型
無人便利店會開到像7-Eleven一樣嗎?

TAG:推酷 |