深入解析DC/OS 1.8
– 高可靠的微服務及大數據管理平台
大家好,歡迎大家參加這次DC/OS的技術分享。
先做個自我介紹,劉超,Linker Networks首席架構師,Open DC/OS社區貢獻者,長期專注於OpenStack, Docker, Mesos等開源軟件的企業級應用與產品化。
從事容器方面工作的朋友可能已經聽說過DC/OS,往往大家誤解DC/OS就是marathon + mesos,其實DC/OS包含很多的組件,DC/OS 1.8九月份發布了,此次分享給大家做一個介紹。
一、DC/OS的基本思想
所謂的DC/OS,全稱為數據中心操作系統,其基本的思想就是使得運維人員操作整個數據中如操作一台電腦一樣。
DC/OS使用了哪些技術可以做到這一點呢?
如圖,左面是普通的Linux操作系統,右面是DC/OS,在這里做了一個對比。
無論是哪種操作系統,都需要管理外部的硬件設備,最重要的四種硬件資源即CPU,內存,存儲,網絡。
最初使用匯編語言寫程序的前輩,還是需要指定使用那些硬件資源的,例如指定使用哪個寄存器,放在內存的哪個位置,寫入或者讀取那個串口等,對於這些資源的使用,需要程序員自己心里非常的清楚,要不然一旦JUMP錯了位置,程序就無法運行。這就像運維數據中心的一台台物理機的前輩一樣,那個程序放在了哪台機器上,使用多少內存,多少硬盤,都需要心里非常的清楚。
為了將程序員從對硬件的直接操作中解放出來,提升程序設計的效率,從而有了操作系統這一層,實現對於硬件資源的統一管理。某個程序使用哪個CPU,哪部分內存,哪部分硬盤,程序只需要調用API就可以了,由操作系統自行分配和管理,其實操作系統只做了一件事情,就是調度。對應到數據中心,也需要一個調度器,將運維人員從指定物理機或者虛擬機的痛苦中解放出來,這就是Mesos。Mesos即使數據中心操作系統的內核。
在使用操作系統的時候,我們可以開發驅動程序來識別新的硬件資源,可以開發內核模塊(例如openvswitch.ko)來干預對於硬件資源的使用,對於Mesos,同樣可以開發isolator來識別新的硬件資源例如GPU,也可以開發Executor來干預資源的使用。
在內核之上,就是系統服務,例如systemd,是用來維護進程運行的,如果systemctl enable xxx,則保證服務掛掉后自動重啟。對於DC/OS,保持服務long run的是marathon,但是僅僅只有marathon還不夠,因為服務是啟動在多台機器上的,而且服務之間是有依賴關系的,一個服務掛掉了,在另外一台機器啟動起來,如何保持服務之間的調用不需要人工干預呢?這需要另外的技術,稱為服務發現,多是通過DNS,負載均衡,虛擬機IP等技術實現的。
使用操作系統,需要安裝一些軟件,於是需要yum之類的包管理系統,使得軟件的使用者和軟件的編譯者分隔開來,軟件的編譯者需要知道這個軟件需要安裝哪些包,包之間的依賴關系是什么,軟件安裝到什么地方,而軟件的使用者僅僅需要yum install就可以了。DC/OS就有這樣一套包管理軟件,和其他的容器管理平台需要自己編譯Docker鏡像,自己寫yml,自己管理依賴不同,DC/OS的軟件使用者只需要dcos package install就可以安裝好軟件了,軟件的配置,節點數目,依賴關系都是有軟件編譯者設置。
在最外層,DC/OS像普通的操作系統一樣,有統一的界面和命令行。通過它們,可以管理安裝包,管理節點,運行任務等。DC/OS不僅僅是運行容器的平台,如果僅僅運行容器,就是容器管理平台,而非數據中心操作系統。通過DC/OS,你可以在每台機器上運行一個命令來進行統一的配置,而無需登錄到每台機器上去。你可以運行容器應用和大數據分析應用並共享資源,並且可以相互發現,這更加符合現代互聯網應用,微服務和大數據不可分割。而且Mesos的架構非常開放,你可以通過開發Framework, Executor, Modules, Hooks等,輕松干預微服務或者大數據任務的執行過程,來定制化你的應用。這也符合操作系統微內核的概念。
二、DC/OS的內核模塊Mesos
Mesos架構如下
這個圖比較的著名了,也有很多文章介紹這個圖,詳情可以看文章http://mesos.apache.org/documentation/latest/architecture/,這里不做過多的介紹。
從圖中可以看到,Mesos有Framework(Framework里面有Scheduler), Master(Master里面有allocator), Agent, Executor, Task幾部分組成。這里面有兩層的Scheduler,一層在Master里面,allocator會將資源公平的分給每一個Framework,二層在Framework里面,Framework的scheduler將資源按規則分配給Task。
Mesos的這幾個角色在一個任務運行的生命周期中,相互關系如下:
Agent會將資源匯報給Master,Master會根據allocator的策略將資源offer給framework的scheduler。Scheduler 可以accept這個資源,運行一個Task,Master將Task交給Agent,Agent交給Executor去真正的運行這個Task。
這個圖相對比較的簡略,真正詳細的過程比這個復雜很多,大家可以參考這篇博客http://www.cnblogs.com/popsuper1982/p/5926724.html,在代碼級別分析了整個任務運行的過程,還畫了一個泳道圖http://images2015.cnblogs.com/blog/635909/201608/635909-20160806163718778-1628977219.png。
要研究Mesos,熟悉整個過程非常重要,這樣一個任務運行出現問題的時候,才能比較好的定位問題在哪里,如果解決。Mesos將一個簡單的任務的運行過程,分成如此多的層次,如此多的角色來做,是為了雙層調度和靈活配置,這是一個內核應該做的事情。
我們如何干預一個Task的運行過程呢?
第一、寫一個Framework
如果你想完全自己控制Task的運行,而非讓Marathon來運行並保持一個無狀態的Task長運行,就需要自己寫一個Framework,在你的Framework里面,三個Task之間的關系你可以自己定義,而非像Marathon一樣,Task * 3,3個任務不分彼此,你的Framework可以控制這三個Task一主兩備,可以控制三個Task的啟動順序,可以將一個先啟動的Task的IP,位置等通過環境變量告知另外兩個Task。
寫一個Framework需要寫一個Scheduler,實現一些接口,如文檔http://mesos.apache.org/documentation/latest/app-framework-development-guide/中所述。
然后使用使用MesosSchedulerDriver來運行這個Scheduler。
其實Mesos這些模塊之間的通信都是通過Protocol Buffer定義消息來交互的,然而如果讓Framework的開發人員還要學會如何使用Protocol Buffer消息和Mesos Master通信,是很痛苦的事情,所以MesosSchedulerDriver幫助你做了這個事情,你只需要實現Scheduler定義的接口就可以了,不需要了解這些接口是誰調用的,調用了接口之后,消息如何傳給Mesos Master。
所有的接口里面,最重要的是resourceOffers函數,根據得到的offers(每個slave都有多少資源),創建一系列tasks,然后調用MesosSchedulerDriver的launchTasks函數,MesosSchedulerDriver會將這些tasks封裝為LaunchTasksMessage發送給Mesos Master。
第二、寫一個Allocator
通過上面的描述,Mesos有兩層調度,第一層就是Allocator,將資源分配給Framework。
Mesos允許用戶通過自己寫Module的方式,寫一個so,然后啟動的時候加載進去,然后在命令行里面指定使用so中的哪個Module。
當然寫Allocator的不多,因為Mesos的DRF算法是Mesos的核心,如果不用這個算法,還不如不用mesos。
Mesos源碼中默認的Allocator,即HierarchicalDRFAllocator的位置在$MESOS_HOME/src/master/allocator/mesos/hierarchical.hpp,而DRF中對每個Framework排序的Sorter位於$MESOS_HOME/src/master/allocator/sorter/drf/sorter.cpp,可以查看其源碼了解它的工作原理。
HierarchicalDRF的基本原理
如何作出offer分配的決定是由資源分配模塊Allocator實現的,該模塊存在於Master之中。資源分配模塊確定Framework接受offer的順序,與此同時,確保在資源利用最大化的條件下公平地共享資源。
由於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}。
HierarchicalDRF核心算法實現在Src/main/allocator/mesos/hierarchical.cpp中HierarchicalAllocatorProcess::allocate函數中。
概況來說調用了三個Sorter(quotaRoleSorter, roleSorter, frameworkSorter),對所有的Framework進行排序,哪個先得到資源,哪個后得到資源。
總的來說分兩大步:先保證有quota的role,調用quotaRoleSorter,然后其他的資源沒有quota的再分,調用roleSorter。
對於每一個大步分兩個層次排序:一層是按照role排序,第二層是相同的role的不同Framework排序,調用frameworkSorter。
每一層的排序都是按照計算的share進行排序來先給誰,再給誰。
這里有幾個概念容易混淆:Quota, Reservation, Role, Weight
-
每個Framework可以有Role,既用於權限,也用於資源分配
-
可以給某個role在offerResources的時候回復Offer::Operation::RESERVE,來預訂某台slave上面的資源。Reservation是很具體的,具體到哪台機器的多少資源屬於哪個Role
-
Quota是每個Role的最小保證量,但是不具體到某個節點,而是在整個集群中保證有這么多就行了。
-
Reserved資源也算在Quota里面。
-
不同的Role之間可以有Weight
在allocator算法結束之后,便調用Master::Offer,最終調用Framework的Scheduler的resourceOffers,讓Framework進行二次調度。同上面的邏輯就串聯起來。
第三、寫一個Hook
你可以寫hook模塊,講代碼插在很多關鍵的步驟,從而改寫整個Executor或者Docker或者Task的啟動的整個過程。
可以干預的hook的地方定義在mesos/hook.hpp中。
Class hook定義如下:
其中比較常用的是slavePrelaunchDockerHook,可以在Docker啟動之前做一些事情,比如准備工作。
還有slaveRemoveExecutorHook,這個可以在executor結束的時候,做一些事情,比如清理工作。
第四、創建Isolator
當你有一種新的資源需要管理,並且每個Task需要針對這個資源進行隔離的時候,寫一個Isolator就是有必要的了。
例如默認的容器並不能動態指定並限制任務硬盤使用的大小,所以mesos-containerizer就有了"disk/du"來定時查看任務使用的硬盤大小,當超出限制的時候采取操作。
Src/slave/containerizer/mesos/containerizer.cpp里面列出了當前支持的isolator,你也可以實現自己的isolator,並且通過modules參數load進去。
Isolator定義了以下函數
在運行一個容器的最后,會調用每一個isolator的isolate函數,通過這個函數,可以對資源進行一定的限制,例如寫入cgroup文件等,但是對於硬盤使用量,其實沒有cgroup可以設置,需要過一段時間du一些,這就需要實現watch函數,過一段時間查看一下硬盤使用量,超過后做一定的操作。
第五、寫一個Executor
如果運行一個普通的容器,或者命令行,則不需要實現Executor,僅僅Mesos默認的Executor就能夠實現這個功能。如果你需要在Executor里面做很多自己定制化的工作,則需要自己寫Executor。
寫一個Executor需要實現一些接口,最重要的就是launchTask接口,然后MesosExecutorDriver將這個Executor運行起來。
就像Framework一樣,Executor也是通過protocol buffer協議和Mesos-Agent進行溝通,通過MesosExecutorDriver,你不需要關心協議的事情,僅僅需要實現接口即可。
三、DC/OS的核心模塊
下面的圖描述了DC/OS的部署架構圖:
在DC/OS看來,所有的節點分為三個區域,一個是管理區域,主要處理對於服務的管理方面的操作,如增刪查改,啟停擴縮等。為了高可用,Master節點可以是多個,在多個Master節點之前,需要有一個負載均衡器。第二個是對外服務區域,也即外界能夠訪問DC/OS內部的服務的區域,這個區域里面的服務多為對外的Nginx之類的,也會有marathon-lb來做外部的負載均衡器,所有對外服務區域的節點之外還需要一個負載均衡器。第三個區域是內部服務區域,用於部署內部服務,如數據庫,消息總線等,這些內部節點不能對外訪問。
第一、Admin Router
AdminRouter是一個反向代理,正是它將對外的區域和對內的區域完全隔離開來,在admin router之外,可以通過公網訪問,在admin router之內全部是私網地址,這樣提供了安全的統一訪問機制。
安裝完畢Open DC/OS之后,安裝一個dcos的命令行工具,通過這個工具可以ssh到master的節點上。
-
eval `ssh-agent -s`
-
ssh-add .ssh/aws01.pem
-
dcos node ssh --master-proxy --leader
在這個節點上/etc/systemd/system路徑下面有三個systemd的service,Open DC/OS的所有組件都是用systemd進行管理的。
-
ip-10-0-7-1 system # ls -l | grep adminrouter
-
lrwxrwxrwx. 1 root root 135 Oct 3 08:00 dcos-adminrouter-reload.service -> /opt/mesosphere/packages/adminrouter--cee9a2abb16c28d1ca6c74af1aff6bc4aac3f134/dcos.target.wants_master/dcos-adminrouter-reload.service
-
lrwxrwxrwx. 1 root root 133 Oct 3 08:00 dcos-adminrouter-reload.timer -> /opt/mesosphere/packages/adminrouter--cee9a2abb16c28d1ca6c74af1aff6bc4aac3f134/dcos.target.wants_master/dcos-adminrouter-reload.timer
-
lrwxrwxrwx. 1 root root 128 Oct 3 08:00 dcos-adminrouter.service -> /opt/mesosphere/packages/adminrouter--cee9a2abb16c28d1ca6c74af1aff6bc4aac3f134/dcos.target.wants_master/dcos-adminrouter.service
可以看到dcos-adminrouter.service是指向/opt/mesosphere/packages下面的一個路徑,Open DC/OS的所有組件都是安裝在這個路徑下面的。
在/opt/mesosphere/packages/adminrouter--cee9a2abb16c28d1ca6c74af1aff6bc4aac3f134/nginx/conf這個路徑下面,有一個文件nginx.master.conf,打開這個文件,就能看到熟悉的對於nginx的配置。
-
upstream mesos {
-
server leader.mesos:5050;
-
}
-
-
upstream marathon {
-
server master.mesos:8080;
-
}
-
-
location /mesos/ {
-
access_by_lua 'auth.validate_jwt_or_exit()';
-
proxy_set_header Host $http_host;
-
proxy_pass http://mesos/;
-
}
-
-
location /marathon/ {
-
# Enforce access restriction. Auth-wise, treat /marathon*
-
# equivalently to /service/marathon*.
-
access_by_lua 'auth.validate_jwt_or_exit()';
-
proxy_set_header Host $http_host;
-
proxy_pass http://marathon/;
-
}
從這個配置文件可以看出,所有對內的訪問marathon的頁面,訪問mesos的頁面,都是通過leader.mesos進行,這個域名是mesos-dns給出的,對應的是內部的IP地址,如果從外部訪問marathon或者mesos的頁面,則必須通過admin router,通過http://admin-router-external-ip/marathon或者http://admin-router-external-ip/mesos來訪問。
第二、Mesos-DNS
對於數據中心操作系統來講,服務發現和負載均衡是最最核心的功能,只有有了這些功能,才能使得服務的物理布局,服務之間的依賴關系,服務掛掉之后的自動修復不需要用戶關心,才能使得用戶像用一台電腦一樣使用整個數據中心。
如果服務之間的相互調用不使用IP地址,而使用域名的話,問題會簡單很多。
如圖所示,對於Mesos上運行的每一個Task,Mesos-DNS都可以通過調用Mesos-Master的API得到,並且為每個Task分配一個域名和IP的對應項。如果一個Task需要訪問另一個Task,則需要配置域名即可,無論Task如何掛掉,如何分配到其他的節點上運行,域名都不會變,當然Task的IP可能會變,但是不用擔心,Mesos-DNS會更新它。每個Mesos-Agent只需要配置/etc/resolv.conf指向mesos-dns就可以了。
當一個Task運行的時候,Mesos-DNS會創建一個域名<task>.<service>.mesos對應:
-
Mesos-Agent的IP地址
-
如果是Mesos Containerizer的話,返回的是Task內部容器的IP
另外<task>.<service>.slave.mesos還會提供所在的物理機的IP地址。這樣通過hostport和Mesos-DNS所給的域名,可以實現服務的發現。
第三:marathon-lb
使用DNS雖然可以實現服務的自發現,但是不容易實現服務的負載均衡和彈性伸縮,而marathon-lb實現了這些功能。
Marathon-lb是一個基於haproxy的負載均衡器,但是它會監聽marathon event bus,每當注冊到marathon-lb上的服務數目變化的時候,marathon-lb也會自動更新haproxy的配置文件,從而實現負載均衡。Marathon-lb可以如圖中實現對外的負載均衡,也可以實現對內的服務之間相互調用的負載均衡。
Marathon的安裝可以在界面中universe里面搜索marathon-lb安裝,也可以通過命令行執行dcos package install Marathon-LB進行安裝,默認安裝的對外的負載均衡器。
我們在服務里面創建如下的應用:
-
{
-
"id": "nginx",
-
"container": {
-
"type": "DOCKER",
-
"docker": {
-
"image": "nginx:1.7.7",
-
"network": "BRIDGE",
-
"portMappings": [
-
{ "hostPort": 0, "containerPort": 80, "servicePort": 10000 }
-
],
-
"forcePullImage":true
-
}
-
},
-
"instances": 1,
-
"cpus": 0.1,
-
"mem": 65,
-
"healthChecks": [{
-
"protocol": "HTTP",
-
"path": "/",
-
"portIndex": 0,
-
"timeoutSeconds": 10,
-
"gracePeriodSeconds": 10,
-
"intervalSeconds": 2,
-
"maxConsecutiveFailures": 10
-
}],
-
"labels":{
-
"HAPROXY_GROUP":"external"
-
}
-
}
在這個應用里面,servicePort為10000則說明我們注冊到marathon-lb上的外部端口為10000, labels里面寫的是external,也即注冊到外部的負載均衡器上。
這個時候,我們訪問public slave上的10000端口,就能看到啟動的nginx的頁面http://54.254.148.129:10000/,內部其他的應用可以通過http://marathon-lb.marathon.mesos:10000來訪問這個nginx
如果我們訪問public slave上的haproxy的配置頁面http://54.254.148.129:9090/haproxy?stats,可以看到如下的映射關系。
對外marathon-lb監聽10000端口,對內映射為10.0.1.78上的20215端口,如果我們從服務頁面上查看,的確啟動的nginx是監聽20215端口的。
接下來我們部署marathon-lb-autoscale,它監控haproxy,發現RPS(request per seconds)超過一定的數目,就對應用進行彈性擴展。
-
{
-
"id": "marathon-lb-autoscale",
-
"args":[
-
"--marathon", "http://leader.mesos:8080",
-
"--haproxy", "http://marathon-lb.marathon.mesos:9090",
-
"--target-rps", "100",
-
"--apps", "nginx_10000"
-
],
-
"cpus": 0.1,
-
"mem": 16.0,
-
"instances": 1,
-
"container": {
-
"type": "DOCKER",
-
"docker": {
-
"image": "brndnmtthws/marathon-lb-autoscale",
-
"network": "HOST",
-
"forcePullImage": true
-
}
-
}
-
}
接下來,我們部署應用siege向nginx發送請求
-
{
-
"id": "siege",
-
"args":[
-
"-d1",
-
"-r1000",
-
"-c100",
-
"http://marathon-lb.marathon.mesos:10000/"
-
],
-
"cpus": 0.5,
-
"mem": 16.0,
-
"instances": 1,
-
"container": {
-
"type": "DOCKER",
-
"volumes": [],
-
"docker": {
-
"image": "yokogawa/siege",
-
"network": "HOST",
-
"privileged": false,
-
"parameters": [],
-
"forcePullImage": false
-
}
-
}
-
}
如果我們看haproxy的stats頁面,發現已經有請求發過來了。這個時候我們增加siege到10,給nginx加壓。
過一段時間就會發現marathon-lb-autoscale已經有動作了。
將一個nginx變成8個nginx
當我們將siege從10個變回0個的時候。
第四、Minuteman
Minuteman是一個內部的東西向的負載均衡器,可用於設置VIP,多個實例使用同一個VIP來進行負載均衡。
在創建服務的時候,選擇Load Balanced,則下面會出現一行地址:nginxdocker.marathon.l4lb.thisdcos.directory:80,這個就是minuteman分配的VIP。
當服務創建好了之后,通過curl http://nginxdocker.marathon.l4lb.thisdcos.directory:80就可以訪問這個服務,但是我們如果ping這個域名卻是不通的,而且對於的IP地址也是很奇怪的IP地址,這個IP就是VIP.
這是怎么做到的呢?minuteman的load balancer是基於Netfilter的,在dcos的slave節點上,我們能看到多出來了四個iptables規則。其中前兩個規則是在raw表里面的,后兩個規則是在filter表里面的。
-
-A PREROUTING -p tcp -m set --match-set minuteman dst,dst -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j NFQUEUE --queue-balance 50:58
-
-A OUTPUT -p tcp -m set --match-set minuteman dst,dst -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j NFQUEUE --queue-balance 50:58
-
-A FORWARD -p tcp -m set --match-set minuteman dst,dst -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j REJECT --reject-with icmp-port-unreachable
-
-A OUTPUT -p tcp -m set --match-set minuteman dst,dst -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j REJECT --reject-with icmp-port-unreachable
根據iptbles的規則raw表中的規則會被先執行,一旦到達了filter表的minuteman的包就都過濾掉了。
NFQUEUE的規則表示將對於包的處理權交給用戶態的一個進程。--queue-balance表示會將包發給幾個queue,然后用戶態進程會使用libnetfilter_queue連接到這些queue中,將包讀出來,根據包的內容做決策后放回內核進行發送。
在每一個Mesos-Agent節點上都運行這一個minuteman的進程,監聽這些queue,我們可以通過訪問API查看VIP的映射關系,curl http://localhost:61421/vips。
我們可以看到VIP的11.112.175.214后面跟着兩個節點10.0.1.78:27003和10.0.1.78:4989,正好對應nginx的兩個實例。
四、DC/OS的微服務及大數據的管理機制
DC/OS是基於Mesos的,Mesos的靈活框架機制可以使得DC/OS既能夠部署容器,也能夠部署大數據框架,大數據框架在不運行任務的時候,幾乎不占用資源,從而真正實現微服務和大數據框架的資源共享。
前面我們部署容器的時候,都是自己准備marathon的json進行部署的,這就需要使用服務的人和設計服務的人同樣的專業。
DC/OS采用了一種package管理機制,將運行一個微服務或者框架所需要的各種配置制作成模板,模板由專業人士制作好上傳到package repository,使用者就不需要那么專業,只要運行dcos package install安裝即可。
Mesosphere提供了官方的package repository,名為universe,地址為https://universe.mesosphere.com/repo,在github上可以找到對應的代碼https://github.com/mesosphere/universe。
對於一個package,往往包含下面的部分:
-
package.json:這里面保存了一些metadata的數據,例如對於spark
-
"name": "spark",
-
"description": "Spark is a fast and general cluster computing system for Big Data. Documentation: https://docs.mesosphere.com/current/usage/service-guides/spark/",
-
"licenses": [
-
{
-
"name": "Apache License Version 2.0",
-
"url": "https://raw.githubusercontent.com/apache/spark/master/LICENSE"
-
}
-
],
-
"tags": [
-
"bigdata",
-
"mapreduce",
-
"batch",
-
"analytics"
-
],
-
config.json:保存一些配置項,例如對於spark
-
"name": {
-
"default": "spark",
-
"description": "The Spark Dispatcher will register with Mesos with this as a framework name. This service will be available at http://<dcos_url>/service/<name>/",
-
"type": "string"
-
},
-
"cpus": {
-
"default": 1,
-
"description": "CPU shares",
-
"minimum": 0.0,
-
"type": "number"
-
},
-
"mem": {
-
"default": 1024.0,
-
"description": "Memory (MB)",
-
"minimum": 1024.0,
-
"type": "number"
-
},
-
"role": {
-
"description": "The Spark Dispatcher will register with Mesos with this role.",
-
"type": "string",
-
"default": "*"
-
},
-
marathon.json.mustache:是一個模板,里面的一些變量會替換為config.json里面的內容,最終變成可以直接發送給marathon的請求。以spark為例
-
"id": "{{service.name}}",
-
"cpus": {{service.cpus}},
-
"mem": {{service.mem}},
-
"container": {
-
"type": "DOCKER",
-
"docker": {
-
"image": "{{resource.assets.container.docker.spark_docker}}",
-
"network": "HOST",
-
"forcePullImage": true
-
}
-
},
-
resource.json:是一些資源,如image,tar.gz文件等
-
"assets": {
-
"container": {
-
"docker": {
-
"spark_docker": "mesosphere/spark:1.0.2-2.0.0"
-
}
-
}
-
},
所有的這些配置都像模板一樣已經預先寫好,安裝的時候界面上一點,或者一行命令就安裝好了。
當然你如果點擊Advanced Installation,則所有的配置都可以定制化
就像yum里面一樣,將mysql-server的yum包的制作者和mysql的使用者分開,普通用戶作為使用者,不需要了解太多的細節,用就是了。
如果想在數據中心里面使用package管理,可以生成自己的local universe,里面放入自己的應用,只要專業人士設計一次,便可以多次使用。也可以一次安裝多個軟件形成一個group,里面包含微服務的,也包含大數據的,兩者可以通過服務發現相互訪問。
我們在這里先安裝一個spark的軟件
最初安裝完畢spark,卻發現只有一個docker
Spark不是一個集群計算框架嗎,怎么會只有一個Docker呢?這就是mesos對大數據框架管理的特殊之處。在spark不運行任務的時候,就僅僅占用這一個docker,其實是一個框架。
安裝過程如圖所示:
-
dcos package install spark會將請求提交給admin router
-
admin router會將請求提交給cosmos,也即package管理的服務
-
cosmos將config.json, resource.json, marathon.json組合成為一個marathon請求提交給marathon
-
marathon將請求交給mesos-master,然后交給mesos-agent
-
mesos-agent啟動一個容器運行spark
-
啟動的spark容器會注冊到mesos里面成為一個新的framework
真正運行spark任務的時候,才會有其他占用資源的任務被創建出來。
-
dcos spark run --submit-args='-Dspark.mesos.coarse=true --driver-cores 1 --driver-memory 1024M --class org.apache.spark.examples.SparkPi https://downloads.mesosphere.com/spark/assets/spark-examples_2.10-1.4.0-SNAPSHOT.jar 30'
Spark運行過程如圖:
-
dcos spark run將任務提交給admin router
-
admin router將任務提交給spark framework
-
spark framework將任務提交給mesos-master
-
mesos-master將任務分發給mesos-agent進行分別處理
-
任務運行完畢后,所有mesos-agent上占用的資源又都釋放了。
正是這種模式,才實現微服務和大數據框架的共享資源,與此相對應的是使用Docker來部署spark集群,然后集群自管理,不歸mesos管理。這樣在創建spark集群的時候,就需要指定spark worker占用的資源,比如16G,然而這16G資源則無論spark是否在計算,都會被占用,都不會被其他的框架使用。
五、DC/OS 1.8的新功能
對於最新的DC/OS 1.8,有一個博客https://dcos.io/blog/2016/introducing-dc-os-1-8-ga/index.html描述了最新的功能。
其中第一個重要的功能為Mesos 1.0 and the Universal Container Runtime,也即可以使用mesos-containerizer來運行Docker的鏡像了。這也是DC/OS對於容器的管理越來越獨立的體現。
我們在mesos-agent所在的機器上可以查看
-
ip-10-0-1-78 dcos.target.wants_slave # ps aux | grep mesos-agent
-
root 1824 0.6 0.3 1069204 46948 ? Ssl Oct03 9:57 /opt/mesosphere/packages/mesos--19a545facb66e57dfe2bb905a001a58b7eaf6004/bin/mesos-agent
mesos-agent的配置在路徑/opt/mesosphere/packages/mesos--19a545facb66e57dfe2bb905a001a58b7eaf6004下面,在/opt/mesosphere/packages/mesos--19a545facb66e57dfe2bb905a001a58b7eaf6004/dcos.target.wants_slave/dcos-mesos-slave.service里面是mesos-slave的啟動參數的設置,通過mesos的文檔,我們知道對於mesos的參數可以使用環境變量進行設置。
-
ip-10-0-1-78 dcos.target.wants_slave # cat dcos-mesos-slave.service
-
[Unit]
-
Description=Mesos Agent: DC/OS Mesos Agent Service
-
-
[Service]
-
Restart=always
-
StartLimitInterval=0
-
RestartSec=5
-
KillMode=control-group
-
Delegate=true
-
LimitNOFILE=infinity
-
TasksMax=infinity
-
EnvironmentFile=/opt/mesosphere/environment
-
EnvironmentFile=/opt/mesosphere/etc/mesos-slave-common
-
EnvironmentFile=/opt/mesosphere/etc/mesos-slave
-
EnvironmentFile=/opt/mesosphere/etc/proxy.env
-
EnvironmentFile=-/opt/mesosphere/etc/mesos-slave-common-extras
-
EnvironmentFile=-/var/lib/dcos/mesos-slave-common
-
EnvironmentFile=-/var/lib/dcos/mesos-resources
-
EnvironmentFile=-/run/dcos/etc/mesos-slave
-
ExecStartPre=/bin/ping -c1 ready.spartan
-
ExecStartPre=/bin/ping -c1 leader.mesos
-
ExecStartPre=/opt/mesosphere/bin/bootstrap dcos-mesos-slave
-
ExecStartPre=/opt/mesosphere/bin/make_disk_resources.py /var/lib/dcos/mesos-resources
-
ExecStartPre=/bin/bash -c 'for i in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 2 > $i; echo -n "$i: "; cat $i; done'
-
ExecStart=/opt/mesosphere/packages/mesos--19a545facb66e57dfe2bb905a001a58b7eaf6004/bin/mesos-agent
在文件/opt/mesosphere/etc/mesos-slave-common中配置了大量的mesos-agent的參數
-
MESOS_MASTER=zk://zk-1.zk:2181,zk-2.zk:2181,zk-3.zk:2181,zk-4.zk:2181,zk-5.zk:2181/mesos
-
MESOS_CONTAINERIZERS=docker,mesos
-
MESOS_LOG_DIR=/var/log/mesos
-
MESOS_MODULES_DIR=/opt/mesosphere/etc/mesos-slave-modules
-
MESOS_CONTAINER_LOGGER=org_apache_mesos_LogrotateContainerLogger
-
MESOS_ISOLATION=cgroups/cpu,cgroups/mem,disk/du,network/cni,filesystem/linux,docker/runtime,docker/volume
-
MESOS_DOCKER_VOLUME_CHECKPOINT_DIR=/var/lib/mesos/isolators/docker/volume
-
MESOS_IMAGE_PROVIDERS=docker
-
MESOS_NETWORK_CNI_CONFIG_DIR=/opt/mesosphere/etc/dcos/network/cni
-
MESOS_NETWORK_CNI_PLUGINS_DIR=/opt/mesosphere/active/cni/
-
MESOS_WORK_DIR=/var/lib/mesos/slave
-
MESOS_SLAVE_SUBSYSTEMS=cpu,memory
-
MESOS_EXECUTOR_ENVIRONMENT_VARIABLES=file:///opt/mesosphere/etc/mesos-executor-environment.json
-
MESOS_EXECUTOR_REGISTRATION_TIMEOUT=10mins
-
MESOS_CGROUPS_ENABLE_CFS=true
-
MESOS_CGROUPS_LIMIT_SWAP=false
-
MESOS_DOCKER_REMOVE_DELAY=1hrs
-
MESOS_DOCKER_STOP_TIMEOUT=20secs
-
MESOS_DOCKER_STORE_DIR=/var/lib/mesos/slave/store/docker
-
MESOS_GC_DELAY=2days
-
MESOS_HOSTNAME_LOOKUP=false
-
GLOG_drop_log_memory=false
默認的mesos-containerizer的隔離只包括cpu和memory,然而在最新的mesos版本里面,多了provisioner這一層,在上面的配置里面隔離了MESOS_ISOLATION=cgroups/cpu,cgroups/mem,disk/du,network/cni,filesystem/linux,docker/runtime,docker/volume,從而可以啟動docker的鏡像了。
第二個最重要的功能是CNI, container network interface。
CNI要工作需要三部分:
首先DC/OS不需要外置的IPAM,而是由mesos-master的replicated_log負責管理分配IP地址,Mesos需要啟動的時候,載入overlay network的modules。
在路徑/opt/mesosphere/etc/mesos-slave-modules下面有文件overlay_slave_modules.json
-
ip-10-0-1-78 mesos-slave-modules # cat overlay_slave_modules.json
-
{
-
"libraries":
-
[
-
{
-
"file": "/opt/mesosphere/active/mesos-overlay-modules/lib/mesos/libmesos_network_overlay.so",
-
"modules":
-
[
-
{
-
"name": "com_mesosphere_mesos_OverlayAgentManager",
-
"parameters" :
-
[
-
{
-
"key": "agent_config",
-
"value" : "/opt/mesosphere/etc/overlay/config/agent.json"
-
}
-
]
-
}
-
]
-
}
-
]
-
}
其次需要載入CNI isolator,這個在MESOS_ISOLATION這個環境變量里面已經配置了。
最后還需要navstar服務來實現跨節點之間的IP互訪問
每個mesos-agent的機器上都有opt/mesosphere/packages/navstar--589afdaef03114a17576ee648ae433a052f7a4b9/,都會運行一個navstar進程。
每個機器上都會創建網卡d-dcos,如果Docker容器使用CNI獲取IP的容器都Attach到這個網卡上,而非docker0上。
每個機器上都會創建網卡m-dcos,如果mesos容器使用CNI獲取IP的容器都Attach到這個網卡上。
每台機器的d-dcos和m-dcos的網段都不同。
每台機器都會創建一個vtep1024的網卡,作為VTEP,背后是vxlan。
每台機器都會創建默認的路由表,從本節點連接到其他的節點默認走vtep1024這個網卡。
-
9.0.0.0/24 via 44.128.0.1 dev vtep1024
-
9.0.1.0/24 via 44.128.0.2 dev vtep1024
-
9.0.3.0/24 via 44.128.0.4 dev vtep1024
對DC/OS的網絡的配置在/opt/mesosphere/etc/dcos/network/cni路徑下
為了試驗這兩個新的功能,我們首先創建一個使用CNI的Mesos容器,但是啟動的是Docker的Image nginx
-
{
-
"id":"nginxmesos",
-
"cmd":"env; ip -o addr; sleep 3600",
-
"cpus":0.10,
-
"mem":512,
-
"instances":1,
-
"ipAddress":{
-
"networkName":"dcos"
-
},
-
"container":{
-
"type":"MESOS",
-
"docker":{
-
"network":"USER",
-
"image":"nginx",
-
"portMappings":[
-
{
-
"host_port": 0,
-
"container_port": 80,
-
"protocol": "tcp"
-
}
-
]
-
}
-
}
-
}
在日志里面,打印出來容器的IP地址是m-dcos網段的。
然后我們再啟動一個使用CNI的Docker容器
-
{
-
"id":"nginxmesos1",
-
"cmd":"env; ip -o addr; sleep 3600",
-
"cpus":0.10,
-
"mem":512,
-
"instances":1,
-
"ipAddress":{
-
"networkName":"dcos"
-
},
-
"container":{
-
"type":"DOCKER",
-
"docker":{
-
"network":"USER",
-
"image":"nginx",
-
"portMappings":[
-
{
-
"host_port": 0,
-
"container_port": 80,
-
"protocol": "tcp"
-
}
-
]
-
}
-
}
-
}
從日志我們看出,配置的IP是d-dcos網段的,而非docker0網段的。
從Mesos上我們看出這兩個容器是在兩個節點上的
登入Docker的容器,ping另外一個CNI的mesos的IP是沒有問題的。
-
ip-10-0-1-78 cni # docker ps
-
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-
e7908deb3017 nginx "/bin/sh -c 'env; ip " 28 minutes ago Up 28 minutes 80/tcp, 443/tcp mesos-b3fbe6d9-236a-4856-a986-9babbba9c02c-S2.e3c96fa7-b5ff-4af6-9099-bbed399c7c37
-
a992929fb0d1 nginx "nginx -g 'daemon off" 6 hours ago Up 6 hours 443/tcp, 0.0.0.0:4989->80/tcp mesos-b3fbe6d9-236a-4856-a986-9babbba9c02c-S2.fca41f8d-816c-49cd-9b19-ba059b95e885
-
8032756dd66e nginx "nginx -g 'daemon off" 6 hours ago Up 6 hours 443/tcp, 0.0.0.0:27003->80/tcp mesos-b3fbe6d9-236a-4856-a986-9babbba9c02c-S2.c0fdd3db-6f17-41d3-ab05-6f2d4d0bfa13
-
ip-10-0-1-78 cni # docker exec -it e7908deb3017 bash
-
root@e7908deb3017:/# ip addr
-
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
-
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
-
inet 127.0.0.1/8 scope host lo
-
valid_lft forever preferred_lft forever
-
inet6 ::1/128 scope host
-
valid_lft forever preferred_lft forever
-
51: eth0@if52: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1420 qdisc noqueue state UP group default
-
link/ether 02:42:09:00:03:82 brd ff:ff:ff:ff:ff:ff
-
inet 9.0.3.130/25 scope global eth0
-
valid_lft forever preferred_lft forever
-
inet6 fe80::42:9ff:fe00:382/64 scope link
-
valid_lft forever preferred_lft forever
-
root@e7908deb3017:/# ping 9.0.2.13
-
PING 9.0.2.13 (9.0.2.13): 56 data bytes
-
64 bytes from 9.0.2.13: icmp_seq=0 ttl=62 time=0.709 ms
-
64 bytes from 9.0.2.13: icmp_seq=1 ttl=62 time=0.535 ms