本章概覽#

組態管理(Configuration Management)是本書所有實踐的基石。作者對其定義為:

組態管理是指儲存、檢索、唯一識別並修改專案中所有相關產出物(artifacts)及其相互關係的流程。

一個健全的組態管理策略,應能讓你對以下問題都回答「是」:

  1. 能否精確重現任何環境,包含作業系統版本、修補層級、網路設定、軟體堆疊、部署的應用程式及其組態?
  2. 能否輕鬆進行增量變更,並部署到任何或所有環境?
  3. 能否追蹤每一次變更——變更了什麼、誰做的、何時做的?
  4. 能否滿足所有合規法規的要求?
  5. 團隊成員能否輕鬆獲取所需資訊並進行必要的變更?策略是否反而阻礙了交付效率?

最後一點經常被忽略——許多組態管理策略雖然解決了前四點,卻在團隊協作上設置了重重障礙,這是不必要的。

本章將問題分為三大部分:版本控制軟體組態管理環境管理

mindmap
  root((組態管理))
    版本控制
      將一切納入版控
      頻繁提交到主幹
      有意義的提交訊息
    軟體組態管理
      組態類型與注入時機
      應用程式組態管理
      跨應用程式管理
    環境管理
      環境建立自動化
      基準線管理
      變更流程管理

版本控制(Version Control)#

版本控制系統(Version Control System, VCS)有兩個核心目的:

  1. 保留並提供存取每一個曾儲存之檔案的每一個版本
  2. 分散的團隊能夠協作

將一切納入版本控制#

所有與專案相關的產出物都應放入版本控制——不僅限於原始碼。

應納入版本控制的項目包括:

  • 原始碼與測試程式碼
  • 資料庫腳本
  • 建置與部署腳本
  • 文件
  • 函式庫與應用程式組態檔
  • 編譯器與工具集
  • 重建測試與生產環境所需的一切資訊(DNS 區域檔、防火牆設定等)
  • 需求文件、測試腳本、發布計畫、風險日誌
  • 開發環境的設定檔(方便團隊使用統一設定)

許多專案也會將應用程式伺服器、編譯器、虛擬機器等的二進位映像檔存入版本控制,可加速新環境的建立並確保基礎組態的正確性。甚至可將整個環境(包含基礎作業系統與組態基準)儲存為虛擬映像檔。

不建議放入版本控制的東西:應用程式編譯後的二進位產出。原因有三:

  1. 檔案龐大且增長快速(每次 commit 都會產生新的二進位檔)
  2. 自動化建置系統應能從原始碼重新建立
  3. 同一版本會出現兩次 commit(原始碼一次、二進位檔一次),破壞版本唯一對應的原則

版本控制帶來的刪除自由#

有了版本控制,你可以大膽刪除不再需要的東西——如果判斷錯誤,隨時可以從歷史中取回。這種刪除的自由有助於維護大型組態集的一致性與組織性,也鼓勵團隊嘗試新方法。

頻繁提交到主幹(Trunk)#

增量式開發新功能,頻繁且定期地提交到版本控制的主幹(trunk)。 每天至少提交一次,通常應每天提交數次。

頻繁提交的好處:

  • 軟體始終保持可運作與整合狀態
  • 自動化測試在每次提交時由 CI 伺服器執行
  • 減少因重構造成的大規模合併衝突
  • 整合問題在發生時立即被發現,修復成本低

作者反對為新功能建立分支(branching),除了第 14 章討論的三個例外情況。分支的問題包括:

  • 與持續整合(CI)相違背——分支延遲了整合,問題只在合併時才被發現
  • 多人同時分支時,問題呈指數級增長,合併過程可能極為複雜
  • 自動合併工具無法解決語意衝突(如一人重新命名方法,另一人在另一分支呼叫該方法)
  • 重構變得非常困難,因為分支通常涉及大量檔案

提交前的兩個實用做法:

  1. 在提交前執行 Commit Test Suite——快速(少於十分鐘)但相對全面的測試,驗證沒有引入明顯的回歸問題
  2. 增量式引入變更——每完成一個獨立的增量變更或重構就提交一次

