云中的資源相互都有關(guān)系。操作一個資源通常會引發(fā)連鎖反應(yīng);例如,當(dāng)刪除一個集群的時候,是非常合理地去刪除屬于該集群的所有主機(jī)并停止所有在這些主機(jī)上運(yùn)行的虛擬機(jī)。傳統(tǒng)的IaaS軟件要么硬編碼連鎖反應(yīng),要么簡單地禁止這些操作,例如,禁止用戶刪除有虛擬機(jī)運(yùn)行的集群。ZStack提供一個級聯(lián)框架,用以散布本來只對一個資源的操作到所有相關(guān)的資源。資源可以通過實現(xiàn)一個簡單的擴(kuò)展點以加入級聯(lián)框架,使得資源的業(yè)務(wù)邏輯與框架解耦。
動機(jī)
云中的資源多多少少都彼此依賴;例如,一個主機(jī)是一個集群的子資源,一個主存儲是一個集群的兄弟資源,L3網(wǎng)絡(luò)是一個區(qū)域的后裔資源。資源之間的關(guān)系可以被描述為一個有向圖:

上圖,我們展示了ZStack的主要資源;不同的IaaS軟件可能使用不同的術(shù)語,上圖主要是想讓你有一個粗略的概念。由上圖所暗示的,當(dāng)對資源進(jìn)行操作時,不僅僅是目標(biāo)資源,相關(guān)資源也將受到影響;例如,當(dāng)刪除一個區(qū)域時,比較理想的是屬于區(qū)域的集群、主機(jī)、主存儲、L2網(wǎng)絡(luò)等資源也同時被刪除。為了處理這個問題,IaaS軟件必須滿足級聯(lián)(cascading)操作的需求。
問題
大多數(shù)IaaS軟件很少考慮級聯(lián)操作。它們要么硬編碼業(yè)務(wù)邏輯,例如,你需要顯式刪除一個將要被刪除帳戶的所有資源;要么直接不允許這種操作,例如,當(dāng)你試圖刪除一個IP地址范圍時,拋出一個錯誤信息“仍有VM使用在這個IP范圍中的IP”。這兩種方法都會帶來很多麻煩。對硬編碼而言,它使軟件不能靈活的添加新的資源,因為你必須修改現(xiàn)有的代碼來添加級聯(lián)操作,例如,修改刪除帳戶的代碼使得賬戶刪除時,新資源也被刪除。對于完全沒有責(zé)任感的錯誤信息,用戶要么去做無聊的工作,例如,在刪除一個IP范圍之前,手動刪除100個虛擬機(jī);要么摧毀現(xiàn)有的一切,然后從零開始,例如,重新部署整個云。
避免誤操作不是借口: 有些人可能會聲稱不允許級聯(lián)刪除是慎重考慮的結(jié)果,因為用戶可能會誤操作,誤操作可能帶來災(zāi)難性的后果;例如,錯誤地刪除區(qū)域會導(dǎo)致?lián)p失掉所有虛擬機(jī)。然而,這種說法只是一個錯誤的借口,并且是一種為用戶做決定的自作聰明。你能想象嗎,當(dāng)你為了刪除一個區(qū)域必須手動刪除10,000個虛擬機(jī),因為軟件認(rèn)為你可能會做錯事,所以迫使你枯燥的重復(fù)10,000次任務(wù)確認(rèn)?一個好的軟件應(yīng)該為用戶提供選擇,并讓他們做出決定。在我們的例子中,IaaS軟件應(yīng)該在進(jìn)行到最后刪除之前警告用戶,還有10,000臺虛擬機(jī)在運(yùn)行;但一旦用戶承認(rèn)他們需要這么做,軟件就應(yīng)該這么做。
級聯(lián)框架
ZStack通過一個級聯(lián)框架解決這一問題;顧名思義,級聯(lián)框架允許一個操作能從一個資源級聯(lián)到其他資源。為了解耦整個架構(gòu),這個級聯(lián)框架被作為一個單獨(dú)的組件創(chuàng)造出來,資源可以按意愿加入框架。要加入框架,資源所需要做的全部事情就是實現(xiàn)一個擴(kuò)展點CascadeExtensionPoint(在我們的例子中AbstractAsyncCascadeExtension是一個實現(xiàn)CascadeExtensionPoint的類):
class VmCascadeExtension extends AbstractAsyncCascadeExtension {
@Override
public void asyncCascade(CascadeAction action, Completion completion) {
if (/* this is from deleting Primary Storage*/) {
/* delete VMs that have root volumes on the primary storage*/
} else if (/*this is from deleting L3 Network*/) {
/* stop VMs that have nics on the L3 network, and remove those nics */
} else if (/* this is from deleting IP range*/) {
/* stop VMs that have nics whose IP is in the IP range */
} else if (/* this is from deleting host*/) {
/* stop VMs that run on the host */
}
completion.success();
}
@Override
public List<String> getEdgeNames() {
return Arrays.asList(
PrimaryStorageVO.class.getSimpleName(),
L3NetworkVO.class.getSimpleName(),
IpRangeVO.class.getSimpleName(),
HostVO.class.getSimpleName()
);
}
@Override
public String getCascadeResourceName() {
return VmInstanceVO.class.getSimpleName();
}
@Override
public CascadeAction createActionForChildResource(CascadeAction action) {
return convertContextToVmRelatedContext(action);
}
}
getCascadeResourceName()方法返回該資源的名稱(VmInstance);getEdgeNames()方法返回一個和資源直接關(guān)聯(lián)的資源名列表,在我們的例子中返回主存儲、L3網(wǎng)絡(luò)、IpRange和主機(jī);所以如果刪除操作在這些edge resources或其上游資源(如區(qū)域)上發(fā)生時,該操作將被級聯(lián)至在getEdgeNames()方法中聲明了這些資源的擴(kuò)展。級聯(lián)擴(kuò)展可以在asyncCascade() 中采取行動,并獲取必須的信息比如操作碼(如刪除),根發(fā)起者(如區(qū)域,下文將很快給出解釋),作為操作來源的父發(fā)起者(如主機(jī),將很快給出解釋)和操作上下文(例如,哪臺主機(jī)正在被刪除)。由于資源的關(guān)系是一個可能有環(huán)路的有向圖,級聯(lián)框架將把圖壓扁成一棵樹,并把環(huán)路變?yōu)榉种?。例如,刪除區(qū)域的操作將最終創(chuàng)建以下樹(一部分):

