持續(xù)集成系統(tǒng)的演進(jìn)之路
iTesting,愛測試,愛分享
本文分析了持續(xù)集成系統(tǒng)的功能點(diǎn),比較Jenkins,Teamcity這兩款持續(xù)集成工具的優(yōu)缺點(diǎn),最后總結(jié)了持續(xù)集成的最佳實(shí)踐要點(diǎn)。
閱讀對象:研發(fā)以及研發(fā)管理人員,研發(fā)工具研究愛好者
持續(xù)集成(Continuous integration),是隨著敏捷開發(fā)一起成長起來的理念,逐漸發(fā)展,當(dāng)前持續(xù)集成的外延基本已經(jīng)涵蓋了持續(xù)測試,持續(xù)部署,持續(xù)反饋,逐漸和持續(xù)交付等同了,只是二者的關(guān)注點(diǎn)略有區(qū)別。
引用《Continuous Integration: Improving Software Quality and Reducing Risk》中的一句話:
持續(xù)集成的核心是減少缺陷引入,發(fā)現(xiàn)和修復(fù)之間的時間間隔。
所以持續(xù)集成的關(guān)鍵在于持續(xù)。
假設(shè)時間回退,我們回到還沒有持續(xù)集成工具的計算機(jī)遠(yuǎn)古時代,遇到了以下場景:
你辛辛苦苦,擼啊擼呀,擼完了功能代碼,高興提交代碼到倉庫里。結(jié)果你的同事更新代碼后發(fā)現(xiàn)程序編譯不通過,后來發(fā)現(xiàn)是你少提交了一個代碼文件或者依賴配置,導(dǎo)致大家的工作中斷。
于是你們商量,找了一臺專用電腦,每次代碼提交都觸發(fā)一個hook,調(diào)用編譯器在服務(wù)器上編譯,如果錯誤就通知對應(yīng)的提交者。這個可能就是最初的持續(xù)集成了。
但后來發(fā)現(xiàn)只編譯還解決不了問題,所以逐漸增加了代碼風(fēng)格檢查,靜態(tài)代碼分析,單元測試調(diào)用,測試覆蓋率檢查等等增強(qiáng)功能。
這是持續(xù)集成第一進(jìn)階,代碼級別的集成。這個級別的集成不依賴獨(dú)立的持續(xù)集成工具也可以實(shí)現(xiàn),一般語言的build工具基本內(nèi)置,比如java的maven,gradle,go內(nèi)置的build工具。
然而好景不長,你們發(fā)現(xiàn)即便是經(jīng)過了完整的單元測試,嚴(yán)格的代碼分析,也不能保證程序正常運(yùn)行,更不能保證接口實(shí)現(xiàn)符合需求定義。于是你們增加了自動部署測試環(huán)境的流程,編寫了集成測試用例,每次提交先進(jìn)行代碼build,然后觸發(fā)測試環(huán)境部署和集成測試。
這是持續(xù)集成的第二進(jìn)階,集成workflow,這才基本實(shí)現(xiàn)了真正的持續(xù)集成。
你們繼續(xù)歡樂的進(jìn)行編程,后來發(fā)生一次部署故障,經(jīng)過集成測試打包后的系統(tǒng)部署到生產(chǎn)環(huán)境后出現(xiàn)了錯誤。原因可能是生產(chǎn)環(huán)境和測試環(huán)境的異構(gòu),也可能是二者配置差異導(dǎo)致的錯誤。于是你們又改進(jìn)一次,在workflow中增加了部署預(yù)生產(chǎn)環(huán)境進(jìn)行灰度集成測試的流程,還進(jìn)一步將生產(chǎn)環(huán)節(jié)的部署和持續(xù)集成系統(tǒng)整合,通過持續(xù)集成系統(tǒng)來觸發(fā)生產(chǎn)環(huán)境的部署,同時做線上環(huán)境部署后的回歸測試。
至此,是持續(xù)集成的第三進(jìn)階,持續(xù)部署與交付。
隨著你們項(xiàng)目的發(fā)展,團(tuán)隊成員越來越多,用戶也越來越多,對穩(wěn)定性的要求也越來越高。原來你們的修改直接提交到代碼倉庫的主分支上,然后觸發(fā)持續(xù)集成。人多了后發(fā)現(xiàn)這樣有問題,于是使用了feature分支模式,比如典型的Github workflow。
借用一張微博@TimYang發(fā)的gitlab workflow交付流程圖:?
分支的每次提交是否需要觸發(fā)CI流程?
每次merge request或者pull request,是否要觸發(fā)CI,進(jìn)行預(yù)驗(yàn)證,然后再進(jìn)行合并?
如果回答是,則需要進(jìn)一步改進(jìn)CI,CI的workflow要支持分支并行,并且需要考慮并行到哪一步,只是代碼級別,還是同時要進(jìn)行測試環(huán)境集成(當(dāng)然分支的測試不建議直接跑到灰度環(huán)境)。另外如果代碼量和單元測試越來越多,開發(fā)者本地執(zhí)行太慢,是否要支持本地未提交代碼在服務(wù)器端進(jìn)行CI?
至此,是持續(xù)集成的第四進(jìn)階,并行多workflow集成以及個性化集成。
假設(shè)我們需要自己設(shè)計一個持續(xù)集成系統(tǒng),現(xiàn)在總結(jié)一下持續(xù)集成系統(tǒng)需要設(shè)計的功能點(diǎn):
User & AuthManager?這個是任何一個系統(tǒng)的基本需求,同時和持續(xù)集成的提醒反饋機(jī)制,以及第四進(jìn)階的個性化集成都相關(guān)。
Project?權(quán)限控制一般是以項(xiàng)目為粒度的,同時持續(xù)集成的Build configure都是關(guān)聯(lián)在項(xiàng)目之下。
Build Runner?因?yàn)楦鞣N語言的build工具以及項(xiàng)目定義都不相同,需要允許擴(kuò)展。為什么不直接用命令行調(diào)用呢?Build Runner需要做的事情包括日志收集,錯誤收集,變量注入等,用腳本比較困難。
?Source configure??項(xiàng)目關(guān)聯(lián)的源碼配置
?Build configure??Builder Runner需要的參數(shù)等設(shè)置,一般都需要支持能配置多個step配置。
?Workflow (pipeline)??這個到第二進(jìn)階的時候就需要。因?yàn)槌掷m(xù)集成系統(tǒng)執(zhí)行的最小單元是Build configure,同一個configure的step不能獨(dú)立執(zhí)行,如果沒有workflow機(jī)制,直接將所有的流程都放到同一個Build configure的不同的step,導(dǎo)致的結(jié)果就是如果后面的step失敗,則必須從頭開始。比如灰度測試的時候,部署腳本出現(xiàn)了錯誤,導(dǎo)致失敗。如果有workflow,修復(fù)腳本后再次運(yùn)行灰度測試這個Build configure即可,否則必須從頭構(gòu)建,非常浪費(fèi)時間。
?Build agent??這個不同的CI工具實(shí)現(xiàn)的機(jī)制不一樣,在這里指真正執(zhí)行Build Runner的單元。Build agent要能支持庫擴(kuò)充部署,方便實(shí)現(xiàn)并行Build。
Notification?通知和反饋對持續(xù)集成非常重要,最開始和就說到,持續(xù)集成包含的一層含義就是持續(xù)反饋。只有Build的結(jié)果及時通知到開發(fā),測試,運(yùn)維人員,才能形成持續(xù)集成環(huán)。
當(dāng)然,當(dāng)前CI工具這么多,我們完全沒必要自己設(shè)計開發(fā)。既然我們總結(jié)出來CI工具需要包含的功能點(diǎn),那么我們就從這幾個方面進(jìn)行比較選擇一下。由于筆者主要使用Jenkins和Teamcity,所以這里只比較這兩個工具,其他的工具也可以按照這幾個角度進(jìn)行比較。另外選擇這兩個工具比較的原因是二者都可以免費(fèi)獲取到,Jenkins是開源的,Teamcity小規(guī)模使用(3個agent,20個Build configure)免費(fèi)。

