和其他極限程式設計的實踐相同,持續整合背後的概念是:既然經常整合程式碼庫對我們有好處,為何不隨時整合呢?就整合而言,「隨時」意指每當有人提交程式到版本控制系統時。我的同事MikeRoberts說:「持續的頻繁程度遠超乎你的想像。」
『持續整合』是種根本上的觀念顛覆。如果沒有持續整合,你開發的軟體直至(通常是測試或整合階段)有人來驗證它能否運作,將一直處於無法執行狀態。有了持續整合,在每次修改後(假如有足夠全面的自動化測試集合)軟體都會被證明是執行的。
即便它被破壞了,你也能夠很快發現,並立即修復。將持續整合使用得很有效率的團隊會比沒有使用它的團隊更快交付軟體,且缺陷也較少。交付期間,缺陷被發現得越早,修復它的成本就越低,因此也就能大幅度節省成本和時間。因此,對於專業的軟體交付團隊,持續整合與版本控制同等重要。
實作持續整合
開始做持續整合之前,你需要完成三件事情:
1.版本控制
與專案相關的所有內容都必須簽入版本控制儲存庫中,包括產品程式碼、測試程式碼、資料庫腳本、建置與部署腳本,以及所有用於建置、安裝、執行和測試應用程式的東西。這些聽起來都是理所當然的事情,但奇怪的是,的確有些專案未曾使用版本控制。有些人認為,他們的專案不大,不需要使用版本控制。可在我們看來,現在沒有哪個專案小到可以不使用它。即便是在自己的電腦中為自己寫一些程式碼,我們也會使用版本控制。更何況現在已有許多簡單易用、功能強大且輕量的免費版本控制工具。
2.自動化建置
你要能在命令列中啟動建置流程。無論是透過命令列啟動IDE來建置應用程式,再執行測試,還是使用多個複雜的建置腳本透過互相呼叫的方式來完成都可以,但無論採用哪種機制,必須滿足此條件:人和電腦都能透過命令列自動執行應用程式的建置、測試及部署流程。
現在,整合式開發環境和持續整合工具的功能都非常強大。通常不需要切換到命令列,你就可以在使用整合式開發環境完成應用程式的建置時,同時執行測試。然而,我們認為,你仍然需要有能力透過命令列來執行,而無需透過整合式開發環境的建置腳本。對於這點,可能存在一些爭議,但我們的理由如下:
● 要能在持續整合環境中以自動化的方式來執行整個建置流程,以便出現問題時能夠審核。
● 應以相同的方式來對待建置腳本與程式碼庫。也就是對建置腳本進行測試,並不斷重構,讓它保持整潔且容易理解,但整合式的開發環境自動產生的建置流程基本上無法做到這件事。專案越複雜,這項工作就越重要。
● 使理解、維護和除錯建置的流程更容易,這將有利於和業務人員更加合作。
3.團隊共識
持續整合並非一種工具,而是一種實踐。它需要開發團隊能夠給予一定的投入並遵守一些準則,也需要每個人都能以小且增量式的方式頻繁將修改後的程式碼簽入主線,並一致認同「修復破壞應用程式的任意修改是最高優先順序的任務」。如果人們不能接受這個紀律,那就完全無法按預期透過持續整合提高品質。
基本的持續整合系統
為了做持續整合,你不一定需要持續整合式的軟體,正如我們所言,它是實踐,並不是工具。James Shore在「Continuous Integration on a Dollar a Day」一文中描述了一個非常簡單的方法,只需要一臺閒置的開發機器(developmentmachine)一個橡膠制玩具雞和一個桌上震鈴。這篇文章值得一讀,從中可以看出,只要有版本控制工具就可以做持續整合了。事實上,現今持續整合工具的安裝和執行都極為簡單。有幾個開放程式碼的工具可供選擇,例如,Hudson和受人尊敬的CruiseControl家族(CruiseControl、CruiseControl.NET和CruiseControl.rb)。其中,Hudson和CruiseControl.rb的啟動和執行特別簡單。CruiseControl.rb是很輕量的,若已掌握一些Ruby知識的人即可容易的擴展它。Hudson有許多外掛程式,這使它可以與建置和部署領域中的許多工具整合。
此書編寫之際,還有兩種商業化持續整合伺服器為小團隊提供了免費版本,它們是ThoughtWorks Studios開發的Go,以及JetBrains的TeamCity。其他流行的商業化持續整合伺服器還包括Atlassian的Bamboo和Zutubi的Pulse。進階的發佈管理及建置加速系統還有UrbanCode的AntHillPro、ElectricCloud的ElectricCommander,以及IBM的BuildForge,它們都可以用於簡單的持續整合。
假如能夠滿足之前所描述的先決條件,那在你選擇並安裝好持續整合工具之後,只要再花幾分鐘的時間設置一下就可以運作了。這些設置包括讓它知道到哪裡尋找原始程式碼控制儲存庫,必要時執行哪個腳本來編譯,並執行自動化提交測試,以及一旦最新提交破壞了應用程式,要透過哪種方式通知你。
首次在持續整合工具上執行建置時,你很可能會發現在執行持續整合工具的機器上缺少了一些必需的軟體和設定。此為獨一無二的學習機會,請將接下來你所做的工作全部記錄下來,並放入你的專案wiki。你應該花一些時間將系統依存的所有軟體和設定都簽入版本控制,並將重建出全新環境的整個活動轉變自動化流程。
接下來要讓全部的人開始使用持續整合伺服器。以下是簡單的流程。
一旦準備好要簽入最新的修改程式碼時,請遵循下列步驟:
1. 查看是否有建置正在執行。如果有你得等它執行完。如果它失敗了,則與團隊中的其他人一起將其修復,接著再簽入自己的程式碼。
2. 一旦建置完成且測試全部通過,就從版本控制儲存庫中將該版本的程式碼更新到自己的開發環境上。
3. 在自己的開發機器上執行建置腳本,並執行測試,以確保你的機器上的所有程式碼的運作都很正常。當然你也可以利用持續整合工具中的個人建置特性來完成此步驟。
4. 如果本地建置成功,就將你的程式碼簽入版本控制。
5. 接著等待包含你此次變更的建置結果。
6. 如果這次建置失敗了,就停止手中正在進行的事,在自己的開發機器上立即修復此問題,接著再回到步驟3。
7. 如果本次建置成功,你可以小小的慶祝一下,並開始下一項任務。
如果團隊中的每個人在每次提交程式時都能夠遵循這些簡單的步驟,你就可以很有把握的說:「只要是與持續整合一模一樣的環境上,我的軟體就可以運作。」
持續整合的前提條件
持續整合不會獨立幫你修復建置流程。事實上,如果你在專案中期才做這件事,就可能會非常痛苦。為了使持續整合能夠更有效,開始之前,你應該先做好以下這些事情。
頻繁簽入
對於持續整合而言,我們最重要的工作就是頻繁簽入程式碼。每天至少應該提交幾次程式。
這能讓你嚴格的看待重構並堅持維護;即便只有微小的變更。確保變更大量文件也不會與他人的工作發生衝突;允許開發人員的探索與嘗試,並在失敗時能還原至最近提交的版本。
建立全面的自動化測試套件
如果沒有一系列全面的自動化測試,那建置成功就只是讓應用程式能夠編譯並將其組裝起來。雖然對於某些團隊而言,這已是非常大的進步,但是,假如有一定程度的自動化測試,會讓你能更有信心的說:「我們的應用程式是可以運作的」。自動化測試有許多種,其中有三類測試我們會在持續整合建置中使用,它們分別是:單元測試、元件測試和驗收測試。
單元測試是用來對應用程式中某些小單元進行單獨測試(例如:一個方法、一個函數,或是一小組方法或函數之間的相互影響)。它們通常不需要啟動整個應用程式就可以執行,而且也不需要連接資料庫(如果應用程式本身就具有資料庫)、檔案系統或網路。它們也不需要將應用程式部署到類生產環境中執行。單元測試應該要執行得非常快,即使對於一個大型應用程式,整個單元測試套件也應該要在十分鐘之內完成。
元件測試用於測試應用程式中幾個元件。與單元測試一樣,它通常不必啟動整個應用程式,但有可能需要連接資料庫、存取檔案系統或其他系統——這些可以使用stub(樁)。元件測試的執行時間通常較長。
『驗收測試』的目的是驗證應用程式是否滿足業務需求所定義的驗收標準,包括應用程式提供的功能,以及其他特定需求,例如:容量、有效性、安全性等。驗收測試最好採用將整個應用程式執行於類生產環境的運作方式。當然,驗收測試的執行時間也較長。一個驗收測試套件連續執行一整天是很平常的事。透過使用這三類測試的組合,你就能確信導入的修改不會破壞任何現有的功能。
保持較短的建置和測試流程
如果程式碼建置和單元測試的執行需要耗費的時間很長,你會遭遇到一些麻煩,如下所示:
● 人們在簽入程式碼之前不願意在本地環境進行完整的建置和執行測試,導致建置失敗的機率越來越大。
● 持續整合流程需要耗費的時間太長,進而導致再次執行建置時,該建置會包含許多次的提交,這將很難確定到底是哪次提交破壞了本次建置。
● 人們簽入的頻率會降低,因為每執行一次建置和測試,都要等一陣子。
理想情況下,簽入前的預編譯和測試流程,以及持續整合伺服器上的編譯和測試流程應該都要在幾分鐘內結束。我們認為,十分鐘是個極限,最好是能在五分鐘以內,九十秒內完成是最理想的。十分鐘對於那些慣於操作小型專案的人而言,應該算是比較長的時間了,但對於那些經歷過需要耗費數小時編譯的老前輩而言,卻是非常短的時間。
接下來的要求乍看之下恰好和上一個(即需要有全面的自動化測試集合)互相矛盾。但是,有許多技術可以幫助你減少建置時間。首先要考慮的事情是讓測試的執行加速。XUnit這類型的工具,例如:JUnit和NUnit,可以提供每個測試的執行時間長度報告。找出那些執行較慢的測試,確認是否需要把它們最佳化,或是在確保相同覆蓋率和信心的前提下縮短測試時間。這件事情應該經常做。
然而,有時候需要將測試分成幾個階段,那要如何劃分階段呢?首先將其分成兩個階段。第1個階段用於編譯並執行軟體,對構成應用程式的所有類別(class)進行單元測試,並建立可部署的binary。此階段稱為「提交階段」。
第2個階段應該利用第1個階段所產生的binary進行驗收測試。假如你有整合測試及性能測試,也得一併執行。利用現代持續整合工具,可以很容易的建立起此種分階段的建置流程,它們能夠同時執行多個任務,並將執行結果收集起來,以便很容易看到執行狀態和結果。這套測試的提交階段應該在簽入前執行,而且每次簽入後,在持續整合伺服器上也要再執行一次。一旦通過簽入測試套件,就要馬上執行驗收測試的第2個階段,但此階段可能會需要更多時間。如果該階段耗時超過半小時,就要考慮使用較大型的多重處理器或是建立建置網格來並執行這些測試。現代的持續整合伺服器都能讓這件事變得很簡單。另外,有時候把一個簡單的冒煙測試套件加入提交階段,也是非常有用的。此冒煙測試套件應該執行一些簡單的驗收和整合測試,用來確保最常見的功能沒有被破壞――並在這些基本功能被破壞時,能夠很快得到回饋。
管理開發工作區
為保證開發人員的開發效率與清晰明確的思路,開發環境的管理是特別重要的。當開發人員剛展開新任務時,應該總是從一個已知的正確狀態開始。他們應該要能夠執行建置、執行自動化測試,以及在其可控制的環境上部署其開發的應用程式,通常是在他們自己的開發機器上。只有在特殊的情況下,才應使用共用環境開發。在本地開發環境上執行應用程式時,應確保所使用的自動化處理與持續整合環境中的一致性,亦與測試環境也是相同的,在生產環境中也是相同的。
達到此目標的第1個先決條件就是細心的設置管理,不僅是管理程式碼,還包括了測試資料、資料庫腳本、建置腳本和部署腳本,這些全都得要放入版本控制,並在開始編寫程式時,以它們「最新的正確版本」當作起點。「最新的正確版本」是指在持續整合伺服器上最近一次通過所有自動化測試的那個版本。
其次是對合作廠商相依設置的管理,也就是那些開發中所使用的函式庫和元件。應確保函式庫或元件的版本都是正確的,即它們的版本與你正在開發的原始程式碼的版本是相互匹配的。有些開放程式碼工具可以幫助管理合作廠商相依性,如:Maven和Ivy。然而,使用這些工具時,你需要格外小心以確保能正確設置這些工具,如此才能保證某些與合作廠商具相依性的最新版本不會每次都被重複下載到本地副本中。
對於大部分的專案,其所依存之合作廠商函式庫版本不會經常發生改變,所以最簡單的方法就是將這些函式庫隨你的程式碼一起提交到版本控制系統中。
最後就是確保自動化測試(包括冒煙測試)都能夠在開發機器上執行。對於一個大型系統,我們可能需要在開發機器上設置中間件系統(middleware system),用來執行記憶體中資料庫或單一使用者的資料庫版本。這的確要用一定的努力,但能夠讓開發人員在每次簽入前嘗試在自己的開發機器上執行應用程式,並在其上進行一次冒煙測試,就可以大幅度的改善應用程式的品質。事實上,一個良好應用程式架構的特徵就是不需要用太大的力氣即可讓應用程式執行在開發機上。
必不可少的實踐
持續整合是種實踐,而非一個工具,它的有效性依存於團隊紀律。要讓持續整合系統能夠發揮作用,特別是在面對大型且複雜的持續整合系統時,整個開發團隊就必須擁有高度的紀律性。持續整合系統的目標是,確保軟體在任何時候都可以運作。為了做到這點,以下是我們在自己的團隊中使用的一些實踐。
建置失敗之後不要簽入新的程式碼
持續整合的首要大忌就是明知建置已經失敗,還向失敗的建置簽入新的程式碼。如果建置失敗,開發人員應該儘快找出失敗的原因,並修復它。假如使用此種策略,我們每次都能非常迅速的找到失敗原因並修復它。如果我們同事中的某人簽入程式碼後使建置失敗了,那他們就是修復建置的最佳人選。此時,他們一定不希望別人再簽入新程式碼,因為如此一來,會觸發新的建置,使問題變得更多。一旦此規則被破壞,耗費更多時間去修復就不可避免了。接著,人們就會習慣於看到建置失敗,而且你很快就會發現,建置會一直處於失敗狀態而無人在意。此種狀態會一直持續下去,直到某個人忍無可忍,挺身而出,費了很多力氣才把它修好。但是,如果無人遵守規則,此情形就會反覆上演。因此,在把建置變綠之後,最好藉此機會提醒每個人都遵守此規則,以確保建置一直是綠的,讓軟體一直處於可用的狀態。
提交前先在本地執行全部的提交測試,或是讓持續整合伺服器完成此事
正如之前提過的,我們希望每次提交都可以產生一個可發佈的候選版本。任何人以任何形式公佈某個東西之前,都會檢查一下自己的工作成果,而候選版本也是個發行物,所以每次簽入前也要做一下檢查。我們希望簽入是件輕量的事,如此即可每隔二十分鐘左右就提交一次,但它也應該是件非常嚴肅的事,因此每次提交之前,我們都得停下來,仔細想一想是否應該提交。提交前在本地執行一次提交測試,就等於是做一次健全性檢查(sanity check)。它也讓我們能確信新增的程式碼的確是按期望的方式在執行的。
當開發人員準備提交時,應該從版本控制系統中,更新專案本地副本,接著進行本地建置,並執行提交測試。只有當全部成功之後,開發人員才能將程式提交到版本控制系統。(摘錄整理自第三章)
持續整合的首要大忌就是明知建置已經失敗,還向失敗的建置簽入新的程式碼。開發人員應該儘快找出失敗的原因,並修復它。
Continuous Delivery中文版:利用自動化的建置、測試與部署完美創造出可信賴的軟體發佈
Jez Humble, David Farley/著;喬梁/譯
傅育文/審校
博碩出版
售價:650元
作者簡介
Jez Humble
ThoughtWorks公司首席諮詢顧問,經常於各種敏捷技術大會上發表演講,致力於幫助企業能夠快速、可靠的交付出高品質的軟體。
David Farley
正使用『本書中描述的主要技術之一』為LMAX(倫敦多元資產交易)組織建設世界上性能最佳的金融交易平臺。
具有20年大型分散式系統的開發經驗,是採行敏捷開發技術的先驅者,曾以技術負責人的身分參與ThoughtWorks公司許多規模最大且最具有挑戰性的軟體專案。
熱門新聞
2024-11-18
2024-11-20
2024-11-12
2024-11-15
2024-11-15
2024-11-19