在現代軟體開發中,基礎設施變更是一項常見但充滿挑戰的任務。如何在不中斷服務的情況下進行基礎設施改造?本文將探討一系列實用技術,幫助團隊安全地實施基礎設施變更,從小型增量改進到大規模架構重組。

小步驟變更的力量

在基礎設施變更中,最大的風險往往來自於一次性實施過大的變更。玄貓在多年的技術實踐中發現,那些在本地累積太多工作再一次推播的做法,往往會導致最嚴重的程式碼混亂。雖然完成整個工作專案的誘惑很大,但將大型變更分解為一系列小步驟需要全新的思維模式和習慣。

軟體開發領域已經為我們指明瞭道路。測試驅動開發(TDD)、持續整合(CI)和持續佈署(CD)等技術都支援逐步構建系統。透過管道逐步測試和交付程式碼變更,使團隊能夠進行小規模變更、推播、取得反饋,並將其投入生產。

採用這些技術的團隊通常會非常頻繁地推播變更。一位工程師可能每小時推播一次變更,每個變更都會整合到主程式碼函式庫中,並在完全整合的系統中測試其生產就緒性。

增量與迭代:實施大型變更的策略

在實施大型變更時,有幾種常見的方法:

  1. 增量式變更:一次增加計劃實施的一個部分。例如,先建立分享網路堆積積疊,然後增加網頁叢集堆積積疊,最後構建應用程式基礎設施堆積積疊。

  2. 迭代式變更:對系統進行漸進式改進。先建立所有堆積積疊的基本版本,然後進行一系列變更,每次擴充套件堆積積疊的功能。

  3. 骨架系統:實施新系統主要部分的基本實作,以驗證其設計和結構。初始實作和工具選擇通常不是長期計劃使用的,而是為了快速驗證交付、佈署和營運可能的工作方式。

  4. 重構:在不改變系統行為的情況下改變系統或元件的設計。重構通常為後續行為變更鋪平道路,可能是為了提高程式碼清晰度或重組程式碼以適應計劃中的變更。

重構範例:分解堆積積疊

假設一個團隊決定將當前堆積積疊分解為多個堆積積疊和堆積積疊例項。計劃包括一個用於託管網頁伺服器的容器叢集堆積積疊例項,以及一個用於分享網路結構的堆積積疊例項。團隊還將為每個服務設定一對堆積積疊,一個用於應用程式伺服器及其相關網路,另一個用於該服務的資料函式庫例項。

團隊還希望替換容器叢集產品,從自行佈署在虛擬機器上的Kubernetes叢集轉向使用雲端供應商提供的容器服務。

團隊決定增量實施其分解架構。第一步是將容器叢集提取到自己的堆積積疊中,然後替換堆積積疊中的容器產品。

這個計劃是使用重構來實作變更的例子。當容器叢集被隔離到自己的堆積積疊中時,更改容器叢集解決方案會比它作為更大堆積積疊的一部分時更容易。當團隊將叢集提取到自己的堆積積疊中時,他們可以定義其與系統其餘部分的整合點,編寫測試和其他驗證以保持分離和整合的清晰。這給團隊信心,他們可以安全地更改叢集堆積積疊的內容。

這個重構範例展示瞭如何透過分解現有架構來為重大變更做準備。將容器叢集隔離到獨立堆積積疊中,不僅使其更容易被替換,還能確保整合點的清晰定義。這種方法遵循"先重構,再變更"的原則,使團隊能夠在較低風險的情況下進行重大技術轉換。

將不完整的變更推播到生產環境

如何在保持服務執行的同時,將重大變更作為一系列小型增量變更交付到生產系統?有些小變更可能本身並不實用,在整套變更完成之前,可能無法實際移除現有功能。

前面的重構範例展示了兩個增量步驟:將容器叢集從一個堆積積疊提取到自己的堆積積疊中,然後替換新堆積積疊中的叢集解決方案。這些步驟都很大,因此可能會作為一系列較小的程式碼推播來實施。

然而,許多較小的變更可能會使叢集本身無法使用。因此,需要找到在保持現有程式碼和功能的同時進行這些較小變更的方法。根據情況,可以使用不同的技術。

平行例項:平行執行新舊系統

在叢集替換範例的第二步中,從原始容器解決方案開始,最終使用新的容器解決方案。

現有解決方案是名為KubeCan的封裝Kubernetes發行版。團隊正在轉向FKS,這是其雲平台提供的託管叢集服務。

