每個(gè)ZStack服務(wù)都是無狀態(tài)的,讓服務(wù)高可用以及橫向拓展(scale out)可以很簡單,只需要啟動(dòng)剩余的服務(wù)實(shí)例,然后進(jìn)行負(fù)載均衡即可。此外,ZStack將所有的服務(wù)打包到名為管理節(jié)點(diǎn)(management node)的單個(gè)進(jìn)程,它讓部署和管理變得超級(jí)簡單。
動(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)來拓展整個(gè)系統(tǒng)的容量的方式稱為 橫向拓展(scale out).
問題
設(shè)計(jì)一個(gè)分布式的系統(tǒng)并不容易。一個(gè)分布式的系統(tǒng),特別是一個(gè)有狀態(tài)的系統(tǒng),必須處理一致性,可用性,以及分區(qū)容忍性(請(qǐng)查看 CAP理論(CAP theorem)),所有這些都很復(fù)雜。相反,一個(gè)無狀態(tài)的分布式系統(tǒng),在某種程度上擺脫了這種復(fù)雜性。首先,因?yàn)樵诠?jié)點(diǎn)之間無需狀態(tài)共享,系統(tǒng)自然保持了一致性;其次,由于節(jié)點(diǎn)之間是類似的,當(dāng)系統(tǒng)遇到一個(gè)分區(qū)問題通常也是OK的。鑒于此,一個(gè)分布式的系統(tǒng),通常更傾向于保持無狀態(tài)而不是有狀態(tài)。但是,設(shè)計(jì)一個(gè)無狀態(tài)的分布式系統(tǒng)也是很困難的,同時(shí),常常比設(shè)計(jì)有狀態(tài)的分布式系統(tǒng)更加困難。提升了消息總線(message bus)和數(shù)據(jù)庫優(yōu)勢的ZStack,構(gòu)建了一個(gè)包含了無狀態(tài)服務(wù)的無狀態(tài)分布式系統(tǒng)。
由于無狀態(tài)服務(wù)是保證整個(gè)系統(tǒng)無狀態(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ī);否則,它將無法知道將請(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í)更易于橫向拓展,理想的方式是,通過彼此分隔狀態(tài)來讓服務(wù)保持無狀態(tài)(查看 服務(wù)無狀態(tài)原則(Service Statelessness Principle)。 有了無狀態(tài)的服務(wù),請(qǐng)求者不再需要詢問何處發(fā)送請(qǐng)求;當(dāng)新的服務(wù)實(shí)例加入,或者舊的服務(wù)實(shí)例脫離的時(shí)候,服務(wù)也不再需要交換狀態(tài)。
注意:在接下來的內(nèi)容中,為了簡單起見,術(shù)語“服務(wù)”和“服務(wù)實(shí)例”交換著使用。
服務(wù)和管理節(jié)點(diǎn)
服務(wù),通過中央消息總線(central message bus)--RabbitMQ,來彼此通訊,它們是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軟件。由于包含了無狀態(tài)服務(wù),管理節(jié)點(diǎn)沒有共享狀態(tài),但是有心跳記錄,以及一致性哈希算法環(huán)(consistent hashing ring)--接下來我們將詳細(xì)介紹。 心跳用來監(jiān)控管理節(jié)點(diǎn)的“健康”(譯者注:即此管理節(jié)點(diǎn)是否存活,是否正常運(yùn)轉(zhuǎn)),只要一個(gè)管理節(jié)點(diǎn)在給定的間隔內(nèi)停止更新心跳,其它的管理節(jié)點(diǎn)將會(huì)驅(qū)除它,同時(shí)開始接管它所管理的資源。

無狀態(tài)服務(wù)
實(shí)現(xiàn)無狀態(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ù)名一起,在消息總線上注冊一個(gè)服務(wù)隊(duì)列。例如,管理節(jié)點(diǎn)可能注冊如下所示的服務(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
說明:你應(yīng)該注意到,所有隊(duì)列都以同樣的UUID結(jié)尾,那是管理節(jié)點(diǎn)的UUID。
資源,如主機(jī),容量,虛擬機(jī),也是通過UUID來標(biāo)識(shí)的。消息,常常和資源相關(guān)聯(lián),是在服務(wù)間傳遞的。在發(fā)送消息之前,發(fā)送者必須選擇基于資源的UUID的接收者服務(wù),這時(shí),一致性哈希算法就開始登場了。

一致性哈希(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ì)通過消息總線廣播到其它節(jié)點(diǎn),這樣使得這些節(jié)點(diǎn)擴(kuò)展或者收縮環(huán),以呈現(xiàn)當(dāng)前系統(tǒng)的狀態(tài)。當(dāng)發(fā)送消息的時(shí)候,發(fā)送者服務(wù)將使用資源的UUID,通過哈希的方式得出目標(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無鎖架構(gòu)的基礎(chǔ)(參閱 ZStack的伸縮性秘密(第三部分):無鎖架構(gòu)(Stack's Scalability Secrets Part 3: Lock-free Architecture)。

當(dāng)一致性哈希環(huán)收縮或釋放的時(shí)候,由于一致性哈希的特性,只有少數(shù)節(jié)點(diǎn)受到輕微影響。
由于一致性哈希環(huán),發(fā)送者無需知道哪一個(gè)服務(wù)實(shí)例即將處理消息;取而代之的是,這將會(huì)被處理掉。服務(wù)無需維護(hù)和交換,關(guān)于它們正在管理什么資源的信息;它們所需要做的就是,處理即將到來的消息,因?yàn)榄h(huán)能夠保證消息找到正確的服務(wù)實(shí)例。這就是服務(wù)如何變得超級(jí)簡單和保持無狀態(tài)的。
除包含資源UUID的消息之外(如 StartVmInstanceMsg, DownloadImageMsg),也有一類無資源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)來說,有一個(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)。通過這種做法,ZStack 隱藏了來自 API 客戶端消息路由轉(zhuǎn)發(fā)的細(xì)節(jié),并簡化了寫一個(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)和無狀態(tài)的服務(wù),Zstack能夠處理大量的并發(fā)任務(wù),現(xiàn)有的IaaS軟件則不能處理。