構建可持續部署的Pipeline

持續之根本

CI的核心目標是快速頻繁集成代碼,通過一些手段(編譯、代碼檢查、運行測試、覆蓋率等)來持續地提供及時有效的反饋,可持續從何而來?前提就是這些手段都是行之有效的。

將CI的那些手段對應到每一次集成中的各個步驟,這些步驟應該是值得信賴的,比如單元測試、集成測試、E2E測試,確保它們都能夠真實有效地反饋當前代碼庫的集成狀態。試想一下,集成中單元測試步驟雖然存在,但其實沒有運行任何有用的測試,亦或代碼中沒有添加任何測試,那么即便這一步通過了,發布的軟件也是不可靠的。持續之根本取決于每一步的可靠性。

以一個Java Web工程為例,一次集成通常含有以下步驟:

這里面的每一個步驟循序漸進,必須都是通過后才能持續往后走,而通過也必須是有意義的通過,而不只是亮了一個空殼綠燈。

實踐指導:
有些項目的E2E測試運行時間很長,E2E測試可能就選在夜間運行,而不是在打包前必須要運行的步驟。另外,有些項目只有單元測試。


CI何所為

所謂CI,本質上在一個可被觸及的中心服務器去集成那些提交到中心代碼庫的代碼,并將一些原本可以重復手工完成檢驗步驟自動化起來。

無CI論者NoCI會說:“CI其實沒啥用,你們大可把代碼提交,我pull下來做集成。我在我的機器跑測試,然后進行部署,一旦掛了我通知你們...”

一句話包含了多少辛酸和無奈:

1. 每天那么多次提交,NoCI這哥們完全不能知曉其他人何時提交。
2. 運行了測試掛了,通知誰?都不知道是誰的提交導致掛了,提供不了即時的反饋。
3. 即便別人提交后通知他,那他每天要多次重復去做這項工作,恐怕沒有什么產出了,就等著被fire了。
4. 自己機器還要開發,還要運行很多其他程序,運行測試跑了1個小時,一天跑八次測試就該下班了。

聽起來,NoCI這哥們也能做這些事情,只不過效果沒那么好,還附帶了被Fire的風險。實際上,CI也并不神奇,一句話簡單概括CI何所為:

CI做的事情就是將重復的手工工作自動化管理起來,并提供即時有效的反饋。

實踐指導:
反過來想,CI能做的事情,你都能在本地手動去做,所以在搭建CI的時候,可以現在本地手動驗證你在CI所設置的任務是否正確。


踏上征途

從畢業到現在,經歷了多個不同項目,從!測試 && !CI-->本地測試-->CI && 測試-->強制 CI && 測試,對CI歷經了從0到1的過程,一開始非常享受CI所帶來的好處,如今便是身處酒巷,久而不覺酒香的狀態:在一個項目啟動的時候,首先會在Iteration 0將CI作為必備的基礎設施搭建好,然后步入開發階段。

CI策略由簡入繁,簡單的比如一個Android的build,跑完單元測試、打包apk、發布apk就完事了,復雜的就好比一個微服務的CI,每一個微服務除了完善的單元測試以及嚴格的集成測試,還需要定義清楚各個微服務之間如下一些構建依賴關系:

1. 構建的順序,服務B會依賴服務A的構建參數或者只有A成功后才構建B。
2. 微服務部署的順序,定義哪些服務部署是允許失敗的,以及失敗后的救援措施。
3. 微服務在多個環境的部署時的版本控制。

實際中的CI實踐通常介于簡單和復雜之間,這里我以過去幾個項目經歷為常規CI的依據,主要包含了四個步驟:

1. 解析依賴(編譯)。
2. 運行測試。
3. 打包。
4. 部署/發布

最重要的屬2和4。因為測試是代碼庫質量的保證,而測試也是我們搭建CI的前提,沒有測試的CI猶如牛刀殺雞。而部署則是關系到軟件的交付,只有做到自動化部署發布,CI才算完成了它的最后使命(那些由某些客觀因素導致無法自動化部署的場景不在討論范圍之內)。


解析依賴(編譯)

CI通常開始源代碼的獲取,獲取了所有完整的代碼庫后,首先要做的事情便是解析項目所需的依賴并經行編譯。為什么會解析依賴呢?在實際開發過程中,項目往往依賴很多第三方類庫,開發人員在搭建本地開發環境的時候,首次需要消耗一定的時間去下載依賴,在后續開發過程中,只要所下載的依賴沒有被刪除,就不會重復下載。而CI也少不了這個過程,所以第一次構建的時候,也會需要去下載這些依賴,然后進行編譯。所以需要保證CI服務器能夠正常下載第三方依賴

當然,也有人說,把這些依賴直接放入代碼庫中不就完事了嗎?對,這就是我正要提的一個反模式,為什么稱之為反模式呢,以下簡單列出這種實踐的幾個弊端:

