GDS Digital Service Manual 中文翻譯網站 (beta)
by HPX-GOV 英國政府數位服務研究小組 GDS Study Group
Home » 英國政府數位服務設計手冊 » 建造軟體 (Making software) » 佈署軟體(Deploying software)

佈署軟體(Deploying software)

確保頻繁且低風險的佈署原則

軟體佈署原則

我們已經找出了一些關於軟體佈署的共同原則,這些原則已被採用於許多基於不同的技術架構和需求的專案裡。這些原則支持了符合使用者需求的軟體佈署流程。這些原則是:

  • 少量多次
  • 高品質軟體
  • 為 cycle time 而優化
  • 可重複的,可審計的佈署
  • 零停機時間的佈署

少量多次

軟體佈署應該要是一個低風險的活動。以頻繁的、逐步小增量的軟體佈署方式,風險得以在幾個不同的方面上降低。請參閱GDS的部落格文章定期發佈降低風險來了解更多內容

頻繁地佈署軟體,可以讓你組織裡的產品經理工作得更好。頻繁的佈署,可以讓產品經理把兩件重要的事及時做好:修正 bugs 和發佈新功能。

GOV.UK 主流產品經理 Roo Reynolds 說:“一星期佈署一次算是慢得嚇人了”。

GOV.UK 網站設計在 2012 年 10 月的首次公開發布後,已經大幅改版了 4 次。透過頻繁的發佈,所能達到的快速收集回饋、快速回應變化,某種程度上讓這樣的短期內之大改版成為可能。

高品質軟體

你發佈到 production 環境的軟體應該有一貫的高品質。Bugs 對使用者的影響當然顯而易見;然而不那麼顯而易見的是:你越早找出 bugs,解決它們就越容易、成本越低。

部署本身不該是一個充滿風險的過程。到了一個版本的軟件要佈署到 production 環境的時候,你應該要有把握確保它會順利且無縫接軌地運作。

為 cycle time 而優化

從開發者更動程式碼到那更動實際上線運作,需要多長的時間?這時間越短,一個產品就能越快回應變化。你可以越快發佈下一次迭代,你就能越快在一個理想的解決方案上收斂。

可重複、可稽核的佈署

在任何時候,你都該要知道,你跑在每個不同環境裡的服務是什麼版本。當佈署進到 production 環境時,你應該要能將它所造成的變化一路追蹤回原始碼 repositories 裡面被包含在這次佈署裡的程式碼 commit。

隨著小與頻繁的發佈,如果真有任何問題進入了 production 環境,你將能夠馬上把出錯的原因限縮在少量幾個 commits 內。

只要系統變動得越少,要回復 (roll back) 到以前的版本就越不繁瑣。而回寫 (roll forward) – 用一個代碼的變更來解決 production 環境的問題 – 是可以實現的,因為佈署過程是自動的、所需的時間很短。

具有可重複的佈署過程的一個附加好處是,縮放規模和從失敗回復會變得容易。要假設你會想要添加更多的應用程式伺服器來承載特定的應用程式,可能是要回應更大量的要求、或是要更換故障的伺服器個體。

一旦你已經置備了所需的機器,你只要在新機器上重跑您的佈署流程就可佈署軟體。如果沒有一個可重複的佈署流程,增添機器就得手動處理、而且容易出錯。

零停機時間的佈署

許多佈署流程會帶來停機成本。你佈署得越頻繁,你就越會因為佈署而經歷更長的停機時間。看你特定專案的需求、這或許是可以接受的,或者您可能需要考量如何改變佈署流程以實現零停機時間。

這不是單方面的問題。讀取操作的零停機時間,比寫入操作的更容易實現。

無論您的專案是否具有零停機時間佈署的業務需要,使零停機時間佈署得以達成的工具和流程依舊是值得考慮的,因為一般而言零停機時間佈署的限制可以促成更好的工程實踐。

實現這些理念的技術和工具

單一產出物

佈署過程中的一個反面模式,是為每個環境各自製作不同的產出物。可能的例子像是控制二進制執行檔裡除錯符號的出現與否,或最佳化參數在使用上的不同變化。但問題是,你在預覽環境裡對代碼產出物所做的測試,可能並不適用於你佈署到 production 環境的產出物。

更好的選擇是建立一個單一的產出物、並將這產出物佈署到所有的環境。在同一份代碼運行在每個不同環境中的前提下,你可以安心地佈署到 production 環境,因為知道這代碼已經在其他每個環境中都測試過、並且沒有發現任何缺失。

注意,一個產出物的確切性質是刻意模糊的。它可以是

  • JVM 語言的一個 .jar 文件
  • 對於沒有編譯產出物的程式語言而言,甚至可以是在源代碼控制系統中的一個標籤
  • 已經將應用程式事先佈署進去了的整個虛擬機映像。

有先後順序的環境