將KubeCan叢集轉變為FKS叢集透過小步驟並不實際。但團隊可以平行執行這兩個叢集。有幾種不同的方式可以與原始堆積積疊的單個例項平行執行兩個不同的容器堆積積疊。

一種選擇是為主堆積積疊設定一個引數,選擇要整合的叢集堆積積疊:

# 主堆積積疊設定範例
parameters:
  cluster_stack_to_use: "kubecan_cluster" # 或 "fks_cluster"

resources:
  # 根據引數選擇啟用哪個叢集堆積積疊
  cluster:
    type: ${cluster_stack_to_use}
    # 其他設定...

這種方法中,一個堆積積疊被啟用並處理實時工作負載,而第二個堆積積疊被停用但仍然存在。團隊可以在完全執行的環境中測試第二個堆積積疊,練習和開發其交付管道和測試套件,並在每個環境中將其與基礎設施的其他部分整合。這種方法的優勢在於可以在不影響生產環境的情況下完全測試新解決方案。

另一種選擇是將兩個堆積積疊都與主堆積積疊整合:

# 同時整合兩個叢集堆積積疊的設定範例
resources:
  kubecan_cluster:
    type: "kubecan_stack"
    enabled: true
    # 設定...
    
  fks_cluster:
    type: "fks_stack"
    enabled: true
    # 設定...
    
  workload_router:
    type: "traffic_director"
    clusters:
      - kubecan_cluster
      - fks_cluster
    routing_rules: ${routing_configuration}

使用這種安排,可以將部分工作負載導向每個叢集堆積積疊。可以透過不同方式分配工作負載:

  1. 工作負載百分比:將部分工作負載導向每個堆積積疊。通常,舊堆積積疊最初處理大部分負載,新堆積積疊接收一小部分以評估其工作效果。隨著新堆積積疊的穩定,可以逐漸增加負載。當新堆積積疊成功管理100%的負載與所有人準備就緒後,可以停用舊堆積積疊。

  2. 服務遷移:一個接一個地將服務遷移到新叢集。主堆積積疊中的工作負載(如網路連線或訊息)被導向到相關服務執行的堆積積疊例項。這對於需要修改服務應用程式才能移動到新堆積積疊的情況特別有用。

  3. 使用者分割槽:在某些情況下,不同的使用者組被導向不同的堆積積疊實施。測試人員和內部使用者通常是第一組。他們可以在冒險接觸"真實"客戶之前進行探索性測試並練習新系統。

向後相容的轉換

雖然某些變更可能需要構建和執行新元件與舊元件平行直到完成,但許多變更可以在不影響使用者或消費者的情況下在元件內進行。

即使在增加或更改提供給消費者元件的內容時,通常也可以增加新的整合點,同時保持現有整合點不變。消費者可以按照自己的時間表切換到使用新的整合點。

例如,一個團隊計劃更改其分享網路堆積積疊,從單一VLAN轉向三個VLAN:

原始分享網路堆積積疊程式碼匯出其VLAN識別符號供消費者堆積積疊發現:

vlans:
- main_vlan
  address_range: 10.2.0.0/8
export:
- main_vlan: main_vlan.id

分享網路堆積積疊的新版本建立三個VLAN並以新名稱匯出其識別符號。它還使用舊識別符號匯出其中一個VLAN識別符號:

vlans:
- appserver_vlan_A
  address_range: 10.1.0.0/16
- appserver_vlan_B
  address_range: 10.2.0.0/16
- appserver_vlan_C
  address_range: 10.3.0.0/16
export:
- appserver_vlan_A: appserver_vlan_A.id
- appserver_vlan_B: appserver_vlan_B.id
- appserver_vlan_C: appserver_vlan_C.id
# 已棄用
- main_vlan: appserver_vlan_A.id

透過保持舊識別符號,修改後的網路堆積積疊仍然適用於消費者基礎設施程式碼。消費者程式碼應該被修改以使用新識別符號,一旦所有對舊識別符號的依賴都消失,它就可以從網路堆積積疊程式碼中移除。這種方法允許系統逐步演進,而不會破壞現有的整合點。

功能開關:在不分支的情況下進行變更

在對元件進行變更時,通常需要繼續使用現有實施直到完成變更。有些人在原始碼控制中分支程式碼,在一個分支中處理新變更,在舊分支中用於生產。這種方法的問題包括:

  • 確保對元件其他區域的變更(如錯誤修復)合併到兩個分支需要額外工作。
  • 確保兩個分支都持續測試和佈署需要努力和資源。
  • 一旦變更準備就緒,將生產例項切換過來更像是一個"大爆炸"操作,失敗風險更高。