使用有意義的提交訊息#

提交訊息沒有「最短」的獎勵。用兩到三句中等長度的句子描述你正在做什麼,日後能省下大量除錯時間。

良好提交訊息的建議:

  • 多段落格式:第一段為摘要(如報紙標題),後續段落補充細節
  • 包含專案管理工具中的任務或 Bug 識別碼連結
  • 某些團隊甚至設定版本控制系統,拒絕沒有任務識別碼的提交

相依性管理(Managing Dependencies)#

外部函式庫(External Libraries)#

  • 建議在本地保留一份外部函式庫的副本(例如 Maven 的組織內部 repository),這對合規要求至關重要,也加快新專案啟動速度
  • 必須指定外部函式庫的確切版本——不精確的版本指定會導致無法重現建置,並可能引發難以追蹤的錯誤
  • 是否將函式庫放入版本控制涉及取捨:有助於建立軟體版本與函式庫版本的對應關係,但會增加 repository 大小

元件管理(Managing Components)#

  • 除最小的應用程式外,都應將應用程式拆分為元件,以限制變更範圍、減少回歸 Bug、鼓勵重用
  • 小型至中型應用程式通常使用**單體建置(monolithic build)**最為高效
  • 當系統成長或有跨專案共享的元件時,可考慮將元件的建置拆分到獨立的 pipeline
  • 拆分後應使用二進位相依(binary dependencies)而非原始碼相依——重新編譯不僅效率低,還可能產生與已測試版本不同的產出物

軟體組態管理(Managing Software Configuration)#

組態是應用程式的三大組成部分之一(另外兩個是二進位檔與資料)。組態可在建置時期、部署時期、執行時期改變軟體行為。

應像對待原始碼一樣對待組態:進行妥善管理與測試。

組態與彈性的陷阱#

「終極可設定性」(Ultimate Configurability)是常見的反模式。 改變應用程式行為本質上就是在程式設計,組態的可設定性越高,需要的程式設計環境就越複雜。常見陷阱包括:

  • 分析癱瘓(Analysis Paralysis):問題看似龐大且棘手,團隊花所有時間思考卻無法實際解決
  • 設定複雜度抵消彈性優勢:設定所需的工作量甚至可與客製開發相比

組態變更的風險不亞於原始碼變更:

  • 原始碼有編譯器和自動化測試作為保護
  • 但大多數組態資訊是自由格式且未經測試的——沒有什麼能阻止你把 URI 改成一段無效的字串

先專注交付高價值功能,使用最少的組態,之後在需要時再逐步增加組態選項。

組態的類型與注入時機#

組態資訊可在不同階段注入應用程式:

注入時機說明
建置時期(Build time)建置腳本拉取組態並納入二進位檔
打包時期(Packaging time)打包軟體(如 EAR、WAR、Gem)注入組態
部署時期(Deploy time)部署腳本或安裝程式取得必要資訊
啟動時期(Startup time)透過環境變數或命令列參數提供
執行時期(Run time)從 registry、資料庫、設定檔或外部設定服務取得
flowchart LR
    A[建置時期] --> B[打包時期]
    B --> C[部署時期]
    C --> D[啟動時期]
    D --> E[執行時期]
    style A fill:#f99,stroke:#c00
    style B fill:#f99,stroke:#c00
    style C fill:#9f9,stroke:#0a0
    style D fill:#9f9,stroke:#0a0
    style E fill:#9f9,stroke:#0a0

一般而言,在建置或打包時期注入組態是不好的做法。 應確保能將相同的二進位檔部署到每個環境,因此任何在不同部署間會變動的東西都應作為組態處理,而不是在編譯或打包時固化。

盡可能透過同一種機制為組織中所有應用程式和環境提供組態資訊。統一的組態來源可避免花費大量時間追蹤某個環境中某個設定值的來源。

管理應用程式組態#

管理應用程式組態時須考慮三個問題:

  1. 如何表示組態資訊?
  2. 部署腳本如何存取它?
  3. 它如何在不同環境、應用程式和版本間變化?

