從 Facebook 的發布工程談起#

Facebook 的發布工程總監 Chuck Rossi 負責管理每日的程式碼推送。2012 年時,他描述了團隊的工作流程:下午一點開始切換到「營運模式」,確認所有有變更的開發者都在線上並積極測試自己的程式碼。如果所有測試儀表板和 canary tests 都是綠燈,就按下按鈕,在二十分鐘內將新程式碼部署到數千台伺服器。

Kent Beck 進一步觀察到,Facebook 發現每次部署能處理的變更數量是固定的,如果想要更多變更,就需要更頻繁的部署。這使得部署頻率從每週一次提升到每日三次。

透過持續整合並使程式碼部署成為低風險流程,Facebook 讓部署成為每個人日常工作的一部分,維持了開發者的生產力。

然而,即使我們的程式碼和環境已經一起測試過,大多數組織仍然不太頻繁地部署到生產環境,因為部署過程是手動的、耗時的、痛苦的、容易出錯的,而且往往需要開發和維運之間不可靠的交接。

因為部署過程痛苦,我們傾向於越來越少做它,結果累積了越來越大的差異,增加了風險和修復難度。本章的目標就是降低生產部署的摩擦,使其可以頻繁且輕鬆地進行。

自動化部署流程#

要達到像 Facebook 那樣的成果,我們需要一個自動化的部署機制。如果部署流程已經存在多年,首先要完整記錄所有步驟(例如透過 value stream mapping),然後逐步簡化和自動化:

  • 將程式碼打包成適合部署的格式
  • 建立預配置的虛擬機映像或容器
  • 自動化中介軟體的部署和配置
  • 複製套件或檔案到生產伺服器
  • 重啟伺服器、應用程式或服務
  • 從範本生成配置檔
  • 執行自動化 smoke tests
  • 腳本化和自動化資料庫遷移

盡可能重新設計架構以移除耗時步驟,同時減少交接次數以降低錯誤和知識流失。

部署管線的要求#

  • 以相同方式部署到每個環境:在開發、測試、生產環境使用相同的部署機制,確保生產部署更可靠
  • 對部署進行 Smoke Test:驗證系統能連接所有支援系統並執行測試交易
  • 確保環境一致性:持續確保開發、測試和生產環境保持同步

Figure 13: The deployment pipeline

當部署過程中出現任何問題時,拉下 Andon Cord 並群策群力解決,就像部署管線在早期階段失敗時一樣。

案例:CSG International 的每日部署(2013)#

CSG International 營運美國最大的帳單列印服務之一。他們的首席架構師 Scott Prugh 為了提高發布的可預測性和可靠性,將發布頻率從每年兩次加倍到四次。

關鍵做法是建立一個 Shared Operations Team (SOT),負責管理所有環境並執行每日部署。因為每天都在部署,任何未修復的問題都會在隔天再次出現,這激勵團隊自動化所有繁瑣的手動步驟。

成果驚人:

  • 生產事故減少 91%
  • MTTR 降低 80%
  • 部署前置時間從十四天縮短到一天

啟用自動化自助式部署#

開發者能夠自行將程式碼部署到生產環境,快速看到客戶使用新功能的滿足感,以及快速修復問題——這種能力在過去十年中因為控制和合規需求而減弱。

然而,Puppet Labs 的 2013 State of DevOps Report 發現,由開發部署程式碼和由維運部署程式碼的組織,其變更成功率沒有統計上的顯著差異。關鍵在於有共同的目標、透明度、責任感和問責制。

為了更好地促進快速流動,我們需要一個可由開發或維運執行的程式碼推廣流程:

  • Build:部署管線從版本控制建立可部署到任何環境的套件
  • Test:任何人都能在自己的工作站或測試系統上執行測試套件
  • Deploy:任何人都能將套件部署到他們有權限的環境

案例:Etsy 的自助式開發者部署(2014)#

在 Etsy,部署由任何想要部署的人執行——開發、維運或資安人員。部署流程已經變得如此安全和例行化,以至於新工程師在上班第一天就會執行生產部署

工程師想要部署時,先進入聊天室加入部署佇列。開發者在提交程式碼前會在工作站上執行 4,500 個單元測試(不到一分鐘)。程式碼提交後,超過七千個自動化測試在 CI 伺服器上立即執行。

透過內部開發的 Deployinator 工具,工程師只需按下按鈕即可部署到 QA 環境,然後再按一個按鈕部署到生產環境。

2009 年時,Etsy 的部署流程是壓力和恐懼的來源。到了 2011 年,它已成為每天發生 25-50 次的例行操作。

將部署與發布解耦#

傳統上,deployment(部署)release(發布) 這兩個術語經常被混用。但它們是兩個截然不同的行動:

  • Deployment(部署):將指定版本的軟體安裝到特定環境中,不一定對客戶可見
  • Release(發布):將功能提供給所有客戶或部分客戶使用

