顧利軍,邱敏明
(上海海事大學(xué)信息工程學(xué)院,上海 201804)
在現(xiàn)代軟件開發(fā)過程中,很多的軟件項(xiàng)目都是由不同的開發(fā)人員負(fù)責(zé),每個(gè)開發(fā)成員負(fù)責(zé)不同的功能模塊,在項(xiàng)目開發(fā)結(jié)束之前,每個(gè)開發(fā)人員只關(guān)心自己的工作,卻不了解項(xiàng)目的整體情況,自己開發(fā)的模塊可以單獨(dú)的運(yùn)行工作,到最后項(xiàng)目集成為最終產(chǎn)品的時(shí)候,會(huì)產(chǎn)生一些意料不到的問題,有的很難去修復(fù),有的不得不去重寫幾周或者幾個(gè)月之前寫的代碼,甚至有的開發(fā)人員開發(fā)的模塊會(huì)被全部否定而從零開始,最嚴(yán)重的情況將導(dǎo)致整個(gè)項(xiàng)目的失敗。把項(xiàng)目的集成放到開發(fā)周期的最后階段,將會(huì)花費(fèi)大量的時(shí)間去進(jìn)行集成工作,嚴(yán)重的bug處理會(huì)涉及到很多分支,給開發(fā)人員的修復(fù)工作也帶來了很多麻煩,同時(shí)也充滿了風(fēng)險(xiǎn)和危險(xiǎn),并且往往會(huì)導(dǎo)致延遲交付,造成額外開銷的后果,引起客戶的不滿。為了解決這樣的問題,人們進(jìn)行了很多的探索,提出了“早集成,常集成”的集成策略,從最開始的分階段構(gòu)建,到之后發(fā)展的日構(gòu)建,再到目前的持續(xù)集成(Continuous Integration,CI),持續(xù)集成的開發(fā)方式從簡單的形式上講就是一個(gè)能夠監(jiān)控版本控制系統(tǒng)變化的工具,只要檢測到程序有新的變更的時(shí)候,這個(gè)工具就會(huì)執(zhí)行一次構(gòu)建,自動(dòng)編譯和測試你的程序,并且可以隨時(shí)打包成可以交付的產(chǎn)品。如果在構(gòu)建的過程中出現(xiàn)了問題,就會(huì)馬上通知開發(fā)人員,開發(fā)人員就會(huì)停下目前的開發(fā)工作,著手去解決構(gòu)建過程中出現(xiàn)的問題,防止到項(xiàng)目后期出現(xiàn)難以修復(fù)的難題,這樣的持續(xù)集成可以保證軟件在開發(fā)的過程中始終處于一個(gè)“健康”的狀態(tài)。持續(xù)集成是一些基本的實(shí)踐。它不是軟件開發(fā)中最耀眼的工作,但持續(xù)集成對于如今的復(fù)雜項(xiàng)目來說是至關(guān)重要的,甚至項(xiàng)目負(fù)責(zé)人會(huì)把CI看成是軟件開發(fā)的中心工作,因?yàn)樗ㄟ^每次代碼的變更執(zhí)行構(gòu)建,構(gòu)建的過程保證了軟件的健康。
本文基于日常多項(xiàng)目的實(shí)踐開發(fā)管理經(jīng)驗(yàn),持續(xù)集成理論的研究以及對目前常見的持續(xù)集成工具的實(shí)踐探索,提出了一套基于Git的持續(xù)集成方案,搭建自己的CI系統(tǒng),從而解決項(xiàng)目開發(fā)中的集成難題、增強(qiáng)項(xiàng)目的可見性。
持續(xù)集成的概念最早出現(xiàn)在極限編程作者M(jìn)artin Fowler的一篇文章《持續(xù)集成》中,持續(xù)集成是一種軟件開發(fā)實(shí)踐,即開發(fā)人員需要經(jīng)常性地對他們的工作進(jìn)行集成,每個(gè)開發(fā)人員每天至少進(jìn)行一次集成工作,這樣每天就會(huì)發(fā)生很多次的集成。每次集成過程都會(huì)執(zhí)行并且執(zhí)行相關(guān)的自動(dòng)測試,這樣就能及時(shí)地發(fā)現(xiàn)軟件中存在的問題。
持續(xù)集成的價(jià)值很高,一個(gè)完善的持續(xù)集成系統(tǒng)能夠減少項(xiàng)目風(fēng)險(xiǎn),一天進(jìn)行多次集成,有利于檢查項(xiàng)目缺陷,及時(shí)的修復(fù)可以保證軟件的健康;能夠減少項(xiàng)目活動(dòng)中代碼編譯、測試、打包、部署的重復(fù)工程,每次構(gòu)建都執(zhí)行相同的過程,減少重復(fù)勞動(dòng);能夠保證項(xiàng)目在任何時(shí)間都可以發(fā)布可部署的軟件,每次小的代碼改動(dòng)都會(huì)及時(shí)地與項(xiàng)目中其他的代碼進(jìn)行集成,及時(shí)發(fā)現(xiàn)問題及時(shí)修復(fù),不采用CI的軟件開發(fā)項(xiàng)目可能會(huì)等到最后交付的時(shí)候才會(huì)進(jìn)行集成及測試,可能會(huì)出現(xiàn)不可修復(fù)的缺陷,導(dǎo)致延遲發(fā)布或項(xiàng)目失??;能夠增強(qiáng)項(xiàng)目的可見性,持續(xù)集成使得項(xiàng)目能夠隨時(shí)保證最新的數(shù)據(jù)來進(jìn)行決策,而沒有CI的項(xiàng)目通常需要開發(fā)人員手動(dòng)收集信息,增加了工作負(fù)擔(dān)浪費(fèi)了很多寶貴的時(shí)間,使得項(xiàng)目進(jìn)程緩慢;最后持續(xù)集成還可以在團(tuán)隊(duì)中建立強(qiáng)大的產(chǎn)品信心,因?yàn)槊恳淮螛?gòu)建的結(jié)果都能讓開發(fā)人員對項(xiàng)目的整體結(jié)構(gòu)做到心里有數(shù),如果沒有持續(xù)集成,開發(fā)人員就不知道修改后的代碼會(huì)不會(huì)對整個(gè)項(xiàng)目造成影響,CI的及時(shí)構(gòu)建結(jié)果及時(shí)通知,讓開發(fā)人員在開發(fā)的過程中更有信心開發(fā)出有品質(zhì)有保障的軟件產(chǎn)品。
目前軟件行業(yè)中使用較多的持續(xù)集成工具是Jenkins,Jenkins最開始被稱Hudson,是一個(gè)用Java語言編寫的開源的持續(xù)集成工具,被應(yīng)用于各種規(guī)模的團(tuán)隊(duì)用于各種語言和技術(shù)的項(xiàng)目中,例如Java、PHP、C#等,而且Jenkins有著豐富的開源插件庫,使得Jenkins的構(gòu)建功能比較健全。但是Jenkins也有很多不足的地方,首先是界面非常的不友好,頁面的有些部分要靠插件生成,使用不夠方便,經(jīng)常會(huì)讓人對插件的使用非常困惑,不知道其對應(yīng)的頁面部分在什么地方,這也許就是Jenkins當(dāng)初在開發(fā)的時(shí)候所采用了Jelly頁面技術(shù)所帶來的局限性吧,同時(shí)插件也沒有中文解釋,只有部分英文資料,對于國內(nèi)的開發(fā)者來說使用有些困難,若從更深的角度講,在擴(kuò)展Jenkins的時(shí)候盡可能的使用插件會(huì)是更好的選擇,可是插件越多Jenkins的性能損耗就越大,這也是Jenkins陷入了難以走出的循環(huán)泥潭,開發(fā)者需要選擇性能還是選擇擴(kuò)展的風(fēng)險(xiǎn)。另外Jenkins的build流程不夠清晰,甚至國內(nèi)有些軟件公司自己重寫了持續(xù)集成的管理頁面來調(diào)度Jenkins的build計(jì)劃,這已經(jīng)足夠說明了使用流程不夠清晰這一點(diǎn)。
基于Jenkins的頁面不夠友好,插件使用不夠靈活方便,插件的增多帶來性能上的損耗以及構(gòu)建流程不夠清晰等這些缺陷,我們將開始設(shè)計(jì)自己的CI系統(tǒng)方案,最終實(shí)現(xiàn)方便使用靈活配置且能夠滿足日常構(gòu)建的持續(xù)構(gòu)建系統(tǒng)。
圖1 持續(xù)構(gòu)建系統(tǒng)的組成部分以及構(gòu)建流程
持續(xù)集成系統(tǒng)的基本要素有開發(fā)人員、Git版本控制系統(tǒng)、持續(xù)集成構(gòu)建系統(tǒng)、多語言構(gòu)建工具以及構(gòu)建結(jié)果的反饋組成,這些組成部分之間相互配合共同完成從版本變化到自動(dòng)構(gòu)建,再到構(gòu)建結(jié)果的反饋,從而形成完整的構(gòu)建過程。持續(xù)集成的應(yīng)用場景開始于開發(fā)人員向版本控制庫提交變更代碼,由版本控制庫Git的Hook腳本向持續(xù)集成系統(tǒng)發(fā)出變更通知,然后持續(xù)集成系統(tǒng)從Git版本庫中檢出項(xiàng)目代碼的最新版本,繼而觸發(fā)持續(xù)集成系統(tǒng)進(jìn)行構(gòu)建。圖1展示了持續(xù)集成系統(tǒng)的基本組成部分和構(gòu)建流程。
基于持續(xù)集成的基本理論以及多種語言項(xiàng)目的探究與實(shí)踐,提出了以下的持續(xù)構(gòu)建系統(tǒng)的設(shè)計(jì)方案,如圖2所示。
持續(xù)集成構(gòu)建系統(tǒng)中,我們選擇Git作為版本控制系統(tǒng),Git是近年來最流行的軟件之一,它的去中心化架構(gòu)以及源碼變更交換的速度被很多開發(fā)者青睞。在Git的眾多優(yōu)點(diǎn)中,最有用的一點(diǎn)莫過于它的靈活性。通過“hooks”(鉤子)系統(tǒng),開發(fā)者可以指定Git在不同事件、不同動(dòng)作下執(zhí)行特定的腳本。該系統(tǒng)中主要利用了Git中的鉤子系統(tǒng),當(dāng)開發(fā)者向Git倉庫提交變更的代碼的時(shí)候,觸發(fā)Git的hooks系統(tǒng),執(zhí)行鉤子腳本,繼而引發(fā)該項(xiàng)目進(jìn)行構(gòu)建。
圖2 持續(xù)集成構(gòu)建系統(tǒng)
如果是第一次進(jìn)行構(gòu)建時(shí),要執(zhí)行創(chuàng)建構(gòu)建項(xiàng)目,進(jìn)入到構(gòu)建配置頁面,進(jìn)行項(xiàng)目配置,包括項(xiàng)目名稱、項(xiàng)目類型、構(gòu)建工具、是否清掉舊的構(gòu)建歷史、是否測試、是否部署等等配置選項(xiàng)。構(gòu)建配置好后點(diǎn)擊保存,數(shù)據(jù)提交到后臺后將會(huì)被序列化保存到xml文件中。一切工作準(zhǔn)備好以后,點(diǎn)擊構(gòu)建,持續(xù)集成系統(tǒng)就會(huì)立刻執(zhí)行構(gòu)建流程,最后將構(gòu)建的結(jié)果顯示到集成構(gòu)建系統(tǒng)的Web頁面上,同時(shí)本地服務(wù)器也會(huì)保存構(gòu)建項(xiàng)目的最近一次的構(gòu)建結(jié)果,這樣每個(gè)開發(fā)人員都可以查看集成構(gòu)建的信息。
(1)構(gòu)建項(xiàng)目配置中項(xiàng)目類型的自動(dòng)檢出
傳統(tǒng)的CI服務(wù)器中構(gòu)建項(xiàng)目的配置頁面無法檢測出項(xiàng)目類型,都需要手動(dòng)配置,而且在配置的時(shí)候都不清楚本地環(huán)境中是否支持該種類型項(xiàng)目的構(gòu)建,即本地是否安裝了相應(yīng)的構(gòu)建工具。我們設(shè)計(jì)的持續(xù)構(gòu)建系統(tǒng)在設(shè)計(jì)之初就對現(xiàn)有的構(gòu)建工具做了大量的調(diào)研,將目前軟件行業(yè)常用的構(gòu)建工具進(jìn)行了分析,發(fā)現(xiàn)不同的構(gòu)建工具都有不同的構(gòu)建配置文件,這些配置文件都有固定的命名規(guī)則或后綴名,根據(jù)命名或后綴名,我們就可以判斷該項(xiàng)目是有哪種構(gòu)建工具構(gòu)建的。如果本地環(huán)境中沒有安裝構(gòu)建工具,系統(tǒng)也會(huì)有相應(yīng)的提醒功能。圖3展示了項(xiàng)目類型自動(dòng)檢出的簡單流程。
圖3 項(xiàng)目類型自動(dòng)檢測
(2)調(diào)用構(gòu)建工具執(zhí)行構(gòu)建
構(gòu)建工具都是通過執(zhí)行shell命令執(zhí)行相應(yīng)的構(gòu)建操作,該系統(tǒng)是基于Java語言開發(fā)的,因此我們調(diào)查研究了如何在Java環(huán)境里執(zhí)行shell命令,發(fā)現(xiàn)Java API中封裝了一個(gè)Runtime的類,該類封裝了操作系統(tǒng)的運(yùn)行時(shí)的環(huán)境,每個(gè)Java程序都有一個(gè)Runtime類實(shí)例,使應(yīng)用程序能夠與其運(yùn)行的環(huán)境相連接,其API中有一個(gè)方法exec(String command),該方法能夠在單獨(dú)的進(jìn)程中執(zhí)行指定的字符串命令。我們通過讀取構(gòu)建配置里的配置參數(shù)將其轉(zhuǎn)換成相應(yīng)構(gòu)建工具中的構(gòu)建命令,后臺程序中將路徑信息path與構(gòu)建命令拼接作為參數(shù),將其傳入到exec方法,即可執(zhí)行構(gòu)建過程,如String command=path+"mvn compile";Runtime.get-Runtime().exec(command);便可完成對MAVEN類型的項(xiàng)目進(jìn)行編譯構(gòu)建,構(gòu)建的結(jié)果會(huì)返回到Web頁面進(jìn)行展示,同時(shí)也會(huì)將構(gòu)建結(jié)果序列化到本地進(jìn)行保存,保證其他人都可以查看集成構(gòu)建信息。
(3)本地部署與遠(yuǎn)程部署
由于不同語言的項(xiàng)目部署情況也不同,而且部署在自動(dòng)構(gòu)建部分屬于可選部分,目前我們在配置頁面提供了一個(gè)Execute shell的輸入文本框,支持編寫自動(dòng)部署的腳本,待構(gòu)建完成后執(zhí)行自動(dòng)部署的操作,如果輸入內(nèi)容為空,默認(rèn)不執(zhí)行部署。
持續(xù)構(gòu)建系統(tǒng)采用Java Web開發(fā)技術(shù),整體架構(gòu)為JSP+Bootstrap+Spring MVC+Spring技術(shù),運(yùn)行服務(wù)器為Tomcat。首先點(diǎn)擊創(chuàng)建一個(gè)構(gòu)建項(xiàng)目,設(shè)置項(xiàng)目構(gòu)建任務(wù)名稱,配置參數(shù)等,然后點(diǎn)擊保存、構(gòu)建,以Maven搭建的一個(gè)Web項(xiàng)目為例,構(gòu)建結(jié)果如圖4所示。
圖4 Maven類型項(xiàng)目的構(gòu)建結(jié)果
最后,構(gòu)建的結(jié)果是BUILD SUCCESS,實(shí)現(xiàn)預(yù)期的構(gòu)建效果。
本文對持續(xù)集成的理論進(jìn)行了研究,分析了持續(xù)集成的基本組成部分及構(gòu)建流程,并對目前流行的持續(xù)集成工具Jenkins進(jìn)行了介紹,分析了不足的地方,在此研究學(xué)習(xí)的基礎(chǔ)之上,提出了基于Git的持續(xù)集成方案,設(shè)計(jì)并開發(fā)了集成構(gòu)建系統(tǒng),該系統(tǒng)集成了前端模塊化打包方案webpack,并集成了多種語言的打包工具,能夠支持前后端打包一體化以及多語言項(xiàng)目的構(gòu)建,省去了Jenkins繁瑣的插件配置,優(yōu)化了構(gòu)建流程。此系統(tǒng)的設(shè)計(jì)開發(fā)由于時(shí)間有限,并考慮到自動(dòng)化測試復(fù)雜性,以及不同語言項(xiàng)目的部署也沒有統(tǒng)一的標(biāo)準(zhǔn),我們提出的持續(xù)集成方案在這兩個(gè)方面沒有做太多的研究,提供了支持編寫部署腳本來進(jìn)行部署,后續(xù)會(huì)對不同語言項(xiàng)目的部署進(jìn)行分類設(shè)計(jì)不同的部署方案來代替部署腳本。