持續集成進階篇

在前一篇文章持續集成入門篇中我大概介紹了下持續集成的概念及工具(抱歉,在前一篇文章中我查的資料不夠與時俱進,工具介紹的都比較老,目前流行的工具應該就屬JenkinsTravis CI 了)。

這篇文章我將就持續集成的話題繼續深入地探討一番。

持續集成的四個步驟

代碼級別的集成

這個級別的集成不依賴獨立的持續集成工具也可以實現,一般語言的build工具基本內置,比如java的maven,gradle,go內置的build工具。

集成 Workflow

單一的編譯-構建工具逐漸地不能滿足產品快速交付的需求。

整個開發流程的重心從『代碼級別的集成』轉移到了更自動化地編譯和更完美的測試驗證,致力於在最短的時間內發現問題,縮短開發周期,提高軟體質量。比較常見的一個場景,某個團隊先進行代碼 Build,觸發單元測試、集成測試,打包測試完畢后再自動部署到測試環境,循環往複,形成「編譯-構建-測試-集成-部署到測試環境」的 Workflow.

Advertisements

持續部署與交付

在上個階段中,產品是自動部署在測試環境,手動部署在生產環境。之所以這樣選擇,是因為產品在從需求到部署的過程中,會經歷若干種不同的環境,例如 QA 環境、各種自動化測試運行環境、生產環境等。這些環境的搭建、配置、管理,在不同環境中的具體部署是比較複雜的。經常會遇到這麼一種場景:明明在測試環境已經部署成功,但線上環境又出現部署故障。這種情況很可能是生產環境和測試環境的異構造成的。

這時候需要改進你的 CI 系統,建立標準化的環境部署順序,在 Workflow 中增加部署預生產環境並進行灰度集成測試的流程,做好線上環境部署后的回歸測試。到這裡,已經真正做到了持續交付。

持續交付並不是指軟體每一個改動都要儘快部署到產品環境中,它指的是任何的代碼修改都可以在任何時候實施部署。而『持續部署』,即自動部署到生產環境中而無需手工干預:得到一個版本后,自動部署該版本到生產環境中。實踐證明,相對獨立快速地部署新功能是一個核心競爭力,可以減輕大規模功能變更的風險。

Advertisements

并行多workflow集成以及個性化集成

隨著項目和團隊規模增長,模塊之間依賴關係變得複雜,如何確保代碼質量的同時,保證代碼構建的一致性和穩定性,成為一大挑戰。Docker 可以方便地以『容器化』的方式部署,它就像集裝箱一樣,打包了所有依賴,在其他伺服器上部署很容易,不至於換伺服器后發現各種配置文件散落一地,這樣就解決了編譯時依賴和運行時依賴的問題。

還有一個問題,開發的分支越來越多,每個活躍分支都進行環境部署和集成測試,對持續集成環境的維護成本也就越高。Docker 的快速啟動和鏡像倉庫是天生為 CI/CD 設計的,以前啟動一個虛擬機需要幾分鐘,而啟動 Docker 只需要幾秒鐘,讓并行的持續集成才能成為可能。

目前,比較常見的基於 Docker 進行持續集成的流程如下:

  • 開發者提交代碼

  • 觸發鏡像構建

  • 構建鏡像上傳至私有倉庫

  • 鏡像下載至執行機器

  • 鏡像運行

持續集成工具

傳統的CI工具

第一個正規的持續集成工具是於2001年所推出的CruiseControl,這是一個基於Java開發的開源軟體。除了持續構建流程之外,它還提供了郵件通知、Ant以及對各種源代碼控制系統的支持,並推出了支持.NET與Ruby的移植版本。儘管Jenkins後來居上,成為第一個得到廣泛應用的CI工具,但CruiseControl已經具備了一個CI工具的基本功能,為CI過程的推廣做出了很大的貢獻。

Jenkins的出現與發展頗有傳奇色彩,它的前身是由一位來自Sun公司的開發者川口浩介(Kohsuke Kawaguchi)於2004年開發的一個基於Java的CI工具Hudson。經過三/四年的發展后,它逐漸超越CruiseControl成為了最流行的CI工具。但自從Oracle收購了Sun之後,希望將Hudson作為收費的商業工具進行開發。以川口為首的開發者社區則決定以Jenkins的名義繼續免費版本的開發。有趣的是,Hudson與Jenkins的開發者各自將對方視為自己的分支版本,而將自身視為正統。在2013年後,Jenkins的發展勢頭已有超越之勢,它的每日提交次數遠遠地超越了Hudson,如今已成為市面上最流行的CI工具。