在不分支的情況下處理主程式碼函式庫的變更有效。可以使用功能開關在不同環境中切換程式碼實施。在某些環境中切換到新程式碼以測試工作,在生產線環境中切換到現有程式碼。使用堆積積疊設定引數指定將哪部分程式碼應用於給定例項。

例如,當一個團隊需要更改應用程式基礎設施堆積積疊以使用新的VLAN時,他們發現這不是一個簡單的變更。團隊決定向應用程式基礎設施堆積積疊增加一個設定引數,根據是使用單一VLAN還是多個VLAN選擇不同的堆積積疊程式碼部分:

input_parameters:
  name: toggle_use_multiple_vlans
  default: false
variables:
- name: appserver_A_vlan
  value:
    $IF(${toggle_use_multiple_vlans} appserver_vlan_A ELSE main_vlan)
- name: appserver_B_vlan
  value:
    $IF(${toggle_use_multiple_vlans} appserver_vlan_B ELSE main_vlan)
- name: appserver_C_vlan
  value:
    $IF(${toggle_use_multiple_vlans} appserver_vlan_C ELSE main_vlan)
virtual_machine:
  name: appserver-${SERVICE}-A
  memory: 4GB
  address_block: ${appserver_A_vlan}
virtual_machine:
  name: appserver-${SERVICE}-B
  memory: 4GB
  address_block: ${appserver_B_vlan}
virtual_machine:
  name: appserver-${SERVICE}-C
  memory: 4GB
  address_block: ${appserver_C_vlan}

如果toggle_use_multiple_vlans開關設定為false,則appserver_X_vlan引數都設定為使用舊的VLAN識別符號main_vlan。如果開關為true,則每個變數都設定為新的VLAN識別符號之一。相同的開關引數用於堆積積疊程式碼的其他部分,團隊在這些部分處理設定路由和其他棘手元素。

關於功能開關的建議:

  • 最小化使用的功能開關數量。功能開關和條件陳述式使程式碼混亂,難以理解、維護和除錯。
  • 保持功能開關的短期存在。盡快移除對舊實施的依賴,並移除開關和條件程式碼。
  • 根據功能開關的作用命名。避免模糊的名稱,如new_networking_code
  • 清晰表明開關的工作方式,避免使用者在條件判斷中使用錯誤的邏輯。

更改實時基礎設施

這些技術和範例解釋瞭如何更改基礎設施程式碼。更改執行中的基礎設施例項可能更棘手,特別是當更改被其他基礎設施消費的資源時。

例如,當團隊應用更改分享網路堆積積疊程式碼(將單一VLAN替換為三個VLAN)時,會發生什麼情況?從其他堆積積疊分配到第一個VLAN的資源會怎樣?

應用網路程式碼會銷毀main_vlan,其中包含三個伺服器例項。在實時環境中,銷毀這些伺服器或將它們與網路分離會中斷它們提供的任何服務。

大多數基礎設施平台會拒絕銷毀附有伺服器例項的網路結構,因此操作會失敗。如果應用的程式碼變更移除或更改其他資源,操作可能會對例項實施這些變更,使環境處於堆積積疊程式碼舊版本和新版本之間的中間狀態。這幾乎總是一件壞事。

處理這種實時基礎設施變更有幾種方法。一種是保留舊的VLAN main_vlan,並增加兩個新的VLAN appserver_vlan_Bappserver_vlan_C

這樣做會留下三個VLAN,如預期的那樣,但其中一個的命名與其他不同。保留現有VLAN可能會阻止更改其他方面,如其IP位址範圍。這些妥協是一個壞習慣,導致不一致的系統和混亂的程式碼。

可以使用其他技術來更改實時系統並使其處於乾淨、一致的狀態。一種是使用基礎設施手術編輯基礎設施資源。另一種是擴充套件和收縮基礎設施資源。

基礎設施手術:編輯資源對映

一些堆積積疊管理工具(如Terraform)提供對對映基礎設施資源到程式碼的資料結構的存取。這些是用於依賴發現的堆積積疊資料查詢模式中使用的相同資料結構。

一些(但不是全部)堆積積疊工具有編輯其資料結構的選項。可以利用此功能對實時基礎設施進行更改。

