在軟件交付領(lǐng)域上有一個(gè)非常有用的啟發(fā)式原則:提前并頻繁地做讓你感到痛苦的事。集成通常是一個(gè)非常痛苦的過程,所以每次有人提交代碼后應(yīng)立刻進(jìn)行集成。
馬丁福勒說:
持續(xù)集成是一種軟件開發(fā)實(shí)踐,即團(tuán)隊(duì)開發(fā)成員經(jīng)常集成他們的工作,通過每個(gè)成員每天至少集成一次,也就意味著每天可能會(huì)發(fā)生多次集成。每次集成都通過自動(dòng)化的構(gòu)建(包括編譯,發(fā)布,自動(dòng)化測(cè)試)來驗(yàn)證,從而盡早地發(fā)現(xiàn)集成錯(cuò)誤。
而持續(xù)集成服務(wù)器(也稱為構(gòu)建服務(wù)器,又稱CI服務(wù)器)是一種軟件工具,它包含所有持續(xù)集成操作,并為我們構(gòu)建項(xiàng)目提供可靠和穩(wěn)定的環(huán)境。
工欲善其事,必先利其器。持續(xù)集成的重要性毋庸置疑,但是理解如何創(chuàng)建安全、高效、可伸縮的持續(xù)集成服務(wù)器將使得持續(xù)集成更加順暢。
CI服務(wù)器 - 安裝方式
CI服務(wù)器的安裝方式有四種:
單機(jī)
單機(jī)的CI服務(wù)器即安裝在一臺(tái)單個(gè)主機(jī)上,通常用于小型項(xiàng)目。托管
托管的CI服務(wù)器由托管平臺(tái)提供,一般是根據(jù)訂閱計(jì)價(jià)。這一般被那些不想維護(hù)CI服務(wù)器的企業(yè)所采納。私有云
一些大型企業(yè)或者想要完全掌控及安全使用他們自己的基礎(chǔ)設(shè)施的組織,通常會(huì)選擇私有云的部署方式。半托管半私有云
這種方式是對(duì)托管部署和私有云部署的折中方案。這種部署方式通常將流水線管理,日志管理,用戶管理部分進(jìn)行托管,而將流水線運(yùn)行時(shí)環(huán)境放在私有云中。這種方式即保證了基礎(chǔ)設(shè)施安全,同時(shí)也降低了維護(hù)CI服務(wù)器的成本。
我所在的團(tuán)隊(duì)最初使用私有云的方式搭建CI服務(wù)器,但出于可維護(hù)性、成本和安全性等方面的折中考慮轉(zhuǎn)向了半托管半私有云的安裝方式。本文將主要討論在私有云和半托管半私有云安裝方式下CI服務(wù)器的部署策略。
CI服務(wù)器 - 主從架構(gòu)
主從架構(gòu)也叫主仆架構(gòu)或者M(jìn)aster-Slave架構(gòu),主從式架構(gòu)意圖提供一個(gè)可伸縮的架構(gòu),其核心是基于分而治之的思想,將一個(gè)原始任務(wù)分解為若干個(gè)子任務(wù),并由專門的工作者來執(zhí)行這些任務(wù),原始任務(wù)的結(jié)通過整合各個(gè)子任務(wù)的處理結(jié)果形成。

