基礎設施程式碼的治理與合規
治理在基礎設施即程式碼的環境中扮演著關鍵角色,特別是對於大型組織和受監管行業。有效的治理確保基礎設施變更符合組織政策、安全標準和合規要求,同時不會過度阻礙創新和效率。
合規即程式碼的概念
「合規即程式碼」是一種將合規要求編碼到自動化流程中的方法。這種方法將合規從手動、事後的檢查轉變為自動化、預防性的控制。透過將合規檢查整合到CI/CD管道中,團隊可以在早期發現並解決合規問題,避免它們在生產環境中造成問題。
這種方法的核心優勢包括:
- 一致性:合規檢查以一致的方式應用於所有變更
- 可追蹤性:所有合規檢查和結果都有記錄
- 效率:自動化減少了手動審查的需求
- 預防性:問題在佈署前被發現和解決
在管道中實作治理
在根據管道的工作流程中,治理可以透過多種方式實作:
自動化政策檢查:使用工具如Open Policy Agent或HashiCorp Sentinel在管道中自動評估基礎設施程式碼是否符合組織政策
安全掃描:整合安全掃描工具,檢測基礎設施程式碼中的漏洞和錯誤設定
合規驗證:自動驗證基礎設施是否符合行業標準和法規要求(如HIPAA、PCI DSS、GDPR等)
審批工作流程:對於高風險變更,實施自動化審批流程,確保適當的利益相關者審查變更
變更管理整合:將管道與變更管理系統整合,確保所有變更都經過適當的記錄和授權
透過將這些控制整合到管道中,組織可以實作「左移」安全和合規,同時保持開發和營運的敏捷性。
平衡治理與敏捷性
有效的治理不應該成為創新和效率的障礙。關鍵是找到適當的平衡,實施足夠的控制以管理風險,同時不會過度限制團隊的能力。
一些實作這種平衡的策略包括:
- 風險分層:根據系統的風險級別調整治理控制的嚴格程度
- 自助服務平台:建立符合治理要求的自助服務平台,使團隊能夠快速佈署符合標準的基礎設施
- 政策即程式碼:將政策定義為程式碼,使其可以像應用程式碼一樣進行版本控制、測試和佈署
- 持續改進:定期審查和調整治理控制,確保它們仍然相關與有效
透過這種方法,治理可以成為使能者而非阻礙者,幫助組織安全、合規地實作其目標,同時保持必要的敏捷性和創新能力。
防止設定漂移的綜合策略
結合前面討論的各種方法,我們可以制定一個綜合策略來有效防止設定漂移:
- 頻繁應用程式碼:定期重新應用基礎設施程式碼,即使程式碼沒有變更
- 持續同步:實施GitOps方法,持續將程式碼從原始碼儲存函式庫同步到環境
- 不可變基礎設施:對適用的元件採用不可變基礎設施方法,透過替換而非修改來進行變更
- 自動化測試:實施全面的自動化測試,確保基礎設施程式碼的變更不會導致意外問題
- 監控與警示:設定監控和警示系統,快速檢測和回應設定漂移
- 定期重建:定期完全重建系統,確保它們與定義的程式碼保持一致
- 治理與合規:將治理和合規檢查整合到基礎設施管道中,確保所有變更都符合組織政策
透過實施這些策略,團隊可以大減少設定漂移的風險,確保其基礎設施保持一致、可靠和安全。這不僅提高了系統的穩定性,還減少了營運團隊處理意外問題的時間,使他們能夠專注於提供價值而非修復問題。
在現代雲環境中,這些實踐變得尤為重要,因為系統變得越來越複雜和分散。透過採用這些防止設定漂移的策略,組織可以確保其基礎設施能夠可靠地支援業務需求,同時保持必要的敏捷性和創新能力。
基礎設施即程式碼:重塑團隊工作流程與責任分配
在前面的內容中,我們探討了品質(包括治理作為品質的一個導向)如何能夠加速交付速度,而快速交付變更的能力又如何提升品質。「合規即程式碼」(Compliance as Code) 正是利用自動化和更具協作性的工作實踐來實作這種正向迴圈。
責任重組:基礎設施即程式碼的新機會
將系統定義為程式碼創造了重新分配基礎設施工作人員責任的機會,並改變了這些人與工作互動的方式。以下是促成這些機會的關鍵因素:
程式碼重用的價值
基礎設施程式碼可以被設計、審查並在多個環境和系統中重複使用。如果你使用已經透過審核流程的程式碼,就不需要為每台新伺服器或環境進行冗長的設計、審查和簽核流程。
在玄貓的實踐中,我發現建立模組化的基礎設施程式碼函式庫能大幅減少重複工作。例如,一個經過安全審核的網路設定模組可以在不同專案中重複使用,無需每次都重新審核相同的安全控制措施。
以實際運作的程式碼為基礎
由於程式碼編寫迅速,團隊成員可以根據實際運作的程式碼和基礎設施範例進行審查和決策。這比爭論圖表和規格書提供了更快速、更準確的反饋迴圈。
# 網路安全群組定義範例
resource "aws_security_group" "web_tier" {
name = "web-tier-sg"
description = "Security group for web tier servers"
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow HTTPS from anywhere"
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow HTTP from anywhere"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow all outbound traffic"
}
tags = {
Name = "WebTierSG"
Environment = var.environment
ManagedBy = "Terraform"
}
}
這段 Terraform 程式碼定義了一個 AWS 安全群組,專為網頁層伺服器設計。它允許來自任何地方的 HTTP (80) 和 HTTPS (443) 流量進入,同時允許所有出站流量。這種定義方式有幾個關鍵優勢:
- 明確性 - 安全規則以程式碼形式清晰定義,比文字描述更精確
- 可審核性 - 安全團隊可以直接審查程式碼,確認符合安全標準
- 版本控制 - 所有變更都有記錄,可追蹤誰做了什麼修改及原因
- 一致性 - 無論佈署多少環境,安全規則都保持一致
這種方法讓團隊能夠直接討論具體實作,而非抽象概念,大幅提高溝通效率。
環境一致性的保證
程式碼建立的環境比人類遵循檢查清單的方式更加一致。因此,在早期環境中測試和審查基礎設施提供了比後期流程更快速、更好的反饋。
當我們使用程式碼定義基礎設施時,開發、測試和生產環境之間的差異可以被明確控制,而不是因人為錯誤而產生意外差異。這種一致性是可靠系統的根本。
自動化測試的優勢
自動化測試(包括針對安全性和合規性等治理關注點的測試)為基礎設施程式碼工作人員提供快速反饋。他們可以在工作過程中糾正許多問題,無需為常規問題尋求工作者協助。
# 基礎設施安全性自動化測試範例
def test_security_group_no_open_ssh():
"""確保沒有安全群組允許從公網直接存取 SSH (22) 連線埠"""
for sg in get_all_security_groups():
for rule in sg.ingress_rules:
if rule.port == 22 and "0.0.0.0/0" in rule.cidr_blocks:
assert False, f"安全群組 {sg.id} 允許從公網直接存取 SSH"
這個 Python 測試函式檢查所有安全群組,確保沒有任何安全群組允許從公網 (0.0.0.0/0) 直接存取 SSH 連線埠 (22)。這種自動化測試可以:
- 在佈署前自動執行,防止不安全設定進入生產環境
- 作為持續整合流程的一部分,確保安全標準始終得到維護
- 減少安全團隊的手動審查負擔,讓他們專注於更複雜的安全挑戰
- 教育開發人員瞭解安全最佳實踐,因為測試失敗會提供明確的錯誤訊息
透過這種方式,團隊成員可以在開發過程中自行解決常見安全問題,只有在遇到特殊情況時才需要安全工作者介入。
品質民主化
非專業人員也可以對基礎設施的敏感區域(如網路和安全策略)進行程式碼更改。他們可以使用工作者建立的工具和測試來檢查他們的更改。工作者仍然可以在變更應用到生產系統之前進行審查和批准。這種審查方式更有效率,因為工作者可以直接檢視程式碼、測試報告和運作中的測試例項。
在我的實踐中,這種方法顯著提高了團隊的效率。例如,應用開發人員可以提出安全群組的變更,安全自動化測試會立即提供反饋,而安全團隊只需要審查那些透過了自動測試的變更請求。
治理通道的建立
基礎設施程式碼函式庫和用於將變更交付到生產例項的管道可以根據其治理要求進行組織。因此,對安全策略的更改會經過不一定適用於不太敏感區域更改的審查和簽核步驟。
# GitLab CI/CD 管道設定範例,包含治理控制
stages:
- validate
- test
- security_review
- deploy_staging
- compliance_check
- deploy_production
validate:
stage: validate
script:
- terraform validate
security_scan:
stage: test
script:
- run_security_scanner
security_review:
stage: security_review
script:
- echo "安全審查透過"
when: manual
only:
changes:
- "security/*.tf"
- "network/*.tf"
deploy_staging:
stage: deploy_staging
script:
- terraform apply -auto-approve -var-file=staging.tfvars
compliance_check:
stage: compliance_check
script:
- run_compliance_checks
deploy_production:
stage: deploy_production
script:
- terraform apply -auto-approve -var-file=production.tfvars
when: manual
這個 GitLab CI/CD 管道設定展示瞭如何根據治理需求組織基礎設施變更流程:
- 所有變更首先經過基本驗證和安全掃描
- 特定目錄(security/ 和 network/)的變更需要手動安全審查
- 變更先佈署到測試環境,然後進行合規性檢查
- 最後,變更需要手動批准才能佈署到生產環境
這種方法實作了:
- 差異化的控制 - 敏感區域有額外的審查步驟
- 自動化合規 - 合規檢查自動執行,減少人為錯誤
- 可追溯性 - 每個步驟都有記錄,便於稽核
- 平衡效率與控制 - 常規變更流程順暢,敏感變更有適當控制
左移:重新定義責任分配
我們可以改變人們管理系統的方式,其中許多涉及將責任在流程中向左移動(Shift Left)。左移是一種策略,將傳統上在開發後期才執行的活動(如測試、安全審查和合規檢查)提前到開發週期的早期階段。
左移的核心理念是:越早發現問題,修復成本越低,對整體交付速度的影響越小。在基礎設施即程式碼的環境中,這意味著:
- 開發人員在編寫基礎設施程式碼時就執行安全和合規檢查
- 自動化測試在提交程式碼時立即執行,而不是等到佈署前
- 安全和合規團隊提供工具和指導,而不僅是在流程末端進行審查
- 問題在開發環境中就被發現和解決,而不是在生產環境中
這種方法不僅提高了效率,還改善了團隊協作。當安全和合規成為開發過程的一部分,而不是外部強加的限制時,團隊更容易接受並遵循這些實踐。
實作左移的關鍵策略
要成功實作責任左移,團隊需要採取以下策略:
自動化工具整合:將安全掃描、合規檢查和設定驗證工具整合到開發環境和CI/CD管道中
知識分享:安全和合規工作者應該分享知識,培訓開發團隊理解基本的安全和合規要求
政策即程式碼:將組織政策轉化為可執行的測試和檢查,使其成為程式碼函式庫的一部分
共同責任文化:培養團隊共同對安全和合規負責的文化,而不是將其視為工作者的專屬領域
漸進式實施:從最關鍵的安全和合規要求開始,逐步擴充套件左移策略的範圍
透過這些策略,團隊可以在保持高品質和合規性的同時,提高交付速度和靈活性。基礎設施即程式碼不僅改變了我們建立和管理系統的方式,也重新定義了團隊成員之間的協作模式和責任分配。
安全變更基礎設施:持續改進的關鍵實踐
基礎設施即程式碼的核心理念是頻繁與快速地進行變更。正如本文一開始所提到的,速度不僅不會導致系統不穩定,反而是穩定性的推動力,反之亦然。我們的理念不是「快速行動並打破東西」,而是「快速行動並改進事物」。
速度與品質的平衡
研究表明,單純最佳化速度或品質都無法達到理想效果。關鍵在於同時最佳化兩者:專注於能夠頻繁、快速與安全地進行變更,並迅速檢測和還原錯誤。
使用程式碼建立一致的基礎設施、將測試融入日常工作流程、將系統分解為更小的元件—這些做法都能實作快速、頻繁與安全的變更。
然而,頻繁變更基礎設施也為提供不間斷服務帶來挑戰。我們需要採用一種思維模式,不將變更視為穩定性和連續性的威脅,而是利用現代基礎設施的動態特性,最大限度地減少變更帶來的中斷。
減少變更範圍
敏捷、極限程式設計、精益等方法透過小增量變更來最佳化交付速度和可靠性。計劃、實施、測試和除錯小型變更比大型變更容易,因此我們的目標是減少批次大小。當然,我們經常需要對系統進行重大變更,但可以將其分解為一系列小型變更,逐一交付。
ShopSpinner 團隊的案例
以ShopSpinner團隊為例,他們最初使用單一基礎設施堆積積疊建立了基礎設施,包括網頁伺服器叢集和應用程式伺服器。隨著時間推移,團隊增加了更多應用程式伺服器,並將部分伺服器轉變為叢集。
他們意識到在單一VLAN中執行網頁伺服器叢集和所有應用程式伺服器是一種不佳的設計,於是改進了網路設計,將這些元素轉移到不同的VLAN中。他們還決定將基礎設施分割為多個堆積積疊,使其更容易單獨變更。
ShopSpinner的原始實作是具有單一VLAN的單一堆積積疊:
graph TD A[單一基礎設施堆積積疊] --> B[單一VLAN] B --> C[網頁伺服器叢集] B --> D[應用程式伺服器]
團隊計劃將其堆積積疊分割為多個堆積積疊,包括分享網路堆積積疊和應用程式基礎設施堆積積疊。計劃還包括一個網頁叢集堆積積疊來管理前端網頁伺服器的容器叢集,以及一個應用程式資料函式庫堆積積疊來為每個應用程式管理資料函式庫例項:
graph TD A[分享網路堆積積疊] --> E[網路資源] B[網頁叢集堆積積疊] --> F[前端容器叢集] C[應用程式基礎設施堆積積疊] --> G[應用程式伺服器] D[應用程式資料函式庫堆積積疊] --> H[資料函式庫例項]
團隊還將單一VLAN分割為多個VLAN,應用程式伺服器將分佈在這些VLAN中以實作冗餘:
graph TD A[分享網路堆積積疊] --> B[多個VLAN] B --> C[VLAN 1] B --> D[VLAN 2] B --> E[VLAN 3] C --> F[應用程式伺服器A複本1] D --> G[應用程式伺服器A複本2] E --> H[應用程式伺服器B]
漸進式變更策略
將基礎設施分解為多個堆積積疊並重新設計網路是一項重大變更。團隊需要一種策略來安全地實施這些變更,同時維持服務的連續性。以下是一種可能的漸進式變更策略:
- 建立新的網路基礎設施:首先建立新的VLAN結構,但不將任何現有服務遷移到其中
- 逐步遷移應用程式伺服器:一次遷移一個應用程式伺服器到新的VLAN
- 分割堆積積疊:在所有服務都在新網路上執行後,將單一堆積積疊分割為多個獨立堆積積疊
- 持續測試:在每個步驟中進行全面測試,確保服務持續執行
這種漸進式方法允許團隊在每個步驟後驗證變更,並在發現問題時快速回復。
增量變更的優勢
採用增量變更方法有多項優勢:
- 降低風險:每次變更的範圍較小,因此出錯的可能性較低
- 更容易測試:小型變更容易徹底測試
- 快速反饋:團隊可以更快獲得反饋並調整方向
- 減少停機時間:小型變更通常需要較短的停機時間或根本不需要停機
- 更容易回復:如果出現問題,回復小型變更加簡單
標準化緊急修復流程
許多團隊為緊急變更設立單獨的流程,以便快速交付修復。需要單獨流程來加速修復是正常變更流程可以改進的跡象。
緊急變更流程透過兩種方式加快速度:
省略不必要的步驟:如果在緊急情況下可以安全地省略某個步驟,那麼在壓力大與風險高的情況下,可能也可以將其從正常流程中省略。
省略必要的步驟:如果跳過某個步驟風險不可接受,那麼應該找到更有效處理它的方法,並在每次變更中執行。
理想情況下,正常流程和緊急流程應該是相同的,只是緊急情況下可能會優先處理某些變更。這種標準化可以減少流程複雜性,並確保所有變更都經過適當的檢查。
當組織定義其基礎設施即程式碼時,人員應該發現自己花在執行例行活動和充當把關者的時間減少了。相反,他們應該花更多時間持續改進系統本身的能力。他們的努力將反映在軟體交付和營運績效的四個指標中:
- 佈署頻率
- 變更前置時間
- 變更失敗率
- 服務還原時間
透過採用這些原則和實踐,組織可以實作快速、頻繁與安全的基礎設施變更,同時提高系統的穩定性和可靠性。這不僅提高了技術團隊的效率,還增強了組織對市場變化的回應能力。
從設計到實踐:基礎設施變更策略
在前文中我們探討瞭如何設計和實作基礎設施堆積積疊的分割方案,現在讓我們深入研究如何在生產環境中安全地從一種實作方式轉換到另一種。
小變更的力量
玄貓在多年的技術實踐中發現,最嚴重的程式碼混亂往往源於在本地累積過多工作後才推播。雖然完成整個工作專案的誘惑很大,但將大型變更拆分為一系列小變更需要全新的思維模式和習慣。
幸運的是,軟體開發世界已經為我們指明瞭道路。測試驅動開發(TDD)、持續整合(CI)和持續佈署(CD)等技術能夠支援我們逐步建構系統。透過管道漸進式地測試和交付程式碼變更,讓我們能夠對程式碼進行小幅度修改、推播、獲得反饋,並將其佈署到生產環境中。
有效使用這些技術的團隊會非常頻繁地推播變更。一位工程師可能每小時推播一次變更,每個變更都會整合到主要程式碼函式庫中,並在完全整合的系統中測試其生產就緒性。
實作重大變更的方法
在技術社群中,有多種術語和技術用於描述如何透過一系列小變更來實作重大變更:
增量式變更
增量式變更是指一次增加計劃實作的一個部分。以ShopSpinner系統為例,我們可以透過一次實作一個堆積積疊來增量式建構系統:先建立分享網路堆積積疊,然後增加網頁叢集堆積積疊,最後建構應用程式基礎設施堆積積疊。
迭代式變更
迭代式變更是對系統進行漸進式改進。從建立所有三個堆積積疊的基本版本開始,然後進行一系列變更,每次擴充套件堆積積疊的功能。
骨架系統
骨架系統是新系統主要部分的基本實作,用於驗證其一般設計和結構。基礎設施專案的骨架系統通常與將在其上執行的應用程式的初始實作一起建立,以便團隊可以瞭解交付、佈署和營運可能如何運作。骨架系統的初始實作和工具選擇通常不是長期計劃使用的工具和服務。
重構
重構涉及更改系統或系統元件的設計,而不改變其行為。重構通常是為了為改變行為的變更鋪平道路。重構可能會改程式式碼的清晰度,使其更容易更改,或者可能重新組織程式碼,使其與計劃的變更保持一致。
重構例項:分解與替換
堆積積疊分解計劃
ShopSpinner團隊決定將其當前堆積積疊分解為多個堆積積疊和堆積積疊例項。計劃的實作包括一個用於託管網頁伺服器的容器叢集的堆積積疊例項,以及另一個用於分享網路結構的堆積積疊例項。團隊還將為每個服務建立一對堆積積疊,一個用於應用程式伺服器及其相關網路,另一個用於該服務的資料函式庫例項。
團隊成員還希望替換他們的容器叢集產品,從自己佈署在虛擬機器上的Kubernetes叢集轉向使用雲端供應商提供的容器即服務。
增量實作策略
團隊決定增量實作其分解架構。第一步是將容器叢集提取到自己的堆積積疊中,然後替換堆積積疊中的容器產品。
這個計劃是使用重構來實作變更的例子。當容器叢集被隔離到自己的堆積積疊中時,更改容器叢集解決方案會比它作為較大堆積積疊的一部分時更容易。當團隊成員將叢集提取到自己的堆積積疊中時,他們可以定義其與系統其餘部分的整合點。他們可以編寫測試和其他驗證,保持分離和整合的乾淨。這樣做使團隊有信心可以安全地更改叢集堆積積疊的內容。
這種方法的核心優勢在於它將複雜變更分解為可管理的步驟。首先將容器叢集隔離到獨立堆積積疊中,確保其與其他系統的整合點明確定義,然後再替換容器技術。這種方法降低了風險,因為每個步驟都可以獨立測試和驗證,而不會影響整個系統的穩定性。
將不完整變更推播到生產環境
如何將重大變更以一系列小的增量變更交付到生產系統,同時保持服務正常運作?有些小變更本身可能沒有用處,在整套變更完成之前,可能無法實際移除現有功能。
前面提到的重構範例展示了兩個增量步驟:將容器叢集從一個堆積積疊提取到自己的堆積積疊中,然後替換新堆積積疊中的叢集解決方案。這些步驟都很大,因此可能會實作為一系列較小的程式碼推播。
然而,許多較小的變更可能會使叢集本身無法使用。因此,我們需要找到方法在保持現有程式碼和功能的同時進行這些較小的變更。根據情況,可以使用不同的技術。
平行例項策略
叢集替換範例的第二步從原始容器解決方案開始,最終使用新的容器解決方案。
現有解決方案是名為KubeCan的封裝Kubernetes發行版。團隊正在轉向FKS,這是其雲平台提供的託管叢集服務。
將KubeCan叢集轉變為FKS叢集透過小步驟並不實際。但團隊可以平行執行這兩個叢集。有幾種不同的方法可以平行執行兩個不同的容器堆積積疊。
引數選擇方法
一種選項是為主堆積積疊增加一個引數,用於選擇要整合的叢集堆積積疊。
使用這種選項,一個堆積積疊被啟用並處理實時工作負載,第二個堆積積疊被停用但仍然存在。團隊可以在完全運作的環境中測試第二個堆積積疊,練習和開發其交付管道和測試套件,並在每個環境中將其與基礎設施的其他部分整合。
這種方法的優勢在於它允許團隊在不中斷生產環境的情況下測試新的容器解決方案。透過引數控制,團隊可以在測試環境中啟用新堆積積疊,在生產環境中保持舊堆積積疊執行,直到新解決方案經過充分測試和驗證。這種方法提供了安全網,如果新解決方案出現問題,可以輕鬆回復到舊解決方案。
為什麼要提取舊容器解決方案?
考慮到我們最終建立了一個帶有新容器解決方案的獨立堆積積疊,我們可能會跳過將舊解決方案提取到自己的堆積積疊中的步驟,直接從頭開始建立新堆積積疊和新解決方案。
但透過提取舊解決方案,我們更容易確保新解決方案與舊解決方案的行為相比對。提取的堆積積疊清楚地定義了叢集如何與其他基礎設施整合。透過在生產中使用提取的堆積積疊,我們保證整合點是正確的。
為提取的堆積積疊增加自動化測試和新管道,確保我們立即發現變更是否破壞了某些功能。
如果我們將舊叢集解決方案留在原始堆積積疊中並單獨構建新解決方案,替換它將是破壞性的。我們直到最後才會知道是否做出了不相容的設計或實作決策。整合新堆積積疊與基礎設施的其他部分,以及測試、除錯和修復問題將需要時間。
雙堆積積疊整合方法
另一種選項是將兩個堆積積疊都與主堆積積疊整合。
使用這種安排,可以將部分工作負載導向每個叢集堆積積疊。可以用不同的方式劃分工作負載:
工作負載百分比
將部分工作負載導向每個堆積積疊。通常,舊堆積積疊最初處理大部分負載,新堆積積疊接收一小部分負載以評估其工作效果。隨著新堆積積疊的穩定,可以逐漸增加負載。在新堆積積疊成功管理100%的負載與所有人都準備好後,可以停用舊堆積積疊。這種選項假設新堆積積疊具有舊堆積積疊的所有功能,並且資料或訊息在堆積積疊之間分割沒有任何問題。
服務遷移
將服務一個接一個地遷移到新叢集。主堆積積疊中的工作負載(如網路連線或訊息)被導向到相關服務執行的堆積積疊例項。當需要修改服務應用程式以將其移動到新堆積積疊時,這個選項特別有用。它通常需要更複雜的整合,甚至可能需要舊叢集堆積積疊和新叢集堆積積疊之間的整合。這種複雜性可能對遷移複雜的服務組合是合理的。
使用者分割槽
在某些情況下,不同的使用者集被導向到不同的堆積積疊實作。測試人員和內部使用者通常是第一組。他們可以在冒險接觸"真實"客戶之前進行探索性測試並練習新系統。在某些情況下,可能會接著給予選擇加入alpha測試或預覽服務的客戶存取許可權。當在新堆積積疊上執行的服務有使用者會注意到的變更時,這些情況更有意義。
這種方法允許更靈活的遷移策略,可以根據服務的特性和風險級別選擇不同的遷移方式。工作負載百分比方法適合功能相同但實作不同的服務;服務遷移適合需要修改應用程式的情況;使用者分割槽則適合需要使用者反饋的新功能或服務。這種多樣化的策略使團隊可以為每個服務選擇最適合的遷移路徑。
有條件地執行系統的新舊部分,或平行執行,是一種抽象分支。逐步將工作負載的部分轉移到系統的新部分是金絲雀發布。暗啟動描述了將新系統功能放入生產環境,但不將其暴露給生產工作負載,以便團隊可以測試它。
向後相容的轉換
雖然某些變更可能需要構建和執行新元件與舊元件平行直到完成,但你可以在不影響使用者或消費者的情況下在元件內進行許多變更。
即使在增加或更改提供給消費者元件的內容時,通常也可以增加新的整合點,同時保持現有整合點不變。消費者可以按照自己的時間表切換到使用新的整合點。
例如,ShopSpinner團隊計劃更改其分享網路堆積積疊,從單一VLAN轉移到三個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
# Deprecated
- main_vlan: appserver_vlan_A.id
這種向後相容的轉換策略允許系統逐步演進,而不會破壞現有的整合點。透過保留舊的識別符號對映到新的資源,修改後的網路堆積積疊仍然可以與消費者基礎設施程式碼一起工作。這給了消費者程式碼時間來適應新的識別符號,一旦所有對舊識別符號的依賴都消失,它就可以從網路堆積積疊程式碼中移除。這種方法實作了平滑過渡,而不是破壞性變更。
功能開關
在對元件進行變更時,通常需要繼續使用現有實作直到完成變更。有些人在原始碼控制中分支程式碼,在一個分支中處理新變更,在生產中使用舊分支。這種方法的問題包括:
- 確保對元件其他區域的變更(如錯誤修復)合併到兩個分支需要額外工作。
- 確保兩個分支都持續測試和佈署需要努力和資源。或者,一個分支的工作進行的測試不那麼嚴格,增加了後期出錯和返工的可能性。
- 一旦變更準備好,將生產例項更改為更多的"大爆炸"操作,失敗風險更高。
在不分支的情況下處理主程式碼函式庫的變更有效。可以使用功能開關在不同環境中切換程式碼實作。在某些環境中切換到新程式碼以測試工作,在生產線環境中切換到現有程式碼。使用堆積積疊設定引數指定將哪部分程式碼應用於給定例項。
ShopSpinner團隊完成向分享網路堆積積疊增加VLAN後,需要更改應用程式基礎設施堆積積疊以使用新的VLAN。團隊成員發現這不是他們最初想的那麼簡單的變更。
應用程式堆積積疊定義了應用程式特定的網路由、負載平衡器VIP和防火牆規則。當應用程式伺服器託管在多個VLAN而不是單一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
引數,團隊可以決定是使用舊的單一VLAN還是新的多VLAN結構。當引數為false時,所有appserver_X_vlan變數都設定為使用舊的VLAN識別符號main_vlan;當引數為true時,每個變數都設定為使用新的VLAN識別符號之一。這種方法允許團隊在不同環境中逐步測試和佈署變更,同時保持生產環境的穩定性。
功能開關建議
關於使用功能開關,玄貓有幾點建議:
最小化功能開關數量:功能開關和條件陳述式使程式碼混亂,難以理解、維護和除錯。保持它們的生命週期短。盡快移除對舊實作的依賴,並移除開關和條件程式碼。任何存在超過幾週的功能開關可能都是設定引數。
根據功能命名開關:避免模糊的名稱,如new_networking_code。前面範例中的開關toggle_use_multiple_vlans告訴讀者它是功能開關,以區別於設定引數。它告訴讀者它啟用多個VLAN,所以他們知道它做什麼。名稱清楚地表明開關的工作方式。閲讀toggle_multiple_vlans或更糟的toggle_vlans這樣的名稱,會讓人不確定它是啟用還是停用多VLAN程式碼,導致錯誤。
更改實時基礎設施
這些技術和範例解釋瞭如何更改基礎設施程式碼。更改執行中的基礎設施例項可能更棘手,特別是當更改被其他基礎設施消費的資源時。
例如,當ShopSpinner團隊應用分享網路堆積積疊程式碼的變更,將單一VLAN替換為三個VLAN時,來自其他堆積積疊的資源會發生什麼情況?
應用網路程式碼會銷毀main_vlan,其中包含三個伺服器例項。在實時環境中,銷毀這些伺服器或將它們與網路分離將中斷它們提供的任何服務。
大多數基礎設施平台會拒絕銷毀附加了伺服器例項的網路結構,因此操作會失敗。如果應用的程式碼變更移除或更改了其他資源,操作可能會對例項實施這些變更,使環境處於堆積積疊程式碼舊版本和新版本之間的中間狀態。這幾乎總是一件壞事。
處理這種實時基礎設施變更有幾種方法。一種是保留舊的VLAN main_vlan,並增加兩個新的VLAN appserver_vlan_B和appserver_vlan_C。
這樣做會留下三個VLAN,如預期的那樣,但其中一個的命名與其他不同。保留現有VLAN可能會阻止你更改其他方面,如其IP位址範圍。同樣,你可能會決定妥協,保持原始VLAN比新VLAN小。
這些妥協是一個壞習慣,導致不一致的系統和令人困惑的程式碼維護和除錯。
可以使用其他技術來更改實時系統並使其處於乾淨、一致的狀態。一種是使用基礎設施手術編輯基礎設施資源。另一種是擴充套件和收縮基礎設施資源。
基礎設施手術
一些堆積積疊管理工具,如Terraform,提供對對映基礎設施資源到程式碼的資料結構的存取。這些是用於依賴發現的堆積積疊資料查詢模式中使用的相同資料結構。
一些(但不是全部)堆積積疊工具有編輯其資料結構的選項。可以利用這種能力對實時基礎設施進行變更。
ShopSpinner團隊可以使用其虛構的堆積積疊工具編輯其堆積積疊資料結構。團隊成員將使用這個來更改他們的生產環境以使用三個新的VLAN。他們首先使用新版本的程式碼建立分享網路堆積積疊的第二個例項。
這三個堆積積疊例項(應用程式基礎設施堆積積疊例項以及分享網路堆積積疊的舊例項和新例項)都有一個資料結構,指示基礎設施平台中哪些資源屬於該堆積積疊。
ShopSpinner團隊將main_vlan從舊堆積積疊例項的資料結構移動到新堆積積疊例項的資料結構中。然後團隊將使用它來替換appserver_vlan_A。
基礎設施平台中的VLAN不會以任何方式更改,伺服器例項將完全不受影響。這些變更完全是堆積積疊工具資料結構中的簿記練習。
團隊執行堆積積疊工具命令,將main_vlan從舊堆積積疊移動到新堆積積疊例項:
$ stack datafile move-resource \
source-instance=shared-networking-stack-production-old \
source-resource=main_vlan \
destination-instance=shared-networking-stack-production-new
Success: Resource moved
下一步是移除appserver_vlan_A。如何做到這一點取決於實際的堆積積疊管理工具。虛構的堆積積疊命令恰好使這個操作非常簡單。執行以下命令會在基礎設施平台中銷毀VLAN並將其從資料結構檔案中移除:
$ stack datafile destroy-resource \
instance=shared-networking-stack-production-new \
resource=appserver_vlan_A
Success: Resource destroyed and removed from the datafile
請注意,團隊成員尚未從堆積積疊源程式碼中移除appserver_vlan_A,因此如果他們現在將程式碼應用於例項,它將重新建立它。但他們不會這樣做。相反,他們將執行一個命令來重新命名他們從舊堆積積疊例項移動的main_vlan資源:
$ stack datafile rename-resource \
instance=shared-networking-stack-production-new \
from=main_vlan \
to=appserver_vlan_A
Success: Resource renamed in the datafile
當團隊將分享網路堆積積疊程式碼應用於新例項時,它不應該改變任何東西。就它而言,程式碼中的所有內容都存在於例項中。
基礎設施手術是一種高階技術,允許直接操作堆積積疊管理工具的內部資料結構。這種方法的優點是可以在不中斷服務的情況下重組資源,但風險很高,因為手動編輯這些結構容易出錯。這種技術應該作為最後的手段,而不是常規操作的一部分。每當團隊不得不編輯這些結構時,應該進行無責任事後分析,以瞭解如何避免重複這種情況。
擴充套件和收縮
基礎設施團隊使用擴充套件和收縮模式(也稱為平行變更)來更改介面而不破壞消費者。這個想法是更改提供者的介面涉及兩個步驟:更改提供者,然後更改消費者。擴充套件和收縮模式解耦了這些步驟。
該模式的本質是首先增加新資源同時保留現有資源,然後將消費者更改為使用新資源,最後移除舊的未使用資源。這些變更中的每一個都使用管道交付,因此經過徹底測試。
透過擴充套件和收縮排行變更類別似於向後相容的轉換。該技術替換了舊資源並將舊介面重新指向新資源之一。然而,將新程式碼應用於執行例項將嘗試銷毀舊資源,這可能會中斷附加到它的任何消費者或無法完成。因此需要一些額外的步驟。
ShopSpinner團隊使用擴充套件和收縮排行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
與平行例項技術和基礎設施手術不同,ShopSpinner團隊不增加堆積積疊的第二個例項,而只是更改現有例項。
應用此程式碼後,現有消費者例項不受影響——它們仍然連線到main_vlan。團隊可以向新的VLAN增加新資源,並可以對消費者進行變更以將它們切換過來。
如何將消費者資源切換到使用新資源取決於特定的基礎設施和平台。在某些情況下,可以更新資源的定義以將其附加到新的提供者介面。在其他情況下,可能需要銷毀並重建資源。
ShopSpinner團隊無法將現有虛擬伺服器例項重新分配給新的VLAN。然而,團隊可以使用擴充套件和收縮模式來替換伺服器。應用程式基礎設施堆積積疊程式碼定義了每個伺服器,並帶有路由流量到伺服器的靜態IP位址:
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
團隊的第一步是增加連線到新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
這段程式碼展示了擴充套件階段,增加了一個新的伺服器例項appserver-${SERVICE}-A2
,它連線到新的VLAN,同時保留了舊的伺服器例項。這允許團隊在不中斷服務的情況下準備新的基礎設施。新伺服器此時尚未接收流量,但已經準備好進行測試和驗證。
團隊的下一步是將使用者流量切換到新的伺服器例項。團隊對程式碼進行另一個變更,修改static_ip陳述式:
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
這是收縮階段,移除了舊的伺服器例項,完成了遷移過程。透過這種方法,團隊可以在不中斷服務的情況下完成從舊VLAN到新VLAN的遷移。整個過程分為三個步驟:擴充套件(增加新資源)、切換(將流量導向新資源)和收縮(移除舊資源)。這種模式提供了安全、可控的方式來進行基礎設施變更。
一旦此變更透過管道推播並應用於所有環境,應用程式基礎設施堆積積疊不再依賴於分享網路堆積積疊中的main_vlan。在所有消費者基礎設施都更改後,ShopSpinner團隊可以從提供者堆積積疊程式碼中移除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
VLAN變更完成,main_vlan的最後殘餘已被清除。
零停機變更
本章描述的許多技術解釋瞭如何增量實作變更。理想情況下,你希望將變更應用於現有基礎設施,而不中斷它提供的服務。有些變更不可避免地涉及銷毀資源,或者至少以可能中斷服務的方式更改它們。
處理這些情況有幾種常見技術。
藍綠變更
藍綠變更涉及建立新例項,將使用切換到新例項,然後移除舊例項。這在概念上類別似於擴充套件和收縮,後者在元件(如堆積積疊)的例項中增加和移除資源。這是實作不可變基礎設施的關鍵技術。
藍綠變更需要一種機制來處理工作負載從一個例項到另一個例項的切換,例如網路流量的負載平衡器。複雜的實作允許工作負載"排空",將新工作分配給新例項,並等待舊例項上的所有工作完成後再銷毀它。一些自動化伺服器叢集和應用程式叢集解決方案將此作為功能提供,例如,啟用叢集中例項的"滾動升級"。
藍綠透過維護兩個環境來實作靜態基礎設施。在任何時間點,一個環境是實時的,另一個準備接收下一個版本。藍色和綠色的名稱強調這些是平等的環境,輪流成為實時環境,而不是主要和次要環境。
玄貓曾與一個實作藍綠資料中心的組織合作。發布涉及將其整個系統的工作負載從一個資料中心切換到另一個資料中心。這種規模變得難以管理,所以我們幫助該組織實作較小規模的佈署,這樣它只會對正在升級的特定服務進行藍綠佈署。
透過這些技術,我們可以在不中斷服務的情況下安全地變更基礎設施。無論是重構現有系統、替換元件還是完全重建,這些方法都提供了一種安全、可控的方式來實作變更,同時保持系統的穩定性和可靠性。
基礎設施變更是一個持續的過程,需要謹慎的計劃和執行。透過採用這些策略,我們可以在保持系統穩定性的同時實作技術演進,確保我們的基礎設施能夠適應不斷變化的需求和技術發展。
從鐵器時代到雲端時代的基礎設施管理轉變
在基礎設施管理的演進過程中,我們經歷了從「鐵器時代」到「雲端時代」的重大轉變。這兩種方法在處理基礎設施變更時有著根本性的差異。當我們過去主要處理實體裝置並手動管理它們時,進行變更的成本極高,犯錯的代價更是不可忽視。
回想我曾經遇到的情況,當我佈署一台記憶體不足的伺服器時,整個修正過程相當繁瑣:訂購額外的RAM至少需要一週時間,然後還要親自前往資料中心,關閉伺服器電源並從機架上取下,開啟機殼安裝額外的記憶體,最後再將伺服器重新安裝到機架上並重新啟動。
相比之下,雲端時代的做法讓變更和錯誤修正的成本大幅降低。如果我佈署的虛擬伺服器記憶體不足,只需幾分鐘就能透過編輯設定檔並應用到虛擬伺服器來修正問題。
從MTBF到MTTR的思維轉變
鐵器時代的持續性方法強調預防,透過犧牲變更的速度和頻率來最佳化MTBF(平均故障間隔時間)。而雲端時代的方法則最佳化MTTR(平均還原時間)。有趣的是,即使是現代方法的一些擁護者也會誤以為專注於MTTR意味著犧牲MTBF,但這種想法是不正確的。
實際上,專注於四個關鍵指標(變更的速度和頻率、MTTR以及變更失敗率)的團隊會自然而然地達到良好的MTBF。關鍵不在於「快速行動並打破東西」,而是「快速行動並修復東西」。
現代基礎設施持續性的多元策略
透過預防錯誤實作持續性
傳統的鐵器時代變更管理主要是預防性的。由於修復錯誤的成本高昂,組織投入大量資源來預防錯誤。因為變更主要是手動進行的,預防措施涉及限制誰可以進行變更。人們需要詳細規劃和設計變更,其他人則對每個變更進行徹底的審查和討論。這種方法的理念是,讓更多人花更多時間在事前考慮變更,以便發現錯誤。
然而,這種方法存在一個問題:設計檔案與實際實作之間存在差距。在圖表中看起來簡單的事情在現實中可能相當複雜。人們在執行大型、不頻繁的升級時特別容易出錯。結果是,傳統的低頻率、高度規劃的大批次變更操作往往有較高的失敗率,還原時間也較長。
我在實踐中發現,基礎架構即程式碼的核心洞見是改變對變更的態度。與其害怕變更並盡可能少地進行變更,不如透過頻繁地進行變更來預防錯誤。提高變更能力的唯一方法是頻繁地進行變更,持續改進系統和流程。
另一個關鍵洞見是,隨著系統變得更加複雜,我們在生產環境之前複製和準確測試程式碼行為的能力會下降。我們需要了解在生產前可以測試什麼、不能測試什麼,以及如何透過改善對生產系統的可見性來降低風險。
透過快速還原實作持續性
本文前面描述的實踐可以減少停機時間。限制變更的規模、增量式地進行變更,以及在生產前測試變更可以降低變更失敗率。但假設錯誤可以完全預防是不明智的,因此我們還需要能夠快速與輕鬆地還原。
基礎架構即程式碼的實踐使重建系統的任何部分變得容易。系統由鬆散耦合的元件組成,每個元件都被定義為冪等程式碼。透過重新應用其程式碼,可以輕鬆修復或銷毀並重建任何元件例項。如果重建元件,需要確保託管在元件上的資料的連續性。
在某些情況下,平台或服務可以自動重建失敗的基礎設施。當元件未透過健康檢查時,基礎設施平台或應用程式執行時會銷毀並重建個別元件。持續應用程式碼到例項會自動還原與程式碼的任何偏差。可以手動觸發管道階段來重新應用程式碼到損壞的元件。
在其他故障情況下,這些系統可能無法自動修復問題。計算例項可能以某種方式出現故障,但仍然透過健康檢查。基礎設施元素可能停止正常工作,但仍然符合程式碼定義,因此重新應用程式碼無濟於事。
這些情況需要某種額外的操作來替換失敗的元件。可能需要標記元件,使自動化系統將其視為失敗,然後銷毀並替換它。或者,如果還原使用重新應用程式碼的系統,可能需要自行銷毀元件並讓系統建立新例項。
對於任何需要人員採取行動的故障情況,應確保有簡單執行的工具、指令碼或其他機制。人們不應該需要遵循一系列步驟,例如在銷毀例項之前備份資料。相反,他們應該呼叫一個執行所有必要步驟的操作。目標是在緊急情況下,不需要思考如何正確還原系統。
持續災難還原:從例外到常態
鐵器時代的基礎設施管理方法將災難還原視為不尋常的事件。從靜態硬體故障中還原通常需要將工作負載轉移到備用的獨立硬體集。
許多組織很少測試其還原操作——最好每隔幾個月,在某些情況下每年一次。我見過很多組織很少測試其容錯移轉過程。假設是團隊將在需要時找出如何讓備份系統執行,即使需要幾天時間。
持續災難還原利用於佈建和變更基礎設施的相同流程和工具。如前所述,可以應用基礎設施程式碼來重建失敗的基礎設施,可能還需要一些額外的自動化來避免資料丟失。
雲端時代基礎設施的原則之一是假設系統不可靠。不能在虛擬機器上安裝軟體並期望它會一直在那裡執行。雲端供應商可能會為了維護、安全補丁或升級而移動、銷毀或替換機器或其主機系統。因此需要準備好在需要時替換伺服器。
將災難還原視為正常操作的延伸使其比將其視為例外情況更可靠。團隊在處理基礎設施程式碼變更和系統更新時,每天多次演練還原流程和工具。如果有人對指令碼或其他程式碼進行了破壞佈建或在更新時導致資料丟失的變更,通常會在管道測試階段失敗,因此可以快速修復。
混沌工程:主動測試系統彈性
Netflix是持續災難還原和雲端時代基礎設施管理的先驅。其Chaos Monkey和Simian Army將持續災難還原的概念更進一步,透過在生產系統中注入錯誤來證明其系統持續性機制的有效性。這演變成了混沌工程領域,即「在系統上進行實驗以建立對系統能力的信心的學科」。
需要明確的是,混沌工程不是關於不負責任地導致生產服務中斷。實踐者會實驗系統預期能夠處理的特定故障情況。這些是證明檢測和還原機制正常工作的基本生產測試。目的是在系統的某些變更對這些機制產生幹擾時獲得快速反饋。
為失敗做計劃:預防與還原並重
失敗是不可避免的。雖然可以並且應該採取措施使其不太可能發生,但也需要採取措施使其危害較小與更容易處理。
團隊可以舉行失敗情境對映工作坊,集思廣益可能發生的失敗型別,然後計劃緩解措施。可以建立每個情境的可能性和影響的對映,建立解決這些情境的行動列表,然後將這些適當地優先排序到團隊的工作積壓中。
對於任何給定的失敗情境,有幾個條件需要探索:
原因和預防
什麼情況可能導致這種失敗,以及可以做什麼使其不太可能發生?例如,當使用量激增時,伺服器可能會耗盡磁碟空間。可以透過分析磁碟使用模式並擴充套件磁碟大小來解決這個問題,以便有足夠的空間應對較高的使用水平。還可以實施自動化機制來持續分析使用水平並進行預測,以便在模式變化時可以預先增加磁碟空間。更進一步的步驟是隨著使用量增加自動調整磁碟容量。
失敗模式
失敗發生時會發生什麼?在沒有人為干預的情況下可以做什麼來減少後果?例如,如果給定的伺服器耗盡磁碟空間,在其上執行的應用程式可能會接受交易但無法記錄它們。這可能非常有害,因此可以修改應用程式,使其在無法將交易記錄到磁碟時停止接受交易。在許多情況下,團隊實際上不知道當給定錯誤發生時會發生什麼。理想情況下,失敗模式應保持系統執行。例如,當應用程式停止回應時,負載平衡器可能會停止向其引導流量。
檢測
如何檢測失敗何時發生?可以做什麼來更快地檢測它,甚至在事前檢測?可能會在應用程式當機並且客戶打電話給CEO投訴時檢測到磁碟已耗盡空間。最好在應用程式當機時收到通知。更好的是在磁碟空間不足時收到通知,在它實際填滿之前。
修正
需要採取哪些步驟從失敗中還原?在某些情況下,如前所述,系統可能會自動修正情況,可能是透過銷毀並重建無回應的應用程式例項。其他情況需要幾個步驟來修復和重新啟動服務。
如果系統自動處理失敗情境,例如重新啟動無回應的計算例項,應考慮更深層次的失敗情境。為什麼例項一開始會變得無回應?如何檢測和修正潛在問題?不應該花幾天時間才意識到應用程式例項每隔幾分鐘就被回收一次。
失敗規劃是一個持續的過程。每當系統出現事故,包括在開發或測試環境中,團隊都應考慮是否有新的失敗情境需要定義和規劃。
應該實施檢查來證明失敗情境。例如,如果認為當伺服器耗盡磁碟空間時,應用程式將停止接受交易,自動增加新的伺服器例項,並提醒團隊,應該有一個自動化測試來演練這個情境。可以在管道階段(可用性測試)或使用混沌實驗來測試這一點。
在變更系統中的資料持續性
許多雲端時代佈署軟體和管理基礎設施的實踐和技術都愉快地建議隨意銷毀和擴充套件資源,只是對資料問題輕描淡寫。可以原諒人們認為DevOps潮人認為整個資料概念是鐵器時代的遺留物——畢竟,一個正確的十二因素應用程式是無狀態的。但現實世界中的大多數系統都涉及資料,人們對資料有著令人感動的依戀。
在增量變更系統時,資料可能會帶來挑戰。執行儲存基礎設施的平行例項可能會建立不一致性甚至損壞資料。許多增量佈署變更的方法依賴於能夠回復變更,這對於資料架構變更可能是不可能的。
動態增加、移除和重建託管資料的基礎設施資源特別具有挑戰性。然而,根據情況,有多種管理方法,包括鎖定、隔離、複製和重新載入。
鎖定
一些基礎設施平台和堆積積疊管理工具允許鎖定特定資源,使其不會被原本會銷毀它們的命令刪除。如果為儲存元素指定此設定,則工具將拒絕對此元素應用變更,允許團隊成員手動進行變更。
然而,這存在一些問題。在某些情況下,如果對受保護的資源應用變更,工具可能會使堆積積疊處於部分修改狀態,這可能導致服務停機。
但根本問題是保護某些資源免受自動化變更鼓勵手動參與變更。手動工作容易出現手動錯誤。找到一種自動化安全變更基礎設施的流程要好得多。
隔離
可以透過將託管資料的資源與系統的其他部分開來隔離資料;例如,透過建立單獨的堆積積疊。透過分離和重新連線磁碟卷,可以毫無顧慮地銷毀和重建計算例項。
將資料儲存在資料函式庫中提供了更大的靈活性,可能允許增加多個計算例項。仍然需要為託管資料的堆積積疊制定資料持續性策略,但它縮小了問題的範圍。使用託管的DBaaS服務可能完全解除安裝資料持續性。
複製
根據資料及其管理方式,可能夠在多個基礎設施例項之間複製資料。一個典型的例子是分散式資料函式庫叢集,它在節點之間複製資料。
使用正確的複製策略,資料會從叢集中的其他節點重新載入到新重建的節點。如果失去太多節點,這種策略會失敗,這可能發生在主要託管中斷時。因此,這種方法作為第一道防線有效,但對於更困難的故障情境需要另一種機制。
重新載入
最著名的資料持續性解決方案是從更可靠的儲存基礎設施備份和還原資料。重建託管資料的基礎設施時,首先備份資料。建立新例項後,將資料重新載入到新例項。
也可能定期進行備份,可以在還原情境中重新載入,儘管會丟失備份和還原之間發生的任何資料變更。透過將資料變更流式傳輸到備份,例如寫入資料函式庫交易日誌,可以最小化甚至消除這種情況。
雲端平台提供不同的儲存服務,具有不同級別的可靠性。例如,像AWS S3這樣的物件儲存服務通常對資料的永續性有更強的保證,比如AWS EBS這樣的區塊儲存服務。因此,可以透過將資料複製或流式傳輸到物件儲存卷來實施備份。
不僅應該自動化備份資料的過程,還應該自動化還原資料的過程。基礎設施平台可能提供輕鬆做到這一點的方法。例如,可以在對磁碟儲存卷應用變更之前自動拍攝快照。
可能夠使用磁碟卷快照來最佳化向資料函式庫叢集等系統增加節點的過程。與其建立帶有空儲存卷的新資料函式庫節點,不如將其連線到另一個節點磁碟的克隆,這可能使同步和使節點上線更快。
「未經測試的備份與沒有備份相同」是我們行業中的一句常見諺語。既然已經遵循基礎架構即程式碼實踐,已經在使用自動化測試系統的各個方面。因此可以對備份做同樣的事情。在管道中或作為混沌實驗來演練備份還原過程,無論是否在生產環境中。
混合資料持續性方法
最佳解決方案通常是隔離、複製和重新載入的組合。隔離資料為更靈活地管理系統的其他部分創造了空間。複製使資料在大多數時間保持可用。而重新載入資料是更極端情況的後盾。
持續性常被現代雲端時代基礎設施管理實踐的倡導者所忽視。保持系統可靠執行的最熟悉方法根據鐵器時代的前提,即進行變更是昂貴與有風險的。這些方法往往會削弱雲端、敏捷和其他專注於快速變更步伐的方法的好處。
我希望本文已經解釋瞭如何利用雲端時代思維使系統更可靠,不是儘管有快速變更步伐,而是因為它。可以利用現代基礎設施平台的動態特性,並實施來自敏捷工程實踐的嚴格測試和一致性關注。結果是對能夠持續交付系統改進並利用失敗作為學習和改進機會的高度信心。
在我的實踐中,我發現這種方法不僅提高了系統的可靠性,還大減少了團隊處理緊急情況的壓力。當系統設計為預期失敗並能夠優雅地還原時,團隊可以更專注於創新和改進,而不是不斷地撲滅火災。這種心態轉變是雲端時代基礎設施管理最有價值的方面之一。 s