CI是敏捷和DevOps關鍵性的一步,數人云最近給大家分享了《致:正在選擇CI平臺的你(10大要素需把控)》今天的文章將討論如何使用CI服務器創建高質量產品以及相關擴展插件和指標度量。最后針對業內標準規則進行了自我思考。
CI服務器
擁有自動化的構建系統,是實現CI唯一強制性要求。但在實踐中,除了自動化構建系統外,還可以安裝和配置“CI服務器”。
概述
CI服務器是大腦,在幕后工作,無縫地將各種行業標準實踐整合到CI實現過程中。
下圖是個典型的CI工作流,包含6個階段,從代碼檢入開始,直到向企業內不同的人員提供反饋信息。
啟動過程
通常,開發人員將代碼簽入源存儲庫時,CI流程就會啟動。
CI工作流也可以通過其他方式觸發,下面列舉幾個方法——
- 手動:用戶通過CI服務器管理控制臺或腳本手動執行時。
- 計劃:預先配置的時間表,如某個夜間構建。
- 觸發:每次提交到源代碼版本控制系統。
夜間構建通常會對代碼執行更大檢查和更久時間。
獲取最新代碼
流程的第二步,CI服務器從源控件獲取最新代碼,可以通過輪詢或推送機制實現。
在輪詢機制中,CI服務器配置為源控制服務器,不斷地在固定時間間隔內對給定的位置進行輪詢,檢測任何新的代碼更改,一旦檢測到,會從源控制服務器下載最新的代碼副本到磁盤上。
推送機制中,源控制系統由一個:“鉤子”提供給CI服務器,當開發者向提交更改時,調用“鉤子”,并讓CI服務器了解更改是可用的。
構建
源代碼一般獨立于構建,即必要的構建腳本與源代碼共存,最新的代碼在前面步驟中被詳細地獲取,綁定的腳本會觸發構建。
對于Java項目,通常構建自動化腳本使用Maven或分級。使用不支持內置依賴管理的構建系統并不常見,在不需要依賴管理的情況下,Make 使用了在Unix系統中廣泛使用的構建機制。
有很多構建工具可以使用,Wiki列出了一些:(https://en.wikipedia.org/wiki/List_of_build_automation_software)
執行測試
在此過程中執行單元測試和集成測試,這些測試和代碼一般捆綁在一起。
基于Java的系統,測試使用類似于Junit的測試框架執行,允許對測試依賴項進行簡單的模擬。
某些編程技術自帶測試運行器框架,如Spring的Spring Junit運行器、Java EE中的Arquillian等。
在CI服務器上運行測試有以下幾點:
曾經遇到過測試在一臺機器上傳遞,但在另一臺上失效的情況嗎?通過在集中的服務器上運行測試,消除了測試環境的問題。
測試會立即執行每個更改,若有代碼破壞預期行為能被快速發現。
很多團隊并行處理特性,甚至團隊成員在全球范圍內分布,每次開發者向存儲庫提交代碼,任何故障都能即刻識別和修復。
結果
CI過程的最后有兩種結果:構建和測試失敗或通過。
每個嵌入都是經過驗證的,并且確保不會破壞現有的代碼,代碼被合并到主干中,會被構建和測試,允許減少主線代碼被破壞的時間。
一般情況下,CI服務器失敗會發送電子郵件(給整體團隊或只負責以前簽入的人),可以快速看到失敗的狀況,并且盡快采取糾正措施。
CI服務器還凸顯了最近構建的狀態,以及最近幾個構建在紅-綠-琥珀光指示器上的狀態,例如,Jenkins使用了下面的構建指示器:
CI服務器插件
上一節介紹了CI的基本工作流程,但大多數企業會使用它完成更多任務,通過安裝各種插件擴展CI服務器,開拓行為,下面介紹一些常用的擴展:
技術債務與代碼質量
在應用中引入的任何變化都會增加系統復雜和無序性被稱之為“技術債務”,當新代碼引入系統時會增加技術債務,如果被忽視太久,越來越多的新功能被緊湊的最后期限所填滿,此時想要在可控范圍內幾乎不可能,這將對應用的生產效率和可維護性造成負面影響。
迭代開發、自動化檢測、和CI的使用來監控每個簽入技術債務是控制技術債務的唯一方法。
如SonarQube工具,采用識別代碼質量和跟蹤技術債務的開源平臺,可以輕松地集成到任何CI服務器中,為技術債務提供實時數據。
其不僅僅衡量技術債務,也度量并涵蓋了代碼質量的7個核心:
SonarQube在簡單的WEB中展現分析結果,是非常有用的工具,不僅是開發者,對于管理其他非技術的人也是如此。
了解更多請參考:
語義
對于開發者來說,引入CI在另一個重要的方面非常具有價值——可以衡量代碼質量——語義及常見的反模式。
靜態代碼工具可以作為CI過程的一部分,以深入了解代碼的健康度,歷史數據也可以存儲,從而在一段時間內提供質量度量。可以為兩個提交作比較,確定每個提交的債務度量標準。
很多工具可以靜態地分析代碼并計算質量度量的多樣性,同時可以被集成到CI服務器中自動運行,例如:
- Checkstyle
- Findbugs
- Sonar
代碼評審
開發者在特性分支上工作,并盡可能頻繁地提交代碼。
代碼分析工具(如:Sonar)可以在CI服務器中執行自動代碼檢測,然后每個開發者負責處理提交時生成的評論注釋。
自動代碼審查基于一組預定義的規則,是發現潛在技術問題很好的方法,這些規則可以從主流的開發語言中下載。
解決了自動化評審意見,會進行同行代碼評審,捕捉那些不能用自動化審查的問題,如驗證業務需求、體系結構問題、代碼易讀性、擴展性等。
如果有一定數量的人沒對代碼進行審查,CI服務器可以配置成阻止對主線分支任何的提交,常見辦法是在主線分支中強制要求每個提交至少2個審核員,Java的項目中,常見工具是Gerrit。
Headless Testing
若想在CI服務器上運行UI測試,必須依賴于Headless(Headless:在缺少顯示屏、鍵盤或者鼠標時的系統配置)測試,因為瀏覽器沒有顯示,這意味著要在沒有圖形用戶界面的情況下運行UI測試,需要類似于流行WEB瀏覽器的Headless瀏覽器,通過命令行界面執行,并具有UI元素的內存模式。內存中的模型用于模擬UI交互,如,在UI中單擊一個按鈕即可模擬內存中的模型對象。
Selenium測試工具可以用于無頭測試,如Phantom JS的Headless瀏覽器也被廣泛應用,在構建中引入這些測試,CI服務器能夠驗證失敗的UI。
安裝CI服務器
CI服務器主要有三種:
- 獨立
- 托管
- 私有云
獨立的CI服務器安裝在一臺機器上進行,通常用于小型項目或少于10人的開發團隊。
托管的CI服務器可以在公有云上使用,大多數情況下,都與CI服務器可以訪問的基于云的源控制系統相連接。這些公司都不希望維護CI或無法維護的公司使用,對于中等規模的團隊,是非常可行的選擇。
私有云是想要完全控制和基于安全考慮的大公司使用的,多個CI服務器代理配置在高性能服務器的集合中,以支持數百個開發者繁重工作負載。
一些被廣泛使用的CI服務器:
- Jenkins
- Travis CI
- TeamCity
- CruiseControl
CI最佳實踐
Martin Fowler(國際著名面向對象分析設計、UML、模式等方面的專家,敏捷開發方法的創始人之一)在他關于CI的白皮書中提到了為任何CI設置的一些關鍵實踐。這些建議已經成為多年來CI的最佳實踐。
這里將根據文章作者的見解來審視每一種最佳實踐。
維護單個源存儲庫
“提倡使用版本控制系統實現項目的源代碼,所有與構建項目相關的都應存放在存儲庫中,在此實踐中,約定系統應該從一個新的簽出中構建,不需要額外依賴項,極限開發倡導者Martin Fowler提到,在使用工具支持分支的情況下,應盡量減少分支的使用,更傾向于進行集成而不是同時維護多個版本的應用,主線(或主干),應該是應用的工作版本。”
提示
原則并非一定按照字面上只需一個單一的存儲庫,關鍵是擁有在存儲庫中構建項目所需的所有相關工件,該項目應該從一個新的簽出中進行構建,而不需要額外的依賴項或手動步驟。
“項目”的定義取決于自身,如果代碼庫很小,或者代碼所構建的邏輯模塊組件,那么就意味著是完整可交付的產品。
觀點
實踐中建議不要在版本控制系統中使用分支,相反,建議只開發項目的某個分支,且讓其一直處于開發階段。
本文作者不同意這種說法,在多數企業中,有許多分支需要同時并行開發,企業需要支持產品的前一個版本,修復BUG,而其他團隊成員則開始著手下一個版本的工作,所以需要在代碼庫中使用多個分支。
自動化構建
“單個命令應該具備構建系統的能力,許多構建工具如Make,已經存在多年,其他近期的工具經常用于CI環境中,構建的自動化應該包括自動化集成,包括部署到類似產品的環境中,在許多情況下構建本本不僅編譯二進制文件,還生成文檔、網站頁面、統計信息和分發介質(如Debian DEB、Red Hat RPM或Windows MSI文件)”
提示
構建的自動化應該包括:編譯代碼、執行單元測試和集成測試,以及許多工具——代碼質量檢測、語義檢測、測量技術債務等,多數現代構建工具都支持這些額外集成,并且用于開發CI環境。
在實際項目中,不同的團隊負責開發系統不同部分,都有自己的存儲庫,因此這種情況幾乎不可能發生,將整個產品進行自動化構建沒有必要,為系統單獨部分開發構建自動化就足夠。
觀點
定義CI的目的除了自動化構建過程外,是否還有投資CI的意思?作為CI的一部分,打算度量哪些標準?很多人將CI設置看做開發者的工具。
CI不是敏捷或DevOps,只是整體過程的工具之一,敏捷或DevOps已經超越了開發技術層面,升華成一種文化。
構建自測
“構建代碼后,所有測試都應運行以確認它如開發者的預期一樣。”
提示
代碼至少要包含單元測試,如Junit框架可以模擬依賴關系。
特定組件與其他模塊是不應該交互的,要確保獨立運行。
觀點
單元測試應該測試行為,而不是實現細節,例如,不要去在乎如何計算汽車的速度,只要使用的方法和工具是正確的即可。
許多測試框架允許斷言模擬對象,如測試模擬對象是否被調用,是否在特定的參數中傳遞等,除非測試實現本身是主要關注點,否則應將其弱化。
保持快速構建
“構建需要快速完成,因此如果集成有問題,會很快被識別。”
提示
- 應該以更快地執行測試為目標,所以需要做比其他類型更多的單元測試。
- 避免在單元測試中使用數據庫,如果可以,也要必滿在集成測試中使用。通常需要集成測試使用另一種數據源,指向內存中的數據庫。如果不可避免,每次測試前都需刷新數據庫,確保數據處于已知狀態,并且測試會以一致的數據開始。
注意
不要依賴大量的UI測試,其非常脆弱,需要大量維護,建議使用如Selenium類似的UI測試框架來緩解UI測試的一些問題,如屏幕上更改UI元素的位置、處理UI事件等。
自動化部署
“很多CI系統允許在構建完成后運行腳本,可以編寫一個腳本將應用部署到每個人都可以查看的實時測試服務器上,此種思維方式進一步發展是CD(持續部署),要求將應用直接部署到生產中,需要額外的自動化防止BUG和回滾。”
觀點
不是所有的項目都需要自動化部署,如果生產站點是由同一家公司托管,那么在CD中投資更加有利,CD是CI的下一個邏輯步驟。
不是所有提交的結果都是可交付的產品,在敏捷社區中每個構建都是可交付的產品是常見的誤解,可交付的產品與工作應用是不同的概念。
原文作者:Deepak Karanth
原文鏈接:https://dzone.com/articles/continuous-integration-part-3-best-practices