主從架構(gòu)看似與CI服務(wù)器毫無關(guān)系,實(shí)則聯(lián)系緊密。因?yàn)楹芏郈I服務(wù)器使用了主從架構(gòu)或者服務(wù)器/代理架構(gòu)。通過主從架構(gòu)的Master可以進(jìn)行用戶管理、流水線管理、日志管理。Master作為任務(wù)調(diào)度者,給多個(gè)主從架構(gòu)的Slave分配任務(wù),Slave會(huì)將流水線任務(wù)運(yùn)行的信息和結(jié)果發(fā)給Master。主從架構(gòu)提供了一個(gè)可伸縮的架構(gòu),所以可以動(dòng)態(tài)的添加或者刪除Slave以支持團(tuán)隊(duì)動(dòng)態(tài)變化的持續(xù)集成任務(wù)。
CI服務(wù)器 - 部署策略
通常,一個(gè)開發(fā)團(tuán)隊(duì)會(huì)有三個(gè)應(yīng)用部署環(huán)境:開發(fā)環(huán)境(Dev),類生產(chǎn)環(huán)境(Staging)和生產(chǎn)環(huán)境(Production)。當(dāng)一次修改通過持續(xù)集成后,就會(huì)被部署到開發(fā)環(huán)境,然后部署到類生產(chǎn)環(huán)境,然后部署到生產(chǎn)環(huán)境。
為此,需要CI服務(wù)器具有操作相應(yīng)部署環(huán)境的權(quán)限。如圖2,開發(fā)環(huán)境中部署了CI服務(wù)器,其中每個(gè)CI服務(wù)器都具有開發(fā)、類生產(chǎn)和生產(chǎn)環(huán)境的權(quán)限。

這種方式粗暴簡(jiǎn)單,看似藥到病除實(shí)質(zhì)上卻會(huì)引發(fā)很嚴(yán)重的安全問題。通常項(xiàng)目中對(duì)開發(fā)、類生產(chǎn)和生產(chǎn)環(huán)境權(quán)限的管控類似于金字塔,越往上權(quán)限管控越嚴(yán)格。但如果開發(fā)環(huán)境的CI服務(wù)器也具有類生產(chǎn)和生產(chǎn)環(huán)境的權(quán)限,那么這座本來堅(jiān)固的金字塔將有可能瞬間崩塌。
為了解決開發(fā)環(huán)境CI服務(wù)器具有多環(huán)境操作權(quán)限的問題,如圖3所示,可以在不同的環(huán)境獨(dú)立部署一套CI服務(wù)器,這樣部署某個(gè)環(huán)境時(shí),就可以使用相應(yīng)環(huán)境的CI服務(wù)器即可。

但隨之而來的問題是,不同的環(huán)境獨(dú)立部署一套CI服務(wù)器,會(huì)使得每個(gè)環(huán)境的CI服務(wù)器之間沒有任何關(guān)系。它們的流水線管理,日志管理,用戶管理都是獨(dú)立的。這意味著原本一個(gè)流水線完成的事情會(huì)分散的對(duì)個(gè)多個(gè)持續(xù)集成系統(tǒng)中,用戶需要在多個(gè)系統(tǒng)中來回切換。這不僅降低了使用體驗(yàn),并且增加了復(fù)雜性和維護(hù)成本。
那么有沒有什么辦法,既保證只部署一套CI服務(wù)器,同時(shí)又能保證CI服務(wù)器不能跨環(huán)境操作?如圖4所示,雖然所有的CI服務(wù)器依然部署在開發(fā)環(huán)境中,但每個(gè)CI服務(wù)器代理已經(jīng)被打上了開發(fā)環(huán)境、類生產(chǎn)和生產(chǎn)環(huán)境的標(biāo)簽,并且只具有操作相應(yīng)環(huán)境的權(quán)限。

這種部署策略看似解決了安全問題,但實(shí)際上有點(diǎn)掩耳盜鈴,因?yàn)橹灰_發(fā)環(huán)境上的用戶具有足夠的權(quán)限,他就有可能可以修改CI服務(wù)器代理的權(quán)限,從而達(dá)到操縱其他環(huán)境的目的。
既然如此,那么可不可以將CI服務(wù)器代理部署到相應(yīng)的環(huán)境中?如圖5所示,不同環(huán)境將維護(hù)相應(yīng)的CI服務(wù)器代理,通過這種部署方式,既保證只部署一套CI服務(wù)器,同時(shí)又能保證CI服務(wù)器代理不能跨環(huán)境操作。