例如,一個團隊可以使用其堆積積疊工具編輯堆積積疊資料結構,將main_vlan從舊堆積積疊例項的資料結構移動到新堆積積疊例項的資料結構中,然後用它替換appserver_vlan_A

基礎設施平台中的VLAN不會以任何方式更改,伺服器例項將完全不受影響。這些更改完全是堆積積疊工具資料結構中的簿記練習。

基礎設施手術是一種高階技術,允許直接操作堆積積疊管理工具的內部資料結構。這種方法的風險很高,因為手動編輯這些結構很容易出錯,可能導致中斷。雖然檢視堆積積疊資料結構對除錯很有用,但應避免編輯它們。任何時候不得不編輯結構,團隊都應該進行無責備的事後分析,以瞭解如何避免重複。

擴充套件和收縮:安全變更的模式

基礎設施團隊使用擴充套件和收縮模式(也稱為平行變更)來更改介面而不破壞消費者。這個想法是更改提供者的介面涉及兩個步驟:更改提供者,然後更改消費者。擴充套件和收縮模式將這些步驟解耦。

該模式的本質是首先增加新資源同時保留現有資源,然後將消費者更改為新資源,最後移除舊的未使用資源。每個變更都使用管道交付,因此經過徹底測試。

透過擴充套件和收縮排行變更類別似於向後相容的轉換。該技術替換了舊資源並將舊介面重新指向新資源之一。然而,將新程式碼應用於執行例項會嘗試銷毀舊資源,這可能會中斷附加到它的任何消費者或無法完成。因此需要一些額外步驟。

團隊使用擴充套件和收縮排行VLAN變更的第一步是向分享網路堆積積疊增加新的VLAN,同時保留舊的main_vlan

vlans:
- main_vlan
  address_range: 10.2.0.0/8
- appserver_vlan_A
  address_range: 10.1.0.0/16
- appserver_vlan_B
  address_range: 10.2.0.0/16
- appserver_vlan_C
  address_range: 10.3.0.0/16
export:
- main_vlan: main_vlan.id
- appserver_vlan_A: appserver_vlan_A.id
- appserver_vlan_B: appserver_vlan_B.id
- appserver_vlan_C: appserver_vlan_C.id

與平行例項技術和基礎設施手術不同,團隊不增加堆積積疊的第二個例項,而只更改現有例項。

應用此程式碼後,現有消費者例項不受影響——它們仍然連線到main_vlan。團隊可以向新VLAN增加新資源,並對消費者進行更改以將它們切換過來。

如何將消費者資源切換到使用新資源取決於特定基礎設施和平台。在某些情況下,可以更新資源的定義以將其附加到新的提供者介面。在其他情況下,可能需要銷毀並重建資源。

例如,團隊無法將現有虛擬伺服器例項重新分配到新的VLAN。但是,團隊可以使用擴充套件和收縮模式替換伺服器:

# 第一步:增加新伺服器例項
virtual_machine:
  name: appserver-${SERVICE}-A2
  memory: 4GB
  vlan: external_stack.shared_network_stack.appserver_vlan_A
virtual_machine:
  name: appserver-${SERVICE}-A
  memory: 4GB
  vlan: external_stack.shared_network_stack.main_vlan
static_ip:
  name: address-${SERVICE}-A
  attach: virtual_machine.appserver-${SERVICE}-A
# 第二步:切換流量到新伺服器
virtual_machine:
  name: appserver-${SERVICE}-A2
  memory: 4GB
  vlan: external_stack.shared_network_stack.appserver_vlan_A
virtual_machine:
  name: appserver-${SERVICE}-A
  memory: 4GB
  vlan: external_stack.shared_network_stack.main_vlan
static_ip:
  name: address-${SERVICE}-A
  attach: virtual_machine.appserver-${SERVICE}-A2
# 第三步:移除舊伺服器
virtual_machine:
  name: appserver-${SERVICE}-A2
  memory: 4GB
  vlan: external_stack.shared_network_stack.appserver_vlan_A
static_ip:
  name: address-${SERVICE}-A
  attach: virtual_machine.appserver-${SERVICE}-A2

這個過程展示瞭如何使用擴充套件和收縮模式安全地替換基礎設施元件。首先增加新資源但不使用它,然後將流量切換到新資源,最後在確認一切正常後移除舊資源。這種方法的優勢在於每個步驟都是可逆的,如果出現問題,可以輕鬆回復變更。

一旦所有消費者基礎設施都已更改,團隊可以從提供者堆積積疊程式碼中移除main_vlan

