針對(duì)“互聯(lián)網(wǎng)+”時(shí)代的業(yè)務(wù)增長(zhǎng)、變化速度及大規(guī)模計(jì)算的需求,廉價(jià)的、高可擴(kuò)展的分布式x86集群已成為標(biāo)準(zhǔn)解決方案,如Google已經(jīng)在幾千萬(wàn)臺(tái)服務(wù)器上部署分布式系統(tǒng)。Docker及其相關(guān)技術(shù)的出現(xiàn)和發(fā)展,又給大規(guī)模集群管理帶來(lái)了新的想象空間。如何將二者進(jìn)行有效地結(jié)合?本文將介紹數(shù)人科技基于Mesos和Docker的分布式計(jì)算平臺(tái)的實(shí)踐。
分布式系統(tǒng)設(shè)計(jì)準(zhǔn)則
可伸縮性
首先分布式系統(tǒng)一定是大規(guī)模的系統(tǒng),有很好的Scalability。出于成本的考慮,很多大規(guī)模的分布式系統(tǒng)一般采用廉價(jià)的PC服務(wù)器,而不是大型的高性能服務(wù)器。
沒(méi)有單點(diǎn)失效
廉價(jià)的PC服務(wù)器在大規(guī)模使用中經(jīng)常會(huì)遇到各種各樣的問(wèn)題,PC服務(wù)器的硬件不可能是高可靠的,比如Google的數(shù)據(jù)中心每天都會(huì)有大量的硬盤(pán)失效,所以分布式系統(tǒng)一定要對(duì)硬件容錯(cuò),保證沒(méi)有任何的單點(diǎn)失效。在這種很不穩(wěn)定、很不可靠的硬件計(jì)算環(huán)境下,搭建一個(gè)分布式系統(tǒng)提供高可靠服務(wù),必須要通過(guò)軟件來(lái)容錯(cuò)。分布式系統(tǒng)針對(duì)不允許有單點(diǎn)失效的要求有兩方面的設(shè)計(jì)考慮,一種是服務(wù)類的企業(yè)級(jí)應(yīng)用,每個(gè)服務(wù)后臺(tái)實(shí)例都要有多個(gè)副本,一兩臺(tái)硬件故障不至于影響所有服務(wù)實(shí)例;另外一種數(shù)據(jù)存儲(chǔ)的應(yīng)用,每份數(shù)據(jù)也必須要有多個(gè)備份,保證即使某幾個(gè)硬件壞掉了數(shù)據(jù)也不會(huì)丟失。
高可靠性
除了單點(diǎn)失效,還要保證高可靠性。在分布式環(huán)境下,針對(duì)企業(yè)級(jí)服務(wù)應(yīng)用,要做負(fù)載均衡和服務(wù)發(fā)現(xiàn)來(lái)保證高可靠性;針對(duì)數(shù)據(jù)服務(wù),為了做到高可靠性,首先要按照某種算法來(lái)把整體數(shù)據(jù)分片(因?yàn)橐慌_(tái)服務(wù)器裝不下),然后按照同樣的算法來(lái)進(jìn)行分片查找。
數(shù)據(jù)本地性
再一個(gè)分布式設(shè)計(jì)理念是數(shù)據(jù)本地性,因?yàn)榫W(wǎng)絡(luò)通信開(kāi)銷是分布式系統(tǒng)的瓶頸,要減少網(wǎng)絡(luò)開(kāi)銷,應(yīng)當(dāng)讓計(jì)算任務(wù)去找數(shù)據(jù),而不是讓數(shù)據(jù)去找計(jì)算。
分布式系統(tǒng)與Linux操作系統(tǒng)的比較
由于縱向拓展可優(yōu)化空間太?。▎闻_(tái)服務(wù)器的性能上限很明顯),分布式系統(tǒng)強(qiáng)調(diào)橫向擴(kuò)展、橫向優(yōu)化,當(dāng)分布式集群計(jì)算資源不足時(shí),就要往集群里面添加服務(wù)器,來(lái)不停地提升分布式集群的計(jì)算能力。分布式系統(tǒng)要做到統(tǒng)一管理集群的所有服務(wù)器,屏蔽底層管理細(xì)節(jié),諸如容錯(cuò)、調(diào)度、通信等,讓開(kāi)發(fā)人員覺(jué)得分布式集群在邏輯上是一臺(tái)服務(wù)器。
和單機(jī)Linux操作系統(tǒng)相比,雖然分布式系統(tǒng)還沒(méi)有成熟到成為“分布式操作系統(tǒng)”,但它和單機(jī)Linux一樣要解決五大類操作系統(tǒng)必需的功能,即資源分配、進(jìn)程管理、任務(wù)調(diào)度、進(jìn)程間通信(IPC)和文件系統(tǒng),可分別由Mesos、Docker、Marathon/Chronos、RabbitMQ和HDFS/Ceph來(lái)解決,對(duì)應(yīng)于Linux下的Linux Kernel、Linux Kernel、init.d/cron、Pipe/Socket和ext4,如圖1所示。
圖1 分布式系統(tǒng)與Linux操作系統(tǒng)的比較
基于Mesos的分布式計(jì)算平臺(tái)
Mesos資源分配原理
目前我們的Mesos集群部署在公有云服務(wù)上,用100多臺(tái)虛擬機(jī)組成Mesos集群。Mesos不要求計(jì)算節(jié)點(diǎn)是物理服務(wù)器還是虛擬服務(wù)器,只要是Linux操作系統(tǒng)就可以。Mesos可以理解成一個(gè)分布式的Kernel,只分配集群計(jì)算資源,不負(fù)責(zé)任務(wù)調(diào)度?;贛esos之上可以運(yùn)行不同的分布式計(jì)算平臺(tái),如Spark、Storm、Hadoop、Marathon和Chronos等。Spark、Storm和Hadoop這樣的計(jì)算平臺(tái)有任務(wù)調(diào)度功能,可以直接使用Mesos SDK跟Mesos請(qǐng)求資源,然后自行調(diào)度計(jì)算任務(wù),并對(duì)硬件容錯(cuò)。Marathon針對(duì)服務(wù)型分布式應(yīng)用提供任務(wù)調(diào)度,比如企業(yè)網(wǎng)站等這類需要長(zhǎng)時(shí)間運(yùn)行的服務(wù)。通常網(wǎng)站應(yīng)用程序沒(méi)有任務(wù)調(diào)度和容錯(cuò)能力,因?yàn)榫W(wǎng)站程序不太會(huì)處理某個(gè)后臺(tái)實(shí)例掛掉以后要在哪臺(tái)機(jī)器上重新恢復(fù)等這類復(fù)雜問(wèn)題。這類沒(méi)有任務(wù)調(diào)度能力的服務(wù)型分布式應(yīng)用,可以由Marathon來(lái)負(fù)責(zé)調(diào)度。比如,Marathon調(diào)度執(zhí)行了網(wǎng)站服務(wù)的一百個(gè)后臺(tái)實(shí)例,如果某個(gè)實(shí)例掛掉了,Marathon會(huì)在其他服務(wù)器上把這個(gè)實(shí)例恢復(fù)起來(lái)。Chronos是針對(duì)分布式批處理應(yīng)用提供任務(wù)調(diào)度,比如定期處理日志或者定期調(diào)Hadoop等離線任務(wù)。
Mesos最大的好處是能夠?qū)Ψ植际郊鹤黾?xì)粒度資源分配。如圖2所示,左邊是粗粒的資源分配,右邊是細(xì)粒的資源分配。
圖2 Mesos資源調(diào)度的兩種方式
圖2左邊有三個(gè)集群,每個(gè)集群三臺(tái)服務(wù)器,分別裝三種分布式計(jì)算平臺(tái),比如上面裝三臺(tái)Hadoop,中間三臺(tái)是Spark,下面三臺(tái)是Storm,三個(gè)不同的框架分別進(jìn)行管理。右邊是Mesos集群統(tǒng)一管理9臺(tái)服務(wù)器,所有來(lái)自Spark、Hadoop或Storm的任務(wù)都在9臺(tái)服務(wù)器上混合運(yùn)行。Mesos首先提高了資源冗余率。粗粒資源管理肯定帶來(lái)一定的浪費(fèi),細(xì)粒的資源提高資源管理能力。Hadoop機(jī)器很清閑,Spark沒(méi)有安裝,但Mesos可以只要任何一個(gè)調(diào)度馬上響應(yīng)。最后一個(gè)還有數(shù)據(jù)穩(wěn)定性,因?yàn)樗?臺(tái)都被Mesos統(tǒng)一管理,假如說(shuō)裝的Hadoop,Mesos會(huì)集群調(diào)度。這個(gè)計(jì)算資源都不共享,存儲(chǔ)之間也不好共享。如果這上面跑了Spark做網(wǎng)絡(luò)數(shù)據(jù)遷移,顯然很影響速度。然后資源分配的方法就是resource offers,是在窗口的可調(diào)度的資源自己去選,Mesos是Spark或者是Hadoop等等。這種方法,Mesos的分配邏輯就很簡(jiǎn)單,只要不停地報(bào)告哪些是可用資源就可以了。Mesos資源分配方法也有一個(gè)潛在的缺點(diǎn),就是無(wú)中心化的分配方式,所以有可能不會(huì)帶來(lái)全局最優(yōu)的方式。但這個(gè)數(shù)據(jù)資源缺點(diǎn)對(duì)目前來(lái)講并不是很?chē)?yán)重?,F(xiàn)在一個(gè)計(jì)算中心資源貢獻(xiàn)率很難達(dá)到50%,絕大部分計(jì)算中心都是很閑的狀態(tài)。
Mesos資源分配示例
下面具體舉例說(shuō)明怎么用Mesos資源分配。如圖3所示,中間是Mesos Master,下面是Mesos Slave,上面是Spark和Hadoop運(yùn)行在Mesos之上。Mesos Master把可用資源報(bào)告給Spark或Hadoop。假定Hadoop有一個(gè)任務(wù)想運(yùn)行,Hadoop從Mesos Master上報(bào)的可用資源中選擇某個(gè)Mesos Slave節(jié)點(diǎn),然后這個(gè)任務(wù)就會(huì)在這個(gè)Mesos Slave節(jié)點(diǎn)上執(zhí)行,這是任務(wù)完成一次資源分配,接下來(lái)Mesos Master繼續(xù)進(jìn)行資源分配。
圖3 Mesos資源分配示例
任務(wù)調(diào)度
Mesos只做一件事情,就是分布式集群資源分配,不管任務(wù)調(diào)度。Marathon和Chonos是基于Mesos來(lái)做任務(wù)調(diào)度。如圖4所示,Mesos集群混合運(yùn)行來(lái)自Marathon和Chronos的不同類型的任務(wù)。Marathon和Chonos基于Mesos做任務(wù)調(diào)度時(shí),一定是動(dòng)態(tài)調(diào)度,也就是每個(gè)任務(wù)在執(zhí)行之前是不知道它將來(lái)在哪一臺(tái)服務(wù)器上執(zhí)行和綁定哪一個(gè)端口。如圖5所示,9臺(tái)服務(wù)器組成的Mesos集群上混合運(yùn)行各種Marathon調(diào)度的任務(wù),中間一臺(tái)服務(wù)器壞掉以后,這臺(tái)服務(wù)器上的兩個(gè)任務(wù)就受影響,然后Marathon把這兩個(gè)任務(wù)遷移到其他服務(wù)器上,這就是動(dòng)態(tài)任務(wù)調(diào)度帶來(lái)的好處,非常容易實(shí)現(xiàn)容錯(cuò)。
圖4 Mesos集群運(yùn)行不同類型的任務(wù)
圖5 Marathon動(dòng)態(tài)任務(wù)調(diào)度
為了減少硬件故障對(duì)應(yīng)用服務(wù)的影響,應(yīng)用程序要盡量做到無(wú)狀態(tài)。無(wú)狀態(tài)的好處是在程序受到影響時(shí)不需要進(jìn)行任何恢復(fù),這樣這個(gè)程序只要重新調(diào)度起來(lái)就可以。無(wú)狀態(tài)要求把狀態(tài)數(shù)據(jù)放到存儲(chǔ)服務(wù)器或者是消息隊(duì)列里面,這樣的好處是容錯(cuò)時(shí)恢復(fù)起來(lái)會(huì)變得很方便。
服務(wù)類的高可靠性
對(duì)于服務(wù)類型的任務(wù),分布式環(huán)境保證服務(wù)的高可靠性,這需要負(fù)載均衡和服務(wù)發(fā)現(xiàn)。在分布式環(huán)境下做負(fù)載均衡有一個(gè)難點(diǎn)就是后臺(tái)這些實(shí)例有可能發(fā)生動(dòng)態(tài)變化,比如說(shuō)某一個(gè)節(jié)點(diǎn)壞掉了,這個(gè)節(jié)點(diǎn)上的實(shí)例會(huì)受到影響,然后遷移到其他節(jié)點(diǎn)上。然而傳統(tǒng)負(fù)載均衡器的后臺(tái)實(shí)例地址端口都是靜態(tài)的。所以在分布式環(huán)境下,為了做負(fù)載均衡一定要做服務(wù)發(fā)現(xiàn)。比如,某個(gè)服務(wù)之前有四個(gè)事例,現(xiàn)在新添加了兩個(gè)實(shí)例,需要告訴負(fù)載均衡器新增加的實(shí)例的地址和端口。服務(wù)發(fā)現(xiàn)的過(guò)程是由幾個(gè)模塊配合完成,比如說(shuō)Marathon給某個(gè)服務(wù)增加了新的實(shí)例,把新調(diào)度的實(shí)例地址端口寫(xiě)到Zookeeper,然后Bamboo把Zookeeper里存放的該服務(wù)新的實(shí)例的地址端口信息告訴負(fù)載均衡器,這樣負(fù)載均衡器就知道新的實(shí)例地址端口,完成了服務(wù)發(fā)現(xiàn)。
數(shù)據(jù)類的高可靠性
對(duì)于服務(wù)類型的應(yīng)用,分布式系統(tǒng)用負(fù)載均衡器和服務(wù)發(fā)現(xiàn)來(lái)保證高可靠性的服務(wù)。對(duì)于數(shù)據(jù)類型的應(yīng)用,分布式系統(tǒng)同樣要保證高可靠的數(shù)據(jù)服務(wù)。首先要做數(shù)據(jù)分片,一臺(tái)服務(wù)器存不下所有數(shù)據(jù)就分成多份來(lái)存,但對(duì)數(shù)據(jù)進(jìn)行分片必須按照某個(gè)規(guī)則來(lái)進(jìn)行分片,后面查找時(shí)要按照同樣的規(guī)則來(lái)進(jìn)行分片查找,就是一致性。假定最原始的方案我們用Hash計(jì)算做成方法,在線性空間上分了三份以后,我要在數(shù)據(jù)分成三塊機(jī)器來(lái)存,三臺(tái)機(jī)器都存滿了時(shí),再把數(shù)據(jù)進(jìn)行分配的時(shí)候不再把它分配到直線線性空間上,而是把它分配到環(huán)狀空間上,把起點(diǎn)和終點(diǎn)連接起來(lái),連成一個(gè)數(shù)據(jù)環(huán),如圖6所示,這樣相應(yīng)的數(shù)據(jù)點(diǎn)就放在這一塊。如果要添加一個(gè)新的數(shù)據(jù)中心就在環(huán)上新切出來(lái)這塊,這樣很方便,切出來(lái)這一部分代表這一部分?jǐn)?shù)據(jù)都應(yīng)該放到新的芯片上,所以把原來(lái)子數(shù)據(jù)分片挪到嵌入式的分片上。
圖6 數(shù)據(jù)分片
還有可能刪除數(shù)據(jù),我們把黃色的數(shù)據(jù)放到紅色的數(shù)據(jù)上,這是環(huán)的好處。實(shí)際為了做到高可靠性,任何一個(gè)數(shù)據(jù)可能假定映射到黃色部分以后,這些黃色的部分只要映射到任何一個(gè)黃色的區(qū)域都會(huì)存在同一片機(jī)器上,同一片機(jī)器底層會(huì)有多個(gè)副本和做數(shù)據(jù)的備份,這是實(shí)際數(shù)據(jù)分片的一個(gè)實(shí)例。這是怎么做數(shù)據(jù)的高可靠性。這些數(shù)據(jù)分片,還有負(fù)載均衡,都是為了對(duì)應(yīng)分布式分片硬件帶來(lái)的不可靠和失效,這是我們用分布式系統(tǒng)最大的特點(diǎn)。
基于Docker的分布式計(jì)算平臺(tái)
Docker工作流
我們主要用Docker來(lái)做分布式環(huán)境下的進(jìn)程管理。Docker工作流如圖7所示,我們不僅把Docker應(yīng)用到生產(chǎn)階段,也應(yīng)用到開(kāi)發(fā)階段,所以我們每天編輯Dockerfile,提升Docker Images,測(cè)試上線,發(fā)Docker鏡像,在我們內(nèi)部私有Docker regis里面,再調(diào)到我們Docker集群生產(chǎn)環(huán)境里面,這和其他的Docker工作流沒(méi)有什么區(qū)別。
圖7 Docker工作流
在Mesos提交Docker任務(wù)
因?yàn)镸esos和Docker已經(jīng)是無(wú)縫結(jié)合起來(lái)。通過(guò)Marathon和Chronos提交服務(wù)型應(yīng)用和批處理型應(yīng)用。Marathon和Chronos通過(guò)RESTful的方式提交任務(wù),用JSON腳本設(shè)定應(yīng)用的后臺(tái)實(shí)例個(gè)數(shù)、應(yīng)用的參數(shù)、以及Docker Images的路徑等等。
分布式環(huán)境下的進(jìn)程通信
在分布式環(huán)境下應(yīng)用服務(wù)之間通信,是用分布式消息隊(duì)列來(lái)做,我們用的是RabbitMQ。RabbitMQ也是一個(gè)分布式系統(tǒng),它也要保證高可靠性、解決容錯(cuò)的問(wèn)題。首先RabbitMQ也有集群,如圖8所示,六個(gè)節(jié)點(diǎn)組成了一個(gè)RabbitMQ的集群,每個(gè)節(jié)點(diǎn)之間是互為備份的關(guān)系,任何一個(gè)壞掉,其他五個(gè)還可以提供服務(wù),通過(guò)冗余來(lái)保證RabbitMQ的高可靠性。
圖8 RabbitMQ集群
其次,RabbitMQ也有數(shù)據(jù)分片機(jī)制。因?yàn)橄㈥?duì)列有可能很長(zhǎng),長(zhǎng)到所有的消息不可能都放到一個(gè)節(jié)點(diǎn)上,這時(shí)就要用分片,把很長(zhǎng)的消息隊(duì)列分為幾段,分別放到不同的節(jié)點(diǎn)上。如圖9所示是RabbitMQ的聯(lián)盟機(jī)制,把一個(gè)消息隊(duì)列打成兩段,一段放在上游一段放在下游,假定下游消息隊(duì)列的消息被消費(fèi)完了就自動(dòng)把上游消息隊(duì)列里的消息移到下游,這樣一個(gè)消息隊(duì)列變成非常長(zhǎng)的時(shí)候也不怕,分片到多個(gè)節(jié)點(diǎn)上即可。
圖9 消息隊(duì)列分片
分布式文件系統(tǒng)
最后講一下分布式文件系統(tǒng)HDFS和Ceph。Hadoop文件系統(tǒng)HDFS,如圖10所示,每個(gè)數(shù)據(jù)塊有三個(gè)備份,必須放在不同的服務(wù)器上,而且三個(gè)備份里面每個(gè)機(jī)架最多放兩份,這么做也是為了容錯(cuò)。Ceph是另一種流行的開(kāi)源分布式文件系統(tǒng)。Ceph把網(wǎng)絡(luò)存儲(chǔ)設(shè)備抽象成一張邏輯硬盤(pán),然后“掛載”到分布式集群的每臺(tái)服務(wù)器上,原理上非常像是Linux操作系統(tǒng)Mount一塊物理硬盤(pán)。這樣一來(lái),用戶程序訪問(wèn)Ceph的文件系統(tǒng)就跟訪問(wèn)Linux本地路徑一樣,非常方便。
圖10 分布式文件系統(tǒng)
分布式環(huán)境下的監(jiān)控
分布式環(huán)境下,程序不是運(yùn)行在本地,而是在集群上面,沒(méi)有監(jiān)控就等于程序運(yùn)行在黑盒子下,無(wú)法調(diào)優(yōu),必須要有監(jiān)控。分布式環(huán)境下的監(jiān)控分為兩個(gè)部分,一是性能監(jiān)控,另一個(gè)是報(bào)警。性能監(jiān)控要知道每個(gè)應(yīng)用程序運(yùn)行狀態(tài)是什么樣,即每一個(gè)應(yīng)用程序占了多少CPU內(nèi)存、服務(wù)的請(qǐng)求處理延遲等。我們是用Graphite來(lái)做應(yīng)用程序性能監(jiān)控;還有其他系統(tǒng),比如MongoDB、Hadoop等開(kāi)源系統(tǒng),我們用Ganglia來(lái)做性能監(jiān)控,比如CPU內(nèi)存硬盤(pán)的使用情況等。報(bào)警是要在關(guān)鍵服務(wù)出現(xiàn)故障時(shí),通知開(kāi)發(fā)運(yùn)維人員及時(shí)排解故障,我們用Zabbix來(lái)做報(bào)警。(責(zé)編/周建丁)
作者簡(jiǎn)介:王璞,先后在硅谷供職于StumbleUpon、Groupon和Google,擅長(zhǎng)海量數(shù)據(jù)處理、分布式計(jì)算以及大規(guī)模機(jī)器學(xué)習(xí)。2014年回國(guó)創(chuàng)辦數(shù)人科技,基于Mesos和Docker構(gòu)建分布式計(jì)算平臺(tái),為企業(yè)客戶提供大數(shù)據(jù)分析處理一站式解決方案。