當我們混淆這兩者時,很難為成功建立問責制。解耦這兩個活動讓開發和維運負責快速、頻繁的部署成功,同時讓產品負責人負責發布的商業成果。

有兩大類發布模式:

  • 基於環境的發布模式(Environment-based):透過多個環境和流量切換來管理發布,如 blue-green deployments、canary releases、cluster immune systems
  • 基於應用程式的發布模式(Application-based):透過修改應用程式來選擇性啟用功能,如 feature toggles 和 dark launching

基於環境的發布模式#

將部署與發布解耦後,我們不再需要在半夜或週末進行部署,而是可以在正常工作時間部署。

Blue-Green 部署模式#

最簡單的模式是 blue-green deployment:維持兩個生產環境(blue 和 green),同一時間只有一個環境服務客戶流量。

要發布新版本時:

  1. 部署到非活躍環境進行測試
  2. 確認一切正常後,切換流量到新環境
  3. 回滾只需將流量切回舊環境

處理資料庫變更#

當部署需要資料庫 schema 變更時,有兩種方法:

  • 建立兩個資料庫:各有自己的資料庫,發布時做備份和還原,但回滾時可能遺失交易
  • 將資料庫變更與應用程式變更解耦:只做加法式變更(不變更現有物件),應用程式不假設特定的資料庫版本

Canary 和 Cluster Immune System 模式#

Canary release 將發布過程自動化為逐步推廣到越來越大的環境。Facebook 建立了三組環境:

  • A1 群組:僅服務內部員工的生產伺服器
  • A2 群組:僅服務小比例客戶的生產伺服器
  • A3 群組:其餘生產伺服器,在 A2 達到驗收標準後才部署

Cluster immune system 在 canary release 的基礎上,將生產監控系統與發布流程連結,當效能偏離預期範圍時自動回滾程式碼。

這種保護機制有兩個重大好處:防禦自動化測試難以發現的缺陷(如 CSS 變更導致頁面元素消失),以及縮短偵測和回應效能下降的時間。

基於應用程式的模式以實現更安全的發布#

實作 Feature Toggles#

Feature toggles 提供了在不需要部署新程式碼的情況下,選擇性地啟用和停用功能的機制。它們通常透過條件判斷語句包裝應用程式邏輯或 UI 元素來實現。

Feature toggles 的優勢:

  • 輕鬆回滾:在生產環境中造成問題的功能可以透過切換設定快速停用
  • 優雅降級:在極高負載下,透過降低功能等級來增加可服務的使用者數量
  • 增加韌性:依賴未完成服務的功能可以先部署但隱藏起來,等服務就緒再啟用

執行 Dark Launch#

Feature toggles 讓我們能使用 dark launching 技術:將功能部署到生產環境但不讓使用者看到,在正式發布前用生產流量進行測試。

例如,讓 1% 的線上使用者對新功能發出不可見的呼叫,觀察新功能在負載下的行為。找到並修復問題後,逐步增加負載,直到對效能有信心。

案例:Facebook Chat 的 Dark Launch(2008)#

2008 年 Facebook 有超過七千萬的每日活躍使用者。Chat 團隊花了將近一年開發聊天功能,期間每天將程式碼部署到生產環境。

透過 Gatekeeper(Facebook 的 feature toggling 服務),Chat 功能最初只對 Chat 團隊可見,後來對所有內部員工可見,但對外部使用者完全隱藏。每個使用者的瀏覽器都載入了一個測試工具,會向後端聊天服務發送不可見的測試訊息,模擬生產級別的負載。

正式發布日到來時,Facebook Chat 從零到七千萬使用者的擴展看起來毫不費力。他們逐步啟用功能——先是內部員工、然後 1% 的客戶、5%——如此遞增。正如工程師 Letuchy 所寫:「一夜之間從零到七千萬使用者的秘訣就是避免一次做完。」

Continuous Delivery 與 Continuous Deployment#

Jez Humble 更新了他對這兩個概念的定義:

  • Continuous Delivery(持續交付):所有開發者在 trunk 上以小批次工作,trunk 始終保持在可發布狀態,可以在正常工作時間按需發布。開發者在引入回歸錯誤時獲得快速回饋
  • Continuous Deployment(持續部署):在持續交付的基礎上,定期通過自助式方式將好的建置部署到生產環境,通常至少每個開發者每天部署一次

Continuous delivery 是 continuous deployment 的前提,正如 continuous integration 是 continuous delivery 的前提。

Figure 14: The ideal and non-ideal automated testing pyramids

Figure 15: Running automated and manual tests in parallel

結論#

正如 Facebook、Etsy 和 CSG 的案例所展示的,發布和部署不必是高風險、高戲劇性的事件。相反地,它們可以成為完全例行化的日常工作。

透過這些實踐,我們可以將部署前置時間從數月縮短到數分鐘,讓組織能夠快速向客戶交付價值,同時不造成混亂和中斷。更重要的是,透過讓 Dev 和 Ops 合作,我們終於可以讓維運工作變得人性化