既然CI服務(wù)器代理可以部署到不同的環(huán)境中,那么CI服務(wù)器的Master還需要繼續(xù)放在開發(fā)環(huán)境中嗎?這視情況而定,如果團(tuán)隊(duì)非常小,也許可以考慮將CI服務(wù)器的Master部署在生產(chǎn)環(huán)境中,以限制操作CI服務(wù)器的Master。如果團(tuán)隊(duì)非常大,如圖6所示,也許可以考慮將CI服務(wù)器的Master部署到一個(gè)獨(dú)立的環(huán)境中,而這個(gè)環(huán)境則由一個(gè)專門的持續(xù)集成團(tuán)隊(duì)來維護(hù)。持續(xù)集成團(tuán)隊(duì)需要保證CI服務(wù)器的Master的可用性。而業(yè)務(wù)開發(fā)團(tuán)隊(duì)只需要維護(hù)CI服務(wù)器代理。

假設(shè),某公司有3個(gè)業(yè)務(wù)開發(fā)團(tuán)隊(duì):Team A、Team B和Team C。如圖7所示,Team A、Team B和Team C可以共用由持續(xù)集成(CI)團(tuán)隊(duì)維護(hù)的Master服務(wù)器。

但多個(gè)開發(fā)團(tuán)隊(duì)共享一個(gè)CI服務(wù)器的Master又引發(fā)了另一個(gè)問題。假設(shè),Team A現(xiàn)在要?jiǎng)?chuàng)建一個(gè)流水線,那么Team B和Team C如何限制Team A無法使用Team B和Team C所維護(hù)的CI服務(wù)器代理?
一個(gè)團(tuán)隊(duì)在創(chuàng)建一個(gè)流程水線時(shí),可以根據(jù)CI服務(wù)器代理的標(biāo)簽來指定流水線不同的任務(wù)使用相應(yīng)環(huán)境的CI服務(wù)器代理。對(duì)于CI服務(wù)器的Master來說,Team A、Team B和Team C的CI服務(wù)器代理都只是普通的代理,它們之間除了具有不同的標(biāo)簽,CI服務(wù)器的Master對(duì)它們其他的信息一無所知。
但對(duì)于CI服務(wù)器代理,它是有能力知道在它上面運(yùn)行的流水線任務(wù)是不是合法。假設(shè)Team A、Team B和Team C的代碼分別位于不同的git組織(organization): team-a-org, team-b-org和team-c-org。通常一個(gè)流水線任務(wù)在CI服務(wù)器代理上執(zhí)行的第一件事情就是在git上拉取代碼庫的代碼,而代碼庫是位于特定的git組織之下的,那么如圖8所示,通過在團(tuán)隊(duì)的CI服務(wù)器代理配置git組織的白名單即可防止其他團(tuán)隊(duì)的流水線任務(wù)使用自己團(tuán)隊(duì)的CI服務(wù)器代理。

隨著公司業(yè)務(wù)的發(fā)展,項(xiàng)目不斷的增多,每日需要持續(xù)集成的任務(wù)越來越多,這就需要更多的CI服務(wù)器代理。如果Team A、Team B和Team C分別增加了CI服務(wù)器代理以滿足自己團(tuán)隊(duì)持續(xù)集成任務(wù)的需要,但這些CI服務(wù)器代理在團(tuán)隊(duì)之間是不能共享的,這就帶來了資源浪費(fèi)。
仔細(xì)研究持續(xù)集成任務(wù),持續(xù)集成的大多數(shù)任務(wù)是不需要操作環(huán)境資源的,它們只需要拉取代碼的權(quán)限,然后運(yùn)行測(cè)試。如此一來,如圖9所示,可以由CI團(tuán)隊(duì)維護(hù)一組共享的CI服務(wù)器代理,這些代理不具有任何操作環(huán)境資源的能力,它只是一個(gè)測(cè)試運(yùn)行的環(huán)境。