早期的Jenkins與其他傳統CI一樣,只支持本地託管。而現在已經有一些雲計算平台推出了基於Jenkins的SaaS方案。這方面比較突出的有CloudBees,它所提供的方案是一種集成了CI與CD的混合方案,可通過Docker Pipeline插件提供對Docker容器的支持。

除了Jenkins之外,其他一些流行的CI工具還包括由JetBrains推出的TeamCity,以及由Atlassian推出的Bamboo等等。這些CI工具基本都提供了以下功能:

  • 對源代碼控制系統的支持,例如Git、Subversion、TFS等等。可以在代碼控制的主線發生代碼提交時自動觸發後續的一系列步驟,例如構建、測試與部署等等。

  • 對依賴管理工具的支持,如Java的Maven、NodeJS的NPM、Ruby的Gem,以及.NET的Nuget等等。

  • 對各種類型測試的支持。早期的CI只支持單元測試,即單個對象或組件的功能驗證。隨後加入了對集成測試的支持,即對組件之間的通信與交互進行難。儘管如此,這還不足以驗證系統確實按照用戶期望的方式進行工作。因此現代化的CI工具開始支持功能性測試,將原先的手工測試替代為基於Selenium等工具的自動化測試。

雲計算環境中的CI工具

曾在大規模企業中嘗試過CI實踐的開發者非常了解:代碼的構建與測試的執行是一種非常消耗資源的操作,如果有多個團隊使用同一個CI平台,那麼這種情況將進一步加劇。近幾年來,軟體團隊逐漸厭倦了本地託管的CI系統對時間與精力的要求。而基於雲計算平台的SaaS解決方案的出現快速地彌補了這方面市場的缺失。

Travis CI是一個基於GitHub API所打造的託管CI服務,使用Travis CI有一個先決條件,即源代碼需要在GitHub進行託管。Travis CI通過webhook對GitHub代碼倉庫中的各種變化進行響應,例如代碼提交或pull request等等。Travis CI也依賴GitHub提供的服務對用戶和組織進行認證。

使用基於雲環境的CI系統讓開發者得以從對本地CI系統的安裝、配置過程中解脫,不必再關注於基礎設施和用戶認證與授權方面的問題。此外,由於大多數SaaS方案都提供了對應的API,因此整個工作流都可以實現API驅動。

基於雲環境的CI系統還有另一大優勢,他們通常會提供更多的測試功能,例如對不同瀏覽器與操作系統組合條件的測試。例如Travis就支持在Linux、Mac和Windows系統上的測試,並支持PHP、NodeJS、Go和C等各種語言。

用於移動應用的CI工具

隨著智能手機的日益普及,移動應用的數量也在不斷增長。但由於移動應用與Web應用相比有一些特別之處,例如它的測試與發布方式,以及完全不同的依賴管理機制,因此移動應用對於構建、測試及部署流程提出了完全不同的要求,這是傳統的CI工具力所不及的。好在如今已經有幾家主流的CI提供商實現了支持移動應用的CI工具,例如CircleCI已經提供了對iOS應用的支持。

移動應用的測試與Web應用具有很大的差別,Web應用的客戶端多數集中在一些主流的瀏覽器與操作系統上,而移動應用的客戶端往往是千差萬別的,特別是在Android平台上。某些測試框架,例如Espresso以及Appium能夠自動替你解決許多困難。而像CrashlyticsHockeyApp這樣的工具除了內置的CI功能之外,還能夠自動生成應用崩潰的報告,為開發者進行問題診斷提供充分的上下文。

而由於移動客戶端的多樣性,以集中化的方式進行所有測試的方式是不太實際的。因此,移動開發社區更推崇beta測試的方式,通過TestFairyTestFlight等工具將潛在的新版本發布給beta測試人員。

移動應用的另一個獨特之處在於它的發布方式,通常需要經過漫長而繁瑣的審核流程才可發布至對應的應用商店。這不僅降低了持續交付的速度,還不得不在流程中引入各種人工步驟,使全自動化的流程無法實現。

為此,像Fastlane這樣的工具可實現將應用審核流程中的大部分元素實現自動化,例如為新應用進行屏幕截圖及處理認證等信息。可結合Jenkins等CI工具以完善整個工作流。

CI工具的未來

CI與CD過程如今已成為現代化應用開發中一個並不可少的元素,絕大多數開發團隊在軟體項目中都需要設計一個完善的CI與CD工作流。