vlans:
- appserver_vlan_A
  address_range: 10.1.0.0/16
- appserver_vlan_B
  address_range: 10.2.0.0/16
- appserver_vlan_C
  address_range: 10.3.0.0/16
export:
- appserver_vlan_A: appserver_vlan_A.id
- appserver_vlan_B: appserver_vlan_B.id
- appserver_vlan_C: appserver_vlan_C.id

零停機變更

本文描述的許多技術解釋瞭如何增量實施變更。理想情況下,希望在不中斷其提供的服務的情況下將變更應用於現有基礎設施。有些變更不可避免地涉及銷毀資源,或至少以可能中斷服務的方式更改它們。

處理這些情況有幾種常見技術:

藍綠變更

藍綠變更涉及建立新例項,將使用切換到新例項,然後移除舊例項。這在概念上類別似於擴充套件和收縮,後者在元件(如堆積積疊)的例項內增加和移除資源。這是實施不可變基礎設施的關鍵技術。

藍綠變更需要一種機制來處理工作負載從一個例項到另一個例項的切換,例如用於網路流量的負載平衡器。複雜的實施允許工作負載"排空",將新工作分配給新例項,並等待舊例項上的所有工作完成後再銷毀它。一些自動化伺服器叢集和應用程式叢集解決方案將此作為功能提供,例如,啟用叢集中例項的"滾動升級"。

藍綠透過維護兩個環境來實施靜態基礎設施。任何時候都有一個環境是實時的,另一個準備接受下一個版本。藍色和綠色的名稱強調這些是平等的環境,輪流成為實時環境,而不是主要和次要環境。

玄貓曾與一個實施藍綠資料中心的組織合作。發布涉及將其整個系統的工作負載從一個資料中心切換到另一個資料中心。這種規模變得難以管理,所以玄貓幫助該組織實施較小規模的佈署,使其只對正在升級的特定服務進行藍綠佈署。

透過這些技術和策略,團隊可以安全地實施基礎設施變更,從小型增量改進到大規模架構重組,同時保持服務的連續性和可靠性。關鍵是將大型變更分解為一系列小步驟,每一步都經過充分測試和驗證,確保系統在整個過程中保持穩定執行。

基礎設施變更不必是一個令人生畏的過程。透過採用這些實踐和模式,團隊可以可控與可預測的方式演進其基礎設施,同時持續提供價值並支援業務目標。

從鐵器時代到雲端時代的基礎設施管理

在傳統的「鐵器時代」基礎設施管理方法中,變更成本高昂與風險巨大。當我們主要處理實體裝置並手動管理它們時,犯錯的代價尤其高昂。我曾經遇過這樣的情況:為伺服器設定不足的記憶體,結果需要花一週以上的時間訂購更多 RAM,將其帶到資料中心,關閉伺服器並從機架上取下,開啟機殼增加額外的記憶體,然後重新安裝並啟動伺服器。

相比之下,「雲端時代」的變更成本和修正錯誤的時間大幅降低。如果我在雲端環境中設定了記憶體不足的伺服器,只需幾分鐘就能透過編輯檔案並應用到虛擬伺服器來修正問題。

從 MTBF 到 MTTR 的思維轉變

鐵器時代的連續性方法強調預防,透過犧牲變更的速度和頻率來最佳化 MTBF(平均故障間隔時間)。而雲端時代的方法則最佳化 MTTR(平均復原時間)。雖然有些現代方法的擁護者誤以為專注於 MTTR 意味著犧牲 MTBF,但這種想法是錯誤的。

專注於四個關鍵指標(變更的速度和頻率、MTTR 和變更失敗率)的團隊會自然而然地達到良好的 MTBF。重點不是「快速行動並打破東西」,而是「快速行動並修復東西」。

現代基礎設施連續性的核心元素

實作現代基礎設施連續性有幾個關鍵元素:

  1. 預防錯誤:雖然這是雲端時代變更管理的重點,但我們需要更有效的方法
  2. 雲端基礎設施和自動化:使用更有效的敏捷工程實踐來減少錯誤
  3. 新技術和實踐:用於還原和重建系統,實作以前無法想像的高水平連續性
  4. 持續演練:不斷測試變更交付和系統還原機制,確保可靠性和災難準備

透過預防錯誤實作連續性

