張兆晨,羅鐵堅
(中國科學院大學 計算機與控制學院, 北京 101408) (2017年3月10日收稿; 2017年5月16日收修改稿)
隨著軟件項目規(guī)模逐漸擴大,市場需求不穩(wěn)定,軟件產(chǎn)品需要快速、持續(xù)、高質(zhì)量的實現(xiàn)軟件交付,從而更好地應對市場及用戶的需求。軟件構建是軟件生命周期的關鍵環(huán)節(jié),包含從源碼編譯到產(chǎn)品交付的整個過程。但缺乏規(guī)范化和標準化的軟件構建流程往往成為影響軟件交付的瓶頸所在,敏捷開發(fā)和DevOps思想理念的產(chǎn)生很好地解決了這一問題。
以迭代開發(fā)為特征的敏捷開發(fā),靈活應對不斷變化的需求,大大提高了開發(fā)效率。持續(xù)集成構建作為敏捷開發(fā)中的一項基本實踐[1],將開發(fā)周期的集成階段放到日常中,開發(fā)者每天都會集成代碼,通過自動化的構建和測試盡早定位軟件缺陷,降低開發(fā)風險。DevOps思想[2]是一組流程、技術和工具的統(tǒng)稱,促進了開發(fā)、技術運營和質(zhì)量保障部門之間的溝通協(xié)作,提供端到端的測試、交付和發(fā)布工作流。
各軟件企業(yè)和相關研究人員對迭代式開發(fā)、持續(xù)集成等實踐不斷地研究嘗試,促進了軟件開發(fā)模式的發(fā)展,開發(fā)效率也在不斷提升。雖然相關研究實踐能夠一定程度提高開發(fā)效率,但硬件資源的分配與限制是影響開發(fā)周期的另一個因素。本文的研究目的是將持續(xù)集成構建和容器技術相結(jié)合,應用在項目開發(fā)中,利用容器技術對硬件資源的分配特性,研究如何標準化環(huán)境從而重復、循環(huán)地交付軟件價值。
本文總結(jié)國內(nèi)外研究人員對敏捷開發(fā)中的持續(xù)集成實踐和虛擬化容器技術的相關研究工作,分析傳統(tǒng)持續(xù)集成系統(tǒng)的基本組成及其不足。提出將Docker技術應用在開發(fā)環(huán)節(jié)中,設計并實現(xiàn)一個容器化的持續(xù)集成構建系統(tǒng)(簡稱CCI),通過設計系統(tǒng)的一體化工作流方案,使各團隊成員之間相互協(xié)作,優(yōu)化軟件開發(fā)流程。最后通過具體應用對系統(tǒng)平臺進行分析和驗證。
不少國內(nèi)外研究人員對敏捷開發(fā)中的迭代式開發(fā)和持續(xù)集成等實踐展開深入的研究,促進了軟件開發(fā)模式的轉(zhuǎn)變和發(fā)展。
Lai和Leu[3]總結(jié)敏捷開發(fā)和持續(xù)集成的優(yōu)勢,提出一種持續(xù)集成過程以有效降低Web應用的開發(fā)風險。Meyer[4]論述實現(xiàn)一個持續(xù)集成系統(tǒng)的關鍵要素包括版本控制系統(tǒng)和持續(xù)集成服務器。使用版本控制系統(tǒng),改變了開發(fā)人員之間的交流和協(xié)作模式,使不同團隊之間可以更好地相互合作[5]。Seth和Khare[6]討論版本控制系統(tǒng)在軟件開發(fā)中的重要作用和分布式版本控制系統(tǒng)Git的優(yōu)勢。
Jenkins是一個開源的持續(xù)集成服務器,支持分布式構建。Rai等[7]討論Jenkins的背景和發(fā)展歷史,提供安裝和配置Jenkins的方法,同時對比分析其他相關持續(xù)集成工具的優(yōu)缺點。Jenkins不僅提供豐富的插件支持,有可擴展的對象模型,這一特性使得可以定制化地開發(fā)插件以擴展Jenkins的功能。另外,Jenkins工具除可以實現(xiàn)持續(xù)集成外,還可以實現(xiàn)持續(xù)交付。Armenise[8]提出Jenkins不僅能夠提供自動化的構建實現(xiàn)持續(xù)集成,還可以通過豐富的插件機制實現(xiàn)產(chǎn)品的發(fā)布、部署,從而實現(xiàn)軟件的持續(xù)交付。
可重復性實驗在科學領域中得到越來越多的關注[9],為解決實驗環(huán)境的可復用性和實驗數(shù)據(jù)的可再現(xiàn)性,出現(xiàn)了兩個主流的解決方案:工作流(Workflow)軟件和虛擬機[10-11]。但是工作流軟件往往不能覆蓋每個研究人員的需求,虛擬機相比工作流軟件而言是一個好的方法,但是它也有自身的一些限制。虛擬機虛擬出一個完整的操作系統(tǒng)實現(xiàn)環(huán)境的隔離,并在此基礎上配置相應的環(huán)境,系統(tǒng)維護性和靈活性比較差且不易擴展[10],不容易實現(xiàn)環(huán)境的可復現(xiàn)性。
輕量容器技術Docker的出現(xiàn),引起業(yè)界很大的反響,受到廣泛關注。它是一個開源的應用容器引擎,與傳統(tǒng)虛擬機的主要不同之處在于它共享宿主機的Linux內(nèi)核,啟動快、占用資源少。Docker容器的虛擬化是建立在主機的操作系統(tǒng)之上的,是系統(tǒng)級的虛擬化而不是針對硬件虛擬化,虛擬鏡像的移植性比傳統(tǒng)基于硬件的虛擬化移植性要強。這使它比傳統(tǒng)虛擬機輕量且具有高性能[12],與傳統(tǒng)虛擬機分鐘級的啟動速度相比,Docker啟動速度達到秒級。傳統(tǒng)的計算機能夠同時運行幾個虛擬機,但卻能夠同時運行上百個甚至更多Docker容器。Boettiger[10]論述Docker在可重復性研究中發(fā)揮的重要作用,它解決了計算重復性所面臨的技術挑戰(zhàn)。在集群、云環(huán)境等分布式系統(tǒng)中使用Docker技術可以使底層應用環(huán)境保持一致性,增加系統(tǒng)的可移植性[13]。另外,Docker在持續(xù)集成和軟件測試領域應用也很廣泛。例如,與性能測試工具結(jié)合,保證實驗環(huán)境的可復用性和數(shù)據(jù)的可再現(xiàn)性[14]。
基于容器的虛擬化技術,利用增量的方式生成鏡像環(huán)境,靈活性更高。根據(jù)Docker的特性,將其應用到軟件項目的生命周期(開發(fā)、測試、部署、維護),能有效地提高各個環(huán)節(jié)的效率。
持續(xù)集成(CI)系統(tǒng)的核心價值在于自動化,服務器的每次集成都是通過自動化的構建來驗證,包括自動編譯、測試、部署等。一個傳統(tǒng)的持續(xù)集成系統(tǒng)通常有3個組成部分(如圖1):實現(xiàn)代碼托管的版本控制系統(tǒng),執(zhí)行集成構建的持續(xù)集成服務器和自動構建編譯的工具。
圖1 傳統(tǒng)持續(xù)集成系統(tǒng)基本組成Fig.1 Basic components of traditional CI system
版本控制系統(tǒng)在項目不同團隊開發(fā)人員之間維護一個統(tǒng)一的代碼庫,實現(xiàn)項目的協(xié)同開發(fā),保證項目的源代碼處于有序的管理中,方便項目開發(fā)人員隨時獲取和提交變更。在持續(xù)集成系統(tǒng)中,版本控制系統(tǒng)起到的主要作用是源代碼的管理。
持續(xù)集成服務器在整個系統(tǒng)中起到至關重要的作用,連通各個部分的基本組件,是實現(xiàn)過程自動化的關鍵。當開發(fā)者提交代碼到版本庫中,服務器輪詢到變更,便會觸發(fā)自動構建工具,進入到后續(xù)的自動化構建過程。這一自動化過程使開發(fā)人員能夠快速收到Bug和相關故障的反饋通知,快速修復軟件質(zhì)量的缺陷,從而及時交付軟件價值。
在自動化編譯構建工具引入之前,項目人員需要進行一系列手動的過程,下載源碼、編譯、測試、部署。手動構建不僅浪費大量的人力進行重復性的工作,而且容易引入人為因素導致的錯誤。
自動化的編譯構建工具對于持續(xù)集成系統(tǒng)是不可或缺的,是實現(xiàn)系統(tǒng)自動化的前提。持續(xù)集成服務器集成構建工具完成構建任務,實現(xiàn)項目的自動化構建、測試、部署流程。
在傳統(tǒng)的持續(xù)集成系統(tǒng)中,基本解放人力,實現(xiàn)了自動化,但仍然存在一些不足之處。
1)環(huán)境的搭建、維護比較復雜且耗時。持續(xù)集成環(huán)境、開發(fā)測試環(huán)境的搭建需要在本地完成種類繁多的軟件安裝和配置,是一件非常復雜且耗時的工作,且環(huán)境移植性比較差。
2)開發(fā)環(huán)境、測試環(huán)境和生產(chǎn)環(huán)境不一致。維護不一致的環(huán)境,影響軟件的交付效率。另一方面容易導致在生產(chǎn)環(huán)境中暴露出開發(fā)測試環(huán)境沒有出現(xiàn)的錯誤。
3)集成構建過程觸發(fā)單元測試、集成測試,但沒有考慮其他方面的測試,不方便測試人員開展工作。
在本文的容器化實現(xiàn)中,采用Github作為版本控制系統(tǒng),Jenkins工具作為持續(xù)集成服務器。與傳統(tǒng)集成平臺相比,將安裝在宿主機本地或虛擬機中的集成環(huán)境進行容器化,將集成服務器主從節(jié)點分別放在不同的容器環(huán)境中。利用Jenkins的分布式特性,將Jenkins主服務器(Master節(jié)點)和Jenkins從服務器(Slave節(jié)點)分別放在Docker容器中,CCI系統(tǒng)架構如圖2所示。
Jenkins主機器負責任務(job)的配置及相關調(diào)配,從版本控制系統(tǒng)中獲取代碼變更來觸發(fā)構建,根據(jù)對構建任務的相關配置,將任務分發(fā)到持續(xù)集成從機器中執(zhí)行構建??梢愿鶕?jù)項目需要,配置不同的從機器環(huán)境執(zhí)行構建,保證任務構建測試環(huán)境的可擴展性。將配置好的集成構建環(huán)境生成鏡像保存到鏡像庫中,保證構建環(huán)境的快速可再現(xiàn)性和可移植性。
3.2.1 Jenkins服務容器化
在CCI系統(tǒng)的實現(xiàn)中,關鍵是將集成構建環(huán)境進行容器化,利用Docker容器技術快速生成軟件構建環(huán)境。為了生成Jenkins容器,Docker提供兩種創(chuàng)建方式:
1)利用現(xiàn)有的鏡像庫生成,使用docker run命令快速創(chuàng)建Jenkins容器。在Docker命令中將容器內(nèi)部的8080端口重定向到宿主機的8080端口,可以通過主機端口訪問容器內(nèi)部Jenkins服務。
圖2 CCI系統(tǒng)架構Fig.2 Architecture of CCI system
dockerrun-d-p8080:8080-namezzcjenkinsjenkins:latest
運行docker run命令時,如果本地沒有鏡像,則會默認從遠端鏡像倉庫中拉取鏡像,將鏡像緩存到本地,再啟動基于鏡像創(chuàng)建的容器。
2)使用Dockerfile自定義的構建Jenkins鏡像環(huán)境。Dockerfile包含創(chuàng)建鏡像所需要的全部指令,使用docker build命令來執(zhí)行命令并創(chuàng)建鏡像,自底向上地打包軟件及其環(huán)境。
兩種方式各有優(yōu)缺點。利用現(xiàn)有鏡像庫方式生成容器,能夠快速獲取現(xiàn)有鏡像生成可運行的容器環(huán)境,但是不方便構建自定義的環(huán)境。而通過Dockerfile的方式,可以減少鏡像和容器的創(chuàng)建過程以簡化部署,方便系統(tǒng)的升級。另一方面,在具體項目應用中,開發(fā)測試人員和運維人員可以利用Dockerfile文檔來溝通軟件項目的執(zhí)行環(huán)境,促進了不同開發(fā)團隊之間環(huán)境的統(tǒng)一。
使用Dockerfile構建擁有更高的靈活性和可維護性,本文的Jenkins容器環(huán)境的實現(xiàn)利用Dockerfile文檔自定義構建Jenkins環(huán)境。在Dockerfile文檔中實現(xiàn)Jenkins及其插件、版本控制服務git及其他相關依賴軟件的安裝配置。
Jenkins容器構建完成之后,需要進行相關任務配置以運行構建任務。為了解決Docker中數(shù)據(jù)的安全性問題,保證項目數(shù)據(jù)的安全,我們不在容器中存放任何與項目相關的數(shù)據(jù)。Jenkins在運行構建時,相關配置和數(shù)據(jù)都放在$JENKINS_HOME下,通過Volume技術將Jenkins的運行目錄掛載到宿主機的/var/lib/docker目錄下。
3.2.2 構建配置從機器
對于一個持續(xù)集成工具而言,Jenkins的分布式特性,給我們帶來很大的便利。在Jenkins分布式架構中,主機器主要負責任務的自動觸發(fā)及調(diào)度分配,實際的構建任務在從機器上執(zhí)行,保證干凈的構建環(huán)境和整體性能。我們把從節(jié)點的環(huán)境也放到容器中,與主機器相似,從機器容器中也不能存放項目數(shù)據(jù),利用Volume技術將容器環(huán)境中的目錄掛載到宿主機。
本文中,Jenkins從機器負責任務的構建和測試,從機器通過Docker技術虛擬出不同的構建環(huán)境執(zhí)行主機器上的構建任務。通過這種方式構建出不同的從機器環(huán)境,使構建、測試環(huán)境多樣化,更好地保證軟件質(zhì)量。本文通過第一種方式執(zhí)行docker run命令直接在鏡像庫中獲取基礎鏡像,使用docker exec命令進入到容器內(nèi)部進行相應的環(huán)境安裝配置操作。
3.2.3 任務的分發(fā)
Jenkins分布式構建機制使同一項目或代碼能夠在不同的環(huán)境或系統(tǒng)中進行編譯、部署,主機器和從機器的關聯(lián)通信主要有3種方式,通過ssh啟動連接、通過java web啟動連接、使用命令行方式啟動連接。
針對容器環(huán)境,在本文中采用SSH機制連接不同機器的通信,減少配置人員的工作量。
對于一個持續(xù)集成系統(tǒng)平臺而言,關鍵是根據(jù)項目需求進行相關的集成配置,將流程進行自動化。從開發(fā)人員提交代碼的那一刻,中間過程通過自動化實現(xiàn),而無需人工參與。
本文設計了CCI系統(tǒng)的主要工作流(如圖3所示)。工作流中的主要參與人員有開發(fā)者、測試人員、部署人員,系統(tǒng)組成要素有版本控制系統(tǒng)Github、Jenkins服務器節(jié)點(Docker容器)、Jenkins從服務器節(jié)點(Docker容器)和Docker鏡像庫。
圖3 CCI系統(tǒng)工作流Fig.3 Flow chart of CCI system
版本控制系統(tǒng)與容器中的持續(xù)集成主服務器Jenkins之間配置了自動觸發(fā)的鉤子,開發(fā)人員將代碼提交到版本控制系統(tǒng)Github上之后,自動觸發(fā)集成服務器進入到構建過程。Jenkins服務器的主節(jié)點輪詢版本控制系統(tǒng),發(fā)現(xiàn)變更,將任務分發(fā)到Jenkins從機器節(jié)點上執(zhí)行構建。在構建過程中,配置相關的測試腳本,執(zhí)行自動化的單元測試。若項目構建成功,則將容器環(huán)境生成鏡像,保存到Docker私有鏡像庫中。測試人員和運維部署人員在私有鏡像庫獲取鏡像,進行測試環(huán)境和線上環(huán)境的搭建,繼而執(zhí)行其他相關操作。通過共享鏡像的方式,統(tǒng)一不同團隊之間的環(huán)境,有利于快速生成軟件價值,實現(xiàn)持續(xù)交付。
傳統(tǒng)的持續(xù)集成平臺是將Jenkins安裝在主機或虛擬機上,本文通過利用Docker將持續(xù)集成平臺進行容器化,可以得到以下幾點好處。
1)Jenkins服務器的容器化提高系統(tǒng)平臺的可重用性和可移植性。
2)將Jenkins從節(jié)點容器化,提供多樣的構建環(huán)境和測試環(huán)境。
3)維護一致的開發(fā)、測試和部署環(huán)境,減少因環(huán)境不一致所造成的時間成本。
本文的基礎實驗環(huán)境主要有3個,分別為宿主機環(huán)境、CCI系統(tǒng)容器環(huán)境和虛擬機環(huán)境,各環(huán)境操作系統(tǒng)及主要配置如下:
1)宿主機的操作系統(tǒng)采用ubuntu14.04版本,分別安裝了docker、git、jdk、vmware軟件環(huán)境;
2)CCI系統(tǒng)包括3個容器環(huán)境,分別是Jenkins容器環(huán)境和2個從節(jié)點容器構建環(huán)境(slave1-ubuntu,slave2-centos);
3)在vmware虛擬機環(huán)境中,生成3臺虛擬機,分別作為Jenkins服務器機器和2個從節(jié)點機器環(huán)境。
本實驗主要有兩個驗證目標:驗證CCI容器系統(tǒng)及本文所提出的工作流方案的可行性;驗證系統(tǒng)資源占用少、快速構建部署的優(yōu)勢。
為了驗證系統(tǒng)平臺在軟件開發(fā)各階段中發(fā)揮的重要作用,我們在容器化的Jenkins系統(tǒng)中執(zhí)行相關集成構建任務測試其可行性。容器內(nèi)部的8080端口重定向到宿主機的8080端口,通過宿主機端口訪問容器內(nèi)部Jenkins服務,系統(tǒng)界面圖如圖4所示。
圖4 容器內(nèi)的Jenkins服務Fig.4 Jenkins services in Docker container
我們利用此系統(tǒng)平臺分別執(zhí)行基于Tomcat的Web項目和對Web應用進行測試的Selenium測試腳本。測試腳本目的是測試應用的系統(tǒng)平臺兼容性,對中國科學院儀器設備共享管理(V3.0)系統(tǒng)[15]的鏡像進行測試,分別在容器中的兩個不同操作系統(tǒng)環(huán)境(slave1-ubuntu,slave2-centos)中執(zhí)行測試構建。
另一方面,為了驗證CCI系統(tǒng)資源占用少、快速構建部署的優(yōu)勢,我們分別在虛擬機和Docker環(huán)境中安裝配置了基于Jenkins的持續(xù)集成構建環(huán)境,記錄安裝部署的時間耗時。
實踐應用表明,基于Docker的Jenkins持續(xù)集成平臺給開發(fā)活動中的各階段都帶來了便利。我們在系統(tǒng)上執(zhí)行的基于Tomcat的Web應用程序,從源碼的獲取到測試構建,能快速地得到應用的部署結(jié)果,并將結(jié)果反饋給開發(fā)人員,極大地方便了開發(fā)活動。針對應用的測試而言,Selenium測試腳本在兩臺從節(jié)點容器環(huán)境上執(zhí)行構建的時間花費如圖5所示,可以通過系統(tǒng)日志觀察到在不同操作系統(tǒng)環(huán)境上的測試構建趨勢和執(zhí)行狀態(tài),方便測試人員定位兼容性錯誤。
圖5 Selenium測試腳本的執(zhí)行結(jié)果Fig.5 Selenium script execution results
從開發(fā)人員的角度來說,開發(fā)者可以利用Docker快速搭建開發(fā)環(huán)境分發(fā)給團隊成員,并利用容器化CCI持續(xù)集成系統(tǒng)平臺,快速得到項目的反饋,提高開發(fā)效率。從測試人員出發(fā),利用容器化CCI持續(xù)集成平臺,可以快速構建出不同的測試環(huán)境,通過分析在不同測試環(huán)境下得到的測試結(jié)果對軟件質(zhì)量進行評估。對于運維人員來說,能夠快速搭建與生產(chǎn)環(huán)境一致的平臺對系統(tǒng)進行評估和維護。
為了驗證容器化系統(tǒng)相較于本地或虛擬機環(huán)境下的優(yōu)勢,分別在虛擬機和Docker環(huán)境中安裝配置基于Jenkins的持續(xù)集成構建環(huán)境。根據(jù)實際安裝測試,在虛擬機或本地宿主機上搭建持續(xù)集成系統(tǒng),往往需要耗費1~2天的時間,需要分別安裝底層操作系統(tǒng)、Jenkins服務器及相關依賴和Slave從節(jié)點機器環(huán)境等各種復雜的配置。而用Docker在10 s左右就能快速生成一個可運行的持續(xù)集成容器環(huán)境,基于docker構建環(huán)境的時間與虛擬機相比幾乎可以忽略不計,搭建環(huán)境的效率提升90%以上,系統(tǒng)啟動時間的效率提高大約80%。表1通過各個指標對比CCI系統(tǒng)與虛擬機持續(xù)集成系統(tǒng)。
表1 容器化CI和虛擬機CI的對比Table 1 Comparison between CCI and virtual machine CI
基于Docker的持續(xù)集成平臺,在環(huán)境搭建耗時上遠遠優(yōu)于傳統(tǒng)的持續(xù)集成系統(tǒng)的搭建,只需要在秒級的時間里快速啟動一個鏡像就得到一個持續(xù)集成環(huán)境。Docker占用系統(tǒng)資源少,啟動速度快,這點是虛擬機沒有辦法比擬的。在測試效率方面,容器化保證開發(fā)測試環(huán)境的統(tǒng)一和環(huán)境的重用性,從而減少測試重復率,提高測試效率。
本文利用Docker實現(xiàn)一個容器化的CCI持續(xù)集成系統(tǒng),提出代碼管理、持續(xù)集成和持續(xù)交付的一體化工作流方案。將搭建環(huán)境的復雜度降低九成,確保環(huán)境的一致性和可移植性,有效促進開發(fā)、測試和運維團隊之間的協(xié)作,能夠大大提升軟件開發(fā)效率。目前本文實現(xiàn)的集成系統(tǒng)還沒有實現(xiàn)容器的管理和部署編排工具,缺乏可視化的用戶管理界面。在下一步的研究工作中,將此系統(tǒng)框架遷移到私有云環(huán)境上進行實驗,將云平臺與容器技術相結(jié)合,在云環(huán)境下進一步驗證系統(tǒng)的可靠性,并深入研究容器相關管理工具的應用,進一步開發(fā)可視化的管理界面。