持續(xù)部署,并不簡(jiǎn)單!
這幾年,持續(xù)集成隨著敏捷在國(guó)內(nèi)的推廣而持續(xù)走熱,與之相伴的持續(xù)部署也一直備受關(guān)注。自前兩年,持續(xù)交付這個(gè)延續(xù)性概念又闖進(jìn)了國(guó)內(nèi)IT圈,慢慢開(kāi)始在社區(qū)和會(huì)議中展露頭角。許多不明真相的群眾跟風(fēng)哭著喊著要“上”,而許多前CI的半吊子玩家換件衣服就接著干,有的甚至衣服都來(lái)不及換……。國(guó)內(nèi)的這些土財(cái)主如果不巧請(qǐng)了某些所謂的戰(zhàn)略家,除了建了一堆持續(xù)集成環(huán)境,以及每天嚷嚷著要這個(gè)要那個(gè),混亂的狀況在根本上沒(méi)有得到改善。本文無(wú)意費(fèi)力探討持續(xù)集成和持續(xù)交付的概念,而是打算談?wù)剬?duì)于大型軟件企業(yè),以持續(xù)集成為基礎(chǔ)實(shí)現(xiàn)持續(xù)部署(交付)時(shí),所要面對(duì)的問(wèn)題以及可行的解決方案。地主老財(cái)們,夜黑風(fēng)正猛,山高路又遠(yuǎn),注意腳下……
And God Said, Let there be light: and there wa— GENSIS, Charpter 1, King James
一、起步
先來(lái)講個(gè)故事……
幾年前,一對(duì)留美的夫婦通過(guò)朋友找到我,讓我?guī)兔υ趪?guó)內(nèi)組建一個(gè)開(kāi)發(fā)團(tuán)隊(duì),該團(tuán)隊(duì)負(fù)責(zé)為其開(kāi)發(fā)一款基于社交網(wǎng)絡(luò)的客戶關(guān)系管理軟件,(暫且稱之為項(xiàng)目A)。這個(gè)項(xiàng)目除了尚不清晰的需求范圍和很緊的期限外,作為業(yè)內(nèi)人士的老公Richard根據(jù)眼下流行的軟件開(kāi)發(fā)過(guò)程還提了諸多額外的要求:
- 功能要及早交付(以便拿去和潛在的投資人洽談)
- 功能在部署到生產(chǎn)環(huán)境前要先部署的一個(gè)測(cè)試環(huán)境(Richard要試用后給予反饋)
- 功能必須經(jīng)過(guò)測(cè)試(長(zhǎng)期作為軟件外包的甲方,對(duì)質(zhì)量要求嚴(yán)格)
- 要減少后期維護(hù)的工作(美國(guó)人精貴,少雇一個(gè)是一個(gè))
- 支持協(xié)同開(kāi)發(fā)(以便維護(hù)人員及早介入)
- ……
這正是持續(xù)集成所要解決的典型場(chǎng)景。針對(duì)Richard的要求,我們只要建立一個(gè)基于Hudson(現(xiàn)在叫Jenkins)+Maven +SVN 的持續(xù)集成環(huán)境(再加上持續(xù)集成所要求的測(cè)試和過(guò)程)就可以很好地滿足上述要要求,此方案的結(jié)構(gòu)如下:
對(duì)于上述方案,讓我們近距離看看各個(gè)服務(wù)器的內(nèi)部情況,以及人員在這種方案下的分工協(xié)作:
我們先談?wù)勆厦娴膱D中涉及的一些概念性問(wèn)題:
1.1)編譯時(shí)依賴和運(yùn)行時(shí)依賴
從字面上不難理解這兩種依賴的類型。但要注意雖然編譯時(shí)依賴常常也是運(yùn)行時(shí)依賴,但并不能推斷出一方必然是另一方。比如,在開(kāi)發(fā)的過(guò)程中需要某些提供API的Jar包,而運(yùn)行時(shí)可能是具體API實(shí)現(xiàn)的Jar包。再者,被依賴的包會(huì)有其自身的依賴,因此,項(xiàng)目對(duì)這些包產(chǎn)生間接依賴(運(yùn)行時(shí)依賴),依此類推,最終形成一個(gè)依賴樹(shù)。當(dāng)項(xiàng)目運(yùn)行時(shí),這些依賴樹(shù)上的包必須全部就位。
Maven在POM中通scope來(lái)界定依賴的類型,從而幫助開(kāi)發(fā)和運(yùn)維人員擺脫手動(dòng)處理依賴樹(shù)的工作,然而運(yùn)行時(shí)所依賴包最終是要安裝到生產(chǎn)環(huán)境的,這部分工作Maven并不能自動(dòng)完成。因此,一個(gè)常用方式是將運(yùn)行時(shí)所依賴的包拷貝到項(xiàng)目文件中,比如Java Web應(yīng)用的WEB-INF/lib,然后將項(xiàng)目總的打一個(gè)包。在安裝項(xiàng)目包后,修改環(huán)境變量,將這些包所在的路徑加入相應(yīng)的環(huán)境變量中,如ClassPath。
再看個(gè)例子,現(xiàn)代的操作系統(tǒng)和其它系統(tǒng)框架都考慮到了運(yùn)行時(shí)依賴樹(shù)的處理問(wèn)題,比如Ubuntu的apt-get,CentOS的yum,Ruby的RubyGem,Node的npm等等。
1.2)依賴時(shí)的復(fù)雜度
項(xiàng)目除了對(duì)程序包的依賴,對(duì)于運(yùn)行環(huán)境也有些具體的要求,比如,Web應(yīng)用需要安裝和配置Web服務(wù)器,應(yīng)用服務(wù)器,數(shù)據(jù)服務(wù)器等,企業(yè)應(yīng)用中可能需要消息隊(duì)列,緩存,定時(shí)作業(yè),或是對(duì)其它系統(tǒng)以Web Service方式暴露的服務(wù)。這些可以看做項(xiàng)目在系統(tǒng)層面對(duì)外部的依賴。這些依賴有些可以由項(xiàng)目自行處理,而有些則是項(xiàng)目無(wú)法處理的,比如運(yùn)行容器,操作系統(tǒng)等,這些是項(xiàng)目的運(yùn)行環(huán)境。
總之,依賴的復(fù)雜度主要有兩個(gè):
- 依賴包間的版本兼容性問(wèn)題。兼容性問(wèn)題是軟件開(kāi)發(fā)的惡夢(mèng)
- 間接依賴,或多重依賴問(wèn)題。這個(gè)問(wèn)題可以類比想像一下C++中的多重繼續(xù)種出現(xiàn)的很多問(wèn)題。
1.3)任務(wù)分工
由于項(xiàng)目簡(jiǎn)單,因此并不需要專門(mén)的運(yùn)維人員。以一個(gè)100人左右以交付為主業(yè)(恩,就是做外包)的公司為例,由于沒(méi)有任何歷史項(xiàng)目和代碼的拖累,且各個(gè)項(xiàng)目間也沒(méi)有任何關(guān)聯(lián),故而只需要配備一個(gè)IT支持人員進(jìn)行資源方面的管理:分配機(jī)器,報(bào)修,初始化系統(tǒng),分配IP地址等。各個(gè)項(xiàng)目的運(yùn)行環(huán)境、數(shù)據(jù)庫(kù)、開(kāi)發(fā)環(huán)境等都由具體項(xiàng)目的開(kāi)發(fā)人員手動(dòng)完成。 環(huán)境出問(wèn)題怎么辦?很簡(jiǎn)單,涼拌——重裝系統(tǒng)。實(shí)際的運(yùn)行效果不錯(cuò)。
1.4)自動(dòng)化部署
由于Hudson這樣的持續(xù)集成環(huán)境提供了自動(dòng)編譯(定時(shí)或觸發(fā)式)的功能,而且可以在編譯過(guò)程中提供了一些擴(kuò)展點(diǎn),因此通過(guò)提供一個(gè)部署用的腳本,就可以非常容易實(shí)現(xiàn)簡(jiǎn)單的自動(dòng)化部署。
毫無(wú)疑問(wèn),持續(xù)集成就是敏捷的魔法藥,它見(jiàn)效快、副作用小、業(yè)界的爭(zhēng)論少。每每運(yùn)用在混亂的項(xiàng)目中時(shí),幾周內(nèi)項(xiàng)目就開(kāi)始持續(xù)的產(chǎn)出經(jīng)過(guò)測(cè)試的功能。對(duì)于獨(dú)立項(xiàng)目,以持續(xù)集成為中心的持續(xù)部署絕對(duì)是不二選擇。
但是,我們有沒(méi)有想過(guò),這會(huì)是一個(gè)自動(dòng)化部署的通用解決方案嗎?持續(xù)集成應(yīng)該位于持續(xù)交付的中心嗎?
#p#
二、困境
回到我們的故事:項(xiàng)目A上線兩年后,運(yùn)營(yíng)業(yè)績(jī)不錯(cuò),投資人第一輪注資后,Richard的公司進(jìn)行了擴(kuò)張,他們對(duì)項(xiàng)目進(jìn)行了重構(gòu),而且隨著用戶數(shù)量的增長(zhǎng),公司分別在美國(guó)、英國(guó)和日本等地建立了運(yùn)營(yíng)中心,并且對(duì)亞洲市場(chǎng)進(jìn)行的定制功能開(kāi)發(fā)(項(xiàng)目A+),接下來(lái),公司又投入開(kāi)發(fā)了團(tuán)購(gòu)系統(tǒng)(項(xiàng)目B)。在獲得了新一輪投資后,各條本來(lái)比較簡(jiǎn)單的業(yè)務(wù)和功能線上越來(lái)越復(fù)雜,需要不斷地細(xì)分,于是公司再度擴(kuò)張(開(kāi)發(fā)人員達(dá)到了300人,國(guó)內(nèi)200多人,而運(yùn)維團(tuán)隊(duì)主要在美國(guó)),隨后又為項(xiàng)目A/A+的高級(jí)用戶開(kāi)發(fā)了問(wèn)答系統(tǒng)(項(xiàng)目C)。目前,他們正準(zhǔn)備開(kāi)發(fā)手機(jī)系統(tǒng)。 看看下面的圖,公司增長(zhǎng)的過(guò)程中,整個(gè)項(xiàng)目環(huán)境也變得復(fù)雜。(注意,這里是一種邏輯結(jié)構(gòu),而在物理層面項(xiàng)目B和項(xiàng)目A的生產(chǎn)環(huán)境可能部署在相同的機(jī)器上)。
同時(shí),原本單一的項(xiàng)目軟件結(jié)構(gòu)隨著業(yè)務(wù)系統(tǒng)的增加也不再簡(jiǎn)單:
而軟件間的版本依賴使這個(gè)問(wèn)題變得更為復(fù)雜:
現(xiàn)在,Richard的公司已經(jīng)不再是一條快樂(lè)的小魚(yú),而是漸漸成為一直龐大的巨獸。雖然只有四個(gè)產(chǎn)品,但公司卻要支持幾百臺(tái)開(kāi)發(fā)機(jī),幾十臺(tái)生產(chǎn)服務(wù)器,還有對(duì)應(yīng)的測(cè)試環(huán)境,數(shù)據(jù)庫(kù)服務(wù)器,以及幾十個(gè)開(kāi)發(fā)小組,和一大堆的內(nèi)部項(xiàng)目。我們盡可以使用持續(xù)集成來(lái)為我們完成自動(dòng)化部署。但,當(dāng)我們?yōu)楦鱾€(gè)項(xiàng)目建立起持續(xù)集成環(huán)境后,它能滿足我們對(duì)于持續(xù)部署的要求嗎?我們前期的工作可以簡(jiǎn)化我們今后項(xiàng)目的持續(xù)交付的工作的難度嗎?它需要我們?yōu)橹⒁粋€(gè)龐大的運(yùn)維團(tuán)隊(duì),還是可以讓我們能節(jié)省下每一毛錢(qián)來(lái)投入到真正的業(yè)務(wù)價(jià)值中去?
讓我們先來(lái)看看復(fù)雜的項(xiàng)目環(huán)境中的幾個(gè)場(chǎng)景:
場(chǎng)景1:環(huán)境升級(jí)
項(xiàng)目A和項(xiàng)目B都依賴于Web容器,公司決定升級(jí)Web容器版本,而公司要升級(jí)的機(jī)器有上百臺(tái),依賴人肉升級(jí)已不現(xiàn)實(shí),維護(hù)團(tuán)隊(duì)因此針對(duì)各種軟件開(kāi)發(fā)了相應(yīng)的自動(dòng)化腳本,但當(dāng)新的軟件出現(xiàn)時(shí),必須要開(kāi)發(fā)新的腳本。而且當(dāng)同時(shí)升級(jí)若干環(huán)境軟件時(shí),則難度隨之增大,手工調(diào)度的方式極易出錯(cuò),當(dāng)升級(jí)失敗時(shí)仍需要大量人工處理。由于存在大量升級(jí)腳本,有一定的維護(hù)成本。
場(chǎng)景2:依賴于環(huán)境的軟件升級(jí)與回滾
針對(duì)環(huán)境升級(jí),公司為項(xiàng)目A和項(xiàng)目B開(kāi)發(fā)了新的版本。但環(huán)境的升級(jí)和軟件的升級(jí)不是同步進(jìn)行,出錯(cuò)的可能性非常大(想一想間接依賴和多重依賴的情況)。當(dāng)新版本部署到生產(chǎn)系統(tǒng)時(shí),發(fā)現(xiàn)問(wèn)題,需要回滾到之前的版本——所有運(yùn)行時(shí)版本都需要回滾,而且環(huán)境也需要同步回滾。幾百臺(tái)機(jī)器……
場(chǎng)景3:運(yùn)行時(shí)依賴
在第一節(jié)的方案中,我們將所有的運(yùn)行時(shí)依賴都打包到一起。當(dāng)項(xiàng)目依賴關(guān)系復(fù)雜時(shí),這樣產(chǎn)生的包將非常臃腫,潛在地延長(zhǎng)了部署的時(shí)間(想一想全世有幾百臺(tái)服務(wù)器,一個(gè)部署計(jì)劃需要部署幾百兆文件的情況),而且產(chǎn)生沖突的可能性非常大,而且對(duì)于不同類型的項(xiàng)目(Java和Ruby項(xiàng)目)缺乏通用性。06年左右,Nortel可是拿Excel統(tǒng)計(jì)過(guò)運(yùn)行時(shí)依賴的,牽涉若干項(xiàng)目組,反復(fù)多次,沒(méi)有個(gè)把月真搞不定。
場(chǎng)景4:泛濫的部署
每個(gè)項(xiàng)目相關(guān)的持續(xù)集成環(huán)境都需要開(kāi)發(fā)自己的部署腳本,重復(fù)投入大,而且各個(gè)項(xiàng)目的部署過(guò)程不一致,并且對(duì)于同一個(gè)項(xiàng)目無(wú)法同時(shí)滿足不同目的部署要求,例如,環(huán)境或系統(tǒng)配置參數(shù)改變后,無(wú)需安裝包,只需做清理和激活的工作。最后,持續(xù)集成只是支持了和代碼修改有關(guān)的部署。
場(chǎng)景5:不一致的環(huán)境
簡(jiǎn)單項(xiàng)目中,開(kāi)發(fā)環(huán)境和運(yùn)行環(huán)境都由開(kāi)發(fā)人員搭建,當(dāng)公司變大時(shí),系統(tǒng)的運(yùn)行環(huán)境將由運(yùn)維人員搭建,而開(kāi)發(fā)環(huán)境如果由運(yùn)維人員搭建則工作量太大,由開(kāi)發(fā)人員自己搭建則操作復(fù)雜又容易產(chǎn)生不一致的情況。
場(chǎng)景6:熱切換
對(duì)于某些部署,需要盡量減少服務(wù)的停止時(shí)間,需要在服務(wù)的同時(shí)進(jìn)行部署。
這些場(chǎng)景只是以持續(xù)集成為中心的持續(xù)部署在面對(duì)大型企業(yè)時(shí)所遇到的部分問(wèn)題。大型企業(yè),人多,項(xiàng)目多,機(jī)器多,項(xiàng)目環(huán)境復(fù)雜,部署維護(hù)工作繁多。以持續(xù)集成為基礎(chǔ)的部署可以解決各個(gè)項(xiàng)目的集成問(wèn)題,卻無(wú)法幫助企業(yè)應(yīng)對(duì)復(fù)雜的項(xiàng)目環(huán)境和各種不同的部署要求。究其更本,大型企業(yè)中的部署不再是一個(gè)簡(jiǎn)單的問(wèn)題,而是一個(gè)交付生態(tài)圈,基礎(chǔ)設(shè)施和環(huán)境管理必須要納入考慮之中。要實(shí)現(xiàn)真正意義上的持續(xù)部署,我們就必須把環(huán)境和項(xiàng)目同等對(duì)待,通通納入管理之中。同時(shí),部署本身要得到統(tǒng)一。一個(gè)好的部署機(jī)制,應(yīng)該是易于建立,易于使用,易于維護(hù)。
#p#
三、任脈——環(huán)境管理
什么是環(huán)境?
系統(tǒng)運(yùn)行所依賴和包含的一切就是其環(huán)境:硬件、操作系統(tǒng),網(wǎng)絡(luò)資源(IP地址、域名),服務(wù)容器,服務(wù)器軟件配置,環(huán)境亦是,運(yùn)行時(shí)依賴的命令和包,項(xiàng)目本身的包和配置都是環(huán)境的一部分。對(duì)于部署而言,廣義上,這些通通應(yīng)該納入環(huán)境管理的范疇,但狹義上,從軟件系統(tǒng)的角度看,一個(gè)環(huán)境就是其運(yùn)行需要的軟件及其配置(我們先把操作系統(tǒng)和網(wǎng)絡(luò)資源當(dāng)做基礎(chǔ)設(shè)施,其在部署時(shí)已處于就位的情況)。因此:
項(xiàng)目A的生產(chǎn)環(huán)境 = 項(xiàng)目A本身的軟件包 + 項(xiàng)目A運(yùn)行時(shí)依賴的軟件包 + 項(xiàng)目A運(yùn)行時(shí)依賴的其它軟件 + 項(xiàng)目A的配置信息
由于,項(xiàng)目本身的軟件包、項(xiàng)目運(yùn)行時(shí)依賴的軟件包,以及項(xiàng)目運(yùn)行時(shí)依賴的其它軟件在本質(zhì)上沒(méi)有區(qū)別——都是軟件,上面的定義可以進(jìn)一步抽象為:
環(huán)境 = 軟件包 + 配置信息
在這個(gè)定義下,我們就必須將運(yùn)行環(huán)境的軟件解構(gòu),并以包的形式導(dǎo)入到公司的整個(gè)項(xiàng)目資源庫(kù)中,比如Apache將作為一個(gè)包被導(dǎo)入,而Apache 依賴的其它包也將依次被導(dǎo)入,并建立起正確的依賴關(guān)系。而且,在導(dǎo)入的過(guò)程中還必須做些相應(yīng)的調(diào)整,如,環(huán)境變量的讀取和設(shè)置,必須來(lái)自于環(huán)境配置模塊,而不要修改系統(tǒng)的環(huán)境變量,防止不同環(huán)境在系統(tǒng)環(huán)境配置上相互影響和依賴。
再回頭審視我們的示例,項(xiàng)目A的生產(chǎn)環(huán)境可以部署在不同的區(qū)域,對(duì)于各個(gè)區(qū)域可能有定制化的設(shè)定。這就像面向?qū)ο笾械念悾梢酝ㄟ^(guò)繼承使子類重用父類的公有屬性和行為并添加自己特有的信息。因此,環(huán)境的概念模型如圖:
通過(guò)這樣的關(guān)系,我們很容易為示例的復(fù)雜環(huán)境建立一種簡(jiǎn)單的結(jié)構(gòu),對(duì)于項(xiàng)目A:
這里,環(huán)境依然是處于知識(shí)層面(Knowledge Level),它并未與具體的基礎(chǔ)設(shè)施相關(guān)聯(lián)。當(dāng)我們將一個(gè)環(huán)境“具現(xiàn)化”成一個(gè)運(yùn)行系統(tǒng)時(shí),我們就產(chǎn)生了一個(gè)真正的環(huán)境實(shí)例。在這兩者之間,我們還必須要考慮環(huán)境實(shí)例的使用目的(開(kāi)發(fā)?測(cè)試?……)以及安裝所依賴的其它信息(如機(jī)器),因此,我們需要增加一個(gè)環(huán)境目標(biāo)來(lái)集中這些信息,而且由于不同目標(biāo)的環(huán)境可能會(huì)有所差別,因此,環(huán)境目標(biāo)也需要配置的能力。概念模型如圖:
圖中的環(huán)境實(shí)例是如何產(chǎn)生的呢?部署,一次部署可能會(huì)產(chǎn)生一個(gè)環(huán)境實(shí)例。一系列部署將產(chǎn)生對(duì)應(yīng)于環(huán)境目標(biāo)的多個(gè)環(huán)境實(shí)例,除去當(dāng)前起作用的環(huán)境實(shí)例外(最新的),其它的是歷史環(huán)境實(shí)例。通過(guò)在歷史環(huán)境實(shí)例中切換,我們自然而然的就可以使整個(gè)環(huán)境回滾,因?yàn)轫?xiàng)目所依賴的一切都已經(jīng)成為的環(huán)境中的軟件包,而且環(huán)境依賴的包的版本會(huì)隨著部署具體確定下來(lái)。如此一來(lái),我們便可以給每個(gè)環(huán)境實(shí)例分配一個(gè)版本號(hào),再通過(guò)環(huán)境實(shí)例的版本號(hào)與軟件包的版本對(duì)應(yīng)起來(lái),從而得知一次部署時(shí)應(yīng)用的具體軟件包,如圖:
目前的環(huán)境管理結(jié)構(gòu),已經(jīng)可以解決場(chǎng)景1、2和5的問(wèn)題。那么對(duì)于場(chǎng)景2,運(yùn)行時(shí)依賴,環(huán)境管理應(yīng)該如何解決呢?
細(xì)心的朋友,可能已經(jīng)發(fā)現(xiàn),在環(huán)境層面上我們確定了環(huán)境依賴的軟件包,這里有兩個(gè)隱藏的含義:
- 環(huán)境定義的是對(duì)軟件包的運(yùn)行時(shí)依賴
- 由于環(huán)境是一個(gè)邏輯上的概念,因此其所用的軟件包也是一個(gè)邏輯上的概念(相對(duì)于版本控制系統(tǒng)中的軟件包)
我們也已經(jīng)知道,在部署時(shí),一個(gè)環(huán)境實(shí)例將具體的確定其依賴的軟件包的版本。某個(gè)版本的軟件包最終與代碼庫(kù)中的物理的軟件包相關(guān)聯(lián)。但軟件包是運(yùn)行時(shí)的安裝包,因此,它應(yīng)該是代碼庫(kù)中包編譯的結(jié)果。在對(duì)代碼庫(kù)的包編譯時(shí),既要將結(jié)果打上版本保存起來(lái),也好在兩者的版本間建立關(guān)系,最后,編譯結(jié)果應(yīng)該是某種既定的安裝包目錄文件結(jié)構(gòu)。
另外,當(dāng)環(huán)境包含的包比較多時(shí),運(yùn)行時(shí)版本樹(shù)會(huì)非常大,手動(dòng)的指定全部的包的版本將是一個(gè)非常大的體力勞動(dòng),這部分工作也要得到簡(jiǎn)化。由此,我們必須
- 建立邏輯軟件包版本和版本庫(kù)中軟件包版本間的關(guān)系
- 為相互依賴的包編譯并打上統(tǒng)一的標(biāo)簽
- 簡(jiǎn)化運(yùn)行時(shí)包依賴關(guān)系的生產(chǎn)
- 簡(jiǎn)化運(yùn)行時(shí)包依賴的指定(可參考apt-get和RubyGem,環(huán)境只需指定直接依賴的包,間接依賴的包從運(yùn)行時(shí)依賴樹(shù)中自動(dòng)導(dǎo)入)
上述討論還沒(méi)有涉及操作系統(tǒng),如果我們的運(yùn)行機(jī)器要支持多個(gè)系統(tǒng),我們又該怎么辦???
配置信息也是個(gè)大問(wèn)題,大家可以思考
- 環(huán)境配置和應(yīng)用配置如何區(qū)分?
- 如何簡(jiǎn)化環(huán)境配置工作?
- 如何使環(huán)境配置的效果只對(duì)具體環(huán)境有效,而不會(huì)泄露到環(huán)境外部?
再者,
- 如何使應(yīng)用支持多運(yùn)行目標(biāo)?
- 環(huán)境管理如何能方便開(kāi)發(fā)環(huán)境的調(diào)試?
- 要如何簡(jiǎn)化版本的選擇?
- 在多個(gè)包有編譯和運(yùn)行時(shí)依賴時(shí),編譯時(shí)如何檢查以減少引入兼容性問(wèn)題的風(fēng)險(xiǎn)?
這些都留待大家思考。
#p#
四、督脈——部署系統(tǒng)
《持續(xù)集成》和《持續(xù)交付》中都對(duì)部署有詳細(xì)的討論,不在贅述。在我看來(lái),部署其就是按照其目的執(zhí)行一系列步驟將環(huán)境置于其目的所指向的狀態(tài)中。我們一會(huì)再回國(guó)頭來(lái)看這段文縐縐的話,先看看第一部分持續(xù)集成的環(huán)境下,我們部署的步驟可能會(huì)是下面這個(gè)樣子:
- 登陸目標(biāo)機(jī)(ssh)
- 停止服務(wù)
- 清理環(huán)境
- 準(zhǔn)備安裝環(huán)境(創(chuàng)建文件夾等)
- 安裝項(xiàng)目包(rsync,解壓,權(quán)限設(shè)置等)
- 配置環(huán)境變量
- 啟動(dòng)服務(wù)
- ……
而在第二部分的情景4中,我們看到如果對(duì)不同的持續(xù)集成環(huán)境建立不同的部署腳本和環(huán)境維護(hù)腳本,這部署過(guò)程的維護(hù)會(huì)非常繁瑣。基于第三部分的環(huán)境管理,我們可以將部署過(guò)程抽象為:
現(xiàn)在回到開(kāi)頭那個(gè)文縐縐的描述:部署其就是按照其目的執(zhí)行一系列步驟將環(huán)境置于其目的所指向的狀態(tài)中。
由于我們已經(jīng)將部署作為環(huán)境管理的一部分,而環(huán)境又是對(duì)外提供服務(wù)的最小實(shí)體,因此,對(duì)環(huán)境的部署就是要根據(jù)部署的類型,在環(huán)境上按一定的步驟執(zhí)行一系列操作,從而使環(huán)境置于部署類型所要的狀態(tài),這個(gè)過(guò)程中可能會(huì)生成對(duì)應(yīng)的環(huán)境實(shí)例。舉例來(lái)說(shuō),我們可能會(huì)修改環(huán)境相關(guān)的一些配置,然后重啟環(huán)境,顯然,這種情況下不需要下載安裝軟件包(沒(méi)有改變),因此也就不需要生成環(huán)境實(shí)例。
對(duì)于標(biāo)準(zhǔn)的部署——安裝軟件包并啟動(dòng)環(huán)境,可能的步驟將會(huì)是:
- 選擇將要部署的軟件包的版本
- 生成新的環(huán)境實(shí)例(確定環(huán)境實(shí)例的版本和其依賴包的版本,確定環(huán)境配置等)
- 清理和準(zhǔn)備目標(biāo)機(jī)環(huán)境
- 下載包
- 設(shè)置環(huán)境配置
- 環(huán)境實(shí)例切換
- 生成部署報(bào)告
- ……
好,部署系統(tǒng)和環(huán)境管理各就各位,我們可以將各個(gè)項(xiàng)目環(huán)境納入我們的環(huán)境管理之中,甚至是持續(xù)集成環(huán)境本身。再補(bǔ)充一句,要讓部署系統(tǒng)和環(huán)境管理能很好的發(fā)揮作用,我們即需要一個(gè)簡(jiǎn)單一致的UI界面(為開(kāi)發(fā)人員),也需要提供一個(gè)清晰明了的服務(wù)接口(供外部系統(tǒng)調(diào)用,如持續(xù)部署系統(tǒng))。對(duì)于與環(huán)境管理相關(guān)的機(jī)器狀態(tài)管理,網(wǎng)絡(luò)資源的配置等等,本文不再涉及,大家可以自己思考。環(huán)境管理的實(shí)現(xiàn)、編譯系統(tǒng)改造以及持續(xù)部署的具體實(shí)現(xiàn),另作文章探討。
就技術(shù)而言(不考慮圍繞持續(xù)部署的過(guò)程實(shí)踐),環(huán)境管理、部署系統(tǒng)以及我們沒(méi)有提及的編譯系統(tǒng)改造才是生產(chǎn)線的真正引擎,持續(xù)部署不過(guò)是水到渠成的傳送帶而已。
五、沒(méi)完
打通了任督二脈后,事還還沒(méi)有完,還有很多細(xì)節(jié)上的問(wèn)題。你想,這個(gè)工具實(shí)在是太好用了,于是公司里成百上千的工程師們都在使用這個(gè)自動(dòng)化部署系統(tǒng),我們又會(huì)面對(duì)很多很多問(wèn)題:
- 部署系統(tǒng)的性能問(wèn)題。幾百號(hào)人不停地在把他們的軟件部署到自己的機(jī)器上,部署到測(cè)試環(huán)境,部署到生產(chǎn)環(huán)境,一天之內(nèi)一個(gè)人可能會(huì)要部署N次,回滾N次,不但有大量部署請(qǐng)求,還有大量的文件在網(wǎng)絡(luò)上傳輸。你得想想這套部署系統(tǒng)如何解決這些性能問(wèn)題,還得考慮未來(lái)更大規(guī)模的性能水平擴(kuò)展問(wèn)題。
- 目標(biāo)機(jī)環(huán)境的管理。在目標(biāo)運(yùn)行機(jī)上需要解決幾個(gè)問(wèn)題:1)兩個(gè)環(huán)境間如果有一些的一樣的包,那就沒(méi)有必要再下載了,這樣可以節(jié)約時(shí)間。2)每次部署都需要把老的部署環(huán)境給保留下來(lái),這樣方便在新舊環(huán)境下的切換。這兩點(diǎn)對(duì)于在生產(chǎn)環(huán)境下部署非常關(guān)鍵。(這需要環(huán)境內(nèi)所有軟件的綠色安裝才能更容易達(dá)到這個(gè)目標(biāo),因些,Unix/Linux會(huì)比Windows更容易做到這點(diǎn))
- 部署一致性事務(wù)問(wèn)題。有時(shí)候,我們需要同時(shí)部署若干臺(tái)服務(wù)器,比如:包A到機(jī)器MA,包B到機(jī)器MB,包C到機(jī)器MC,……(Web Service的SOA架構(gòu)),這些包之間有運(yùn)行依賴性和兼容性問(wèn)題,要么一次性全部完成,要么就全部失敗。回滾也是一樣的,這是一個(gè)部署事務(wù)或部署一致性的問(wèn)題。如何解決呢?
- 部署環(huán)境的版本控制問(wèn)題。前面說(shuō)過(guò),我們的一個(gè)環(huán)境就會(huì)和若干個(gè)包的版本耦合,環(huán)境必需管理要部署的包的版本。于是,當(dāng)你的部署越來(lái)越多的時(shí)候,各個(gè)環(huán)境的包的版本開(kāi)始出現(xiàn)混亂,各種依賴間的版本也會(huì)出現(xiàn)不統(tǒng)一的情況,也就是說(shuō),就算你有這樣的一個(gè)工具,在一個(gè)高速開(kāi)發(fā)的環(huán)境下,我們的部署環(huán)境的管理還是會(huì)出現(xiàn)很多混亂的情況,需要你不斷地統(tǒng)一大家的開(kāi)發(fā)、測(cè)試環(huán)境。
- 部署計(jì)劃。我們可能會(huì)有很多部署計(jì)劃,比如:設(shè)定定時(shí)部署,提升或降低部署優(yōu)先級(jí),部署事務(wù)定義,部署策略(如:先部署10%的機(jī)器,如果沒(méi)有問(wèn)題,再把剩下的系統(tǒng)部署了),熱切計(jì)劃和策略…… 等等 ,等等 。
- 部署的監(jiān)控和維護(hù)。任何軟件和系統(tǒng)都會(huì)有這樣的問(wèn)題,當(dāng)規(guī)模上去了以后,我們的自動(dòng)化部署系統(tǒng)的監(jiān)控和維護(hù)的復(fù)雜度并不亞于一個(gè)大型的互聯(lián)網(wǎng)應(yīng)用。
六、總結(jié)
這里只談一點(diǎn)自己的看法,從傳統(tǒng)的持續(xù)集成到面向大型軟件的持續(xù)部署,我們將系統(tǒng)所依賴的軟件環(huán)境和軟件包抽象為一致的實(shí)體納入到管理之中,并將運(yùn)維人員的工作真正的分?jǐn)偟介_(kāi)發(fā)人員身上。而云計(jì)算的出現(xiàn),使得計(jì)算機(jī)本身也可以自動(dòng)化的創(chuàng)建和回收,這樣環(huán)境管理的范疇將進(jìn)一步擴(kuò)充。相應(yīng)的,部署的能力和靈活性也是一次質(zhì)的飛躍,將再一次減輕運(yùn)維人員的工作壓力。
說(shuō)了這么多廢話,總結(jié)一下自己的觀點(diǎn),對(duì)于向大型軟件企業(yè)推銷基于持續(xù)集成的持續(xù)部署(交付)的哥們:
- 你就是在耍流氓,如果你不解決環(huán)境管理!!!
- 你就是在耍流氓,如果你不建立部署系統(tǒng)!!!
- 你就是在耍流氓,如果你不擴(kuò)展編譯系統(tǒng)!!!
- 你就是在耍流氓,如果你只是推銷小團(tuán)隊(duì)的實(shí)踐而不考慮改造大環(huán)境!!!
- 你就是個(gè)流氓,如果你只是不斷地告訴別人怎么做,自己卻從來(lái)不動(dòng)手寫(xiě)一個(gè)測(cè)試或建立一個(gè)持續(xù)集成環(huán)境!!!
最后,用Linus最經(jīng)典的話來(lái)結(jié)束本文——“ Talk is Cheap, Show me the Code!”

































