深刻解析DC/OS 1.8 html
– 高可靠的微服務及大數據管理平臺 node
你們好,歡迎你們參加此次DC/OS的技術分享。mysql
先作個自我介紹,劉超,Linker Networks首席架構師,Open DC/OS社區貢獻者,長期專一於OpenStack, Docker, Mesos等開源軟件的企業級應用與產品化。linux
從事容器方面工做的朋友可能已經據說過DC/OS,每每你們誤解DC/OS就是marathon + mesos,其實DC/OS包含不少的組件,DC/OS 1.8九月份發佈了,這次分享給你們作一個介紹。nginx
所謂的DC/OS,全稱爲數據中心操做系統,其基本的思想就是使得運維人員操做整個數據中如操做一臺電腦同樣。git
DC/OS使用了哪些技術能夠作到這一點呢?程序員
如圖,左面是普通的Linux操做系統,右面是DC/OS,在這裏作了一個對比。github
不管是哪一種操做系統,都須要管理外部的硬件設備,最重要的四種硬件資源即CPU,內存,存儲,網絡。算法
最初使用匯編語言寫程序的前輩,仍是須要指定使用那些硬件資源的,例如指定使用哪一個寄存器,放在內存的哪一個位置,寫入或者讀取那個串口等,對於這些資源的使用,須要程序員本身內心很是的清楚,要否則一旦JUMP錯了位置,程序就沒法運行。這就像運維數據中心的一臺臺物理機的前輩同樣,那個程序放在了哪臺機器上,使用多少內存,多少硬盤,都須要內心很是的清楚。sql
爲了將程序員從對硬件的直接操做中解放出來,提高程序設計的效率,從而有了操做系統這一層,實現對於硬件資源的統一管理。某個程序使用哪一個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等,輕鬆干預微服務或者大數據任務的執行過程,來定製化你的應用。這也符合操做系統微內核的概念。
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的運行過程呢?
若是你想徹底本身控制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。
經過上面的描述,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
在allocator算法結束以後,便調用Master::Offer,最終調用Framework的Scheduler的resourceOffers,讓Framework進行二次調度。同上面的邏輯就串聯起來。
你能夠寫hook模塊,講代碼插在不少關鍵的步驟,從而改寫整個Executor或者Docker或者Task的啓動的整個過程。
能夠干預的hook的地方定義在mesos/hook.hpp中。
Class hook定義以下:
其中比較經常使用的是slavePrelaunchDockerHook,能夠在Docker啓動以前作一些事情,好比準備工做。
還有slaveRemoveExecutorHook,這個能夠在executor結束的時候,作一些事情,好比清理工做。
當你有一種新的資源須要管理,而且每一個Task須要針對這個資源進行隔離的時候,寫一個Isolator就是有必要的了。
例如默認的容器並不能動態指定並限制任務硬盤使用的大小,因此mesos-containerizer就有了"disk/du"來定時查看任務使用的硬盤大小,當超出限制的時候採起操做。
Src/slave/containerizer/mesos/containerizer.cpp裏面列出了當前支持的isolator,你也能夠實現本身的isolator,而且經過modules參數load進去。
Isolator定義瞭如下函數
在運行一個容器的最後,會調用每個isolator的isolate函數,經過這個函數,能夠對資源進行必定的限制,例如寫入cgroup文件等,可是對於硬盤使用量,其實沒有cgroup能夠設置,須要過一段時間du一些,這就須要實現watch函數,過一段時間查看一下硬盤使用量,超事後作必定的操做。
若是運行一個普通的容器,或者命令行,則不須要實現Executor,僅僅Mesos默認的Executor就可以實現這個功能。若是你須要在Executor裏面作不少本身定製化的工做,則須要本身寫Executor。
寫一個Executor須要實現一些接口,最重要的就是launchTask接口,而後MesosExecutorDriver將這個Executor運行起來。
就像Framework同樣,Executor也是經過protocol buffer協議和Mesos-Agent進行溝通,經過MesosExecutorDriver,你不須要關心協議的事情,僅僅須要實現接口便可。
下面的圖描述了DC/OS的部署架構圖:
在DC/OS看來,全部的節點分爲三個區域,一個是管理區域,主要處理對於服務的管理方面的操做,如增刪查改,啓停擴縮等。爲了高可用,Master節點能夠是多個,在多個Master節點以前,須要有一個負載均衡器。第二個是對外服務區域,也即外界可以訪問DC/OS內部的服務的區域,這個區域裏面的服務多爲對外的Nginx之類的,也會有marathon-lb來作外部的負載均衡器,全部對外服務區域的節點以外還須要一個負載均衡器。第三個區域是內部服務區域,用於部署內部服務,如數據庫,消息總線等,這些內部節點不能對外訪問。
AdminRouter是一個反向代理,正是它將對外的區域和對內的區域徹底隔離開來,在admin router以外,能夠經過公網訪問,在admin router以內所有是私網地址,這樣提供了安全的統一訪問機制。
安裝完畢Open DC/OS以後,安裝一個dcos的命令行工具,經過這個工具能夠ssh到master的節點上。
在這個節點上/etc/systemd/system路徑下面有三個systemd的service,Open DC/OS的全部組件都是用systemd進行管理的。
能夠看到dcos-adminrouter.service是指向/opt/mesosphere/packages下面的一個路徑,Open DC/OS的全部組件都是安裝在這個路徑下面的。
在/opt/mesosphere/packages/adminrouter--cee9a2abb16c28d1ca6c74af1aff6bc4aac3f134/nginx/conf這個路徑下面,有一個文件nginx.master.conf,打開這個文件,就能看到熟悉的對於nginx的配置。
從這個配置文件能夠看出,全部對內的訪問marathon的頁面,訪問mesos的頁面,都是經過leader.mesos進行,這個域名是mesos-dns給出的,對應的是內部的IP地址,若是從外部訪問marathon或者mesos的頁面,則必須經過admin router,經過http://admin-router-external-ip/marathon或者http://admin-router-external-ip/mesos來訪問。
對於數據中心操做系統來說,服務發現和負載均衡是最最核心的功能,只有有了這些功能,才能使得服務的物理佈局,服務之間的依賴關係,服務掛掉以後的自動修復不須要用戶關心,才能使得用戶像用一臺電腦同樣使用整個數據中心。
若是服務之間的相互調用不使用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對應:
另外<task>.<service>.slave.mesos還會提供所在的物理機的IP地址。這樣經過hostport和Mesos-DNS所給的域名,能夠實現服務的發現。
使用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進行安裝,默認安裝的對外的負載均衡器。
咱們在服務裏面建立以下的應用:
在這個應用裏面,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)超過必定的數目,就對應用進行彈性擴展。
接下來,咱們部署應用siege向nginx發送請求
若是咱們看haproxy的stats頁面,發現已經有請求發過來了。這個時候咱們增長siege到10,給nginx加壓。
過一段時間就會發現marathon-lb-autoscale已經有動做了。
將一個nginx變成8個nginx
當咱們將siege從10個變回0個的時候。
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表裏面的。
根據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是基於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,每每包含下面的部分:
全部的這些配置都像模板同樣已經預先寫好,安裝的時候界面上一點,或者一行命令就安裝好了。
固然你若是點擊Advanced Installation,則全部的配置均可以定製化
就像yum裏面同樣,將mysql-server的yum包的製做者和mysql的使用者分開,普通用戶做爲使用者,不須要了解太多的細節,用就是了。
若是想在數據中內心面使用package管理,能夠生成本身的local universe,裏面放入本身的應用,只要專業人士設計一次,即可以屢次使用。也能夠一次安裝多個軟件造成一個group,裏面包含微服務的,也包含大數據的,二者能夠經過服務發現相互訪問。
咱們在這裏先安裝一個spark的軟件
最初安裝完畢spark,卻發現只有一個docker
Spark不是一個集羣計算框架嗎,怎麼會只有一個Docker呢?這就是mesos對大數據框架管理的特殊之處。在spark不運行任務的時候,就僅僅佔用這一個docker,實際上是一個框架。
安裝過程如圖所示:
真正運行spark任務的時候,纔會有其餘佔用資源的任務被建立出來。
Spark運行過程如圖:
正是這種模式,才實現微服務和大數據框架的共享資源,與此相對應的是使用Docker來部署spark集羣,而後集羣自管理,不歸mesos管理。這樣在建立spark集羣的時候,就須要指定spark worker佔用的資源,好比16G,然而這16G資源則不管spark是否在計算,都會被佔用,都不會被其餘的框架使用。
對於最新的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所在的機器上能夠查看
mesos-agent的配置在路徑/opt/mesosphere/packages/mesos--19a545facb66e57dfe2bb905a001a58b7eaf6004下面,在/opt/mesosphere/packages/mesos--19a545facb66e57dfe2bb905a001a58b7eaf6004/dcos.target.wants_slave/dcos-mesos-slave.service裏面是mesos-slave的啓動參數的設置,經過mesos的文檔,咱們知道對於mesos的參數可使用環境變量進行設置。
在文件/opt/mesosphere/etc/mesos-slave-common中配置了大量的mesos-agent的參數
默認的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
其次須要載入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這個網卡。
對DC/OS的網絡的配置在/opt/mesosphere/etc/dcos/network/cni路徑下
爲了試驗這兩個新的功能,咱們首先建立一個使用CNI的Mesos容器,可是啓動的是Docker的Image nginx
在日誌裏面,打印出來容器的IP地址是m-dcos網段的。
而後咱們再啓動一個使用CNI的Docker容器
從日誌咱們看出,配置的IP是d-dcos網段的,而非docker0網段的。
從Mesos上咱們看出這兩個容器是在兩個節點上的
登入Docker的容器,ping另一個CNI的mesos的IP是沒有問題的。