每個(gè)ZStack服務(wù)都是無(wú)狀態(tài)的,讓服務(wù)高可用以及橫向拓展(scale out)可以很簡(jiǎn)單,只需要啟動(dòng)剩余的服務(wù)實(shí)例,然后進(jìn)行負(fù)載均衡即可塔粒。此外,ZStack將所有的服務(wù)打包到名為管理節(jié)點(diǎn)(management node)的單個(gè)進(jìn)程筐摘,它讓部署和管理變得超級(jí)簡(jiǎn)單。
動(dòng)機(jī)
在ZStack的伸縮性秘密武器——異步架構(gòu)(ZStack's Scalability Secrets Part 1: Asynchronous Architecture) 一文中, 我們已經(jīng)詳細(xì)解釋了異步架構(gòu)咖熟,它讓單個(gè)ZStack管理節(jié)點(diǎn)能勝任大多數(shù)的云端工作負(fù)載圃酵。然而,當(dāng)用戶希望建立高可用的生產(chǎn)環(huán)境馍管,或者處理超級(jí)大的并發(fā)工作負(fù)載的時(shí)候郭赐,一個(gè)管理節(jié)點(diǎn)是不夠的。解決方案是确沸,構(gòu)建一個(gè)分布式的系統(tǒng)捌锭,這樣工作負(fù)載可以延展到每一個(gè)單一管理節(jié)點(diǎn)。這種增加新節(jié)點(diǎn)來(lái)拓展整個(gè)系統(tǒng)的容量的方式稱為 橫向拓展(scale out).
問(wèn)題
設(shè)計(jì)一個(gè)分布式的系統(tǒng)并不容易罗捎。一個(gè)分布式的系統(tǒng)观谦,特別是一個(gè)有狀態(tài)的系統(tǒng),必須處理一致性桨菜,可用性豁状,以及分區(qū)容忍性(請(qǐng)查看 CAP理論(CAP theorem)),所有這些都很復(fù)雜倒得。相反泻红,一個(gè)無(wú)狀態(tài)的分布式系統(tǒng),在某種程度上擺脫了這種復(fù)雜性霞掺。首先谊路,因?yàn)樵诠?jié)點(diǎn)之間無(wú)需狀態(tài)共享,系統(tǒng)自然保持了一致性菩彬;其次缠劝,由于節(jié)點(diǎn)之間是類似的,當(dāng)系統(tǒng)遇到一個(gè)分區(qū)問(wèn)題通常也是OK的挤巡。鑒于此剩彬,一個(gè)分布式的系統(tǒng),通常更傾向于保持無(wú)狀態(tài)而不是有狀態(tài)矿卑。但是喉恋,設(shè)計(jì)一個(gè)無(wú)狀態(tài)的分布式系統(tǒng)也是很困難的,同時(shí)母廷,常常比設(shè)計(jì)有狀態(tài)的分布式系統(tǒng)更加困難轻黑。提升了消息總線(message bus)和數(shù)據(jù)庫(kù)優(yōu)勢(shì)的ZStack,構(gòu)建了一個(gè)包含了無(wú)狀態(tài)服務(wù)的無(wú)狀態(tài)分布式系統(tǒng)琴昆。
由于無(wú)狀態(tài)服務(wù)是保證整個(gè)系統(tǒng)無(wú)狀態(tài)的根基氓鄙,在討論它是什么之前,讓我們先了解下什么是“狀態(tài)”业舍。在ZStack里面抖拦,資源升酣,如主機(jī),虛擬機(jī)态罪,鏡像噩茄,以及用戶,都是由單個(gè)服務(wù)管理的复颈;當(dāng)系統(tǒng)中存在多余一個(gè)服務(wù)實(shí)例的時(shí)候绩聘,資源會(huì)被劃分為不同的實(shí)例。例如耗啦,假如有10,000個(gè)虛擬機(jī)和兩個(gè)虛擬機(jī)服務(wù)實(shí)例凿菩,理想的情況下,每個(gè)實(shí)例將會(huì)管理5000個(gè)虛擬機(jī):
由于存在兩個(gè)服務(wù)實(shí)例帜讲,在向虛擬機(jī)發(fā)送請(qǐng)求之前衅谷,請(qǐng)求者必須知道哪一個(gè)實(shí)例正在管理虛擬機(jī);否則舒帮,它將無(wú)法知道將請(qǐng)求發(fā)往何處会喝。像 ”哪個(gè)服務(wù)實(shí)例正在管理什么資源“ 的認(rèn)知,正是我們正在談?wù)摰臓顟B(tài)玩郊。如果服務(wù)是有狀態(tài)的肢执,狀態(tài)也就顯現(xiàn)在服務(wù)之中。請(qǐng)求者需要在某個(gè)地方咨詢這些狀態(tài)译红。當(dāng)服務(wù)實(shí)例的數(shù)目發(fā)生變化的時(shí)候预茄,服務(wù)需要交換狀態(tài),例如侦厚,當(dāng)一個(gè)新的服務(wù)實(shí)例加入耻陕,或者當(dāng)前的服務(wù)實(shí)例脫離的時(shí)候。
狀態(tài)交換是讓人擔(dān)憂的刨沦,它很容易導(dǎo)致錯(cuò)誤诗宣,常常會(huì)限制系統(tǒng)的可拓展性。為了讓系統(tǒng)更可靠想诅,同時(shí)更易于橫向拓展召庞,理想的方式是,通過(guò)彼此分隔狀態(tài)來(lái)讓服務(wù)保持無(wú)狀態(tài)(查看 服務(wù)無(wú)狀態(tài)原則(Service Statelessness Principle)来破。 有了無(wú)狀態(tài)的服務(wù)篮灼,請(qǐng)求者不再需要詢問(wèn)何處發(fā)送請(qǐng)求;當(dāng)新的服務(wù)實(shí)例加入徘禁,或者舊的服務(wù)實(shí)例脫離的時(shí)候诅诱,服務(wù)也不再需要交換狀態(tài)。
注意:在接下來(lái)的內(nèi)容中送朱,為了簡(jiǎn)單起見娘荡,術(shù)語(yǔ)“服務(wù)”和“服務(wù)實(shí)例”交換著使用干旁。
服務(wù)和管理節(jié)點(diǎn)
服務(wù),通過(guò)中央消息總線(central message bus)--RabbitMQ它改,來(lái)彼此通訊疤孕,它們是ZStack中的“第一等公民”。
不像通常的微服務(wù)架構(gòu)央拖,其每個(gè)服務(wù)都在單獨(dú)的進(jìn)程或單獨(dú)的機(jī)器上運(yùn)行,ZStack將所有的服務(wù)打包到一個(gè)名為管理節(jié)點(diǎn)的單一進(jìn)程鹉戚。對(duì)于這個(gè)號(hào)稱 進(jìn)程中的微服務(wù)(in-process microservices)架構(gòu)鲜戒,我們有充分的理由,你可以參看進(jìn)程中的微服務(wù)架構(gòu)(The In-Process Microservices Architecture)抹凳。
一個(gè)管理節(jié)點(diǎn)是一個(gè)完整功能的ZStack軟件遏餐。由于包含了無(wú)狀態(tài)服務(wù),管理節(jié)點(diǎn)沒有共享狀態(tài)赢底,但是有心跳記錄失都,以及一致性哈希算法環(huán)(consistent hashing ring)--接下來(lái)我們將詳細(xì)介紹。 心跳用來(lái)監(jiān)控管理節(jié)點(diǎn)的“健康”(譯者注:即此管理節(jié)點(diǎn)是否存活幸冻,是否正常運(yùn)轉(zhuǎn))粹庞,只要一個(gè)管理節(jié)點(diǎn)在給定的間隔內(nèi)停止更新心跳,其它的管理節(jié)點(diǎn)將會(huì)驅(qū)除它洽损,同時(shí)開始接管它所管理的資源庞溜。
無(wú)狀態(tài)服務(wù)
實(shí)現(xiàn)無(wú)狀態(tài)服務(wù)的核心技術(shù),特別是對(duì)于ZStack的業(yè)務(wù)邏輯碑定,就是一致性哈希算法(consistent hashing algorithm)流码。在啟動(dòng)的時(shí)候,每個(gè)管理節(jié)點(diǎn)都會(huì)被分配一個(gè) 版本4UUID(version 4 UUID)(管理節(jié)點(diǎn)UUID)延刘,它會(huì)和服務(wù)名一起漫试,在消息總線上注冊(cè)一個(gè)服務(wù)隊(duì)列。例如碘赖,管理節(jié)點(diǎn)可能注冊(cè)如下所示的服務(wù)隊(duì)列:
zstack.message.ansible.3694776ab31a45709259254a018913ca
zstack.message.api.portal
zstack.message.applianceVm.3694776ab31a45709259254a018913ca
zstack.message.cloudbus.3694776ab31a45709259254a018913ca
zstack.message.cluster.3694776ab31a45709259254a018913ca
zstack.message.configuration.3694776ab31a45709259254a018913ca
zstack.message.console.3694776ab31a45709259254a018913ca
zstack.message.eip.3694776ab31a45709259254a018913ca
zstack.message.globalConfig.3694776ab31a45709259254a018913ca
zstack.message.host.3694776ab31a45709259254a018913ca
zstack.message.host.allocator.3694776ab31a45709259254a018913ca
zstack.message.identity.3694776ab31a45709259254a018913ca
zstack.message.image.3694776ab31a45709259254a018913ca
zstack.message.managementNode.3694776ab31a45709259254a018913ca
zstack.message.network.l2.3694776ab31a45709259254a018913ca
zstack.message.network.l2.vlan.3694776ab31a45709259254a018913ca
zstack.message.network.l3.3694776ab31a45709259254a018913ca
zstack.message.network.service.3694776ab31a45709259254a018913ca
zstack.message.portForwarding.3694776ab31a45709259254a018913ca
zstack.message.query.3694776ab31a45709259254a018913ca
zstack.message.securityGroup.3694776ab31a45709259254a018913ca
zstack.message.snapshot.volume.3694776ab31a45709259254a018913ca
zstack.message.storage.backup.3694776ab31a45709259254a018913ca
說(shuō)明:你應(yīng)該注意到驾荣,所有隊(duì)列都以同樣的UUID結(jié)尾,那是管理節(jié)點(diǎn)的UUID崖疤。
資源秘车,如主機(jī),容量劫哼,虛擬機(jī)叮趴,也是通過(guò)UUID來(lái)標(biāo)識(shí)的。消息权烧,常常和資源相關(guān)聯(lián)眯亦,是在服務(wù)間傳遞的伤溉。在發(fā)送消息之前,發(fā)送者必須選擇基于資源的UUID的接收者服務(wù)妻率,這時(shí)乱顾,一致性哈希算法就開始登場(chǎng)了。
一致性哈希(Consistent hashing)是一種特別的哈希宫静,當(dāng)哈希表調(diào)整大小的時(shí)候走净,就會(huì)用到一致性哈希,其中只有一部分鍵(key)需要重新映射孤里。關(guān)于一致性哈希的更多內(nèi)容伏伯,更詳細(xì)的請(qǐng)參閱 這里。在ZStack之中捌袜,管理節(jié)點(diǎn)由一致性哈希環(huán)組成说搅,如下所示:
每個(gè)管理節(jié)點(diǎn)都維護(hù)一份一致性哈希環(huán)的拷貝,這個(gè)環(huán)包含了系統(tǒng)中所有管理節(jié)點(diǎn)的UUID虏等。當(dāng)管理節(jié)點(diǎn)加入或者脫離的時(shí)候弄唧,生命周期事件(lifecycle event)就會(huì)通過(guò)消息總線廣播到其它節(jié)點(diǎn),這樣使得這些節(jié)點(diǎn)擴(kuò)展或者收縮環(huán)霍衫,以呈現(xiàn)當(dāng)前系統(tǒng)的狀態(tài)候引。當(dāng)發(fā)送消息的時(shí)候,發(fā)送者服務(wù)將使用資源的UUID慕淡,通過(guò)哈希的方式得出目標(biāo)管理節(jié)點(diǎn)的UUID背伴。例如,發(fā)送VM的UUID為932763162d054c04adaab6ab498c9139的StartVmInstanceMsg峰髓,偽代碼如下:
msg = new StartVmInstanceMsg(); destinationManagementNodeUUID = consistent_hashing_algorithm("932763162d054c04adaab6ab498c9139"); msg.setServiceId("vmInstance." + destinationManagementNodeUUID); cloudBus.send(msg)
如果有一個(gè)穩(wěn)定的環(huán)傻寂,那么包含同樣資源UUID的消息就總是會(huì)路由到某個(gè)管理節(jié)點(diǎn)上同樣的服務(wù),這就是ZStack無(wú)鎖架構(gòu)的基礎(chǔ)(參閱 ZStack的伸縮性秘密(第三部分):無(wú)鎖架構(gòu)(Stack's Scalability Secrets Part 3: Lock-free Architecture)携兵。
當(dāng)一致性哈希環(huán)收縮或釋放的時(shí)候疾掰,由于一致性哈希的特性,只有少數(shù)節(jié)點(diǎn)受到輕微影響徐紧。
由于一致性哈希環(huán)静檬,發(fā)送者無(wú)需知道哪一個(gè)服務(wù)實(shí)例即將處理消息;取而代之的是并级,這將會(huì)被處理掉拂檩。服務(wù)無(wú)需維護(hù)和交換,關(guān)于它們正在管理什么資源的信息嘲碧;它們所需要做的就是稻励,處理即將到來(lái)的消息,因?yàn)榄h(huán)能夠保證消息找到正確的服務(wù)實(shí)例。這就是服務(wù)如何變得超級(jí)簡(jiǎn)單和保持無(wú)狀態(tài)的望抽。
除包含資源UUID的消息之外(如 StartVmInstanceMsg加矛, DownloadImageMsg),也有一類無(wú)資源UUID的消息煤篙,通常是創(chuàng)建型的消息(如 CreateVolumeMsg)和非資源消息(如 AllocateHostMsg)--它們不會(huì)操控單獨(dú)的資源斟览。考慮到這些消息可以發(fā)送到任意管理節(jié)點(diǎn)的服務(wù)辑奈,它們可能被故意發(fā)送到本地的管理節(jié)點(diǎn)苛茂,由于發(fā)送者和接收者在同樣的節(jié)點(diǎn),當(dāng)發(fā)送者發(fā)送消息的時(shí)候鸠窗,接收者當(dāng)然也是可達(dá)的味悄。
對(duì) API 消息(例如:APIStartVmInstanceMsg)來(lái)說(shuō),有一個(gè)特殊的處理塌鸯,它們總是發(fā)送一個(gè)眾所周知的服務(wù) ID api.portal 。在消息總線上唐片,一個(gè)全局的隊(duì)列被叫做 zstack.message.api.portal 丙猬,它被所有的管理節(jié)點(diǎn) API 服務(wù)所共享,消息服務(wù) ID api.portal 將會(huì)自動(dòng)對(duì)其中的一個(gè)API服務(wù)做負(fù)載均衡费韭,這個(gè)服務(wù)還會(huì)路由轉(zhuǎn)發(fā)消息到正確的目的地茧球,并使用了一致性哈希環(huán)(consistent hashing ring)。通過(guò)這種做法星持,ZStack 隱藏了來(lái)自 API 客戶端消息路由轉(zhuǎn)發(fā)的細(xì)節(jié)抢埋,并簡(jiǎn)化了寫一個(gè)ZStack API 客戶端的工作。
msg = new APICreateVmInstanceMsg()
msg.setServiceId("api.portal")
cloudBus.send(msg)
摘要
在這篇文章中督暂,我們證明了Zstack 構(gòu)建伸縮性的分布式系統(tǒng)揪垄。因?yàn)楣芾砉?jié)點(diǎn)共享的信息比較少,很容易建立一個(gè)大的集群逻翁,可能有幾十個(gè)甚至幾百個(gè)管理節(jié)點(diǎn)饥努。然而實(shí)際上,在私有云方面八回,兩個(gè)管理節(jié)點(diǎn)可以有很好的擴(kuò)展性酷愧;在公共云方面,管理員能根據(jù)工作量創(chuàng)建一個(gè)管理節(jié)點(diǎn)缠诅。依靠異步架構(gòu)和無(wú)狀態(tài)的服務(wù)溶浴,Zstack能夠處理大量的并發(fā)任務(wù),現(xiàn)有的IaaS軟件則不能處理管引。