鐵器時代的變更管理主要是預防性的。由於修復錯誤的成本高昂,組織投入大量資源來預防錯誤。由於變更主要是手動的,預防涉及限制誰可以進行變更。人們需要詳細規劃和設計變更,而其他人則對每個變更進行徹底審查和討論。這種方法的理念是讓更多人花更多時間考慮變更,以提前發現錯誤。

這種方法的一個問題是設計檔案和實際實作之間存在差距。在圖表上看起來簡單的東西在現實中可能很複雜。人們在執行大型、不頻繁的升級時容易犯錯。結果是傳統的低頻率、高度計劃的大批次變更操作失敗率高,還原時間往往很長。

敏捷方法的核心洞見

敏捷軟體開發和基礎架構即程式碼的核心洞見是改變對變更的態度。與其害怕變更並盡可能少地進行變更,不如透過頻繁變更來預防錯誤。提高變更能力的唯一方法是頻繁地進行變更,不斷改進系統和流程。

另一個關鍵洞見是,隨著系統變得更加複雜,我們在生產環境前複製和準確測試程式碼行為的能力會下降。我們需要了解在生產前可以測試什麼,不能測試什麼,以及如何透過改善生產系統的可見性來降低風險。

透過快速復原實作連續性

限制變更的規模、增量式變更和在生產前測試變更可以降低變更失敗率。但假設錯誤可以完全預防是不明智的,所以我們還需要能夠快速與輕鬆地還原。

現代基礎設施實踐使重建系統的任何部分變得容易。系統由鬆散耦合的元件組成,每個元件都定義為冪等程式碼。透過重新應用程式碼,可以輕鬆修復或銷毀並重建任何元件例項。如果重建元件,需要確保託管在元件上的資料的連續性。

自動化復原機制

在某些情況下,平台或服務可以自動重建失敗的基礎設施:

  • 當元件未透過健康檢查時,基礎設施平台或應用程式執行時會銷毀並重建個別元件
  • 持續將程式碼應用於例項會自動還原與程式碼的任何偏差
  • 可以手動觸發管道階段來重新應用程式碼到損壞的元件

在其他故障情況下,這些系統可能無法自動修復問題:

  • 計算例項可能以某種方式故障,但仍然透過健康檢查
  • 基礎設施元素可能停止正常工作,但仍然符合程式碼定義,因此重新應用程式碼沒有幫助

這些情況需要採取額外行動來替換失敗的元件。可能需要標記元件,使自動化系統將其視為失敗,並銷毀和替換它。或者,如果復原使用重新應用程式碼的系統,可能需要自行銷毀元件,並讓系統建立新例項。

對於任何需要人為干預的故障情況,應確保有簡單執行的工具、指令碼或其他機制。人們不應該需要遵循一系列步驟,例如在銷毀例項前備份資料。相反,他們應該呼叫一個執行所有必要步驟的動作。目標是在緊急情況下,不需要思考如何正確還原系統。

持續災難復原

鐵器時代的基礎設施管理將災難復原視為不尋常的事件。從靜態硬體故障中還原通常需要將工作負載轉移到備用的硬體集。

許多組織很少測試其復原操作——最好每隔幾個月,有些情況下一年一次。我見過很多組織很少測試其容錯移轉流程。假設是團隊將在需要時找出如何讓備份系統執行,即使需要幾天時間。

持續災難復原的優勢

持續災難復原利用相同的流程和工具來設定和變更基礎設施。如前所述,可以應用基礎設施程式碼來重建失敗的基礎設施,可能還需要一些額外的自動化來避免資料丟失。

雲端時代基礎設施的原則之一是假設系統不可靠。不能在虛擬機器上安裝軟體並期望它會一直執行。雲端供應商可能會為了維護、安全補丁或升級而移動、銷毀或替換機器或其主機系統。因此需要準備好在需要時替換伺服器。

將災難復原視為正常操作的延伸使其比將其視為例外情況更可靠。團隊在處理基礎設施程式碼變更和系統更新時,每天多次演練復原流程和工具。如果有人對指令碼或其他程式碼進行了破壞設定或在更新時導致資料丟失的變更,通常會在管道測試階段失敗,因此可以快速修復。

混沌工程的實踐

Netflix 是持續災難復原和雲端時代基礎設施管理的先驅。其 Chaos Monkey 和 Simian Army 將持續災難復原的概念更進一步,透過向生產系統注入錯誤來證明其系統連續性機制的有效性。這演變成了混沌工程領域,「在系統上進行實驗以建立對系統能力的信心」。

