本章從設計與維護基礎架構系統的角度出發,探討如何以安全且可持續的方式設計組態(Configuration)。組態是 SRE 日常工作中極為常見的任務,通常發生在兩種情境:初始設定(時間充裕)或緊急重新配置(事故處理中)。一個設計良好的組態介面,能讓變更快速、自信且可測試。

什麼是組態?#

系統可被拆解為三個關鍵組成部分:

  • 軟體(Software)
  • 資料集(Data Set)
  • 系統組態(Configuration)

組態可以寬泛定義為一種人機介面,用於修改系統行為。當業務需求變化但重新編譯和部署的成本太高時,組態提供了一種低開銷的方式來改變系統功能。SRE 在部署系統、調校效能以及事故回應中經常使用組態。

然而,這三個組成部分之間的界線往往並不清晰。例如,許多系統使用程式語言來進行組態(如 Apache 的 mod_lua),資料集也可能包含程式碼(如 SQL 預存程序)。

組態與可靠性#

組態的品質直接影響組織運行系統的可靠性,其影響類似於程式碼品質對系統可維護性的影響。但組態與程式碼有幾個關鍵差異:

  • 影響範圍大:單一組態選項的變更可能帶來劇烈的功能改變(例如一條錯誤的防火牆規則可能讓你被鎖在系統外)
  • 缺乏測試:組態通常存在於未經測試(甚至不可測試)的環境中
  • 壓力下操作:事故期間可能需要在巨大壓力下修改組態

早期飛機設計中,令人困惑的控制裝置和指示器導致了事故,研究顯示操作員失誤頻繁發生,與技能或經驗無關。可用性與可靠性之間的關聯同樣適用於計算系統。

分離哲學與機制#

本章將組態討論分為兩個層面:

  • 組態哲學(Philosophy):完全獨立於所選語言和其他機制的面向,涵蓋如何結構化組態、如何達到正確的抽象層次、如何無縫支援不同使用案例
  • 組態機制(Mechanics):涵蓋語言設計、部署策略、與其他系統的互動等主題

將兩者分開討論有助於更清晰地推理組態設計。實務上,無論組態語言是 XML 還是 Lua,如果組態需要大量難以理解的使用者輸入,語言選擇就不重要。

組態哲學#

理想的組態是「不需要組態」#

最理想的組態狀態是系統能根據部署、工作負載或已存在的組態自動辨識正確的設定。雖然這在實務上很難完全達成,但它指出了組態設計的方向:從大量可調參數走向簡潔

Figure 14.1: Control panel in the NASA spacecraft control center

歷史上,關鍵任務系統提供大量的控制選項(如 NASA 太空船控制中心),但也要求操作員接受大量訓練。在現代計算系統中,這種程度的訓練對多數產業已不可行。減少控制雖然降低了對系統的掌控程度,但也減少了錯誤的表面積和操作員的認知負荷。

組態是在「問使用者問題」#

無論組態的形式為何,人機互動本質上是一個介面在向使用者提問。對此有兩種觀點:

  • 基礎架構導向(Infrastructure-centric):提供越多組態旋鈕越好,讓使用者可以精確調校
  • 使用者導向(User-centric):組態是使用者在回到實際業務目標前必須回答的問題,旋鈕越少越好

本章偏好使用者導向的觀點。有限的組態選項反而能帶來更好的採用率,因為上手成本大幅降低,軟體基本上「開箱即用」。

問題應接近使用者目標#

使用者輸入可被視為一個光譜:一端是使用者用自己的術語描述需求(較少選項),另一端是使用者精確指定系統如何實現需求(較多選項)。

以泡茶為例:使用者可以簡單要求「熱綠茶」,也可以指定水量、沸騰溫度、茶品牌、浸泡時間、杯子類型等。後者可能更接近完美,但所需的心力可能超過其邊際效益。

當使用者描述高層次目標而非精確步驟時,系統可以隨時間改進其實現方式。工作排程就是好例子:Kubernetes 或 Mesos 讓你專注於「執行分析」的目標,而不必煩惱具體在哪台機器上執行。

必要問題與選擇性問題#

  • 必要問題(Mandatory):必須回答才能提供任何功能(例如計費帳號)
  • 選擇性問題(Optional):不影響核心功能,但回答後可改善品質(例如 worker 數量)