由此,可以將CI服務(wù)器代理分為環(huán)境操作無關(guān)和環(huán)境操作相關(guān)兩類。環(huán)境操作無關(guān)的CI服務(wù)器代理由CI團(tuán)隊(duì)維護(hù),通常用來做持續(xù)集成,環(huán)境操作相關(guān)的CI服務(wù)器代理由業(yè)務(wù)開發(fā)團(tuán)隊(duì)維護(hù),通常用來做持續(xù)部署。
如圖10所示,當(dāng)代碼修改后會(huì)自動(dòng)觸發(fā)CI流水線。對(duì)于與環(huán)境操作無關(guān)的任務(wù):?jiǎn)卧獪y(cè)試和驗(yàn)收測(cè)試,可以使用由CI團(tuán)隊(duì)維護(hù)的共享的CI服務(wù)器代理來執(zhí)行。而對(duì)于與環(huán)境操作相關(guān)的任務(wù):部署開發(fā)環(huán)境、部署類開發(fā)環(huán)境和部署開發(fā)環(huán)境則使用具有相應(yīng)環(huán)境操作權(quán)限的CI服務(wù)器代理來執(zhí)行。

對(duì)于一個(gè)組織來說,如果選擇同時(shí)維護(hù)CI服務(wù)器Master和CI服務(wù)器代理,那么使用的就是私有云安裝方式。但是維護(hù)CI服務(wù)器Master需要投入大量的人力來維護(hù)其可用性,并不是每個(gè)組織都愿意為它買單,在這種情況下可以選擇使用半托管半私有云的安裝方式,如圖11所示:

通過使用半托管半私有云的安裝方式,一個(gè)組織里每個(gè)團(tuán)隊(duì)只需要維護(hù)CI服務(wù)器代理。這些CI服務(wù)器代理通過Key校驗(yàn)后與托管的Master相連,CI服務(wù)器代理不維護(hù)任何狀態(tài),它只提供了一個(gè)任務(wù)運(yùn)行時(shí)的環(huán)境,在任務(wù)運(yùn)行的過程中CI服務(wù)器代理將任務(wù)運(yùn)行的狀態(tài)和日志發(fā)給Master。這種情況下當(dāng)一個(gè)CI服務(wù)器代理執(zhí)行完任務(wù)后如果沒有其它的任務(wù),其實(shí)這個(gè)CI服務(wù)器代理所占的資源就可以被釋放了,當(dāng)有任務(wù)時(shí)再立即分配資源給CI服務(wù)器代理,然后執(zhí)行任務(wù)。
CI服務(wù)器 - 按需分配
為了合理的利用資源,需要對(duì)CI服務(wù)器進(jìn)行按需分配。然而現(xiàn)在主流的CI服務(wù)器都沒有原生的支持按需分配的功能,所以需要開發(fā)團(tuán)隊(duì)自己實(shí)現(xiàn)如何對(duì)CI服務(wù)器進(jìn)行按需分配。
如圖12所示,通過信息收集器(Metrics Collector)來收集Team A的CI服務(wù)器代理的使用狀態(tài),收集的信息如:正在運(yùn)行任務(wù)的數(shù)量、已安排要運(yùn)行任務(wù)的數(shù)量、未完成任務(wù)的數(shù)量、空閑CI服務(wù)器代理的數(shù)量、繁忙CI服務(wù)器代理的數(shù)量和CI服務(wù)器代理的總數(shù)。通過這些信息判斷是否需要對(duì)CI服務(wù)器代理擴(kuò)容或者減容,通過這種方式達(dá)到按需分配。

總結(jié)
選擇何種CI服務(wù)器的安裝方式或者部署策略取決于團(tuán)隊(duì)對(duì)于安全性、可維護(hù)性、成本和團(tuán)隊(duì)自身能力的權(quán)衡。團(tuán)隊(duì)?wèi)?yīng)該根據(jù)團(tuán)隊(duì)當(dāng)前的情況動(dòng)態(tài)調(diào)整CI服務(wù)器的安裝方式和部署策略。