綜合評價,由上表可以看出,Teamcity在易用性以及抽象設(shè)計上勝出,開箱即用,不用太多自定義即可滿足大多數(shù)場景,但由于是非開源應(yīng)用,不便于自定義擴(kuò)展。建議初創(chuàng)團(tuán)隊或者沒有太多研發(fā)精力自定義CI工具的團(tuán)隊選擇。Jenkins勝在開源,插件豐富,可完全自定義,建議有專門的CI研發(fā)人員的團(tuán)隊選擇。
Teamcity的cloud-agent有個bug,不能支持amazon aws的中國區(qū)Region,因?yàn)闆]有源碼我只能提bug等待他們修復(fù),有興趣的可以上去幫忙給投個票。TW-42235。
再說說持續(xù)反饋問題
前面說了,如果CI的反饋不能及時通知到相關(guān)人員,或者相關(guān)人員響應(yīng)不及時,最后導(dǎo)致的結(jié)果就是CI形同虛設(shè)。所以CI都有各種Notification插件,還有人弄了個硬件的報警器,build失敗就報警,據(jù)說效果不錯。
郵件?默認(rèn)都是通過郵件通知的。但郵件的缺點(diǎn)是不及時,同時大家為了避免打擾,往往會設(shè)置過濾器過濾掉。
Jabber等內(nèi)部IM?前提是必須團(tuán)隊內(nèi)有搭建使用內(nèi)部Jabber服務(wù)的習(xí)慣。
桌面客戶端以及IDE通知?需要所有成員都安裝插件或者桌面agent。
Slack等團(tuán)隊通訊SaaS?Slack的集成可以將build結(jié)果通知到channel內(nèi),團(tuán)隊共享通知,也可以及時討論,但缺點(diǎn)是會打擾到不相干的人。
Grouk?Grouk 是筆者團(tuán)隊研發(fā)的團(tuán)隊通訊工具。在CI通知集成上嘗試進(jìn)行改進(jìn),在build失敗的消息中 @ 導(dǎo)致build失敗的提交者,這樣大家把提醒設(shè)置為 @ 就可以避免打擾了(此功能尚未上線,敬請期待)。
創(chuàng)業(yè)團(tuán)隊是否需要持續(xù)集成
前一段時間微博上關(guān)于技術(shù)工具,架構(gòu),團(tuán)隊的爭論非常火熱,有一種觀點(diǎn)是創(chuàng)業(yè)團(tuán)隊關(guān)鍵是要生存發(fā)展,對生存發(fā)展沒有幫助的花架子都不要,辦法土點(diǎn)也沒關(guān)系。
技術(shù)人創(chuàng)業(yè)失敗后對自信心打擊是非常大的,因?yàn)闀l(fā)現(xiàn)自己優(yōu)雅的代碼架構(gòu),完善的單元測試覆蓋率,良好的持續(xù)集成,竟然也沒干過對手找外包擼的爛系統(tǒng),于是就有了上面的觀點(diǎn)。筆者也遭遇過創(chuàng)業(yè)團(tuán)隊失敗,對這種心情深有所感。
但創(chuàng)業(yè)失敗和成功的因素太多了,很難說優(yōu)雅的代碼架構(gòu),完善的單元測試覆蓋率,良好的持續(xù)集成能有多大作用。我個人后來想明白了,這些只是作為一個技術(shù)人專業(yè)態(tài)度的體現(xiàn),與成功與否關(guān)系不大。
持續(xù)集成最大的作用是讓研發(fā)團(tuán)隊更優(yōu)雅的進(jìn)行功能開發(fā),快速迭代,而不是忙于撲火,填坑。具體值得花多大代價去維護(hù),是磨刀和砍柴的關(guān)系。如果只是砍棵樹,磨磨斧頭就行了,如果是片林子,你可能需要弄臺伐木機(jī)了。
持續(xù)集成最佳實(shí)踐要點(diǎn)
還是引用@TimYang上一個配圖的說明:
當(dāng)然這里面[持續(xù)集成]最容易的就是畫流程,難改變是流程之外的東西。
持續(xù)集成無論是工具使用還是流程定義,其實(shí)都不難,難的是如何形成這樣的習(xí)慣與文化。筆者通過自己的實(shí)踐經(jīng)驗(yàn)總結(jié)了以下持續(xù)集成的最佳實(shí)踐要點(diǎn),可以幫助讓這個改進(jìn)更容易些:
首先要選擇一個可以脫離IDE進(jìn)行build的語言以及項(xiàng)目定義工具。這個很明顯,CI是要在服務(wù)器上跑的。如果你的團(tuán)隊進(jìn)行build還完全依賴IDE,這事情就沒法搞。
CI工具越早引入越好,最好是寫第一行代碼的時候就先弄個CI,但配置不用一步到位,可以按照上面的進(jìn)階一步步完善。這樣才容易形成圍繞CI進(jìn)行開發(fā)的習(xí)慣。
集成測試用例最好使用項(xiàng)目本身開發(fā)語言編寫和單元測試類似,至少是團(tuán)隊開發(fā)人員都熟悉的語言。并且項(xiàng)目代碼要和集成測試用例在同一個源碼倉庫里。如果你的團(tuán)隊有專門的QA人員寫測試用例,那最好讓QA和開發(fā)人員共享同一個代碼倉庫。如果你的集成測試系統(tǒng)是通過配置實(shí)現(xiàn)的,那也請將測試用例作為配置文件放到代碼倉庫中,而不是通過web編輯器放到數(shù)據(jù)庫中。這樣做的最大好處是項(xiàng)目代碼和集成測試代碼共享同一個分支,同一個build number,只有這樣才能做多分支的并行測試。否則如果測試用例單獨(dú)維護(hù),代碼的分支如何和測試用例對應(yīng)起來?最后的結(jié)果就是,自動化測試用例都是上線后補(bǔ)充的,上線前還是依賴人肉測試。
編寫測試用例和功能開發(fā)最好是同一個人,如果做不到,編寫測試用例的開發(fā)也要有權(quán)限修改業(yè)務(wù)代碼。因?yàn)橐鲎詣踊瘻y試必須在系統(tǒng)中留一些后門來給自動化測試提供便利。比如提供用戶的批量生成和銷毀,比如對測試的請求不記錄到統(tǒng)計日志中等等。
部署的腳本或者配置最好和項(xiàng)目在同一個源碼倉庫。只有這樣,自動化部署才方便實(shí)施。因?yàn)轫?xiàng)目的改進(jìn)以及重構(gòu),往往伴隨著依賴資源以及部署機(jī)制上的改造。
服務(wù)最好不依賴外部容器,可以獨(dú)立運(yùn)行。這個專指java的容器,其他的如go,nodejs都沒有這個問題。java的容器是企業(yè)應(yīng)用為了降低部署成本帶來的習(xí)慣,但當(dāng)前虛擬化,docker等技術(shù)這樣成熟的情況下,應(yīng)用容器已經(jīng)完全沒必要了。如果非要用,也最好直接和應(yīng)用打包在一起,讓應(yīng)用可以直接運(yùn)行,這對開發(fā)效率以及集成測試,都非常有幫助。
最好提供一種直接可以單進(jìn)程運(yùn)行整個系統(tǒng)而不依賴外部資源的配置,外部資源都用內(nèi)存版的庫進(jìn)行mock。這樣做的好處是可以非常快速的進(jìn)行初步的集成測試驗(yàn)證,同時也非常方便統(tǒng)計集成測試覆蓋率(通過單元測試覆蓋率工具即可實(shí)現(xiàn))。
如果公司有多個研發(fā)團(tuán)隊,最好共享CI池,這樣成本最小。有的公司為了省錢,避免超過免費(fèi)限制,部署多套CI。其實(shí)算下來這樣成本比購買商業(yè)版更高。
總結(jié),持續(xù)集成是最能體現(xiàn)一個團(tuán)隊的DevOps氛圍以及水平的一個場景,因?yàn)檎麄€流程需要開發(fā),測試,運(yùn)維的緊密協(xié)作,缺一不可。
注:本文轉(zhuǎn)自?Jolestar 的個人博客,鏈接http://jolestar.com/ci-teamcity-vs-jenkins/,微信公眾號 午夜咖啡
技術(shù)討論
· 猜你喜歡的文章?·
