CI/CD笔记:《持续交付:发布可靠软件的系统方法》
生活随笔
收集整理的這篇文章主要介紹了
CI/CD笔记:《持续交付:发布可靠软件的系统方法》
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
《持續交付:發布可靠軟件的系統方法》
- 前言
- 軟件交付的問題
- 配置管理
- 持續集成
- 測試策略的實現
- 部署流水線解析
- 構建與部署的腳本化
- 提交階段
- 自動化驗收測試
- 非功能需求的測試
- 應用程序的部署與發布
- 基礎設施與環境管理
- 數據管理
- 組件和依賴管理
- 版本控制進階
- 持續交付管理
前言
- 自動化是關鍵,它讓開發人員、測試人員和運營人員能夠通過一鍵式操作完成軟件創建和部署過程中的所有常見任務。
- 我們的目標是改變軟件交付方式,將其由開發人員的手工操作變成一種可靠、可預期、可視化的過程并在很大程度上實現了自動化的流程,而且它要具備易于理解與風險可量化的特點。
- 敏捷宣言的第一原則:“我們的首要任務是盡早持續交付有價值的軟件并讓客戶滿意。”這也反映了這樣一個現實:對于成功的軟件,首次發布只是交付過程的開始。
- 書中描述的所有技術都與交付軟件新版本給客戶相關,旨在減少時間和降低風險。這些技術的核心是增加反饋,改進負責交付的開發、測試和運維人員之間的協作。這些技術能確保當需要修改應用程序(也許是修復缺陷,也許是開發新功能)時,從修改代碼到正式部署上線之間的時間盡可能短,盡早發現缺陷以便快速修復,并更好地了解與本次修改相關的風險。
軟件交付的問題
- 對于應用程序的配置、源代碼、環境或數據的每個變更都會觸發創建一個新流水線實例的過程。流水線的首要步驟之一就是創建二進制文件和安裝包,而其余部分都是基于第一步的產物所做的一系列測試,用于證明其達到了發布質量。每通過一步測試,我都會更加相信這些二進制文件、配置信息、環境和數據所構成的特殊組合可以正常工作。如果這個產品通過了所有的測試環節,那么它就可以發布了。
- 部署流水線的目標有三個。首先,它讓軟件構建、部署、測試和發布過程對所有人可見,促進了合作。其次,它改善了反饋,以便在整個過程中,我們能夠更早地發現并解決問題。最后,它使團隊能夠通過一個完全自動化的過程在任意環境上部署和發布軟件的任意版本。
- 手工部署過程依賴于部署專家。如果專家去度假或離職了,那你就有麻煩了。盡管手工部署枯燥且極具重復性,但仍需要有相當程度的專業知識。若要求專家做這些無聊、重復,但有技術要求的任務則必定會出現各種我們可以預料到的人為失誤,同時失眠,酗酒這種問題也會接踵而至。然而自動化部署可以把那些成本高昂的資深高技術人員從過度工作中解放出來,讓他們投身于更高價值的工作活動當中。
- 我們的對策就是將測試、部署和發布活動也納入到開發過程中,讓它們成為開發流程正常的一部分。這樣的話,當準備好進行系統發布時就幾乎很少或不會有風險了,因為你已經在很多種環境,甚至類生產環境中重復過很多次,也就相當于測試過很多次了。而且要確保每個人都成為這個軟件交付過程的一份子,無論是構建發布團隊、還是開發測試人員,都應該從項目開始就一起共事。
- 軟件發布能夠(也應該)成為一個低風險、頻繁、廉價、迅速且可預見的過程。
- 有用性的一個重要部分是質量。我們的軟件應該滿足它的業務目的。質量并不等于完美,正如伏爾泰所說“追求完美是把事情做好的大敵”,但我們的目標應該一直是交付質量足夠高的軟件,給客戶帶來價值。因此,盡快地交付軟件很重要,保證一定的質量是基礎。
- 持續集成:每次提交都對應用程序進行構建并測試,這稱作持續集成。
- 流程反饋:它是指完全以自動化方式盡可能地測試每一次變更。
- 人力資源是昂貴且非常有價值的,所以我們應該集中人力來生產用戶所需要的新功能,盡可能快速地交付這些新功能,而不是做枯燥且易出錯的工作。像回歸測試、虛擬機的創建和部署這類工作最好都由機器來完成。
- 盡可能全面,即75%左右的代碼庫覆蓋率。只有這樣,這些測試通過以后,我們才對自己寫的軟件比較有信心。
- 對于快速交付高質量的軟件來說,基于持續改進的過程是非常關鍵的。迭代過程有助于為這類活動建立規律性,例如每個迭代至少開一次回顧會議,在會上每個人都應參與討論如何在下一個迭代中改進交付過程。
- 精益制造的目標是確保快速地交付高質量的產品,它聚焦于消除浪費,減少成本。
- 部署流水線的一個關鍵點是,它是一個“拉動”(pull)系統,它使測試人員、運維人員或支持服務人員能夠做到自服務,即他們可以自行決定將哪個版本的應用程序部署到哪個環境中。
- 減少壓力的關鍵在于擁有一個我們前面所描述的自動化部署過程,并頻繁地運行它,當部署失敗后還能夠快速恢復到原來狀態。
- 在每次以同一種方式部署應用軟件時,也是驗證我們的部署機制是否正確的時機。事實上,向其他任何環境的任何一次部署過程都是生產環境部署的一次演練。
- 如果在軟件開發中的某個任務令你非常痛苦,那么解決痛苦的方法只有更頻繁地去做,而不是回避。因此,我們應該頻繁做集成,事實上應該在每次提交修改后都做集成。持續集成這個實踐將頻繁集成發揮到了極至,而“持續集成”轉變了軟件開發過程。持續集成會及時檢測到任何一次破壞已有系統或者不滿足客戶驗收測試的提交。一旦發生這種情況,團隊就立刻去修復問題(這是持續集成的首要規則)
- 軟件發布的可重復性和可靠性來自于以下兩個原則:(1)幾乎將所有事情自動化;(2)將構建、部署、測試和發布軟件所需的東西全部納入到版本控制管理之中。
- 驗收測試是可以自動化的,數據庫的升級和降級也是可以自動化的,甚至網絡和防火墻配置也是可以自動化的。你應該盡可能自動化所有的東西。
- 如果創建應用程序的說明文檔是你的痛點,那么每開發一個功能時就應寫好文檔,而不是留到最后一起寫。把一個功能的說明文檔也作為“DONE”的一個驗收條件,并盡可能自動化這個過程。
- 越早發現缺陷,修復它們的成本越低。如果在沒有提交代碼到版本控制之前,我們就能發現并修復缺陷的話,代價是最小的。
- “內建質量”還有另外兩個推論。(1)測試不是一個階段,當然也不應該開發結束之后才開始。如果把測試留在最后,那就為時晚矣,因為可能根本沒有時間修復那些剛被發現的問題。(2)測試也不純粹或主要是測試人員的領域。交付團隊的每個人都應該對應用程序的質量負責。
- 理想情況下,團隊中的成員應該有共同的目標,并且每個成員應在工作中互相幫助來實現這一目標。無論成功還是失敗,其結果都屬于這個團隊,而非個人。
- 關鍵在于組織中的每個人都要參與到持續改進過程當中。如果只在自己所在角色的內部進行反饋環,而不是在整個團隊范圍內進行的話,就必將產生一種“頑疾”:以整體優化為代價的局部優化,最終導致互相指責。
配置管理
- 我們所討論的有關加快發布周期和提高軟件質量的所有實踐,從持續集成、自動化測試,到一鍵式部署,都依賴于下面這個前提:與項目相關的所有東西都在版本控制庫中。
- 只要能從版本控制庫中取出所需要的一切,就能保證為開發、測試,甚至生產環境提供一個穩定的平臺。然后你可以將整個環境(包括配置基線上的操作系統)做成一個虛擬鏡像,放在版本控制庫中,這可以作為更高級別的保證措施,而且可以提高部署的簡單性。
- 這種很長時間才提交的做法是有問題的。因為提交越頻繁,越能夠體現出版本控制的好處。除非每個人都頻繁提交,否則“安全地對系統進行重構”這件事基本上是不可能完成的任務。因為長時間不提交代碼會讓合并工作變得過于復雜。如果你頻繁提交,其他人可以看到你的修改且可與之交互,你也可以清楚地知道你的修改是否破壞了應用程序,而且每次合并工作的工作量會一直很小,易于管理。
- 任何改變應用程序的行為,無論修改了什么,都算是編程,即使只是修改一行配置信息。你進行修改所使用的語言可能或多或少地受到限制,但此時仍是在編程。根據定義,要為用戶提供的軟件配置能力越強,你能置于系統配置的約束就應越少,而你的編程環境也會變得越復雜。
- 將那些特定于測試環境或生產環境的實際配置信息存放于與源代碼分離的單獨代碼庫中通常是非常必要的。因為這些信息與源代碼的變更頻率是不同的。
- 如果應用程序所依賴的任何部分沒有準備好,部署或安裝腳本都應該報錯,這相當于配置設置的冒煙測試。
- 我們通常在需要時才臨時決定如何管理配置信息,其后果是每個應用的配置信息被放在不同的位置,而應用程序又以不同的方式獲取這些配置。這會給確定“哪些環境中有哪些配置”帶來不必要的困難。
- 環境管理的關鍵在于通過一個全自動過程來創建環境,使創建全新的環境總是要比修復已受損的舊環境容易得多。
- 即便很微小的變化也可能把環境破壞掉。任何變更在上線之前都必須經過測試,因而要將其編成腳本,放在版本控制系統中。這樣,一旦該修改被認可,就可以通過自動化的方式將其放在生產環境中。
- 沒有配置管理,根本談不上持續集成、發布管理以及部署流水線。它對交付團隊內部的協作也會起到巨大的促進作用。
持續集成
- 持續集成要求每當有人提交代碼時,就對整個應用進行構建,并對其執行全面的自動化測試集合。而且至關重要的是,假如構建或測試過程失敗,開發團隊就要停下手中的工作,立即修復它。持續集成的目標是讓正在開發的軟件一直處于可工作狀態。
- 持續集成不是一種工具,而是一種實踐。它需要開發團隊能夠給予一定的投入并遵守一些準則,需要每個人都能以小步增量的方式頻繁地將修改后的代碼提交到主干上,并一致認同“修復破壞應用程序的任意修改是最高優先級的任務”。如果大家不能接受這樣的準則,則根本無法如預期般通過持續集成提高質量。
- 單元測試用于單獨測試應用程序中某些小單元的行為(比如一個方法、一個函數,或一小組方法或函數之間的交互)。它們通常不需要啟動整個應用程序就可以執行,而且也不需要連接數據庫(如果應用程序需要數據庫的話)、文件系統或網絡。它們也不需要將應用程序部署到類生產環境中運行。單元測試應該運行得非常快,即使對于一個大型應用來說,整個單元測試套件也應該在十分鐘之內完成。
- 組件測試用于測試應用程序中幾個組件的行為。與單元測試一樣,它通常不必啟動整個應用程序,但有可能需要連接數據庫、訪問文件系統或其他外部系統或接口(這些可以使用“樁”,即stub技術)。組件測試的運行時間通常較長。
- 驗收測試最好采用將整個應用程序運行于類生產環境的運作方式。當然,驗收測試的運行時間也較長。一個驗收測試套件連續運行一整天是很平常的事兒。
- 在使用持續集成之前,很多開發團隊都使用每日構建(nightly build)。當時,微軟使用這個實踐已經很多年了。誰破壞了構建,就要負責監視后續的構建過程,直至發現下一個破壞了構建的人。
- 在提交代碼時,做出了這一代碼的開發人員應該監視這個構建過程,直到該提交通過了編譯和提交測試之后,他們才能開始做新任務。在這短短幾分鐘的提交階段結束之前,他們不應該離開去吃午飯或開會,而應密切注意構建過程并在提交階段完成的幾秒鐘內了解其結果。
- 在這里需要澄清一下,我們并不建議你工作到很晚來修復失敗的構建,而是希望你有規律地盡早提交代碼,給自己足夠的時間處理可能出現的問題。或者,你可以第二天再提交。很多有經驗的開發人員在下班前一小時內不再提交代碼,而是把它作為第二天早上的第一件事情。如果所有手段都不好使,那么把版本控制庫中的代碼回滾到上一次成功構建的狀態,并在本地保留一份失敗的代碼就可以了。
- 如果某次提交失敗了,無論采取什么樣的行動,最重要的是盡快讓一切再次正常運轉起來。如果無法快速修復問題,無論什么原因,我們都應該將它回滾到版本控制庫中前一個可工作的版本上,之后再在本地環境中修復它。
- 那些已經成功運行了一段時間的測試失敗時,失敗的原因可能很難找。這種失敗是否真的意味著發現了一個回歸問題呢?也許這個測試不再是有效的測試了,也許是因為原有功能因需求變化被改變了。找出真正的失敗原因可能需要向很多人了解情況,并且需要花上一段時間,但這是值得的。我們的選擇是要么修復代碼(如果是回歸問題的話),要么修改測試(如果該測試以前的某個假設不成立了),或者刪除它(如果被測試的功能已經不存在了)。
- 假如提交代碼后,你寫的測試都通過了,但其他人的測試失敗了,構建結果還是會失敗。通常這意味著,你引入了一個回歸缺陷。你有責任修復因自己的修改導致失敗的那些測試。
- 只有非常高的單元測試覆蓋率才有可能保證快速反饋(這也是持續集成的核心價值)。完美的驗收測試覆蓋率當然也很重要,但是它們運行的時間會比較長。根據我們的經驗,能夠達到完美單元測試覆蓋率的唯一方法就是使用測試驅動開發。盡管我們盡量避免在本書中教條式地提及敏捷開發實踐,但我們認為測試驅動開發是持續交付實踐成為可能的關鍵。
- 所謂測試驅動開發是指當開發新的功能或修復缺陷時,開發人員首先要寫一個測試,該測試應該是該功能的一個可執行規范。這些測試不但驅動了應用程序的設計,而且既可以作為回歸測試使用,也是一份代碼的說明文檔,描述了應用程序預期的行為。
- 重構是指通過一系列小的增量式修改來改善代碼結構,而不會改變軟件的外部行為。通過持續集成和測試驅動開發可以確保這些修改不會改變系統的行為,從而使重構成為可能。
- 只要你指定某個倉庫作為主庫(master),每次更改這個倉庫就觸發持續集成服務器上的一次構建,并讓每個人都將其修改推送到這個倉庫中來實現共享。很多使用分布式系統的項目都使用這種方式,而且非常成功。
- 持續集成的使用會為團隊帶來一種開發模式上的轉變。沒有持續集成的話,直到驗證前,應用程序可能一直都處于無法工作的狀態,而有了持續集成之后,應用程序就應該是時刻處于可工作狀態的了,雖然這種自信取決于自動化測試覆蓋率。
測試策略的實現
- 測試是跨職能部門的活動,是整個團隊的責任,應該從項目一開始就一直做測試。
- 質量內嵌是指從多個層次(單元、組件和驗收)上寫自動化測試,并將其作為部署流水線的一部分來執行,即每次應用程序的代碼、配置或環境以及運行時所需軟件發生變化時,都要執行一次。
- 手工測試也是質量內嵌的關鍵組成部分,如演示、可用性測試和探索性測試在整個項目過程中都應該持之以恒地做下去
- 測試策略的設計主要是識別和評估項目風險的優先級,以及決定采用哪些行動來緩解風險的一個過程。
- 回歸測試是自動化測試的全集。它們用來確保任何修改都不會破壞現有的功能,還會讓代碼重構變得容易些,因為可以通過回歸測試來證明重構沒有改變系統的任何行為。
- 一般我們將代碼覆蓋率高于80%的測試視為“全面的”測試,但測試質量也非常重要,單單使用覆蓋率這一指標是不夠的。
- 一個很好的經驗法則就是,一旦對同一個測試重復做過多次手工操作,并且你確信不會花太多時間來維護這個測試時,就要把它自動化
- 一般來說,驗收測試都是端到端的測試,并運行在一個與生產環境相似的真實工作環境中。
- 單元測試不應該訪問數據庫、使用文件系統、與外部系統交互。或者說,單元測試不應該有系統組件之間的交互。這會讓單元測試運行非常快,因此可以得到更早的反饋,了解自己的修改是否破壞了現有的任何功能。這些測試也應該覆蓋系統中每個代碼分支路徑(最少達到80%)。這樣,它們就組成了回歸測試套件的主要部分。
- 部署測試用于檢查部署過程是否正常。換句話說,就是應用程序是否被正確地安裝、配置,是否能與所需的服務正確通信,并得到相應的回應。
- 探索性測試是一個創造性的學習過程,并不只是發現缺陷,它還會致使創建新的自動化測試集合,并可以用于覆蓋那些新的需求。
- 非功能測試是指除功能之外的系統其他方面的質量,比如容量、可用性、安全性等。
- 樁(stub)是在測試中為每個調用提供一個封裝好的響應,它通常不會對測試之外的請求進行響應,只用于測試。
- 盲目地用書寫差勁的驗收條件實現自動化測試是產生不易維護的驗收的測試套件的主要原因之一。
- 假如你發現對同一個功能重復進行了多次的手工測試,就要判斷一下這個功能是否還會被修改。如果不會的話,就將這個測試自動化。
- 坐下來與用戶一起識別系統中高價值的功能是非常重要的。利用前面一節所說的技術,創建一套廣泛的自動化測試,覆蓋這些高價值的核心功能。
- 對于遺留系統來說,這些覆蓋核心功能的測試就是非常重要的冒煙測試了。
- 與沒有考慮可測試性的那些系統相比,在設計時就考慮到可測試性的系統,其標準組件化的傾向更強,而且更容易測試。
- 當軟件需要在很多不同的環境上運行時,情況就不同了。此時,自動化測試和類生產環境的自動化部署相結合,會給項目帶來巨大的價值,因為可以把腳本直接指向需要測試的環境,從而節省大量的手工測試時間與精力。
- 在組件測試和集成測試之間的分界線并不十分清晰(尤其當“集成測試”這個詞被賦予了太多的意義)。我們所說的“集成測試”是指那些確保系統的每個獨立部分都能夠正確作用于其依賴的那些服務的測試。
- 這些自動化集成測試可以當成向生產環境部署系統時的冒煙測試,也可以作為一種診斷方法來監控生產環境中的系統行為。
- 我們可以把缺陷分為嚴重(critical)、阻塞(blocker)、中(medium)和低(low)四個級別。
- 測試主要是建立反饋環,而這個反饋環會驅動開發、設計和發布等活動。將測試推遲到項目后期的計劃最終都會失敗,因為它破壞了產生高質量、高生產率,以及(最重要的)反映項目進展情況的反饋環。
部署流水線解析
- 持續集成主要關注于代碼是否可以編譯成功以及是否可通過單元測試和驗收測試。但持續集成并不足以滿足我們的需要。持續集成的主要關注對象是開發團隊。持續集成系統的輸出通常作為手工測試流程和后續發布流程的輸入
- 由于很容易將應用程序部署到測試環境中,所以團隊可以同時得到軟件功能和部署流程兩個方面的快速反饋。
- 從精益的角度來看,我們實現了一個“拉式系統”(pull system),即測試團隊只要自己單擊按鈕,就能將某個特定的軟件版本部署到測試環境中。運維人員也可以通過單擊一下按鈕就把軟件部署到試運行環境和生產環境中。
- 部署流水線是指軟件從版本控制庫到用戶手中這一過程的自動化表現形式。
- 二進制包應該只在構建流水線的提交階段生成一次。這些二進制包應該保存在文件系統的某個位置上,讓流水線的后續階段能夠輕松地訪問到這個位置,但要注意不要放在版本控制庫中,因為它只是一個版本的衍生品,并不是原生態的定義。
- 我們不想在那些明顯有問題的版本上花時間和精力,所以當開發人員提交變更到版本控制系統后,我們希望盡快地評估一下這個最新版本。提交者要一直等到構建結果,然后才能做下一項工作。
- 當缺陷還比較容易修復時,盡快得到反饋是非常重要的,而不應花更大的代價得到全面的反饋。
- 對于在這個待發布的候選版本,第一階段的成功是一個重要的里程碑,它是這個部署流水線的一個關卡。一旦通過這個關卡,開發人員就被從上一個任務中釋放出來,開始做下一個任務了。然而,他們仍舊有責任監視后續階段的運行狀況。即使后續階段出了問題,修復失敗的構建仍舊是開發團隊的首要任務。我們賭自己能成功,可一旦賭輸了,也已準備好去償還技術債。
- 生產環境中的大多數問題往往是由不充分的控制導致的。正如我們在第11章中所講的,生產環境應該是完全受控的,即對生產環境的任何修改都應該通過自動化過程來完成。這不僅包括應用程序的部署,還包括對配置、軟件棧、網絡拓撲以及狀態的所有修改。只有在這種方式下,我們才可能對它們進行可靠地審計和問題診斷,并在可預計的時間內修復它們。隨著系統復雜性的增加,不同類型服務器的增多,以及不斷提高的性能需求,我們就更需要這種程度的控制力。
- 實現部署流水線的第一步是將構建和部署流程自動化。構建過程的輸入是源代碼,輸出結果是二進制包。
- 每當有人提交后,持續集成服務器就應執行構建,持續集成服務器應該監視版本控制系統,每當發現有新提交的代碼時,就簽出或更新源代碼,運行自動化構建流程,并將生成的二進制包放在文件系統的某個地方,使整個團隊都能通過持續集成服務器的用戶界面獲取。
- 部署活動可能包含:(1)為應用程序打包,而如果應用程序的不同組件需要部署在不同的機器上,就要分別打包;(2)安裝和配置過程應該實現自動化;(3)寫自動化部署測試腳本來驗證部署是否成功了。部署流程的可靠性是非常重要的,因為它是自動化驗收測試的前提條件。
- 在開發構建和部署系統的過程中,一定要確保遵循前面說過的那些原則,如只生成一次二進制包,將配置信息與二進制包分離,以便在不同環境的部署中可以使用相同的二進制包。這能確保配置管理有一個健全的基礎。
- 開發部署流水線的下一步就是實現全面的提交階段,也就是運行單元測試、進行代碼分析,并對每次提交都運行那些挑選出來的驗收測試和集成測試
- 因為單元測試并不需要訪問文件系統或數據庫(與之對應的是組件測試),所以運行速度應該很快。這也是構建應用程序之后就直接運行單元測試的原因。與此同時,還可以運行一些靜態分析工具,得到一些有用的分析數據,比如代碼風格、代碼覆蓋率、圈復雜度、耦合度等。
- 反饋是所有軟件交付流程的核心。改善反饋的最佳方法是縮短反饋周期,并讓結果可視化。你應該持續度量,并把度量結果以一種讓人無法回避的方式傳播出去。
構建與部署的腳本化
- 所有構建工具都有一個共同的核心功能,即可以對依賴關系建模。在執行過程中,它能以正確的順序執行一系列的任務,計算如何達到你所指定的目標,而且被依賴的任務也僅需要運行一次。
- 這種流行的“慣例勝于配置”(convention over configuration)的原則意味著,只要項目按Maven指定的方式進行組織,它就幾乎能用一條命令執行所有的構建、部署、測試和發布任務,卻不用寫很多行的XML。
- 功能驗收測試腳本會調用部署工具,將應用程序部署到適當環境中,并準備相關數據,之后再運行驗收測試。你還可再用一個腳本運行任何非功能測試,比如壓力測試和安全測試。
- 事實上,當你查看我們的部署系統時會發現,它只是由一組非常簡單的、增量的步驟組成的復雜系統,而這些步驟也是隨著項目的進行不斷完善的。我們想說的是,并不是完成所有的步驟之后才能獲得價值。事實上,當你第一次寫了一個腳本用于在本地的開發環境上部署應用程序,并將其分享給整個團隊時,就已經節省了很多開發人員的時間。
- 我們應該遵循Java命名習慣,如包名用PascalCase方式,而類名使用camelCase方式。在代碼提交階段做代碼分析時應利用一些開源工具(比如CheckStyle或FindBugs)來做檢查,迫使大家遵循這些命名習慣
- 單元測試應該放在與包名相對應的目錄中。也就是說,某個類的測試應該與該類放在同一個包中
- 環境管理的核心原則之一就是:對測試和生產環境的修改只能由自動化過程執行。也就是說,我們不應該手工遠程登錄到這些環境上執行部署工作,而應該將其完全腳本化。
- 構建中最常見的錯誤就是默認使用絕對路徑。這會讓構建流程和某臺特定機器的配置形成強依賴,從而很難被用于配置和維護其他服務器。
- 假如我們已經讓你深入理解了構建腳本化,并使你了解到各種各樣的可能性的話(更重要的是激發你走向自動化),我們的目的也就達到了。
提交階段
- 對于開發人員來說,提交階段是開發環節中最重要的一個反饋循環。它會為開發人員引入的最常見錯誤提供迅速反饋。
- 修復那些在提交階段發現的問題,要比修復那些由后續運行大量測試的階段發現的問題簡單得多。
- 只有在某個錯誤讓提交階段的其他任務無法執行時,我們才會讓提交階段停下來,比如編譯錯誤,否則就直至提交階段全部運行完后,才匯總所有的錯誤和失敗報告,以便可以一次性地修復它們。
- 有人認為,在提交階段結束時,應該提供更豐富的信息,比如關于代碼覆蓋率和其他度量項的一些圖表。實際上,這些信息可以使用一系列閾值聚合成一個“交通燈信號”(紅色、黃色、綠色),或者浮動的衡量標度。比如,當單元測試覆蓋率低于60%就令提交階段失敗,但是如果它高于60%,低于80%的話,就令提交階段成功通過,但顯示成黃色。
- 隨著項目的進行,要不斷努力地改進提交階段腳本的質量、設計和性能。一個高效、快速、可靠的提交階段是提高團隊生產效率的關鍵,所以只要花點兒時間和精力在這上面,讓它處于良好的工作狀態,就會很快收回這些投入成本。
- 不能低估專家們的專業知識,但他們的目標應該是建立并使用良好的結構、模式和技術,并將他們的知識傳授給交付團隊。一旦建立了這些基本規則,只有對腳本結構進行較大修改時才需要他們的專業知識,而日常構建維護工作不應該由他們來做。
- 提交測試中,絕大部分應由單元測試組成。單元測試最重要的特點就是運行速度非常快。有時候,我們會因為測試套件運行不夠快而令構建失敗。第二個重要的特點是它們應覆蓋代碼庫的大部分(經驗表明一般為80%左右),讓你有較大的信心,能夠確定一旦它通過后,應用程序就能正常工作。當然,每個單元測試只測試應用程序的一小部分,而且無須啟動應用程序。因此,根據定義,單元測試套件無法給你絕對信心說“應用程序可以工作”,而這正是部署流水線后續部分的任務。
- 我們建議盡量消除提交階段測試中的異步測試。依賴于基礎設施(比如消息機制或是數據庫)的測試可以算做組件測試,而不是單元測試。更復雜、運行得更慢的組件測試應該是驗收測試的一部分,而不應該屬于提交階段。
- 打樁是指利用模擬代碼來代替原系統中的某個部分,并提供已封裝好的響應。樁并不對外界作出響應
- 在一些簡單的斷言中,你能指定測試中期望該模擬類作出什么行為。這是模擬技術和樁技術的根本不同。使用樁時,我們不需要關心樁是如何被調用的,而使用模擬對象時,可以驗證我們的代碼是否以期望的方式與模擬對象進行交互。
- 很容易落入一個陷阱,即為了支撐測試,精心地建立起一堆難以理解和維護的數據結構。理想的測試應該能很容易和快速地進行測試準備,而清理工作也應該更快、更容易。對于結構良好的代碼來說,其測試代碼往往也非常整潔有序。如果測試看起來繁瑣復雜,那可能是系統設計有問題。
自動化驗收測試
- 一旦正確實施自動化驗收測試,你就是在測試應用程序的業務驗收條件,即驗證應用程序是否為用戶提供了有價值的功能。驗收測試通常是在每個已通過提交測試的軟件版本上執行的。
- 對于一個單獨的驗收測試,它的目的是驗證一個用戶故事或需求的驗收條件是否被滿足
- “驗收與我們的單元測試有什么區別?”其不同點在于驗收測試是針對業務的,而不是面向開發的。它能在一個類生產環境中的應用程序運行版本上一次性地測試所有的故事。單元測試的確是自動化測試策略的關鍵部分,但是,它通常并不足以使人們確信程序能夠發布。而驗收測試的目標就是要證明應用程序的確實現了客戶想要的,而不是以編程人員所認為的正確方式來運行的。雖然有時單元測試也會實現同樣的目標,但并不總是這樣的。
- 除驗收測試外,沒有哪種測試能夠基本上代替生產環境中的實際運行來證明軟件能為客戶提供他們所期望的業務價值。單元測試和組件測試都不測試用戶場景,因此也無法發現那種用戶與應用程序進行一系列交互后呈現出來的缺陷。而驗收測試就是為這而設計的.
- 驗收測試在以下幾個方面也表現出不俗的查錯能力:線程問題、以事件驅動方式實現的應用程序出現的緊急行為(emergent behavior),以及由架構問題或環境及配置問題造成的其他類型的bug。這類缺陷很難通過手工測試發現,更不用說單元測試和組件測試了。
- 選擇放棄自動化驗收測試的團隊會令測試人員的負擔非常重,測試人員必須在惱人且重復的回歸測試上花費相當多的時間。
- 根據我們的經驗,與完全由開發人員編寫的自動化驗收測試相比,那些有測試人員參與編寫的自動化驗收測試能更好地發現用戶場景中的缺陷。
- 將可測試性銘記在心,寫出來的應用程序就會有一個API,使GUI和測試用具(test harness)都能用它來驅動應用程序。如果應用程序能夠做到這一點的話,我們建議直接基于業務層執行測試,這是一個合理的策略。唯一的要求就是開發團隊在這方面的紀律性,即讓表現層只負責展現,不要涉足業務領域或應用邏輯。
- 在迭代交付方法中,分析人員會花大量時間定義驗收條件。團隊用這些驗收條件來評判某個具體需求是否被滿足。
- 行為驅動開發的核心理念之一就是驗收測試應該以客戶期望的應用程序行為的方式來書寫。這樣,就可以拿這些寫好的驗收條件直接在應用程序之上運行,來驗證它是否滿足規格說明了。
- 測試用例的工作就是讓應用程序達到“假如”中所述的狀態,然后執行“當……”中所描述的動作,最后驗證應用程序是否處于“那么”中所描述的狀態。
- 原子測試會創建它所需要的一切,并在運行后清理干凈。除了是否成功以外,不會留下其他東西。
- 剛接觸自動化測試的人會發現,想讓代碼可測試,必須修改對它的設計,事實的確如此。
- 自動化測試會給你壓力,讓你的代碼更趨向于模塊化和更好的封裝性。但是如果你通過破壞封裝性讓它變得可測試,那么通常就會錯過達到同一目的的好方法。
- 自動化驗收測試與用戶驗收測試并不完全一樣。其中一個不同點就是:自動化驗收測試不應該運行在包含所有外部系統集成點的環境中。相反,應該為自動化驗收測試提供一個受控環境,并且被測系統應該能在這個環境上運行。這里所說的“受控”是指,可以為每個測試創建正確的初始化狀態。如果與真正的外部系統集成,我們很可能就無法做到這一點。
- 令驗收測試失敗的構建版本不能被部署。在部署流水線模式中,只有已經通過這一階段的候選發布版本才能走向后續階段。而后續階段常常被認為是需要人為評判的:在大多數項目中,如果某個候選發布版本無法通過容量測試,就會有人來決定這次失敗是否足以嚴重到要取消這個候選版本的發布資格,還是讓它繼續走下去。可是,對于驗收測試,不應該提供這種人為評定的機會。如果成功,就可以繼續,如果失敗,就不能向前。
- 不斷運行這些復雜的驗收測試,的確會花費開發團隊很多時間。然而,根據我們的經驗,這種成本投入是一種投資,會節省很多倍的維護成本。當對應用程序進行大范圍修改時,它就是一張防護網,而且軟件質量也會得到保證。這也符合我們的總原則:將流程中的痛點盡量提前。
- 當某個驗收測試失敗時,團隊要停下來立即評估問題。它是一個脆弱的測試,還是由于環境配置問題,或者是由于應用程序的某個修改使原有的假設不成立了,還是一個真正的失敗?然后,讓某人立即采取行動,使測試通過。
- 我們認為,持續地關注維護驗收測試套件,以保持它的良好結構和連貫性是非常重要的,但是自動化驗收測試的全面性要比測試在10分鐘內運行完成更重要。
- 通常,驗收測試套件花幾個小時而不是幾分鐘才能運行完。這是可以接受的,很多項目的驗收測試階段都要花幾個小時,但也運行良好。但是,仍舊有辦法可以提高效率。有一系列的技術能用來縮短從驗收測試階段得到運行結果的時間,從而提高團隊的整體效率。
- 自動化驗收測試通常要比單元測試復雜,需要更多的時間進行維護。而且,由于它在修復某個失敗與使所有驗收測試套件成功通過之間那種固有的滯后性,所以與單元測試相比,它處于失敗狀態的時間要長一些。
- 雖然在我們參與的項目中,確保驗收測試持續運行是一項很困難的工作,而且帶來了一些復雜問題,但是,我們從來沒有后悔使用驗收測試。它使我們能對系統安全地進行大規模重構。我們還堅信,在開發團隊中鼓勵關注這種測試的是軟件成功交付的有力武器。
- 手工測試是軟件行業中的一種基準,并且常常是一個團隊進行測試的唯一形式。我們發現,手工測試的成本不但極其昂貴,而且也不足以確保生產出高質量的軟件。當然,手工測試有其自己的位置,如探索性測試、易用性測試和用戶驗收測試和演示。人類生來就不適合做那種索然無味的、需要不斷重復但卻非常復雜的工作,然而,不幸的是,這些恰恰都是做手工回歸測試所需要的。這種低質量過程必然生產出低質量的軟件。
非功能需求的測試
- “性能”是對處理單一事務所花時間的一種度量,既可以單獨衡量,也可以在一定的負載下衡量。“吞吐量”是系統在一定時間內處理事務的數量,通常它受限于系統中的某個瓶頸。在一定的工作負載下,當每個單獨請求的響應時間維持在可接受的范圍內時,該系統所能承擔的最大吞吐量被稱為它的容量。
- 把非功能需求與功能需求區別對待,就很容易把它從項目計劃中移除,或者不給予它們足夠的分析。然而,這可能就是一個災難,因為非功能需求常常是項目風險的來源之一。在交付過程的后期才發現應用程序因基本的安全漏洞或很差的性能而導致項目無法驗收,這種常見現象會導致項目推遲交付甚至被取消。
- 過早且過分地關注應用程序的容量優化是低效且昂貴的。而且,最終交付的應用系統也很少是高性能的。更糟糕的是,它甚至可能讓項目無法交付。
- 現代軟件系統中,最昂貴的是網絡通信或磁盤存儲。在性能和應用程序的穩定性方面,跨進程或網絡邊界的通信是昂貴的,所以這類通信應該盡量最小化。
- 為了能夠獲得項目成功,必須避免兩個極端:一是假設自己能在項目后期解決所有容量問題;二是因害怕未來可能出現的容量問題而寫一些具有防范性的、過分復雜的代碼。
- 如果對于應用程序來說,性能或吞吐量是一個重要指標的話,我們就需要用一些測試來斷言系統能夠滿足業務需求,而不是通過技術經驗來猜測某個特定組件的吞吐量應該是多少。
- 假如對某應用程序來說,容量或性能是一個非常關鍵的問題,那么就一定要有所投入,為該系統的核心部分準備一個生產環境的副本。使用相同的軟硬件規格要求,遵循我們關于如何管理配置信息的建議,以確保每個環境中都使用相同的配置文件,包括網絡配置、中間件及操作系統的配置。
- 要記住:代碼的修改對系統容量的影響與其對功能的影響一樣重要。當做了修改之后,要盡早掌握容量會下降多少,這樣就能快速且有效地修復它。這就要在部署流水線中加入一個階段,即容量測試階段。
- 我們要一直遵守這樣的格言,即做最少的工作達到我們的目標,這也是YAGNI(“You Ain’t Gonna NeedIt”)原則所暗示的。YAGNI提醒我們,增加防御性行為都有可能成為浪費。如果遵循高德納的格言,應該直到明確需要優化而且到了最后時刻才做優化。另外,還要基于應用程序運行時分析結果,直接解決最重要的瓶頸問題。
應用程序的部署與發布
- 創建發布策略的最重要部分是在項目計劃階段就與應用程序的所有干系人會面。討論的關鍵在于,要對整個應用程序的生命周期中的部署與維護達成共識。然后把這個共識作為發布策略寫下來。在整個生命周期中,干系人應該對該文檔進行更新和維護。
- 要讓軟件的部署活動能以一種可靠且一致的方式進行,其關鍵在于每次部署時都使用同樣的實踐方法,即使用相同的流程向每個環境進行部署,包括生產環境在內。在首次向測試環境部署時就應該使用自動化部署。寫個簡單的腳本來做這件事,而不是手工將軟件部署到環境中。
- 我們認為,項目首個迭代的主要目標之一就是在迭代結束時,讓部署流水線的前幾個階段可以運行,且能夠部署并展示一些成果,即使可展示的東西非常少。盡管我們不建議讓技術價值的優先級高于業務價值的優先級,但此時是個例外。你可以把這一策略看做實現部署流水線的“抽水泵”。
- 當制定發布回滾計劃時,需要遵循兩個通用原則。首先,在發布之前,確保生產系統的狀態(包括數據庫和保存在文件系統中的狀態)已備份。其次,在每次發布之前都練習一下回滾計劃,包括從備份中恢復或把數據庫備份遷移回來,確保這個回滾計劃可以正常工作。
- 金絲雀發布就是把應用程序的某個新版本部署到生產環境中的部分服務器中,從而快速得到反饋。就像發現一只煤礦坑道里的金絲雀那樣,很快就會發現新版本中存在的問題,而不會影響大多數用戶。像藍綠部署一樣,你要先部署新版本到一部分服務器上,而此時用戶不會用到這些服務器。然后就在這個新版本上做冒煙測試,如果必要,還可以做一些容量測試。最后,你再選擇一部分用戶,把他們引導到這個新版本上。有些公司會首先選擇一些“超級用戶”來使用這個新版本。甚至可以在生產環境中部署多個版本,根據需要將不同組的用戶引導到不同的版本上。
基礎設施與環境管理
- 強調合作是DevOps運動的核心原則之一。DevOps運動的目標是將敏捷方法引入到系統管理和IT運營世界中。這場運動的另一個核心原則是,利用敏捷技術對基礎設施進行有效管理。
數據管理
- 測試獨立性是指確保每個測試都具有原子性。也就是說,每個測試不應該用其他測試的結果建立它的初始狀態,并且其他測試也不應該以任何形式影響該測試的成功或失敗
- 我們通過測試來斷言我們所開發的應用程序的行為符合我們期望的結果。我們運行單元測試來避免剛做的修改破壞已有的應用程序。我們運行驗收測試來斷言應用程序交付了用戶所期望的價值。我們執行容量測試來斷言應用程序滿足我們的容量需求。可能,我們還會通過運行一套集成測試來確認應用程序與其依賴的第三方服務可以正常通信。
組件和依賴管理
- 對于“應用程序功能的可用性”這個問題,持續集成可以給你某種程度上的自信。而部署流水線(持續集成的擴展)用于確保軟件一直處于可發布狀態。但是,這兩個實踐都依賴于一件事,即主干開發模式[插圖]。
- 為了在變更的同時還能保持應用程序的可發布,有如下四種應對策略。? 將新功能隱蔽起來,直到它完成為止。? 將所有的變更都變成一系列的增量式小修改,而且每次小的修改都是可發布的。? 使用通過抽象來模擬分支(branch by abstraction)的方式對代碼庫進行大范圍的變更。? 使用組件,根據不同部分修改的頻率對應用程序進行解耦。
- “通過抽象來模擬分支”是一次性實現復雜修改或分支開發的替代方法。它讓團隊在持續集成的支撐下持續開發應用程序的同時替換其中的一大塊代碼,而且這一切都是在主干上完成的。如果代碼庫的某一部分需要修改,首先要找到這部分代碼的入口(一個縫隙),然后放入一個抽象層,讓這個抽象層代理對當前實現方式的調用。然后,開發新的實現方式。到底使用哪種實現方式由一個配置選項來決定,可以在部署時或者運行時對這個選項進行修改。
- 區分組件和庫:庫是指團隊除了選擇權以外,沒有控制權的那些軟件包,它們通常很少更新。相反,組件是指應用程序所依賴的部分軟件塊,但它通常是由你自己的團隊或你公司中的其他團隊開發的。組件通常更新頻繁。
- 一個相當有爭議的陳述是這樣的:“組件是可重用的代碼,它可以被實現了同樣API的其他代碼所代替,同時可獨立部署,并封裝了一些相關的行為和系統的部分職能。”
- 依據功能領域而不是組件來組建團隊確保了每個人都有權力修改代碼庫的任何部分,同時在團隊之間定期交換人員,確保團隊之間有良好的溝通。這種方法還有一個好處,即確保所有的組件能組合在一起正常工作是所有人的責任,而不只是最后負責集成的那個團隊的責任。
版本控制進階
- 分支的唯一目的就是可以對代碼進行增量式或“通過抽象來模擬分支”方式的修改。
- 如果一個團隊的不同成員在不同分支或流上工作的話,那么根據定義,他們就不是在做持續集成。讓持續集成成為可能的一個最重要實踐就是每個人每天至少向主干提交一次。因此,如果你每天將分支合并到主線一次(而不只是拉分支出去),那就沒什么。如果你沒這么做,你就沒有做持續集成。
- 在開發過程中,通過頻繁向主干提交的方式做這種增量式修改幾乎總是最正確的做事方法,所以請一直把它作為備選列表中的第一項。
- 從一個代碼庫上挑選一些變更發送給另一個代碼庫,這個過程叫做摘櫻桃(cherry-picking)。也就是說,與其總是要合并整個分支,不如只合并想要的那些特性.
持續交付管理
- 通過確保交付團隊能得到應用程序在類生產環境上的不斷反饋,是部署流水線達成“執行度”這個目標的方法和手段。部署流水線使交付流程更加透明,來幫助團隊達成符合度。
- 團隊組建與磨合常常會經歷五個階段:創建期(forming)、風暴期(storming)、規范期(norming)、運轉期(performing)和調整/重組期(mourning/reforming)
- “啟動階段”是對開始寫產品代碼前這段時間最簡單的描述。一般來說,此時會對需求進行收集和分析,并對項目的范圍和計劃進行初步規劃。
- 迭代開發提供的是項目進展情況的客觀度量,它是用開發團隊能夠供給用戶可工作的軟件,并且該軟件完成了多少被用戶認可,滿足用戶目標的功能來衡量項目進度的。
- 對于每個項目的成功來說,管理都是至關重要的。良好的管理所創建的流程令軟件更高效地交付,同時確保風險被適當地管理,規章制定被嚴格遵守。
總結
以上是生活随笔為你收集整理的CI/CD笔记:《持续交付:发布可靠软件的系统方法》的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: r型电源变压器的哪些参数很重要?
- 下一篇: 搞掉Windows Media Play