而CI的發展並不會停下腳步,它仍處於高速的發展中。在對Web及移動項目支持的基礎上,未來幾年之內,我們將看到CI在其他類型的開發中的應用,例如智能手錶、智能汽車以及物聯網中,甚至是在虛擬現實與生物科技項目中的應用。

CI過程目前所面臨的一個挑戰在於在開發環境中執行的自動化測試與生產環境之間總是存在著或多或少的差別。隨著近來年以Docker為代表的容器化技術在(微)服務系統中的廣泛應用,CI過程也從容器的使用中受益匪淺。Docker的高可移植性使多個CI提供商開始擁抱Docker。舉例來說,CircleCI就支持基於容器的應用,而CodeShip近期也推出了Jet,這是一個對Docker應用進行測試與部署的解決方案。

持續集成最佳實踐要點

持續集成無論是工具使用還是流程定義,其實都不難,難的是如何形成這樣的習慣與文化。筆者通過自己的實踐經驗總結了以下持續集成的最佳實踐要點,可以幫助讓這個改進更容易些:

  • 首先要選擇一個可以脫離IDE進行build的語言以及項目定義工具。這個很明顯,CI是要在伺服器上跑的。如果你的團隊進行build還完全依賴IDE,這事情就沒法搞。

  • CI工具越早引入越好,最好是寫第一行代碼的時候就先弄個CI,但配置不用一步到位,可以按照上面的進階一步步完善。這樣才容易形成圍繞CI進行開發的習慣。

  • 集成測試用例最好使用項目本身開發語言編寫和單元測試類似,至少是團隊開發人員都熟悉的語言。並且項目代碼要和集成測試用例在同一個源碼倉庫里。如果你的團隊有專門的QA人員寫測試用例,那最好讓QA和開發人員共享同一個代碼倉庫。如果你的集成測試系統是通過配置實現的,那也請將測試用例作為配置文件放到代碼倉庫中,而不是通過web編輯器放到資料庫中。這樣做的最大好處是項目代碼和集成測試代碼共享同一個分支,同一個build number,只有這樣才能做多分支的并行測試。否則如果測試用例單獨維護,代碼的分支如何和測試用例對應起來?最後的結果就是,自動化測試用例都是上線後補充的,上線前還是依賴人肉測試。

  • 編寫測試用例和功能開發最好是同一個人,如果做不到,編寫測試用例的開發也要有許可權修改業務代碼。因為要做自動化測試必須在系統中留一些後門來給自動化測試提供便利。比如提供用戶的批量生成和銷毀,比如對測試的請求不記錄到統計日誌中等等。

  • 部署的腳本或者配置最好和項目在同一個源碼倉庫。只有這樣,自動化部署才方便實施。因為項目的改進以及重構,往往伴隨著依賴資源以及部署機制上的改造。

  • 服務最好不依賴外部容器,可以獨立運行。這個專指java的容器,其他的如go,nodejs都沒有這個問題。java的容器是企業應用為了降低部署成本帶來的習慣,但當前虛擬化,docker等技術這樣成熟的情況下,應用容器已經完全沒必要了。如果非要用,也最好直接和應用打包在一起,讓應用可以直接運行,這對開發效率以及集成測試,都非常有幫助。

  • 最好提供一種直接可以單進程運行整個系統而不依賴外部資源的配置,外部資源都用內存版的庫進行mock。這樣做的好處是可以非常快速的進行初步的集成測試驗證,同時也非常方便統計集成測試覆蓋率(通過單元測試覆蓋率工具即可實現)。

  • 如果公司有多個研發團隊,最好共享CI池,這樣成本最小。有的公司為了省錢,避免超過免費限制,部署多套CI。其實算下來這樣成本比購買商業版更高。

總結

持續集成是最能體現一個團隊的DevOps(關於DevOps將在接下來的文章中進行講解)氛圍以及水平的一個場景,因為整個流程需要開發,測試,運維的緊密協作,缺一不可。

參考文章

  • 持續集成系統的演進之路(http://jolestar.com/ci-teamcity-vs-jenkins/)

  • 不可錯過的「持續集成」進階指南(http://blog.flow.ci/ci_advancedguide/)

  • 以持續集成工具實現DevOps之禪(http://www.infoq.com/cn/news/2016/04/DevOps-continuous-integration-to)

  • 另一種聲音:持續集成已死(http://www.infoq.com/cn/news/2014/10/continuous-integration)

參考書籍

  • 持續集成:軟體質量改進和風險降低之道(https://book.douban.com/subject/2580604/)

  • 持續交付:發布可靠軟體的系統方法(https://book.douban.com/subject/6862062/)

Advertisements

你可能會喜歡