大綱
持續(xù)集成是個(gè)什么鬼
持續(xù)集成(CI, Continuous Integration)宴胧,是極限編程(XP, Extremely Programming)中的一種實(shí)踐。它主張每天提交若干次代碼阵谚,每次提交都經(jīng)過自動(dòng)化編譯和測(cè)試(最低要求為單元測(cè)試)后迷帜,集成到代碼庫的主干上去迹鹅。
有什么好處
通過持續(xù)集成担孔,我們可以:
- 快速發(fā)現(xiàn)問題(邏輯錯(cuò)誤江锨、bug等)吃警。并且糕篇,我們只需要在很小范圍內(nèi)的代碼修改中去尋找問題所在。
- 避免了在項(xiàng)目后期才做集成所造成的混亂酌心。當(dāng)大家在項(xiàng)目后期才進(jìn)行代碼/功能集成的話拌消,所產(chǎn)生的沖突必然增多。并且當(dāng)你發(fā)現(xiàn)問題或bug的時(shí)候,需要檢查代碼的范圍也會(huì)增大墩崩。這樣會(huì)提高集成的難度氓英,甚至難以集成。
- 當(dāng)一個(gè)bug導(dǎo)致必須回滾代碼時(shí)鹦筹,所損失的代碼量也大大地降低铝阐。
- 可以讓產(chǎn)品快速迭代,同時(shí)還能保持質(zhì)量铐拐。
- 不需要固定一個(gè)人去維護(hù)一段代碼徘键,功能的添加或者程序的修改都可以通過自動(dòng)化測(cè)試去保證其正確性。緩解了猿們?cè)谛薷牟皇煜さ拇a時(shí)那種害怕牽一發(fā)而動(dòng)全身的情況遍蟋。
需要什么
實(shí)現(xiàn)持續(xù)集成吹害,一般需要以下內(nèi)容:
1. 具有版本控制功能的代碼庫
例如:SVN, Git。相信現(xiàn)在的項(xiàng)目沒有不對(duì)代碼進(jìn)行版本管理的虚青,所以這方面內(nèi)容大家也應(yīng)該非常熟悉它呀。在這里不再詳述。
2. 構(gòu)建工具
在持續(xù)集成的過程中棒厘,需要對(duì)已存在的或者新提交的代碼進(jìn)行編譯纵穿、打包等操作。這樣绊谭,就需要構(gòu)建工具幫助構(gòu)建一個(gè)編譯環(huán)境政恍,并對(duì)代碼進(jìn)行編譯、集成达传、打包等操作篙耗。而構(gòu)建的方式越簡(jiǎn)單越好,最好是一句命令就可以啟動(dòng)構(gòu)建∠芨希現(xiàn)在宗弯,各種語言都有自己的構(gòu)建工具,例如Java中的Maven搂妻、Gradle蒙保、Ant,前端中的Grunt欲主、Gulp等等邓厕,好好利用這些工具,就能幫你完成這部分工作扁瓢。
3. 測(cè)試
測(cè)試是持續(xù)集成中重要的一環(huán)详恼。代碼提交前,需要在本地運(yùn)行單元測(cè)試引几,通過測(cè)試后再提交代碼昧互。構(gòu)建完成后,需要運(yùn)行全部測(cè)試(單元測(cè)試,功能測(cè)試敞掘,端到端測(cè)試)以確保產(chǎn)品質(zhì)量叽掘。如果有一個(gè)測(cè)試沒有通過,那么這次提交的代碼不能進(jìn)入主干玖雁;或者這次構(gòu)建的產(chǎn)物是一個(gè)失敗的構(gòu)建品更扁,不能用于發(fā)布。另外赫冬,由于持續(xù)集成依賴于這些測(cè)試去保證產(chǎn)品質(zhì)量疯潭,所以測(cè)試的覆蓋率要盡可能高。測(cè)試覆蓋率不夠高(包含代碼覆蓋率和功能覆蓋率)面殖,就無法充分反映代碼的變動(dòng)是否對(duì)系統(tǒng)帶來影響竖哩。而低覆蓋率的測(cè)試,壓根就無法保證產(chǎn)品質(zhì)量脊僚。當(dāng)上線的時(shí)候才發(fā)現(xiàn)問題就太遲了相叁。
4. 使用持續(xù)集成,通常還涉及到另外兩個(gè)概念:
4.1 持續(xù)交付
持續(xù)交付(Continuous Delivery)辽幌,指的是頻繁地將軟件的新版本增淹,交付給質(zhì)量團(tuán)隊(duì)或者用戶,以供評(píng)審乌企。如果評(píng)審?fù)ㄟ^虑润,代碼就進(jìn)入生產(chǎn)階段。
持續(xù)交付可以看作持續(xù)集成的下一步加酵。它強(qiáng)調(diào)的是拳喻,不管怎么更新,軟件是隨時(shí)隨地可以交付的猪腕。[1]
4.2 持續(xù)部署
持續(xù)部署(Continuous Deployment)冗澈,是持續(xù)交付的下一步,指的是代碼通過評(píng)審以后陋葡,自動(dòng)部署到生產(chǎn)環(huán)境亚亲。
持續(xù)部署的目標(biāo)是,代碼在任何時(shí)刻都是可部署的腐缤,可以進(jìn)入生產(chǎn)階段捌归。[2]
4.3 用一張圖描述兩者的關(guān)系
5. CI工具
CI工具的作用是將整個(gè)CI過程管理起來并自動(dòng)化,結(jié)果可視化岭粤。部分工具還結(jié)合了CD(持續(xù)交付)的功能∠鳎現(xiàn)在已經(jīng)有很多CI工具去滿足你不同的需求,例如Jenkins绍在,專為Github開源項(xiàng)目提供的Travis门扇,.Net用的CruiseControl.Net。他們各有特色偿渡,根據(jù)自己的需求選擇適合自己的工具即可臼寄。
怎樣做
好了,當(dāng)集齊以上內(nèi)容后溜宽,我們可以開始召喚神龍了吉拳。我們用一個(gè)例子來介紹一下一個(gè)典型流程是怎樣的。
例子背景描述
假設(shè)我們現(xiàn)在有一個(gè)產(chǎn)品P适揉,以war包形式發(fā)布留攒,由三個(gè)模塊module A, module B, module C構(gòu)建而成。三個(gè)模塊的關(guān)系為:A和B為獨(dú)立模塊提供不同功能嫉嘀。C依賴A和B炼邀,然后構(gòu)成產(chǎn)品P。我們使用了Git作為我們代碼庫的版本管理工具剪侮,用Java進(jìn)行開發(fā)拭宁,maven作為我們的構(gòu)建工具。在每個(gè)模塊里瓣俯,有我們基于JUnit寫的單元測(cè)試代碼杰标。獨(dú)立于三個(gè)模塊外,有一塊代碼彩匕,也是基于JUnit寫的腔剂,作為我們的功能測(cè)試代碼(集成測(cè)試)。
集成代碼
當(dāng)我們完成開發(fā)工作驼仪,需要提交代碼到代碼庫前掸犬,我們至少需要在本地跑一次單元測(cè)試,在保證全部測(cè)試通過后绪爸,才可以將代碼提交至我們的代碼庫Git上面去登渣。例如,在我們上面描術(shù)的項(xiàng)目中毡泻,我對(duì)module A的代碼進(jìn)行了修改胜茧,那我最起碼得在本地運(yùn)行一次mvn test(執(zhí)行Maven命令,test代表將會(huì)執(zhí)行到maven default生命周期中從validate到test階段)仇味, 執(zhí)行成功后呻顽,我才會(huì)將代碼commit and push到遠(yuǎn)程Git庫上去。要做到這樣效果的話丹墨,就需要保證單元測(cè)試代碼也同步完成廊遍。而在極限編程中(XP),人們比較傾向于測(cè)試驅(qū)動(dòng)開發(fā)(TDD, Test-driven Development)的實(shí)踐贩挣,通過提前寫好自動(dòng)化的單元測(cè)試喉前,保證好每一步功能開發(fā)的質(zhì)量没酣。
自動(dòng)構(gòu)建
通過CI工具,可以設(shè)置一個(gè)勾子卵迂,當(dāng)代碼提交后觸發(fā)相應(yīng)構(gòu)建裕便。例如,我們提交了module A的代碼時(shí)见咒,Jenkins會(huì)掃描到我們這次提交偿衰,勾子觸發(fā)module A的構(gòu)建。這個(gè)過程會(huì)做如下操作:
- Jenkins調(diào)用Git插件改览,從Git庫上下載最新代碼下翎;
- Jenkins調(diào)用Maven插件,執(zhí)行Maven命令(一般為mvn install宝当,如果需要上傳至遠(yuǎn)端Maven庫视事,也可以執(zhí)行mvn deploy)對(duì)該模塊進(jìn)行構(gòu)建。經(jīng)過編譯庆揩、通過單元測(cè)試后郑口,便可以打包并安裝到本地Maven庫,以供其它依賴所用盾鳞。這次構(gòu)建成功犬性,意味module A在模塊自身的單元測(cè)試范圍內(nèi)是正常的。
- 因?yàn)閙odule A是包含在產(chǎn)品P里面腾仅,所以乒裆,也需要回歸一產(chǎn)品功能測(cè)試。由于module C依賴A推励,并構(gòu)建成產(chǎn)品鹤耍。所以在CI工具里面,我們需要配置好在module A構(gòu)建成功后验辞,自動(dòng)觸發(fā)module C的構(gòu)建稿黄,經(jīng)過類似步驟1、2這樣的構(gòu)建后跌造,最終會(huì)生成產(chǎn)品P的war包杆怕。而C的構(gòu)建成功,只代表著通過了module C自身的單元測(cè)試壳贪,還不能對(duì)生成的war包進(jìn)行功能測(cè)試陵珍。然后就要看我們下一步的工作--自動(dòng)部署了。
自動(dòng)部署
在功能測(cè)試之前违施,我們需要在CI工具里配置一項(xiàng)任務(wù)互纯,用于將最新構(gòu)建出來的產(chǎn)品包部署到測(cè)試工環(huán)境中去。這個(gè)任務(wù)由產(chǎn)品構(gòu)建任務(wù)成功而被觸發(fā)磕蒲,而部署方式根據(jù)不同使用方式及不同的實(shí)際情況而多種多樣留潦。例如通過腳本將新構(gòu)建的war包上傳至指定位置只盹,等待web容器自動(dòng)掃描及部署。能或者產(chǎn)品有自己的安裝腳本兔院,我們?cè)谌蝿?wù)中配置好運(yùn)行安裝腳本殖卑,就可以自動(dòng)將產(chǎn)品部署到指定的測(cè)試環(huán)境中去。
功能測(cè)試(集成測(cè)試)
當(dāng)部署成功后秆乳,真正的功能測(cè)試就可以開始了。一般情況下钻哩,我們可以獨(dú)立出一塊代碼屹堰,基于JUnit編寫好我們的功能測(cè)試代碼(JUnit是作為測(cè)試的入口以及基本測(cè)試框架。如果你的需求比較復(fù)雜街氢,那你完全可以將其它三方框架與JUnit集成使用)扯键。功能測(cè)試過程和構(gòu)建過程非常相似,均是依賴Git和Maven去完成:
- Jenkins調(diào)用Git插件珊肃,從Git庫上下載最新代碼荣刑;
- Jenkins調(diào)用Maven插件,執(zhí)行Maven命令:mvn clean test伦乔。
區(qū)別在于功能測(cè)試階段厉亏,Maven只執(zhí)行到default生成周期的test階段,不會(huì)執(zhí)行后面的package和install烈和。因?yàn)樗恍枰狹aven幫忙運(yùn)行測(cè)試代碼即可爱只,它本身沒有什么可以構(gòu)建的。
P.S.
如果還需要更復(fù)雜的端到端測(cè)試的話招刹,可能就需要準(zhǔn)備更復(fù)雜的部署腳本恬试,或者預(yù)先準(zhǔn)備好整套端到端測(cè)試環(huán)境,后面只需要部署好war包即可疯暑。但無論怎樣训柴,最終原來理還是相同。
交付
當(dāng)新提交代碼后構(gòu)建出來的產(chǎn)品包妇拯,通過了各種各樣殘酷的測(cè)試后幻馁,就說明這個(gè)包是穩(wěn)定的,能達(dá)到基本交付條件的(前提是自動(dòng)化測(cè)試的覆蓋率足夠高越锈,當(dāng)然宣赔,有一些極端的情況需要人工測(cè)試的另說)。那么瞪浸,我們就可以將這個(gè)包放到指定目錄作為交付品儒将,供其它測(cè)試團(tuán)隊(duì)獲取并進(jìn)行進(jìn)一步的測(cè)試,甚至供生產(chǎn)環(huán)境部署使用对蒲。
總結(jié)
- 持續(xù)集成作為極限編程中的一個(gè)實(shí)踐钩蚊,現(xiàn)在已被很多公司使用贡翘。但是,使用持續(xù)集成砰逻,并不是說你得接受極限編程的全部東西鸣驱。相反,它可以獨(dú)立開來蝠咆,與其它實(shí)踐結(jié)合使用踊东。
- 持續(xù)集成是敏捷開發(fā)中快速迭代的重要保證。
- 自動(dòng)化測(cè)試是持續(xù)集成中重要一環(huán)刚操,要真正用好持續(xù)集成闸翅,就要盡量提高自動(dòng)化測(cè)試的覆蓋率。