表示方式:名稱-值(name-value)字串對是最常見的模型。Windows 屬性檔、YAML、Java properties 檔案等格式通常已足夠。複雜度的上限大約是 XML 檔案。

儲存位置:資料庫、版本控制系統、目錄或 registry。版本控制最簡單,還能免費獲得組態的歷史記錄。

不要將密碼簽入原始碼控制或硬編碼在應用程式中。 密碼應在部署時由執行部署的人員輸入。可使用憑證、目錄服務或單一登入系統來處理多層系統的認證。

組態的儲存位置與應用程式存取組態的機制不同。應用程式可透過本機檔案系統、Web 服務、目錄服務或資料庫來存取組態。

存取組態#

  • 最有效的方式是透過集中式服務讓每個應用程式取得所需組態
  • 檔案系統是最簡單的方式——跨平台且所有語言支援,但在叢集環境中可能有同步問題
  • 也可從集中式 repository(RDBMS、LDAP、Web 服務)取得組態
  • 建議用簡單的 Facade 類別封裝組態存取技術的細節,便於測試和更換儲存機制

組態建模#

每個組態設定可視為一個 tuple,其可用集合與值通常取決於三個維度:

維度說明
應用程式哪個應用
應用程式版本哪個版本
環境開發、UAT、效能測試、Staging、生產

建模時應考慮的使用案例:

  • 新增環境(新的開發者工作站、容量測試環境等)
  • 建立應用程式新版本(新增/移除組態設定,且需支援回滾到舊版本)
  • 將新版本從一個環境推廣到另一個環境
  • 資料庫伺服器搬遷(需能簡單更新所有相關組態)
  • 使用虛擬化管理環境

一種管理跨環境組態的方法:以生產環境組態為預設值,其他環境只覆蓋必須變更的屬性。這簡化了需要設定的內容。但需確保設有防火牆,避免誤觸生產系統。

測試系統組態#

組態測試分為兩個階段:

  1. 驗證外部服務參照:部署腳本應確認所設定的訊息匯流排、模擬服務等確實可用。至少應 ping 所有外部服務,如有不可用則部署失敗
  2. 執行冒煙測試(Smoke Tests):安裝後執行少量測試,驗證依賴組態設定的功能正常運作。理想情況下,測試失敗應停止應用程式並使部署流程失敗

跨應用程式管理組態#

在中大型組織中,管理組態特別複雜。關鍵任務:

  • 維護每個應用程式所有組態選項的目錄,記錄儲存位置、生命週期與變更方式
  • 盡可能從應用程式程式碼自動產生此資訊
  • 透過營運團隊的生產監控系統(如 Nagios、OpenNMS、HP OpenView)即時查看每個應用程式的當前組態與部署版本
  • 在專案啟動時就規劃組態管理,盡可能使用與生態系統中其他應用程式相同的方法

應用程式組態管理原則#

  1. 考慮在應用程式生命週期的哪個階段注入特定組態——打包、部署、啟動還是執行時期
  2. 組態選項與原始碼放同一個 repository,但組態另外存放(密碼等敏感資訊絕不簽入版本控制)
  3. 組態應由自動化流程執行,使用組態 repository 中的值
  4. 組態系統應能根據應用程式、版本與環境提供不同的值
  5. 使用清晰的命名慣例,讓人不需手冊就能理解組態屬性
  6. 確保組態資訊的模組化與封裝,避免牽一髮動全身
  7. 遵循 DRY 原則(Don’t Repeat Yourself),每個概念在組態中只有一個表示
  8. 極簡主義——保持組態簡單聚焦,避免不必要的組態選項
  9. 避免過度工程化組態系統
  10. 確保在部署或安裝時有組態的測試,包括檢查外部服務可用性與功能冒煙測試

環境管理(Managing Your Environments)#

每個應用程式都依賴硬體、軟體、基礎設施與外部系統——這就是應用程式的環境

環境的組態與應用程式的組態同等重要。例如,若應用程式依賴的訊息匯流排未正確設定,應用程式就無法運作。

手動管理環境的問題#