你應該準備幾個環境要佈署。最起碼,你要有一個正在運行最新版本軟體的開發環境,以及一個正在讓線上使用者去使用的 production 環境。您可能也還有其他投入於探索性測試、用戶測試、效能測試的環境,或一個進入 production 環境之前的 staging 環境等。

這些環境應該要有先後順序,一個版本的軟體 在前面一點的環境進行佈署和測試之前 不得佈署到後面的環境。這樣一來,軟體在佈署到 production 環境前一定得先在前面的每個環境都被測試過。

這並不需要是個嚴格的線性排序。有些測試可以並行地運行,像是使用者驗收測試和效能測試。不過通常會有一個單一的 production 環境是排在所有環境的最後面,以及會有一個單一的開始環境排在所有環境的最前面。

基礎設施之組態的可重複佈署

好的佈署其中一個原則,是可重複的佈署。這原則不只適用於應用程式的代碼。應用程式不是在真空中運行的,而且往往對它們所運行於上的底層系統具有特殊要求。該系統的組態應該是自動且可重複的。

主要的問題有兩個:確保機器的新建置是可重複的,和 確保一旦建置完成,機器不會遭受到組態漸變:其中小的、手動的組態變更隨著時間陸續發生,造成了一個不在可重複狀態的系統。

編寫新機器之組態的腳本不難。它總是從已知狀態開始,且可以有幾個任務來安裝軟體、把將組態檔案放到對的位置並啟動服務,直至機器進入良好的狀態。

管理組態漸變就比較棘手了,為了要抵消對組態的手動變更,你的系統一定得要強健到能把一個機器從一個未知的狀態設成一個已知的狀態。有一些用來管理基礎設施組態的工具,像是 CFEngineChef,和 Puppet。這些工具都被設計得可以重複地以電腦運作,以減輕組態的漸變。

部署組態管理代碼

如果你正在用這些工具的其中之一,你需要提供一個方法去佈署新版的基礎設施代碼。分發基礎設施代碼有兩種方式:

使用一台伺服器 (Chef Server,Puppet): 在這種系統裡,你將有一台中央伺服器,用來將組態碼分發到你環境中的每台機器。部署新版的代碼只需要部署到這伺服器上,它將隨後將其分發給客戶端。

無伺服器 (Chef Solo, Masterless Puppet): 在這裡,你需要將組態代碼分發到每個節點,並確保每個節點運行該代碼。

避免組態管理代碼

避免組態漸變的另一個策略,是使用 immutable server 模式,一旦一台機器的組態設定完成了之後就永遠不會再更動了。要佈署新版軟體,就得將舊機器的整個丟棄、重新配置新的。因為伺服器的生命週期很短、並經常被新的取代,所以可以避免組態漸變。在虛擬化環境、而且應用程式的產出物又是預先佈署了應用程式的虛擬機模版,那麼 immutable server 模式則是天生的絕配,但這也可以達過像是 LXC 那樣的容器化技術達成。

代碼的可重複部署

有許多佈署你代碼的選項:

  • 將你的產出物包裝成作業系統的軟體套件 (.debs 或 .rpms),並使用基礎設施的組態管理工具,從本地軟體套件庫去進行安裝(apt 或 yum)
  • 使用基於推送的系統來佈署,例如 fabriccapistrano,或類似的工具
  • 為每次佈署建立一個新的 immutable server

你應該想想你會如何找到你要佈署的主機。在簡單的情況下,你所要佈署的應用程式伺服器列表可能已經寫死在佈署腳本裡面了。

這種情況會有種風險:就是寫死了的伺服器列表可能與實際上存在著的伺服器群漸漸開始有出入。這種風險在更大、更動態的基礎架構會更容易發生。

還有更複雜的主機發現機制,例如內部 DNS、Zookeeper、或使用以訊息佇列為基礎的系統,如 MCollective。

環境變數組態的管理

無論是對 基礎設施之組態管理代碼 還是 應用程式代碼,既然你應該在不同環境佈署同一個產出物,你將不可避免地發現需要置入隨環境而有所不同之組態,像是相關服務的網址。

對於應用程式的組態,你的佈署機制應提供一種方式,在不同的環境裡置入該環境特定的組態檔。

對於基礎設施的組態,您的基礎架構工具應該提供一些能實現這目標的方法。例如,Puppet 3 提供 Hiera,一個用來管理這些設定值的分層數據存儲。

機密資料

在管理機密資料,例如資料庫密碼或 SSL 密鑰 時,要格外小心。你要確保:

  • 在大尺度的階層,機密資料不能在使用到此資料的環境之外被存取
  • 在細尺度的階層,機密資料只有該環境裡需要知道此資料的機器才能知道。

例如,在一個由資料庫、應用伺服器、web 伺服器所構成的三層應用程式裡,資料庫伺服器不需要知道網站的 SSL (secure sockets layer) 私鑰,而 web 伺服器也不需要知道資料庫的認證憑據。