1. 代碼庫體積過大。第三方依賴往往體積較大(多則幾個GB),提交到代碼庫會導致代碼庫體積很大,而我們應該有責任去讓代碼庫盡量保持精簡。
2. 影響部署效率。第三方依賴體積過大,導致每次部署的時候需要傳輸的大量文件,較大程度影響了部署流程,降低效率。
3. 版本沖突。團隊中開發人員A升級了依賴的版本,而其他開發人員在本地也進行了升級,提交后會難免會引發沖突,造成不必要的麻煩。
4. 違背了DRY(Don't Repeat Yourself)。因為每個人都保存了一份完整的依賴,一旦有改動,需要通知并傳達給所有人大量的信息,而如果每個人只是持有依賴的定義(配置文件),相當于持有對象的引用,所有人根據引用來更新依賴,他們之間需要傳遞的信息就變得非常精簡。

實踐指導:
當開發、CI只能在內網中進行時,就無法從互聯網上下載依賴,此時需要在CI所在的內網中搭建一個私有倉庫,用來專門存放第三方依賴,項目所需要的依賴從該倉庫中下載。比如Java工程,可以搭建一個私有的Maven倉庫,Gradle在解析依賴的時候,將源指向該私有倉庫。


測試

運行測試的策略通常借鑒于測試金字塔,單元測試、集成測試、E2E測試。關于測試的最佳實踐則是將這三部分測試都涵蓋進去。首先CI會每次自動運行單元測試,然后自動運行集成測試,最后是E2E測試。完美的情況下,每一次提交三部分測試都會運行,只有所有測試通過后才進入下一個環節。而在實際中,有些項目E2E測試運行的時間較長,以至于對集成和部署造成了一定的影響,此時我們需要做一些優化措施,可以并發運行E2E測試(Jenkins中join pulgin):

實踐指導:
測試三步曲中,最難的是數據準備,要做到測試能夠并發運行,就需要在設計E2E測試用例的時候,保證Test Case的準備數據和數據回滾的獨立性,但考慮到每一個Test Case都完全獨立,成本較大(數據庫數據的初始化和回滾),通常是對同一個功能模塊中存在依賴關系的Test Case做一次數據準備和數據回滾,Test Case則按照一定的順序運行。或者根據并發Salve的粒度來劃分獨立邊界。

關于測試數據準備和數據回滾的邊界,舉個例子:

一個電子商務系統中有登錄、用戶管理、地址管理、商品管理模塊、購物車5個模塊,我們將登錄、用戶管理和地址管理模塊的測試放在的Slave A上,將商品管理和購物車模塊的測試放在Slave B上運行,邊界劃分的粒度就存在以下三種:
1. 以單個Test Case作為邊界,運行任何Test Case都會做一次初始化和回滾。(非常耗時)
2. 以功能模塊作為邊界,每運行一個模塊的測試做一次初始化和回滾。(較耗時)
3. 以Slave作為邊界,比如Salve A在運行E2E測試的只做一次初始化和回滾,且Slave A和Slave B的數據互不干擾。(推薦的方式)

打包

之前某位UX跟我們開發人員要炸包,一開始把我愣住了,后來得知她原來是想問工程構建的jar包。

沒錯,Java開發人員最常接觸包就是jar文件(jar包)或war文件(war包)。另外,Android工程則會輸出apk,IOS工程會輸出ipa,JavaScript工程則會輸出丑化后的css和js文件,等等。所以,CI打包步驟是繼測試后的一個打包步驟,它為部署做準備。以Java Web工程為例,CI在測試通過后,構建出一個war包,之后就將該war包存檔起來,供下一步使用。所構建的war需要兼顧以下幾點:

1. 包含程序運行所需要的第三方依賴。
2. 包含項目中涉及的資源文件(css、js、image、icon等)。
3. 壓縮css和js相關資源。
4. 不包含任何測試相關的代碼和類庫。
5. 不包含任何程序運行時不需要的文件。

部署/發布

通產情況下,部署/發布 往往被認為是從開發到生產環境的最后一步,在本文的范疇中,我也把它列為了最后一步(實際中,部署到生產環境后,運營監控才剛剛開始)。

依賴解析完成、自動化測試都通過、軟件包構建完畢,此時我們需要將軟件包部署到生產環境中供測試和用戶使用了。有人會說:“這個簡單,直接將軟件包部署到生產環境的服務器上,啟動服務不就完畢了!” 真有這么簡單?先來嘗試回答一下幾個問題:

1. 直接部署到生產環境,軟件不存在漏洞了嗎?
2. 軟件在交付前,回歸測試能夠直接在生產環境上進行嗎?
3. 在敏捷開發中,需要定期給客戶showcase,此時用什么環境來demo呢?
4. 軟件在正式上線前,用戶通常會安排一個期限的UAT(用戶驗收測試),這個環境又如何來?
5. 重新部署到生產環境,如果用戶正在使用著軟件怎么辦?

以上幾個問題引出了經典的TestStagingUATProduction四個環境。分別用于測試、演示、用戶驗收、生產運營。另外,開發人員通常在本地機器會運行一個Dev環境。下圖總結了以上四個環境的部署方式:

UATProduction 環境被虛線框起來,因為這兩個環境在某些情況下是交付團隊是沒有權限去控制的,此時是由客戶的專門人員負責一鍵部署。

實踐指導:
通常部署后需要管理多個服務的啟動,而且這些服務存在依賴關系,它們的啟動順序也是有一定先后,可以使用surpervisord,來管理這些服務的啟動順序和重啟策略。


總結

對于一個敏捷團隊(高效協作和快速反饋),CI是項目交付中必不可少的基礎設施,無論你是一名開發人員還是測試人員或者是Team Lead(PM),都應該盡全力捍衛CI的地位。


注釋

  • CI: Contiuous Integration,持續集成
  • PM:Project Manager,項目經理
  • UAT:User Acceptance Test,用戶驗收測試
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容