手動安裝軟體並編輯設定檔(ad-hoc 方式)是最常見但最糟糕的策略:

  • 組態資訊量龐大
  • 一個小變更就可能導致整個應用程式崩潰或嚴重效能下降
  • 問題一旦發生,排查和修復時間不可預測,且需要資深人員
  • 極難精確重現手動設定的環境以供測試
  • 不同節點的組態容易隨時間漂移(drift)

《The Visible Ops Handbook》的作者將手動設定的環境稱為「藝術品」(works of art)。我們的目標是將環境變成可量產的物件——其建立是可重複且耗時可預測的。

環境建立自動化#

建立新環境的成本應始終低於修復舊環境。 環境建立必須是完全自動化的流程。

自動化環境建立的重要性:

  • 消除因人員離職而無法理解基礎設施組態的風險
  • 修復環境可能耗費數小時,但重建可在可預測的時間內完成
  • 測試環境在軟體組態上應是生產環境的精確複本,以便及早發現組態問題

需關注的環境組態資訊:

  • 作業系統版本、修補層級與設定
  • 支援應用程式的額外軟體套件及其版本與設定
  • 應用程式所需的網路拓撲
  • 應用程式依賴的外部服務及其版本與設定
  • 環境中的資料或其他狀態(如生產資料庫)

兩大基本原則:

  1. 二進位檔與組態資訊保持獨立
  2. 所有組態資訊集中在一處

第三方軟體的評估標準#

選擇第三方軟體時,首先要問:

  • 能否自動化部署?
  • 能否有效地對其組態進行版本控制?
  • 如何融入我們的自動化部署策略?

好的軟體應有能從命令列無需人工介入即可執行的安裝程式,且其組態可在版本控制中管理。若第三方軟體不符合這些標準,應尋找替代方案。

基準線管理(Baseline)#

  • 處於正確部署狀態的環境稱為基準線(baseline)
  • 自動化環境佈建系統應能建立或重建專案近期歷史中的任何基準線
  • 每次變更環境時,應儲存變更以建立新版本的基準線,並與對應的應用程式版本關聯

應像對待程式碼一樣對待環境——增量式變更並簽入版本控制。每次變更都應經過測試,確認不會影響在新版本環境中運行的任何應用程式。

環境管理工具#

  • Puppet、CfEngine:可用宣告式方式定義使用者存取權限、安裝軟體等,定義可存入版本控制,代理程式定期拉取最新組態並更新系統
  • 虛擬化:可將環境中每台機器的副本作為基準線儲存,一鍵建立新環境。還能整合硬體、標準化硬體平台

變更流程管理#

  • 生產環境應完全鎖定——任何人未經變更管理流程都不能進行變更
  • 變更必須先經過測試,應先腳本化並簽入版本控制,獲得核准後再以自動化方式推送到生產環境
flowchart TD
    A[提出變更需求] --> B[腳本化變更]
    B --> C[簽入版本控制]
    C --> D{獲得核准?}
    D -->|是| E[在測試環境驗證]
    D -->|否| A
    E --> F{測試通過?}
    F -->|是| G[自動化推送到生產環境]
    F -->|否| B
  • 測試環境應與生產環境採用相同的管理方式——核准流程可以更簡單,但組態管理方式應完全一致
  • 測試環境在軟體組態上應與生產環境高度相似(不代表硬體規格要一樣,而是管理、部署與設定機制要一致)

本章總結#

組態管理是本書一切實踐的基礎,沒有它就無法實現持續整合、發布管理與部署流水線。健全的組態管理流程應能讓你回答「是」的問題:

  • 能否從版本控制的資產中從零完整重建生產系統(不含生產資料)?
  • 能否回退到先前已知良好的應用程式狀態?
  • 能否確保生產、Staging、測試中的每個已部署環境都以完全相同的方式設定?

應制定儲存基準線與控制變更的策略,涵蓋:

  • 原始碼、建置腳本、測試、文件、需求、資料庫腳本、函式庫與組態檔
  • 開發、測試與營運的工具鏈
  • 開發、測試與生產中使用的所有環境
  • 應用程式的完整軟體堆疊(二進位檔與組態)
  • 每個應用程式在其所運行的每個環境中,貫穿整個生命週期(建置、部署、測試、營運)的組態