Mesos 架構(gòu)以及源碼淺析
Mesos 按照官方的介紹,是分布式操作系統(tǒng)的內(nèi)核。目標是 ”Program against your datacenter like it’s a single pool of resources”,即可以將整個數(shù)據(jù)中心當做一臺電腦一樣使用??梢哉f這個目標是所有宣稱自己是DCOS的系統(tǒng)的共同目標,本文從架構(gòu)和源碼層面分析Mesos以及周邊框架,看看Mesos是如何實現(xiàn)這個目標的,當前距這個目標還有多大差距。最后比較了一下Mesos和Kubernetes這兩個都受Google的Borg影響的系統(tǒng)的異同。
閱讀對象:對Mesos或者分布式系統(tǒng)感興趣的技術(shù)人
設(shè)計理念以及架構(gòu)
引用Mesos paper里的一句話,來說明Mesos的設(shè)計理念:
define a minimal interface that enables efficient resource sharing across frameworks, and otherwise push control of task scheduling and execution to the frameworks
定義一個最小化的接口來支持跨框架的資源共享,其他的調(diào)度以及執(zhí)行工作都委托給框架自己來控制。
這句話標志Mesos不試圖作為一個一棧式解決問題的系統(tǒng),而是以最小的成本來實現(xiàn)資源共享。先來看看官方提供的架構(gòu)圖:
主要組件以及概念:
- Zookeeper 主要用來實現(xiàn)Master的選舉,支持Master的高可用。
- Master Mesos的主節(jié)點,接受Slave和Framework scheduler的注冊,分配資源。
- Slave 從節(jié)點,接受master發(fā)來的task,調(diào)度executor去執(zhí)行。
- Framework 比如上圖中的Hadoop,MPI就是Framework,包括scheduler,executor兩部分。scheduler獨立運行,啟動后注冊到master,接受master發(fā)送的Resource Offer消息,來決定是否接受。Executor是給slave調(diào)用的,執(zhí)行framework的task。Mesos內(nèi)置了CommandExecutor(直接調(diào)用shell)和DockerExecutor兩種executor,其他的自定義Executor需要提供uri,供slave下載。
- Task Mesos最主要的工作其實就是分配資源,然后詢問scheduler是否可以利用該資源執(zhí)行Task,scheduler將資源和Task綁定后交由Master發(fā)送給指定的Slave執(zhí)行。Task可以是長生命周期的,也可以使用批量的短生命周期的。
官方提供的另外一個資源分配的例子:
- Slave1 向 Master 報告,有4個CPU和4 GB內(nèi)存可用
- Master 發(fā)送一個 Resource Offer 給 Framework1 來描述 Slave1 有多少可用資源
- FrameWork1 中的 FW Scheduler會答復(fù) Master,我有兩個 Task 需要運行在 Slave1,一個 Task 需要<2個CPU,1 GB內(nèi)存=””>,另外一個Task需要<1個CPU,2 GB內(nèi)存=””>
- 最后,Master 發(fā)送這些 Tasks 給 Slave1。然后,Slave1還有1個CPU和1 GB內(nèi)存沒有使用,所以分配模塊可以把這些資源提供給 Framework2
這個例子可以看出來,Mesos的核心工作其實很少,資源管理和分配以及task轉(zhuǎn)發(fā)。調(diào)度由Framework實現(xiàn),task的定義以及具體執(zhí)行也由Framework實現(xiàn),Mesos的資源分配粒度是按task的,但由于executor執(zhí)行task可能在同一個進程中實現(xiàn),所以資源限制只是一種流控的機制,并不能實際的控制到task這個粒度。
看了上面官方的架構(gòu)說明,大約應(yīng)該明白了Mesos的大致架構(gòu),但具體Mesos為什么要做成這樣,下面我們具體分析下。
歷史演進
我們把時間回退到Mesos發(fā)明的2009年,那時候Hadoop逐漸成熟,并廣泛應(yīng)用,正在吞噬著大家的服務(wù)器。Spark當時也在醞釀中。而服務(wù)器配置管理領(lǐng)域Puppet還沒發(fā)布1.0,Chef才剛出現(xiàn),"Configuration management Infrastructure as Code" 的思想才慢慢被接受,Ansible/Salt還沒出現(xiàn)。大家管理服務(wù)器還是靜態(tài)劃分的方式,購買服務(wù)器的時候就安排好了,這幾臺服務(wù)器上運行什么服務(wù),應(yīng)該配置多少CPU/內(nèi)存/磁盤,安裝維護還多用的是shell腳本。
靜態(tài)管理服務(wù)器,無論是用shell還是Puppet這種工具,一方面是比較浪費資源,另外一方面服務(wù)故障恢復(fù),服務(wù)遷移都需要人工介入,因為這種工具都只在部署時進行管理,服務(wù)進程運行后就不歸部署工具管理了。而Mesos則看到了這種方式的弊端,試圖實現(xiàn)一個資源共享的平臺,提高資源利用率,實現(xiàn)動態(tài)運維。
按照這種方式就比較容易理解Mesos的做法,Mesos的master相當于Puppet/Salt的master,Mesos的slave相當于Puppet/Salt的agent,二者干的事情都是把指令通過master發(fā)送給slave/agent執(zhí)行,區(qū)別是Puppet/Salt成功執(zhí)行后就不關(guān)心了,而Mesos執(zhí)行后會在Master維護task的狀態(tài),task掛掉后可以重啟或者遷移。
同時Mesos看到Hadoop等分布式系統(tǒng)都自己實現(xiàn)了scheduler以及executor,所以將scheduler以及executor的具體實現(xiàn)讓出來,通過制定Framework標準,由第三方分布式框架自己實現(xiàn),自己只負責轉(zhuǎn)發(fā)task到slave,由slave調(diào)用Framework的executor去執(zhí)行task。
也就是說由于歷史時機的問題,Mesos采用了一種保守的策略來進行演進。
資源沖突以及隔離機制
資源共享導(dǎo)致的首要問題就是如何解決資源沖突問題:
- cpu/mem 這個Mesos默認內(nèi)置了一個Mesos Containerizer,可以通過cgroups和namespaces進行限制。
- 網(wǎng)絡(luò)端口 Mesos默認會給每個task分配一個(可以分配多個端口)隨機的未使用的端口,需要應(yīng)用從環(huán)境變量中獲取到這個端口進行使用,是一種約定的規(guī)則,并不強制限制。
- 文件系統(tǒng) 默認情況下Mesos的文件系統(tǒng)是多應(yīng)用共享的,默認給每個task分配一個sandbox目錄作為工作目錄,sandbox在主機上的目錄是和taskid相關(guān)的,不會沖突,生命周期和task綁定。另外也支持Persistent Volume,用于應(yīng)用保存持久數(shù)據(jù),也是通過目錄映射的方式實現(xiàn)的。Persistent Volume的生命周期獨立于task,由scheduler決定是否復(fù)用,也就是說Mesos將持久化數(shù)據(jù)和動態(tài)遷移之間的沖突問題交給了scheduler自己處理。另外可以選擇使用Docker Containerizer,通過Docker的方式隔離文件系統(tǒng)。
- 服務(wù)發(fā)現(xiàn)以及負載均衡 Mesos默認不支持服務(wù)發(fā)現(xiàn)和負載均衡,需要用戶自己實現(xiàn)。Mesos之上的Marathon Framework提供了一個marathon-lb,通過監(jiān)聽Marathon的event修改HAProxy,實現(xiàn)動態(tài)的負載均衡,不過只支持通過Marathon部署的應(yīng)用。
- 容器的改進 Mesos內(nèi)置的容器不是基于Image的,但Docker是基于Image的,帶來的問題就是有些功能要通過兩種方式實現(xiàn)(比如磁盤隔離等)。另外Docker本身的daemon機制讓Mesos不好直接管理容器進程,所以Mesos也在計劃改進內(nèi)置的容器支持Image,兼容Docker/Appc的Image,不會把Docker作為默認的容器。具體參看:https://github.com/apache/mesos/blob/master/docs/container-image.md,MESOS-2840。
源碼架構(gòu)分析
Mesos的核心是用c++寫的,主要使用了一個libprocess的庫,這是一個c++的actor模型的庫(不太了解actor模型的可以參看我的前一篇文章:并發(fā)之痛 Thread,Goroutine,Actor,這個庫順帶把Option,Nothing,Try, Future,Lambda,Defer這些都實現(xiàn)了,讓我充分體會了c++的魔法)。libprocess基本是參考erlang的模式實現(xiàn)的,其中的actor都叫做process,每個process有一個獨立的ID,我們這里為了方便理解,把其中的抽象都叫做actor,具體actor協(xié)作模式見下圖:
在Mesos的master節(jié)點中,每個Framework以及Slave都是一個遠程的actor。而slave節(jié)點上,每個executor是一個actor,只不過內(nèi)置的executor是在同一個進程中的,而其他自定義的executor是獨立的進程,executor和slave之間通過進程間通信方式(網(wǎng)絡(luò)端口)交互。
Mesos通過actor模型,簡化了分布式系統(tǒng)的調(diào)用以及并發(fā)編程的復(fù)雜度,actor之間都通過消息異步通信,只需要知道對方的ID即可,無需了解對方和自己是否在同一個節(jié)點上。libprocess封裝的actor manager知道接收方是本地的actor還是遠程的actor,如果是遠程的則通過請求接口轉(zhuǎn)發(fā)消息。libprocess也封裝了網(wǎng)絡(luò)層,傳輸層使用的是http協(xié)議,使用方對不同的消息注冊不同的handler即可,也支持http的長輪詢模式訂閱事件。mesos為了提高消息傳遞解析效率,消息傳遞支持json和protobuf兩種格式。
這種架構(gòu)的好處是Mesos省去了對消息隊列的依賴。一般情況下這種分布式消息分發(fā)系統(tǒng)都需要消息隊列或者中心存儲的支持,比如Salt使用的是ZeroMQ,Kubenetes使用的是Etcd,而Mesos則不依賴外部的資源支持,只通過actor模型的容錯機制來實現(xiàn)。而缺點也是actor本身的缺點,因為消息都是異步的,需要actor處理消息的丟失以及超時邏輯,Mesos不保證消息的可靠投遞,提供的投遞策略是 “at-most-once”,actor需要通過超時重試機制解決消息丟失的問題。不過任何需要遠程調(diào)用的分布式系統(tǒng)需要處理的類似的問題吧。
Framework的實現(xiàn)解析
從上面的分析可以了解到,F(xiàn)ramework在Mesos中扮演著重要的角色。如果你自己要開發(fā)一個分布式系統(tǒng),打算運行在Mesos中,就需要考慮自己實現(xiàn)一個Framework。
Mesos提供了一個Framework的基礎(chǔ)庫,第三方只需要實現(xiàn)scheduler,和executor兩個接口即可?;A(chǔ)庫是用c++實現(xiàn)了,通過jni提供了java版本,通過python的native方式提供了python版本。而golang的版本是獨立開發(fā)的,沒有依賴c++庫,代碼架構(gòu)比較優(yōu)雅,想用go實現(xiàn)actor的可以參考。這個Framework基礎(chǔ)庫(SchedulerDriver以及ExecutorDriver的實現(xiàn))主要做的事情就是實現(xiàn)前面我們提到的actor模型,和master以及slave交互,收到消息后回調(diào)用戶自定義的scheduler和executor。
下面是java的Scheduler接口:
- public interface Scheduler {
- void registered(SchedulerDriver driver,
- FrameworkID frameworkId,
- MasterInfo masterInfo);
- void reregistered(SchedulerDriver driver, MasterInfo masterInfo);
- //這個是最主要的一個方法,當系統(tǒng)有空閑資源的時候,會詢問Scheduler,
- //是否接受或者拒絕該Offer。如果接受,則同時需要封裝使用該Offer的task信息,調(diào)用driver去執(zhí)行。
- void resourceOffers(SchedulerDriver driver, List<Offer> offers);
- //如果Offer被取消或者被別的Framework使用,則回調(diào)本方法
- void offerRescinded(SchedulerDriver driver, OfferID offerId);
- //該Framework啟動的task狀態(tài)變化時回調(diào)。
- void statusUpdate(SchedulerDriver driver, TaskStatus status);
- //自定義的框架消息
- void frameworkMessage(SchedulerDriver driver,ExecutorID executorId,SlaveID slaveId,byte[]() data);
- void disconnected(SchedulerDriver driver);
- void slaveLost(SchedulerDriver driver, SlaveID slaveId);
- void executorLost(SchedulerDriver driver,ExecutorID executorId,SlaveID slaveId,int status);
- void error(SchedulerDriver driver, String message);
- }
Mesos的Framework是獨立于Mesos系統(tǒng)的,具體的部署方式,以及高可用都需要Framework自己解決,所以要實現(xiàn)一個完備的,高可用的Framework,復(fù)雜度還是挺高的。另外Framwork的機制比較適合需要任務(wù)分發(fā)以及調(diào)度的分布式系統(tǒng),比如Hadoop,Jenkins等。其他的分布式數(shù)據(jù)庫比如Cassandra,Mesos做的事情是通過Scheduler調(diào)度CassandraExecutor部署和管理(包括節(jié)點上的維護操作,比如備份)Cassandra節(jié)點,詳細可參看 https://github.com/mesosphere/cassandra-mesos 。
另外Mesos Master本身沒有持久化存儲,所有數(shù)據(jù)都在內(nèi)存,重啟后數(shù)據(jù)會丟失。但活躍的Framework和Slave注冊時會把自己當前的狀態(tài)發(fā)送給Master,Master通過這種方式恢復(fù)數(shù)據(jù)。所以如果Framework需要持久化Task的執(zhí)行記錄,需要自己實現(xiàn)持久化存儲。
Mesos Slave提供了recovery的機制,用于實現(xiàn)Slave進程重啟后的恢復(fù)。默認情況下,Slave進程重啟后,和該進程相關(guān)的executor/task都會被殺掉,但如果Framework配置開啟了checkpoint設(shè)置,則該Framework相關(guān)的executor/task信息會被持久化到磁盤上,用于重啟后的recovery。
Marathon
Marathon按照官方的說法是個基于Mesos的私有PaaS。它實現(xiàn)了Mesos的Framework,支持通過shell命令和Docker部署應(yīng)用,提供Web界面,支持cpu/mem,實例數(shù)等參數(shù)設(shè)置,支持單應(yīng)用的scale,但不支持復(fù)雜的集群定義。
Marathon本身是通過Scala實現(xiàn)的,也使用了actor模型。它提供了event bus接口,允許其他應(yīng)用通過監(jiān)聽event bus來實現(xiàn)動態(tài)配置,比如前面提到的marathon-lb。
Aurora
Aurora試圖用一種自定義的配置語言去定義Mesos之上的task以及順序關(guān)系,解決各種環(huán)境的異構(gòu)問題,降低用shell腳本的復(fù)雜度。可以理解為Mesos之上的一種粘合劑語言,地位類似于Salt/Ansible的yaml,只不過Mesos本身沒支持類似的配置語言,于是Aurora通過Framework的方式來支持。
Mesos和Kubernetes比較
Mesos和Kubernetes雖然都借鑒了Borg的思想,終極目標類似,但解決方案是不同的。Mesos有點像聯(lián)邦制,承認各邦(Framework)的主權(quán),但各邦讓渡一部分公用的機制出來由Mesos來實現(xiàn),最大化的共享資源,提高資源利用率,F(xiàn)ramework和Mesos是相對獨立的關(guān)系。而Kubernetes有點像單一制,搭建一個通用的平臺,盡量提供全面的能力(網(wǎng)絡(luò),磁盤,內(nèi)存,cpu),制定一個集群應(yīng)用的定義標準,任何復(fù)雜的應(yīng)用都可以按照該標準定義并以最小的變更成本在上面部署運行,主要的變更需求也是因為想享受Kubernetes的動態(tài)伸縮能力帶來的。所以Mesos盡量做的比較少,而Kubernetes盡量做的比較多。Mesos定義是DCOS的kernel,但OS的kernel具體應(yīng)該承擔哪些職責,相關(guān)爭論也從來沒停止過。
相對來說Kubernetes使用要比較容易些,而Mesos更靈活些,需要做的定制開發(fā)工作也比較多。拿前面提到的cassandra來比較,cassandra-mesos的實現(xiàn)很復(fù)雜,而Kubernetes的cassandra例子則只有一個類,就是實現(xiàn)了KubernetesSeedProvider,通過Kubernetes的服務(wù)發(fā)現(xiàn)機制尋找cassandra的seed節(jié)點。當然Mesos上的cassandra可以通過Mesos發(fā)送備份等管理任務(wù),而Kubernetes不提供任務(wù)轉(zhuǎn)發(fā)的功能,這類需求用戶可以通過kubectl的exec方法實現(xiàn)。從這個例子也大致能明白二者的差異點。
Kubernetes下的Kubernetes on Mesos項目的目的就是利用Mesos的這個特性,實現(xiàn)Kubernetes和Mesos上的其他Framework共享資源。如果想了解Kubernetes的更多資料可以看這次一并推送的文章:"Kubernetes 架構(gòu)淺析"。
【本文為51CTO專欄作者“王淵命”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者微信公眾號jolestar-blog獲取授權(quán)】