大型復雜軟件產品持續集成的實踐與反思
文 / 黃亮 王立杰
持續集成作為一種敏捷軟件開發實踐,已經被越來越多的開發者所接受。持續集成倡導開發團隊頻繁地進行系統集成——通常一天一次到數次,每次集成都能被自動編譯和測試驗證,從而能在最短的時間內發現問題,縮短開發周期,提高軟件質量。
筆者面對的是具有十多年開發維護歷史的5個相互依賴產品,每個產品均超過百萬行代碼的復雜系統。集成本身涉及很多煩瑣的手工操作,很難實現過程自動化。在實施過程中,受困于軟件系統的歷史遺留問題,而通常市面上的持續集成工具又不能滿足系統的需求,讓我們不得不著手開發自己的集成系統。經過近一年的持續努力,終于完成了系統集成的自動化,將集成頻度從數周甚至數月集成提高到日集成,大大提高了生產效率。
集成的困境
OSS系統是我們一個具有十多年生命歷程的復雜系統,多年來一直處于不斷的開發和維護中,子產品的數目和系統的代碼量也隨著時間日益增長,集成的周期、發布周期也隨之逐漸增長。OSS系統包含有5個需要集成的產品:ComLIB 、DB-Com、Data Broker、DataGen、DataAgent,每個產品均有上百萬行源代碼和龐大的測試用例,產品可以單獨構建,但運行時有相互依賴關系。OSS產品棧如圖1所示,上層產品對下層產品具有依賴,部分產品棧亦可以組成一個應用系統,如DataGen+CommonLib可以組成一個應用系統,DataBroker+ DBCom+CommonLib也可以組成一個應用系統。
圖1 OSS產品棧
同時,OSS的每個產品又包含不同的發布單元,用于運行于不同功能服務器上:Control Server、SITE、PAP、Workstation、Application Server和不同的操作系統環境中,產品在不同的服務器上安裝單元如表1所示。
表1 OSS產品在不同服務器上的安裝單元
傳統手工集成需要完成如下事情:
1.從代碼庫取出產品棧中每個產品需要集成的代碼;
2.在Build server上對每個產品進行編譯,單元測試(每個產品編譯需要2~3小時,單元測試需要4~5小時);
3.在Package server上對編譯好的產品進行打包(每個產品打包需要約1小時);
4.在Lab中卸載舊的產品;
5.在不同的服務器上(Control Server、Site、Workstation、APP Server)安裝產品中相應的產品的相應部件,需要按照產品依賴關系進行安裝 (2~3小時);
6.檢查每個產品的每個部件在Lab環境中的安裝情況,確認安裝成功;
7.運行功能測試腳本做回歸測試(10~12小時);
8.檢查測試結果(3小時);
9.對系統進行卸載測試,檢查卸載結果 (1~2小時)。
如果在此過程中,一旦遇到問題,則需要花費更長的時間。如此昂貴的集成代價使得OSS系統通常數周甚至更長的時間才艱難地進行一次集成。而自動化還要解決多產品、多平臺、多種安裝和運行環境、多個操作需要人工干預的難題。
集成目標
持續集成需與自動化結合才能發揮其威力,為給OSS系統瘦身,我們設定了如下自動化持續集成目標:
1.在集成服務器上配置cron job,每天晚上進行集成,第二天早上發布結果;
2.在集成服務器上配置集成方案,指定所需要的產品棧或子棧,指定所需產品的版本或分支,指定需要進行卸載、安裝測試,功能測試的Lab以及Lab里的服務器;
3.只需修改少量參數配置,即可以實現不同的持續集成方案。
自動化實現
通過一系列自動化工具的開發和整合,我們逐步完成了多產品編譯、測試、安裝、卸載、功能測試等一系列步驟的自動化操作,為了縮短集成時間,我們廣泛地采用并行工作,在編譯、測試、安裝、卸載等多個環節均采用并行,最后實現了成熟的集成系統。
1.自動化構建+并行編譯
首先完成5個產品統一的Daily build。對5個產品采用統一的編譯、單元測試、打包接口,利用不同的編譯服務器對5個產品同時進行編譯,對同一產品的不同編譯單元,也采用負荷分擔的方式分發到多臺服務器上進行并行編譯、并行單元測試、并行打包,并對編譯、單元測試結果進行統一管理。
2.自動化監視
通過監視編譯、打包、測試過程的輸出和進程狀態等對編譯、打包、測試過程進行監控,發現諸如NFS錯誤或者編譯系統故障之類非代碼錯誤及時取消并重啟操作,確保Daily build不會因為編譯服務器等異常發生中斷。
3.自動部署安裝
利用腳本工具打包好的系統進行自動并行安裝,免除手動安裝的煩瑣操作。當所有待安裝的產品都編譯、打包完畢后,利用腳本工具將集成后的系統并行安裝到多個Lab(每個Lab由一套不同機器的設備組成,包含Control Server、Site、WorkStation、Application Server),對于同一Lab中的不同機器亦進行并行部署,并對安裝日志進行統一管理。
4.自動功能測試
卸載、安裝操作成功后,即對選用的測試用例進行測試,測試用例經過精心挑選,根據功能分為不同的核心單元,周一到周五只運行核心的測試用例,周末時間再運行所有的用例。用例無法在一個Lab一個晚上運行時,則將用例分布配置在多個Lab里面,并行運行。
5.結果匯報及存檔
定點(如早上9:00)對前一天晚上進行的所有操作進行匯報,包括不同產品的編譯、打包、測試,不同Lab上不同服務器的卸載、安裝結果,不同Lab上功能測試用例通過情況等信息自動以郵件的方式通知開發和測試人員,并將結果存檔方便隨時查看,對其中的關鍵數據如測試用例通過率等寫于數據庫,便于跟蹤分析。
自動測試統計結果如圖2所示。
圖2 自動測試統計結果
6.流程整合和協作
流程1~2、5發生在編譯服務器群上,流程3~4發生在運行環境Lab群上,需要將1~5流程集成自動化,并且流程間需要相互協作。我們采取集中控制的方式,將1~5所需要腳本工具和配置文件集中放置,這樣便于管理和維護,其他服務器通過NFS共享訪問腳本工具和配置文件,只需要在其他服務器上配置相關的cron job,便能相互協作起來。協作流程如圖3所示。
圖3 協作流程示意圖
回顧與反思
經過近一年的完善,我們的OSS系統終于從之前的數周才能一次手工集成,達到了每天集成,許多人工操作和檢查統計工作均自動化,不僅僅大大解放了Build Manager的工作,而且避免了人工差錯,提高了效率。對于像OSS這種歷史悠久的復雜系統,持續集成是一個長期優化、完善的實踐過程。畢竟持續集成不是一朝一夕就可以實現的,而應該成為一種持之以恒的習慣。
總之,一個完善的運行良好的持續集成,必須考慮如下問題:
1.持續集成的頻度和粒度
每個開發人員提交代碼更改之前,在自己的branch上需要確保編譯、單元測試通過,避免不必要的集成失敗,只有開發人員確認的可靠代碼,才進行集成。對于小型系統可以做到每次提交都集成,對于像我們OSS這樣的大型系統,可以做到每天一集成。
2.統一構建、并行構建
對于多產品的系統,采用統一的構建、打包、安裝方式有助于靈活實現自動化。并行構建、并行測試、并行安裝等可以充分利用服務器物理資源縮短集成時間。
3.協作整合
在并行的同時,也需要采用合適的同步協作的方法,以保證系統正確運行。如編譯、打包、單元測試的進程在編譯服務器群上,安裝、卸載、功能測試等進程在運行系統的Lab服務器群上,而郵件通知則又需要將這些進程運行的結果整合起來。簡單的文件或目錄共享遠程調用往往就能完成這些控制權的轉移,實現協作和工具整合。
4.高性能build server
高性能的編譯服務器或服務器群也是持續集成中強有力的支撐。支持并行編譯的話將更好。
5.完善的結果報告
完善的結果報告能縮短開發或測試人員發現、解決問題的時間。完整有效的報告應該包含軟件版本信息,本次集成中所做的修改,單元測試和回歸測試結果,卸載/安裝結果。
6.持續集成工具
在持續集成實踐過程中,一個完善的SCM系統(如ClearCase、SVN、CVS),分支并行開發的策略幾乎是必備的支撐。同時,如果在Unix或Linux環境下,亦無須迷信于專門的持續集成工具,有些時候,借助各種腳本工具與cron job完全可以滿足這些需要。
7.自動化
持續集成必須跟自動化結合,才能真正的發揮其威力。