需要明確的是,混沌工程不是關於不負責任地導致生產服務中斷。實踐者會實驗系統預期能夠處理的特定故障情況。這些是證明檢測和復原機制正常工作的基本生產測試。目的是在系統的某些變更對這些機制產生副作用時獲得快速反饋。

規劃故障

故障是不可避免的。雖然可以並且應該採取措施使其不太可能發生,但也需要採取措施使其危害較小與更容易處理。

團隊可以舉行故障情境對映研討會,集思廣益可能發生的故障型別,然後規劃緩解措施。可以建立每個情境的可能性和影響的對映,建立解決這些情境的行動列表,然後適當地將這些優先排序到團隊的工作積壓中。

故障情境分析框架

對於任何給定的故障情境,有幾個條件需要探索:

原因和預防

什麼情況可能導致這種故障,以及如何降低其可能性?例如,當使用量激增時,伺服器可能會耗盡磁碟空間。可以透過分析磁碟使用模式並擴充套件磁碟大小來解決這個問題,以便有足夠的空間應對更高的使用水平。還可以實施自動化機制來持續分析使用水平並進行預測,以便在模式變化時可以預先增加磁碟空間。更進一步的步驟是隨著使用量增加自動調整磁碟容量。

故障模式

故障發生時會發生什麼?在沒有人為干預的情況下,可以做什麼來減少後果?例如,如果給定的伺服器耗盡磁碟空間,在其上執行的應用程式可能會接受交易但無法記錄它們。這可能非常有害,因此可以修改應用程式,使其在無法將交易記錄到磁碟時停止接受交易。在許多情況下,團隊實際上不知道當給定錯誤發生時會發生什麼。理想情況下,故障模式應保持系統執行。例如,當應用程式停止回應時,負載平衡器可能會停止向其引導流量。

檢測

如何檢測故障何時發生?如何更快地檢測它,甚至可能在事前檢測?可能在應用程式當機與客戶打電話給 CEO 投訴時檢測到磁碟已耗盡空間。更好的是在應用程式當機時收到通知。更好的是在磁碟空間不足時收到通知,在它實際填滿之前。

修正

需要採取哪些步驟從故障中還原?在某些情況下,如前所述,系統可能會自動修正情況,可能是透過銷毀和重建無回應的應用程式例項。其他情況需要幾個步驟來修復和重新啟動服務。

如果系統自動處理故障情境,例如重新啟動無回應的計算例項,應考慮更深層次的故障情境。為什麼例項一開始會變得無回應?如何檢測和修正潛在問題?不應該花幾天時間才意識到應用程式例項每隔幾分鐘就被回收一次。

故障規劃是一個持續的過程。每當系統發生事件,包括在開發或測試環境中,團隊都應考慮是否有新的故障情境需要定義和規劃。

應該實施檢查來證明故障情境。例如,如果認為當伺服器耗盡磁碟空間時,應用程式將停止接受交易,自動增加新的伺服器例項,並提醒團隊,應該有一個自動化測試來演練這個情境。可以在管道階段(如「我們應該用基礎設施測試什麼?」中描述的可用性測試)或使用混沌實驗來測試這一點。

增量改進連續性

定義雄心勃的復原措施很容易,讓系統優雅地處理每一個可以想像的故障而不中斷服務。我從未見過一個團隊有時間和資源來建立他們想要的一半。

在對映故障情境和緩解措施時,可以定義一組可以實施的增量措施。將它們分解為單獨的實施故事,並根據情境的可能性、潛在損害和實施成本在積壓工作中排定優先順序。例如,雖然在應用程式磁碟空間不足時自動擴充套件磁碟空間會很好,但在它耗盡之前獲得警示是一個有價值的第一步。

變動系統中的資料連續性

許多雲端時代的實踐和技術在佈署軟體和管理基礎設施時都輕鬆地建議隨意銷毀和擴充套件資源,只是對資料問題輕描淡寫。可能會認為 DevOps 潮人認為整個資料概念是鐵器時代的遺留物——畢竟,一個正確的十二因素應用程式是無狀態的。但現實世界中的大多數系統都涉及資料,人們對資料有著令人感動的依戀。

資料在增量變更系統時可能帶來挑戰,如「將不完整的變更推播到生產環境」中所述。執行儲存基礎設施的平行例項可能會造成不一致甚至損壞資料。許多增量佈署變更的方法依賴於能夠回復變更,這對於資料結構變更可能是不可能的。