注:如你所見,刪除區(qū)域操作將多次級聯(lián)到虛擬機(jī)的級聯(lián)擴(kuò)展;這是刻意的,因為級聯(lián)擴(kuò)展通常依賴于父發(fā)起者去決定該采取什么行動;在這個例子中,虛擬機(jī)的父發(fā)起者為主存儲、主機(jī)、L3網(wǎng)絡(luò)和IP范圍;然而,對于不同的父發(fā)起者,擴(kuò)展可能會采取不同的行動;例如,如果父發(fā)起者為主存儲并且操作碼為delete,該擴(kuò)展將摧毀所有根云盤在該主存儲的虛擬機(jī);但如果父發(fā)起者是主機(jī),擴(kuò)展將會只停止在那臺主機(jī)上的虛擬機(jī),因為這些虛擬機(jī)稍后就可以在其他主機(jī)上啟動??紤]到ZStack沒有產(chǎn)生沖突的級聯(lián)操作,例如,不會有一個操作導(dǎo)致虛擬機(jī)在路徑A啟動而在路徑B停止,所以級聯(lián)操作從不同路徑進(jìn)行多次延伸是沒有問題的。
當(dāng)級聯(lián)一個操作時,該框架從該操作被應(yīng)用的root issuer開始;在上述刪除區(qū)域的示例中,zone是根發(fā)起者;那么框架將從根發(fā)起者遍歷樹,并調(diào)用擴(kuò)展的createActionForChildResource() 方法為每一條路徑上的每一個擴(kuò)展創(chuàng)建上下文;一旦所有上下文創(chuàng)建成功,該框架將再次遍歷樹,不過是從葉子節(jié)點到根,并調(diào)用每個擴(kuò)展的asyncCascade()方法 ;一個擴(kuò)展可以依靠父發(fā)起者去決定應(yīng)該做哪些操作,父發(fā)起者在getEdgeNames() 方法中以資源名的方式聲明;例如,如果父發(fā)行者是主機(jī),則停止虛擬機(jī);如果父發(fā)行者是主存儲,則刪除虛擬機(jī)。

這兩個階段的遍歷保證,一個操作(例如刪除)將只會被應(yīng)用到根發(fā)起者,在所有下游資源都做完一些合適的操作后。例如,一個區(qū)域只在所有子孫資源都被刪除后才能被刪除。
由于并不是所有的操作都需要級聯(lián),一個資源可以在它需要的時候直接調(diào)用CascadeFacade.asyncCascade()。
總結(jié)
在這篇文章中,我們演示了ZStack的級聯(lián)框架,這是一個強(qiáng)大的工具,用于擴(kuò)散操作而不需要硬編碼。ZStack用很多方式使用了它,除了我們在文中提到的以外,一些操作,如卸載主存儲(這將停止將被卸載的集群中的所有虛擬機(jī)),卸載L2網(wǎng)絡(luò)(這將停止將被卸載的集群中的所有虛擬機(jī))都是以這種方式實現(xiàn)的。有了它的幫助,管理員可以快速嘗試不同的云部署而無需擔(dān)心不方便;你可以只刪除你的部署的一部分并重新創(chuàng)建一個新的,而不需要僅因為你在一個設(shè)計錯誤的L2網(wǎng)絡(luò)上創(chuàng)建了許多虛擬機(jī),就重新部署整個云(舉個例子)。