如果您使用的是 hiera,那麼 hiera-GPG 為這問題提供了一個解決方案。它允許我們從 GPG 加密過的文件裡取值來進行置入。只有那些持有適當私鑰的主機可以存取該內容。只要在一個環境裡為每台主機建立一個 GPG 密鑰,你可以一台一台地決定哪些主機可以存取哪些機密資料。

如果您使用的是 chef,chef data bag 提供了一個類似的解決方案。

零停機時間的佈署

在具有高可用性需求的專案裡,如果每次佈署都會造成短期的停機,頻繁地佈署小的代碼變動到 production 環境,可能會產生不可接受的服務中斷。因此,考量須用什麼樣的施作方式得以讓佈署不會造成停機,是很重要的。

這得看你所謂的停機是什麼。要維持以讀為基礎之動作的正常運行,相對比較簡單:一個可以提供舊資料的快取層可以對外隱瞞住應用程序伺服器的離線;如果一個資料庫先被置入 read-only 模式,從一個 master 遷移到另一個會比較容易。

要維持以寫為基礎之動作的正常運行就比較棘手,需要前期的思考和設計。如果你曉得你在 以寫為基礎之動作 或 transactional 動作 上會有很高的正常運行需求,你需要考量那將如何影響你的架構和基礎設施。

資料庫遷移

當應用程式隨著時間演進,它們對資料庫的需求也會。資料庫遷移腳本是小小的資料庫代碼,用來以某種方式轉變資料庫,以服務應用程式。

為了實現零停機時間的佈署,您應該讓資料庫遷移與應用程式的佈署脫鉤。如果你正在執行零停機時間的佈署,你最後一定會造成幾個不同版本的軟體在同時運行。反過來說,應用程式將需要能容忍的生命週期內同時運行資料庫遷移腳本的可能性。

要注意的是,資料庫遷移應該要依循與應用程式代碼一樣嚴格的佈署流水線。資料庫遷移應該要先佈署到測試環境,且只有在所有其他環境中都運用且驗證過之後才能佈署到 production 環境。

服務的相依性

透過應用程式介面 (API) 互相依賴的服務 可能體驗到類似依賴於資料庫之應用程式的佈署問題。例如,一個前端應用程式,它透過某種 API 與後端應用程式進行溝通。

再一次,答案是將應用程式的佈署加以脫鉤,以確保前端應用程式能容忍後端 API 的增加,且很相似地 後端 API 可以增加功能而又不打斷前端應用程式的運行。

使寫入非同步化

另一個避免佈署期間失敗的方法,是將寫入的操作排進一個訊息佇列裡使它非同步化。這樣,當從訊息貯列裡消化操作要求的後端系統在佈署過程中被停用時,前端不會看到錯誤;相反地,它只是看到一個寫入操作在後續的讀取操作中實際反映出來之所需時間的增加。

煙霧測試

一旦你佈署你的應用程式,你應該確認應用程式是否如預期運作。如果它無法運作,佈署可以取消或回復。用來做這種確認的測試,通常被稱為 “煙霧測試”。

好煙霧測試非常簡單、快速,並且操的不只是應用程式本身,還包含它所有重要的從屬物。例如,如果一個應用程式需要一個資料庫存在才能有效運作,煙霧測試應該去操演 在資料庫不存在或傳回錯誤時會發生錯誤的應用程式代碼路徑。

當煙霧測試失敗時,你應該知道你的反應會是如何。最簡單的選擇是是手動回復到以前版本的應用程式 – 如果你有一個在版本控制下的產出物 repository 可以把不同版本的應用程式從中取出,那這應該很容易。

最簡單的選擇是手動回復到以前版本的應用程序 – 如果你有一個版本的人工製品倉庫來借鑒應用,這應該是很容易。

一個更成熟的解決方案可以自動偵測到煙霧測式的失敗並取消佈署或回復為前一個版本。

一個理想的解決方案,在一個新版的應用程式在經過煙霧測試並確認品質優良之前 甚至不會將它掛載到 production 環境的負載平衡器下。如果應用程式未能通過的煙霧測試,就直接被丟棄;不需要回復,服務也不會發生中斷。這方案特別適合搭配 immutable server 模式一起運作。

緊急佈署

偶爾會發生一些情況,使得你會想要馬上佈署到 production 環境。原因可能是一個你正在用的函式庫裡有一個安全漏洞剛被公佈,或者是因為有個 bug 已經進到了 production 環境弄壞了一些使用者正在用的系統。

狀況可能是你脫離了平常佈署所依循的程序去修正問題,然後等危機過了之後 再把你在 production 環境所做的修改 (或所謂的 “hotfix”) 給移植回你的開發環境然後走正常的佈署流程出去。如果是這種情況,那麼你的 cycle time 太長了。在隨後對何處出了錯的事後分析裡,你應該問問為什麼佈署程序沒有足夠精簡,以適應快速將修正佈署到 production 環境的需求。

譯者:盧紀憲
校稿者:Sharon Wang
原始出處:https://www.gov.uk/service-manual/making-software/deployment.html

請留言

你的email信箱不會被發布出來. Required fields are marked *

*