為保持使用者導向和易於採用,系統應最小化必要問題的數量。最簡單的方式是將必要問題轉為選擇性問題,即提供適用於大多數使用者的預設值。

預設值不一定是靜態的硬編碼值,也可以是動態預設(Dynamic Defaults)。例如:

  • 計算密集型系統可根據容器的執行核心數自動決定計算執行緒數
  • Java 應用程式可根據容器的可用記憶體自動調整堆積限制

選擇預設值時要謹慎考量其影響。經驗顯示大多數使用者會使用預設值,因此這既是機會也是責任。以器官捐贈為例:預設為「選擇加入」的國家,器官捐贈者比例遠高於預設為「選擇退出」的國家。

跳脫簡潔#

為了兼顧進階使用者的需求,可以採用選擇性覆寫預設行為的模式。使用者先設定「綠茶」,然後補充「浸泡五分鐘」。這種方式讓預設組態保持高層次且貼近使用者目標,但允許微調低層次細節。這與高階程式語言(如 C++ 或 Java)允許插入機器指令的概念類似。

設計時應考量整個組織花在組態上的總時數,包括決策癱瘓、修正錯誤、因低信心而減緩變更速度等因素。

組態機制#

分離組態與結果資料#

Figure 14.2: Configuration flow with a separate configuration interface

關於「組態是程式碼還是資料」這個問題,實務經驗顯示兩者兼備但彼此分離是最佳方案:

  • 系統基礎架構應操作純靜態資料(如 Protocol Buffers、YAML、JSON)
  • 使用者則透過高階介面(如 Python DSL、Lua、Jsonnet 或 Web UI)與組態互動
  • 高階介面產生靜態資料的過程類似於「編譯」

這種分離帶來多項好處:

  • 不同組織可根據文化規範或需求使用不同的組態語言
  • 靜態資料可用於資料分析(例如載入 PostgreSQL 查詢哪些參數被誰使用)
  • 系統可輕鬆支援多種語言

工具的重要性#

工具往往在設計組態系統時被忽略,但它們能決定系統是一場混亂的噩夢還是可持續且可擴展的系統。

語意驗證(Semantic Validation):不僅檢查語法正確性,還要驗證組態是否語意上有意義。例如偵測使用者是否參照了不存在的目錄(因為打字錯誤),或要求了實際上不存在的大量 RAM。

組態語法工具

  • 語法高亮:在公司使用的編輯器中提供
  • Linter:識別語言使用中的常見不一致
  • 自動格式化工具:標準化格式以減少無關緊要的討論,並降低認知負荷

所有權與變更追蹤#

  • 每段組態應有明確的擁有者
  • 組態應進行版本控制(如 Git),以便追溯任何時間點的組態狀態
  • 記錄組態變更和其對系統的影響,這在事故回應中尤為重要
  • 確定組態變更的完整編輯集有助於自信地回滾,並通知受影響的各方

安全的組態變更應用#

安全的組態變更必須具備三個特性:

  1. 能夠漸進式部署,避免全有或全無的變更(例如 Kubernetes 的 rolling update 策略)
  2. 能夠回滾變更,回滾比臨時修補更快且更有信心
  3. 自動回滾(或至少停止推進),當變更導致操作者失去控制時

為了能前滾和回滾組態,組態必須是封閉的(Hermetic)。依賴於封閉環境之外可變更外部資源的組態,會非常難以回滾。例如,存放在版本控制系統中但參照網路檔案系統資料的組態,就不是封閉的。

系統應特別小心處理可能導致操作者突然失去控制的變更。就像桌面系統的螢幕解析度變更常會提示倒計時確認一樣,系統管理員也經常不小心用防火牆把自己鎖在正在設定的系統外面。

結論#

微不足道的組態變更可能以戲劇性的方式影響生產系統,因此需要刻意設計組態來降低風險。組態設計兼具 API 和 UI 設計的特質,應該是有目的的設計,而非系統實作的副產品。

實際案例顯示,Google 在設計 Canary Analysis Service 時花了大約一個月來減少必要問題並為選擇性問題找到好答案。這些努力創造了一個簡潔的組態系統,因為易於使用而在內部被廣泛採用,幾乎不需要使用者支援。