動態增加、移除和重建託管資料的基礎設施資源尤其具有挑戰性。然而,根據情況,有多種管理方法,包括鎖定、隔離、複製和重新載入。

資料連續性策略

鎖定

某些基礎設施平台和堆積積疊管理工具允許鎖定特定資源,使其不會被原本會銷毀它們的命令刪除。如果為儲存元素指定此設定,則工具將拒絕對此元素應用變更,允許團隊成員手動進行變更。

然而,這存在一些問題。在某些情況下,如果對受保護的資源應用變更,工具可能會使堆積積疊處於部分修改狀態,這可能導致服務中斷。

但根本問題是保護某些資源不受自動化變更的影響會鼓勵手動參與變更。手動工作容易導致手動錯誤。找到一種自動化安全變更基礎設施的方法要好得多。

隔離

可以透過將託管資料的資源與系統的其他部分開來隔離資料;例如,透過建立單獨的堆積積疊(「微堆積積疊模式」中給出了一個例子)。透過分離和重新連線磁碟卷,可以毫無顧慮地銷毀和重建計算例項。

將資料儲存在資料函式庫中提供了更大的靈活性,可能允許增加多個計算例項。仍然需要為託管資料的堆積積疊制定資料連續性策略,但這縮小了問題的範圍。使用託管的 DBaaS 服務可能完全解除安裝資料連續性。

複製

根據資料及其管理方式,可能夠在多個基礎設施例項之間複製資料。一個典型的例子是分散式資料函式庫叢集,它在節點之間複製資料。

使用正確的複製策略,資料會從叢集中的其他節點重新載入到新重建的節點。如果失去太多節點,這種策略會失敗,這可能發生在主要託管中斷時。因此,這種方法作為第一道防線,需要另一種機制來應對更困難的故障情況。

重新載入

最著名的資料連續性解決方案是從更可靠的儲存基礎設施備份和還原資料。重建託管資料的基礎設施時,首先備份資料。建立新例項後,將資料重新載入到新例項。

也可以定期進行備份,可以在復原情況下重新載入,雖然會丟失備份和復原之間發生的任何資料變更。透過將資料變更流式傳輸到備份,例如寫入資料函式庫交易日誌,可以最小化甚至消除這種情況。

雲端平台提供不同的儲存服務,具有不同級別的可靠性。例如,像 AWS S3 這樣的物件儲存服務通常對資料的永續性有更強的保證,比如 AWS EBS 這樣的區塊儲存服務。因此,可以透過將資料複製或流式傳輸到物件儲存卷來實施備份。

應該自動化不僅備份資料而與還原資料的流程。基礎設施平台可能提供輕鬆做到這一點的方法。例如,可以在對其應用變更之前自動拍攝磁碟儲存卷的快照。

可能夠使用磁碟卷快照來最佳化向像資料函式庫叢集這樣的系統增加節點的流程。與其建立具有空儲存卷的新資料函式庫節點,不如將其連線到另一個節點磁碟的克隆,這可能使同步和使節點上線更快。

「未經測試的備份與沒有備份相同」是我們行業中的一句常見諺語。既然已經遵循基礎架構即程式碼實踐,已經在使用自動化測試來測試系統的各個方面。因此,可以對備份做同樣的事情。在管道中或作為混沌實驗演練備份還原流程,無論是否在生產環境中。

混合資料連續性方法

最佳解決方案通常是隔離、複製和重新載入的組合。隔離資料為更靈活地管理系統的其他部分創造了空間。複製使資料在大多數時間保持可用。而重新載入資料是更極端情況的後盾。

連續性常被現代雲端時代基礎設施管理實踐的倡導者忽視。保持系統可靠執行的最熟悉方法根據鐵器時代的前提,即進行變更是昂貴與有風險的。這些方法往往會削弱雲端、敏捷和其他專注於快速變更的方法的好處。

我希望本文已經解釋瞭如何利用雲端時代思維使系統更可靠,不是儘管快速變更,而是因為快速變更。可以利用現代基礎設施平台的動態特性,並實施來自敏捷工程實踐的嚴格測試和一致性關注。結果是對能夠持續交付系統改進並將故障作為學習和改進機會的高度信心。

在現代雲端環境中,連續性不再是一個特殊事件,而是日常營運的一部分。透過自動化、頻繁測試和混沌工程等實踐,我們可以建立真正彈性的系統,不僅能夠承受故障,還能從中學習並變得更強大。最終,真正的連續性來自於擁抱變更,而不是害怕它。