DockOne微信分享(一三一):Juice——一種基於MesosFramework的任務雲架構

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
【編者的話】近年來,隨著Mesos在生產環境中的普及,使大規模的叢集管理變得簡單,而基於MesosFramework開發的Juice架構,能夠完成分布式任務的分發,處理,對於資源使用率的提高有很大的協助,今天就為大家介紹一下這套架構。

【3 天燒腦式容器儲存網路訓練營 | 深圳站】本次培訓以容器儲存和網路為主題,包括:Docker Plugin、Docker storage driver、Docker Volume Pulgin、Kubernetes Storage機制、容器網路實現原理和模型、Docker網路實現、網路外掛程式、Calico、Contiv Netplugin、開源企業級鏡像倉庫Harbor原理及實現等。

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

About MesosFramework

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

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

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

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

MesosFramework互動API

互動分為2部分API,分別為SchedulerAPI 與ExecutorAPI, 每個API都會以TYPE來區分,具體的處理流程如下:
  1. Scheduler提交一個請求(type='SUBSCRIBE')到Master(http://master-ip:5050/api/v1/scheduler), 並需要設定'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')
  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撤銷。
  5. 把這個Agent從master的Replicated log中刪除(Mesos Master依賴Replicated log中的部分持久化叢集配置資訊進行failer over/recovery)。


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

目前有很多基於MesosFramework的開源架構,例如Marathon。我們在生產環境中已經使用了Marathon架構,一般用它來運行long-run service/application,依靠marathon來管理應用服務,它支援應用服務自動/手動起停、水平擴充、健全狀態檢查等。我們依靠Jenkins + Docker + Marathon完成服務的自動化發布及部署。

Why Juice

下面來講下我基於MesosFramework所開發的一套架構——Juice。(開源地址:https://github.com/HujiangTechnology/Juice.git)

在開發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有以下一些特點,Juice架構分為Juice-Rest(Juice互動API層,可以完成外界對於Juice Task的CRUD操作)和Juice-Service(Juice核心層,負責與MesosMaster之間的互動,資源分派、任務提交、任務狀態更新等),在一套基於Juice架構的應用系統中,通常部署1-N個Juice-Rest(取決於系統的TPS),以及N個Juice-Service(Juice-Service分主從模式,為1主多從,by ZooKeeper),對於同一個Mesos叢集來說,可以部署1-N套Juice架構,以FrameworkID來區分,需要部署多套的話在Juice-Service的設定檔中設定mesos.framework.tag為不同的值即可。

Juice-Rest參數設定

Juice-Rest採用Spring-Boot編寫(Juice-API介面參見:https://github.com/HujiangTech ... nt.md), 處理外界發起的對任務CURD操作,當提交一個任務到Juice-Rest時,需要設定一些參數,比如:
example to run docker:
{
"callbackUrl":"http://www.XXXXXXXX.com/v5/tasks/callback",
"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":"/home/app/entrypoint.sh"

這裡支援Commands模式的原因有2點:
  1. 有時調用方可能只是想在某台制定的Agent上運行一個指令碼。
  2. 公司內部其他有些項目組還在使用Jar包啟動的模式,預留一個Shell指令碼的入口可以對這些項目產生支援。


env設定樣本,設定啟動並執行任務環境為dev:
"env":{"name":"environment","value":"dev"} 

args設定樣本,設定檔案路徑:
"args":["/tid/res/test.mp4"] 

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錯誤:
    throw new UnrecoverException(message, true)


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中)
mesos.framework.attr=lms,qa,mid|big

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

Agent的attr設定通過/etc/mesos-slave/attributes來設定。這個檔案通常為這樣的:
cat /etc/mesos-slave/attributes

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的任務在JuiceRest提交時是被放入了一個MQ中,這個MQ在Juice-Service中被稱為juice.task.queue。除此之外,還有另外幾個MQ,分別是juice.task.retry.queue、juice.task.result.queue、juice.management.queue。下面來分別說說這些Queue的用處。

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,任務的淘汰演算法如下:
  1. 到期時間淘汰制,任務處於juice.task.result.queue的時間長度>TASK_RETRY_EXPIRE_TIME,則淘汰(DEFAULT_TASK_RETRY_EXPIRE_TIME = 86400秒)。
  2. 大於最大檢索次數,任務被取出檢索但沒有被執行達到最大檢索次數>MAX_RESERVED,則淘汰(DEFAULT_MAX_RESERVED = 1024)。


juice.task.result.queue:任務結果隊列,Juice-Service在得到一個任務的狀態後(不一定是最終狀態),將任務的TaskResult對象放入juice.task.result.queue,Juice-Rest端從該隊列取出TaskResult,如果已經是任務的最終狀態,比如Task_Finished或者Task_Failed,則通過外部在提交任務時所填寫的callbackUrl回調調用方告知任務狀態。

juice.management.queue:管理類隊列,支援放入Reconcile類或Kill類的任務,由AuxiliaryService發起任務的查詢同步或Kill一個正在執行的任務。

通過SDK提交一個任務

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

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

總結及未來

目前Juice 1.1.0開源版本已經處於測試階段,新版本除修複一些Bug之外,還增加了2個新功能:
  1. 增加了任務插隊功能,可以通過在傳入參數中設定priority=1來提高一個任務的執行優先順序,該任務會被置於處理隊列的最前端。
  2. 任務失敗自動重試功能,設定傳入參數retry=1,任務失敗會自動重試,最多重試3次。


面對複雜的業務需求,Juice目前的版本還有一些特性/功能不支援,對於此,最好的方式是請大家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'事件觸發相關任務-資源分派的代碼塊。
由於得到的Offer對象實際為一個列表,處理邏輯會迴圈為每一個Offer分配具體的任務,而每個Offer的工作清單總資源(CPU、Memory等)必需小於Offer resources * RESOURCES_USE_THRESHOLD(資源使用閥值,可通過設定檔resources.use.threshold設定,預設0.8),每分配完一個Offer的task_infos後,便產生Accept Call由發送線程池進行發送處理,整個過程都是非同步非阻塞的。
Q:所有的任務都存檔在Docker裡面對於一些臨時的任務如何處理?

A:臨時的任務確實會產生一些垃圾的鏡像,需要定期對Docker倉庫進行清理,一般設定清理周期為1個月。
Q:任務系統是是否有協助使用者完成Docker封裝的操作?

A:目前沒有,所以使用者必需會一些Docker的基本操作,至少要會打鏡像,提交鏡像等。當然,像一些Docker的設定,比如掛載Volume,網路(bridge、host)等可以在提交任務時通過參數設定。
Q:Mesos和Kubernetes的優劣勢是什嗎?

A:其實我主要使用Mesos,Mesos相對Kubernetes應該是一套更重的系統,Mesos更像是個分布式作業系統,而Kubernetes在容器編排方面更有優勢(Pod之類)。
以上內容根據2017年07月13日晚群分享內容整理。分享人 徐佳,滬江Java工程師,開源架構Juice作者,10多年開發經驗。 DockOne每周都會組織定向的技術分享,歡迎感興趣的同學加:liyingjiesa,進群參與,您有想聽的話題或者想分享的話題都可以給我們留言。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.