Netflix混沌工程手冊Part 2:混沌工程原則

本文翻譯自Netflix工程師合著的 *Chaos Engineering*一書。這本書介紹了混沌工程的主要概念,以及如何在組織中實踐這些概念和經驗。也許我們開發的相關工具只適用於Netflix自身的業務和系統環境,但我們相信工具背後的原則可以更廣泛地應用於其他領域。

InfoQ 將就這一專題持續出稿,感興趣的同學可以持續關注。

本文略長,共計1.3萬字,預計閱讀時間35分鐘。

混沌工程原則

優化複雜系統的性能通常需要在混亂的邊緣進行,即系統行爲將要變得混亂且無跡可尋之前。

Sydney Dekker,《陷入失敗》

“混亂”一詞讓我們想起隨機性和無序。然而,這不意味着混沌工程的實施也是隨機和隨意的,也不意味着混沌工程師的工作就是引發混亂。相反,我們把混沌工程視爲一種學科,一種實驗學科。

在上面的引用中,Dekker觀測了分佈式系統的整體行爲,他也主張從整體上了解複雜系統是如何失效的。我們不應該僅僅着眼於發生故障的組件,而是應該嘗試去理解,例如組件交互中的一些偶發意外行爲,最終如何導致系統整體滑向不安全,不穩定的狀態。

你可以將混沌工程視爲一種解決“系統離混亂的邊界有多遠” 的經驗方法。從另一個角度去思考,“如果我們把混亂注入到系統裏,它會怎麼樣?”

在這一部分,我們會介紹混沌工程實驗的基本設計,之後我們會討論一些更高級的原則。這些原則建立在真實實踐混沌工程的大規模系統之上。在實踐混沌工程的過程中,並不是必須遵照所有高級原則,但我們發現,運用的原則越多,你對系統彈性的信心就越充足。

實驗

在大學裏,電氣工程專業的學生必須學習一門“信號和系統”的課程,在這個課程中他們學習如何使用數學模型來推理電氣系統的行爲。其中一門需要掌握的技術被稱爲拉普拉斯變換。你可以用拉普拉斯變換,將整個電路的行爲用一個數學函數表達,我們稱之爲傳遞函數。傳遞函數描述的是系統在受到脈衝時如何響應,輸入信號包含所有可能的輸入頻率的總和。一旦你有了一個電路的傳遞函數,就可以預測它在受到所有可能的輸入信號時會如何響應。

軟件系統裏並沒有類似的傳遞函數。像很多複雜系統一樣,我們無法爲軟件系統表現出的各種行爲建立一個預測模型。如果我們有這樣一個模型,可以推導出一次網絡延遲驟升會給系統帶來什麼影響,那樣就太完美了。但不幸的是,迄今爲止我們並沒有發現這樣一個模型。

因爲我們缺乏這樣的理論預測模型,所以就不得不通過經驗方法來理解,在各種不同情況下系統會如何表現。我們通過在系統上運行各種各樣的實驗,嘗試給系統製造各種麻煩,看它會發生什麼狀況。

但是,我們肯定不會給系統隨機的不同的輸入。我們在系統性分析之後,期望可以最大化每個實驗可以獲得的信息。正如科學家通過實驗來研究自然現象一樣,我們通過實驗來揭示系統的行爲。

FIT 故障注入測試

分佈式系統的經驗告訴我們,各種系統問題基本都是由預期外的事件或不良的延遲導致的。2014年初,Netflix開發了一個名爲FIT的工具,意思是故障注入測試(Failure Injection Testing)。這個工具能讓工程師在訪問服務的一類請求的請求頭中注入一些失敗場景,當這些注入了失敗場景的請求在系統中流轉時,微服務中被注入的故障錨點會根據不同的失敗場景觸發相應的邏輯。

例如,我們要測試系統在某個保存用戶數據的微服務中斷時的彈性能力。我們預計系統中某些服務不會如預期運行,但諸如重放等基本功能仍適用於已登錄用戶。使用FIT,我們指定進入服務的所有請求中,有5%會在請求頭中包含失敗場景。當這些請求在系統中傳播時,只要發到客戶數據微服務的請求都會自動收到故障響應。

高級原則

在開發混沌工程實驗時,牢記以下原則將有助於實驗設計。在接下來的章節裏將會深入探討以下每個原則:

  • 建立穩定狀態的假設;
  • 多樣化現實世界事件;
  • 在生產環境運行實驗;
  • 持續自動化運行實驗;
  • 最小化“爆炸半徑”。

預測和預防故障

在2017年美洲SRECon大會上,Preetha Appan介紹了她和她的團隊在 indeed.com 開發的一個引入網絡故障的工具。在演講中,她闡述了預防故障的切實需求,而不是僅僅在故障發生時做出響應。他們的工具Sloth,作爲一個守護進程,運行在基礎設施的每一個節點上,包括數據庫和索引服務器。參見 https://www.usenix.org/conference/srecon17americas/program/presentation/appan

3. 建立穩定狀態的假設

對於任何複雜系統,都會有許多可變動的部件,有許多形式的輸入輸出。我們需要有一個通用的方式來區分系統行爲是預期中的,還是預期之外的。我們將系統正常運行時的狀態定義爲系統的“穩定狀態”。

如果你在開發或運行一個軟件服務,你如何清楚地瞭解它是否在正常工作?你如何認定它的穩定狀態?你應該從哪裏着眼來回答上面的問題?

穩定狀態

在系統思維社區中使用“穩定狀態”這個術語,來指代系統維持在一定範圍內或一定模式的屬性,諸如人體維持體溫在一定範圍內一樣。我們期望通過一個模型,基於所期望的業務指標,來描述系統的穩定狀態。這是我們在識別穩定狀態方面的一個目標。要牢記穩定狀態一定要和客戶接受程度一致。在定義穩定狀態時,要把客戶和服務之間的服務水平協議(SLA)納入考量。

如果你的服務是一個新服務,想知道它是否正常工作的唯一途徑可能只有自己去運行一下。例如服務可以通過網站訪問,那你可能需要打開網頁並嘗試觸發一個任務或事務來檢查這個服務。

這種快速檢查系統健康的方法顯然並不理想:他是勞動密集型的,也就是說我們基本不會或不常會做這件事。我們可以自動化運行類似測試,但這還不夠。如果這些測試不能找到系統中的問題,該怎麼辦?

更好的方法是先蒐集和系統健康有關的數據。如果你在閱讀本書,我們相信你已經在使用某種指標收集系統來監控你的系統。有大量的開源和商業工具可以採集系統方方面面的數據:CPU負載,內存使用情況,網絡I/O,以及各類時序信息,例如需要多長時間響應一個Web請求,或者查詢各種數據庫的耗時。

系統的指標有助於幫助我們診斷性能問題,有時也能幫助我們發現功能缺陷。業務指標與系統指標形成對比,業務指標通常回答這樣的問題:

  • 我們正在流失用戶嗎?
  • 用戶目前可以操作網站的關鍵功能嗎?例如在電子商務網站裏爲訂單付款,添加購物車等。
  • 目前存在較高的延遲致使用戶不能正常使用我們的服務嗎?

某些組織有非常明確的,和收入直接相關的實時指標。例如像Amazon和eBay會跟蹤銷售量,Google和Facebook會跟蹤廣告曝光次數。

由於Netflix使用的是按月訂閱模式,所以我們沒有這類指標。我們也會測量註冊率,這是一個重要的指標,但是隻看註冊率不能反映整體系統的健康狀況。

我們真正想要的是一個可以反映當前活躍用戶的滿意狀況的指標,因爲滿意的用戶纔有可能連續訂閱。可以這麼說,如果當前和系統做交互的用戶是滿意的,那麼我們基本可以確定系統目前是健康的。

遺憾的是,我們目前還沒找到一個直接、實時的可以反映用戶滿意度的指標。我們會監控客服電話的呼叫量,這或許是一個可以間接反映客戶滿意度的指標,但是從運營角度出發,我們需要更快、更細粒度的反饋。Netflix有一個還不錯的可以間接反映用戶滿意度的指標——播放按鈕的點擊率。我們管這個指標叫做視頻每秒開始播放數,簡稱爲SPS(Starts per sencond)。

SPS很容易測量,而且因爲用戶付費訂閱服務的直接目的就是看視頻,所以SPS應該和用戶滿意度密切相關。這個指標在東海岸下午6點會明顯高於早上6點。我們就可以據此來定義我們系統的穩定狀態了。

相比某個服務的CPU負載來說,Netflix的可靠性工程師(SREs)更關注SPS的下降:SPS下降會立刻向他們發送警報。CPU負載的尖刺有時重要有時不重要,而像SPS這樣的業務指標纔是系統邊界的表述。這纔是我們要關注並驗證的地方,而不是那些像CPU負載類的內部指標。

很多現有的數據採集框架默認採集大量的系統級別指標,所以通常來說,讓系統有能力抓取到業務級別的指標比系統級別更難。然而花精力採集業務級別指標是值得的,因爲它們纔是系統健康的真實反映。

這些指標獲取的延遲越低越好:那些在月底算出來的業務指標和系統今天的健康狀況毫無關係。

對於選擇的任何指標,需要平衡以下幾點:

  • 指標和底層架構的關係;
  • 收集相關數據需要的工作量;
  • 指標和系統後續行爲間的時間延遲。

如果你還不能直接獲取和業務直接相關的指標,可以暫時先利用一些系統指標,比如系統吞吐率,錯誤率,99%以上的延遲等。你選擇的指標和業務關係越強,得到的可以採取可執行策略就越強。你可以把這些指標想象成系統的生命特徵指標,像脈搏、呼吸、血壓、體溫等。同樣重要的是,在客戶端驗證服務產生的警報可以提高整體效率,而且可以作爲對服務端指標的補充,以構成某一時刻用戶體驗的完整畫面。

3.1 如何描述穩定狀態

與人體的生命體徵一樣,你需要清楚“健康”的數值範圍。例如我們知道37攝氏度以下是人體的健康溫度。

請牢記目標:我們期望通過一個模型,基於所期望的業務指標,來描述系統的穩定狀態。

很多業務指標並不像我們的體溫那樣穩定,他們也許會經常劇烈波動。我們再舉一個醫學中的例子,心電圖測量心臟附近人體表面的電壓差,這個信號是用來觀測心臟行爲的。但心電圖採集的信號會隨着心臟跳動而變化,因此醫生不能將這個信號與單個閾值進行比較來判斷患者是否健康。醫生需要比較的是信號波動的模式和患者健康時的模式是否一致。

在Netflix,SPS也不是一個和人體體溫一樣的穩定指標,它也隨着時間波動。下圖描繪的就是SPS隨時間變化的波動情況,可以看出,它有一個穩定的模式。這是因爲人們習慣於在晚餐時間看電視節目。因爲SPS隨時間的變化可以預期,所以我們就可以用一週前的SPS波動圖作爲穩定狀態的模型。Netflix的可靠性工程師們總是將過去一週的波動圖放在當前的波動圖之上,以發現差異。就像下圖中當前的圖線是紅色,上一週的圖線是黑色。

圖1 SPS隨時間變化

你所處的行業決定了你的指標是否以一種可以預期的方式隨時間波動。例如,如果你負責一個新聞網站,流量的尖刺可能來源於一個大衆關注度高的新聞事件。某些事件的尖刺可以預期,比如選舉、重大賽事,但是其他類型的事件不太可能被預測到。在這一類場景中,準確描述系統的穩定狀態將會非常複雜。無論哪種情況,描述穩定狀態都是建立有意義假設的必要前提。

3.2 建立假設

每當你進行混沌工程實驗的時候,你應該首先在心裏對實驗結果有一個假設。將你的系統直接置於各種事件裏看看系統會怎麼樣的想法可能比較誘人,然而,沒有一個預先的假設,你就不清楚應該從數據裏找什麼,最終也難以得出有效結論。

定義好指標並理解其穩定狀態的行爲之後,你就可以使用它們來建立實驗的假設。思考一下當你向系統注入不同類型的事件時,穩定狀態行爲會發生什麼變化。例如你向中間層的服務增加請求數量,穩定狀態會被破壞還是保持不變?如果被破壞了,你期待系統如何表現?

在Netflix,我們使用混沌工程來提高系統彈性,因此我們實驗的假設一般是這樣的形式:向系統注入的事件不會導致系統穩定狀態發生明顯的變化。

例如,我們在彈性實驗裏故意讓一個非關鍵服務中斷,以驗證系統是否可以優雅降級。我們可能會中斷基於用戶瀏覽歷史展示的個性化影片列表服務,這時系統應該返回默認的影片列表。

每當我們執行非關鍵服務中斷實驗時,我們的假設都是注入的故障不會對SPS產生影響。換句話說,我們的假設是實驗措施不會使系統行爲偏離穩定狀態。

我們還會定期執行演習,例如將一個AWS區域的流量全部轉移到另外兩個區域。目的是驗證我們在進行這類故障恢復時,SPS不會偏離穩定狀態。這可以讓我們對出現一個區域中斷性故障時執行故障恢復充滿信心。

最後,讓我們思考一下,如何衡量穩定狀態行爲的變化。即便你已經建立了穩定狀態行爲模型,你也需要定義清楚,當偏離穩定狀態行爲發生時你要如何測量這個偏差。如果你曾調節過報警系統的閾值,就應該清楚,定義偏離穩定狀態的偏差是否在合理的範圍是具有挑戰性的。只有定義清楚“正常”的偏差範圍,纔可以獲得一套驗證假設的完善的測試集。

金絲雀分析

在Netflix,我們使用金絲雀發佈:我們首先把新代碼發佈到一個只接受一小部分生產流量的小型集羣,然後進行驗證以確保新發布的健康性,最後再進行全面發佈。

我們使用名叫自動金絲雀分析ACA(Automated Canary Analysis)的內部工具,通過穩定狀態的指標,來驗證金絲雀集羣是否健康。ACA拿金絲雀集羣與一個大小相同,並運行舊代碼的基準集羣,比較一系列系統指標。金絲雀集羣的得分必須足夠高才能通過金絲雀發佈階段。服務擁有者還可以向ACA中添加應用的自定義指標。

通過ACA,工程師可以清晰地觀察描述穩定狀態的重要參數,同時可以對基於穩定狀態建立的假設,在不同集羣間的表現進行比較驗證。我們其他的一些混沌工程工具也會使用ACA提供的服務來測試穩定狀態變化的各類假設。

4. 多樣化現實世界事件

每個系統,從簡單到複雜,只要運行時間足夠長,都會受到不可預測的事件和條件的影響。例如負載的增加、硬件故障、軟件缺陷、還有非法數據(有時稱爲髒數據)的引入。我們無法窮舉所有可能的事件或條件,但常見的有以下幾類:

  • 硬件故障;
  • 功能缺陷;
  • 狀態轉換異常(例如發送方和接收方的狀態不一致);
  • 網絡延遲或隔離;
  • 上行或下行輸入的大幅波動以及重試風暴;
  • 資源耗盡;
  • 服務之間的不正常的或者預料之外的組合調用;
  • 拜占庭故障(例如性能差或有異常的節點發出有錯誤的響應、異常的行爲、對調用者隨機返回不同的響應,等等);
  • 資源競爭條件;
  • 下游依賴故障。

也許最複雜的情況是上述事件的各類組合導致系統發生異常行爲。

要徹底阻止對可用性的各種威脅是不可能的,但是我們可以儘可能減輕這些威脅。在決定引入哪些事件時,我們應當估算這些事件發生的頻率和影響範圍,然後權衡引入他們的成本和複雜度。在Netflix,我們選擇關閉節點的一方面原因就是,節點中斷在現實中發生頻率很高,同時引入關閉事件的成本和難度很低。對於區域故障來說,即使引入的成本高昂且複雜,我們還是必須要做,因爲區域性故障對用戶的影響是巨大的,除非有足夠的彈性應對它。

文化因素也是一種成本。例如對於傳統數據中心來說,健壯性、穩定性、變更的嚴格控制,在文化上要優先於敏捷性——隨機關閉節點類型的實驗對傳統數據中心文化上是一種挑戰。隨着遷移到雲上帶來的硬件職責外部化,工程部門對硬件故障越來越習以爲常。這種認知實際上在鼓勵一種對待故障可預期性的態度,這種態度可以進一步推動混沌工程的採用和被支持。雖然硬件故障並不是線上事故的最常見原因,但是它相對容易理解,同時也是在組織裏引入混沌工程並獲益的一個較簡單的途徑。

和硬件故障一樣,一些現實世界的事件也可以直接注入:例如每臺機器的負責增加、通信延遲、網絡分區、證書失效、時間偏差、數據膨脹等等。除此之外,其他的一些事件的注入可能會具有技術或文化的障礙,所以我們需要找尋其他方法來看看它們會如何影響生產環境。例如發佈有缺陷的代碼。金絲雀發佈可以阻止許多簡單和明顯的軟件缺陷被大規模發佈到生產環境,但並不能阻擋全部的缺陷被髮布出去。故意發佈有缺陷的代碼風險太大,可能會造成對用戶過度的影響(參見最小化“爆炸半徑”一節)。要模擬這類發佈帶來的缺陷問題,一種辦法是對相應的服務調用注入異常。

Blockade

戴爾雲管理團隊開發了一個開源的,基於Docker的,用來測試分佈式應用的網絡故障或隔離的混沌工程工具,名叫Blockade。它通過在Docker的宿主機網絡管理中創建各種異常場景,來影響在Docker容器中運行的應用。這個工具的一些功能包括:在容器間創建任意分區、容器數據丟包,容器網絡延遲注入,以及在故障注入時便捷的系統監控能力。

我們瞭解了通過對相應服務調用注入異常,來模擬一次失敗的部署,這是因爲錯誤代碼帶來的影響已經被隔離在僅僅是運行他們的幾臺服務器中。通常來說,故障隔離既可以是物理隔離也可以是邏輯隔離。隔離是容錯的必要但不充分條件。要獲得一個可以接受的結果,還需要某種形式的冗餘或者優雅降級。只要子部件的故障能引發整個系統不可用,那麼這個故障就沒有被隔離。故障的影響範圍和隔離範圍被稱爲故障的故障域。

提供產品的組織設定產品可用性的預期,並負責制定SLA ——哪些東西一定不能失敗或者失敗是否具備預案。發現和驗證故障域以確保滿足產品的可用性預期是工程師團隊的責任。

故障域還爲混沌工程提供了一個乘數效應。回到剛纔的例子,如果對服務調用失敗的模擬是成功的,那麼這不僅可以驗證該服務對部署缺陷代碼的彈性,同時也可以驗證它在高負載、錯誤配置、其他異常終止等場景引發失敗時的彈性。此外,在故障域中你可以向系統注入各類故障來觀察故障的症狀。例如你在真實環境中看到了一些症狀,你可以逆向找出症狀的根源故障及其發生的機率。在故障域的級別進行實驗還有個好處是,可以提前對不可預見的導致故障的原因做好準備。

我們不應該給系統注入引發故障的根因事件。每一個資源都會形成一個故障域,這個故障域包括所有對它有強依賴的部件(當該資源不可用時,所有依賴也都不再可用)。向系統注入故障根因的事件會暴露出這些因爲資源共享形成的故障域。團隊也經常會爲這類資源共享的情況而感到驚訝。

我們不需要窮舉所有可能對系統造成改變的事件,只需要注入那些頻繁發生且影響重大的事件,同時要足夠理解會被影響的故障域。工程師在設計系統架構的時候也許已經考慮了故障域。例如在微服務架構中,服務組是最重要的故障域之一。有時團隊認爲他們的服務不是關鍵服務,但最後卻在故障時因爲沒有做合理的隔離導致整個系統宕機。所以,在系統中用實驗驗證這些預先定好的邊界非常關鍵。

再強調一次,注入的事件一定是你認爲系統能處理的。同時,注入的事件應該是所有可能的真實世界的事件,而不僅僅是故障或延遲。我們上面舉的例子更多關注軟件部分,但真實世界裏人的因素對系統彈性和可用性也起到了至關重要的作用。例如對故障處理中人工控制的流程和工具進行實驗或演習也會提高可用性。

Netflix的混沌工程的規範化

在Netflix,混亂猴子和混亂金剛是由一個集中的團隊進行開發,發佈,維護和制定規則和執行的。而FIT是一個自助服務的工具。這也是第一次我們的工具需要微服務工程師們的時間和接受才能運行。當然,他們不得不對工具發現的對彈性的威脅做出迴應。即便許多團隊在開發階段確實嘗試到了FIT的好處,但還是難以廣泛和高頻率的使用。我們在FIT有這麼強大的工具來提高彈性,但是我們卻遇到了普及問題。

Netflix的混沌工程團隊認爲現在我們分別有了小尺度和大尺度(如關閉節點和關閉整個區域)上的實踐,但我們還缺少中間環節的最佳實踐:持續提高我們對微服務各類故障的彈性。FIT對這方面的探索提供了一個基礎,但是執行這樣一個實驗的負擔,使得我們並沒能像混亂猴子和混亂金剛那樣,在工程師團隊中形成一致。

我們重新回過頭從頭到尾仔細思考了如何將混沌工程作爲一個落地的實踐。我們走訪了一些工程師,詢問他們混沌工程對他們意味着什麼。多數的回覆都是混沌工程就是在生產環境中搞破壞。聽起來很有意思,很多人都在生產環境中搞破壞,但這些破壞都毫無價值。我們要想辦法讓混沌工程規範化。

2015年中,我們發表了混沌工程原則(Principles of Chaos Engineering),定義了混沌工程作爲計算機科學中的一門新的學科。

通過這個新的規範形式,我們在Netflix正式推動了混沌工程。我們爲混沌工程的組成繪製了藍圖:我們清楚目標是什麼,而且我們清楚如何評估做的夠不夠好。原則爲我們奠定了基礎,讓我們可以把混沌工程提升到新的高度。

5. 在生產環境運行實驗

在我們這個行業裏,在生產環境中進行軟件驗證的想法通常都會被嘲笑。“我們要在生產環境中驗證”這句話更像是黑色幽默,它被翻譯成“我們在發佈之前不打算完善地驗證這些代碼”。

經典測試的一個普遍信條是,尋找軟件缺陷要離生產環境越遠越好。例如,在單元測試中發現缺陷比在集成測試中發現更好。這裏的邏輯是,離生產環境越遠,或者是離發佈越遠的時候,發現的缺陷就越容易被找到根本原因並徹底修復。如果你曾經分別在單元測試,集成測試,和生產環境中debug過問題,上述邏輯的智慧就不言而喻了。

但是在混沌工程領域裏,整個策略卻要反過來了。在離生產環境的越近的地方進行實驗越好。理想的實踐就是直接在生產環境中執行實驗。

傳統軟件測試中,我們是在驗證代碼的邏輯正確性。是我們對函數和方法的行爲有很好理解的情況下,寫測試來驗證它們對不對,換句話說,是驗證代碼寫得對不對。

而當我們執行混沌工程實驗時,我們所感興趣的是整個系統作爲一個整體的行爲。代碼只是整個系統的重要組成部分,而除了代碼之外,整個系統還有很多其他方面。特別是,狀態、輸入、以及第三方系統導致的難易預見的系統行爲。

下面我們深入瞭解一下爲什麼在生產環境中執行對混沌工程來說是至關重要的。我們要建立的是對系統在生產環境的信心,我們當然就需要在生產環境中進行實驗。否則,我們就僅僅是在其他我們並不太關心的環境中建立信心,這會大大削弱這些實踐的價值。

5.1 狀態和服務

我們之前簡要討論過系統的“狀態”。在這一節我們仔細討論一下有狀態服務。如果我們不需要在系統中維護任何狀態,那麼軟件工程將會簡單很多。然而狀態卻在我們建造的這類系統中無處不在。

在微服務架構中,當我們提到狀態的時候,通常我們說的是有狀態服務,例如數據庫服務。數據庫僅僅保存一些測試設置開關的系統,與保存所有生產數據的系統,在行爲上是不同的。其他一些有狀態服務包括緩存服務,對象存儲服務,以及可持久化的消息服務。

配置數據是另一種影響系統行爲的狀態。無論使用靜態配置文件,動態配置服務(像etcd),還是兩者的組合(像我們在Netflix一樣),這些配置信息本身也是一種狀態,而且它們可以嚴重影響系統行爲。

即使在無狀態的服務中,狀態仍然以內存中的數據結構的形式存在於請求之間,並因此影響到後續請求。

還有無數的角落裏隱藏着許許多多的狀態。在雲服務中,自動伸縮組中虛擬機或者容器的個數也是一個系統的狀態,這個狀態會隨時間、隨外部需求或集羣變化而不斷改變。網絡硬件如交換機和路由器也有狀態。

總有一些意想不到的狀態會傷害到你。如果你是我們的目標讀者,你應該已經踩過一些坑了。要想解決混沌工程關注的對於系統彈性的威脅,你需要把生產環境中存在的各式各樣的狀態問題暴露給混沌工程實驗。

5.2 生產環境中的輸入

對於軟件工程師來說,最難的一課莫過於,系統的用戶永遠不會如你預期的那樣與你的系統進行交互。這一課在設計系統的UI的時候尤爲典型,在設計混沌工程實驗的時候也需要牢記。

設想一下,你的系統提供了從用戶接收不同類型請求的服務。你可以爲用戶輸入設計一個組合數據模型,但因爲用戶永遠不會如你預期般行動,生產系統總是會收到測試覆蓋之外的輸入數據組合。

真正對系統的建立信心的唯一方法就是在生產環境中針對真實的輸入數據驗證實驗。

5.3 第三方系統

分佈式系統就是,其中有臺你根本不知道的機器故障了,有可能會讓你自己的服務也故障。

Leslie Lamport

即使可以預見所有在控制範圍內系統的狀態,我們也總是會依賴於外部系統,它們的行爲我們不可能全都知道。2012年的平安夜,在Netflix全體員工的記憶上烙上了深深的印記。AWS 一個區域的 ELB(Elastic Load Balancing)服務故障,導致了Netflix全部服務嚴重中斷。

如果系統部署在像AWS或Azure這樣的雲服務中,那麼存在大量的你所依賴,而又不完全瞭解的外部服務是顯而易見的。但即使你的系統全都運行在自己的數據中心,但在生產環境中,系統還是會依賴其他的外部服務,例如DNS,SMTP,NTP,等等。就算這些服務你都自己部署,他們也經常會需要和外部的不受你控制的服務進行交互。

如果你的服務提供了一個Web UI,那麼你的用戶所使用的瀏覽器就變成了你係統的一部分,但它不受你控制。即使你對客戶端擁有全面的控制,例如IoT設備,你仍然逃不開用戶所連接的網絡環境和設備的影響。

第三方系統的行爲在他自己的生產環境,與他和其他環境的集成的大環境中的行爲總是有所不同。這進一步強調了你需要在生產環境運行實驗的事實,生產環境纔是你的系統和第三方系統進行真實交互的唯一場所。

面向雲服務的混沌工程工具

只要系統部署在雲上,那麼混亂就是不可避免的。所幸我們的社區已經注意到並且開發了一些優秀的基於雲的混沌工程工具,並且可以和不同的雲提供商整合使用。除了混亂猴子之外,值得一提的工具有Chaos Lambda,它可以讓我們在生產時間隨機關閉AWS的Auto Scaling Group(ASG)實例。還有Microsoft Azure的Fault Analysis Service,它是專爲測試在Microsoft Azure Service Fabric所構建服務的工具。

5.4 生產環境變更

在Netflix,我們的系統一直在不斷更新。每天工程師和自動腳本都在通過不同的方式更新着系統,例如發佈新代碼,更改動態配置,添加持久化的數據,等等。

如果我們擴展我們系統的概念來包括這些生產環境中的變更,那麼很明顯在測試環境中想要模擬這些系統行爲有多困難。

5.5 外部有效性

當心理學家或教育研究人員等社會科學家進行實驗時,他們的主要關注點之一就是“外部有效性”:這個實驗的結果能否概括我們真正感興趣的現象?或者測量結果的產品運行環境是否是專門爲了測試而準備的?

如果你不直接在生產環境中運行混沌工程實驗,那麼本章所討論的問題(狀態、輸入、第三方系統,生產環境變更)就都是混沌工程實驗的外部有效性的潛在威脅。

5.6 不願意實踐混沌工程的藉口

我們認識到,在有些環境中,直接在生產環境中進行實驗可能非常困難甚至不可能。我們並不期待工程師將干擾注入到行駛中的自動駕駛汽車的傳感器裏。但是,多數用戶應該都不是在操作這類安全悠關的系統。

5.6.1 我很確定它會宕機!

如果你不願在生產環境執行混沌工程實驗的原因是,你對系統在你注入事件時會如何反應缺乏信心,那麼這可能是你的系統還不夠成熟來應對混沌工程實驗的信號。你應該在對系統的彈性具備一定信心的時候再進行混沌工程實驗。混沌工程的一個主要目的是識別系統中的薄弱環節。如果已經看到明顯的薄弱環節,那你應該首先專注於提高系統在這一點上的彈性。當你確信系統有足夠的彈性時,就可以開始進行混沌工程實驗了。

5.6.2 如果真的宕機了,麻煩就大了!

即使你對系統彈性有很大的信心,你也可能會猶豫不決要不要執行混沌工程實驗,因爲擔心如果實驗揭示出系統薄弱環節的同時造成過多的破壞。

這是一個非常合理的顧慮,這也是我們正在致力解決的問題。我們採用的方法是通過下面兩個途徑來最小化潛在的影響範圍:

  • 支持快速終止實驗;
  • 最小化實驗造成的“爆炸半徑”。

當你執行任何混沌工程實驗之前,應該先有一個用來立即終止實驗的“大紅色按鈕”(我們真的有一個大紅色按鈕,雖然是虛擬的)。更好的方法是自動化這個功能,當它監測到對穩定狀態有潛在危害的時候立即自動終止實驗。

第二個策略涉及在設計實驗時,要考慮到如何既能從實驗中獲得有意義的結論,同時兼顧最小化實驗可能造成的潛在危害。這一點在後面的最小化“爆炸半徑”一節中討論。

5.7 離生產環境越近越好

即便你不能在生產環境中執行實驗,你也要儘可能的在離生產環境最接近的環境中運行。越接近生產環境,對實驗外部有效性的威脅就越少,對實驗結果的信心就越足。

不能在生產環境中做實驗?

在2015年紐約Velocity Conference上,來自Fidelity Investment的Kyle Parrish和David Halsey做了題爲“太大而無法測試:如何破壞一個在線交易平臺而不造成金融災難”的演講。他們在一個包括大型機的金融交易系統上運行了混沌工程實驗。用他們的話來說,“我們意識到,用我們的災備環境,配合生產環境的前端,就可以用現有的一些部件,再構造出一套生產環境。我們越深入,這個想法就越可行。我們發現大型機災備系統非常理想,因爲它與生產環境保持實時同步,包含所有生產環境的代碼,數據,處理能力和存儲能力,支持團隊也完全瞭解它是如何運行的。我們也清楚的瞭解到,可以在這個系統上執行2倍或3倍於實際生產峯值時的流量。我們可以再造一套生產環境!”這是一個有創造力的繞過制度規範來模擬生產環境的實例。

記住:爲了保障系統在未來不會遭受大規模中斷事故,冒一點可控的風險是值得的。

6. 持續自動化運行實驗

自動化是最長的槓桿。在混沌工程的實踐中,我們自動執行實驗,自動分析實驗結果,並希望可以自動創建新的實驗。

6.1 自動執行實驗

手動執行一次性的實驗是非常好的第一步。當我們想出尋找故障空間的新方法時,我們經常從手動的方法開始,小心謹慎地處理每一件事以期建立對實驗和對系統的信心。所有當事人都聚集在一起,並向CORE(Critical Operations Response Engineering,Netflix的SRE團隊的名稱)發出一個警示信息,說明一個新的實驗即將開始。

用這種較恐懼和極端級別的態度有利於:1)正確運行實驗 2)確保實驗有最小的“爆炸半徑”。當我們成功執行了實驗之後,下一步就是自動化這個實驗以讓其持續運行。

如果一個實驗不是自動化的,那他就是作廢了。

當今系統的複雜性意味着我們無法先驗的知道,生產環境的哪些變動會改變混沌工程實驗的結果。基於這個原因,我們必須假設所有變動都會改變實驗結果。在共享狀態、緩存、動態配置管理、持續交付、自動伸縮、時間敏感的代碼等等的作用之下,生產環境實際上處在一個無時不在變化的狀態。導致的結果就是,對實驗結果的信心是隨着時間而衰減的。

理想情況下,實驗應該隨着每次變化而執行,這有點兒像是混沌金絲雀。當發現新的風險時,操作人員如果相當確定根源是即將發佈的新代碼,那他就可以選擇是否應該阻止發佈新版本並優先修復缺陷。這種方法可以更深入瞭解生產中的可用性風險發生和持續的時間。在另一個極端,每年一次的演習中的問題調查難度更高,需要完全從零開始檢查,而且很難確定這個潛在的問題存在於生產環境多久了。

如果實驗不是自動化的,那麼它就不會被執行。

在Netflix,服務的可用性由該服務的開發和維護團隊全權負責。我們的混沌工程團隊通過培訓,工具,鼓勵和壓力來幫助服務所有者提高服務的可用性。我們不能也不應該讓工程師犧牲開發速度,專門花時間來手動定期執行混沌工程實驗。相反的,我們自己投入精力來開發混沌工程的工具和平臺,以期不斷降低創建新實驗的門檻,並能夠完全自動運行這些實驗。

混沌工程自動化平臺 Chaos Automation Platform(ChAP)

我們的混沌工程團隊在2015年的大部分時間裏都在以諮詢的形式,在關鍵微服務上運行混沌工程實驗。這對於真正掌握FIT的能力和侷限非常必要,但是我們也知道這樣手把手的諮詢方式沒辦法規模化。我們需要一個可以在整個組織規模化這個實踐機制。

到2016年初,我們有了一個計劃將混沌工程的原則引入到微服務層。我們注意到FIT有一些問題妨礙了自動化和廣泛應用。其中一部分可以在FIT本身解決,另外一部分則需要較大的工程量,比請求頭修改和FIT提供的代碼故障模擬點注入要複雜得多。

2016年底我們發佈了混沌工程自動化平臺,簡稱ChAP,旨在解決這些不足之處。

FIT中大多數問題都是由於缺乏自動化導致的。過多的對人工參與,例如設置故障場景,觀測運行時關鍵指標的變化等,這些被證明是大規模應用的障礙。我們傾向於依靠現有的金絲雀分析(參見前面對金絲雀分析的介紹)來自動判斷實驗是否在可接受的範圍內執行。

隨後我們使用一個自動化模板開始進行真正的實驗。在上面討論過的FIT例子裏,我們影響了5%的流量,觀察它對SPS是否有影響。如果我們沒有觀察到任何影響,我們會將受影響的流量提高到25%。任何影響都有可能被其他和SPS相關的噪音所掩蓋。像這樣用大量流量做實驗是有風險的,它只能給我們較低的信心我們可以隔離一些小的影響,它也可以防止多個故障同時出現。

爲了最小化爆炸半徑,ChAP爲每個被實驗的微服務創建一個控制節點和一個實驗集羣。例如我們像上述的實例中說的,測試一個用戶信息的微服務,ChAP會詢問我們的持續集成工具Spinnaker關於這個集羣的信息。ChAP用這個信息創建這個服務的兩個節點,一個作爲控制節點,另一個作爲實驗節點。然後它會分流一小部分流量,並平均分配在控制節點和實驗節點上。只有實驗節點裏注入了故障場景。當流量在系統中流轉時,我們可以實時比對控制節點和實驗節點上的成功率和操作問題。

有了自動化的實驗,我們就有了較高的信心,我們可以通過一對一比對控制節點和實驗節點來監測到即使是很小的影響。我們隻影響了流量中的很小部分,並且已經隔離了實驗,因此我們就可以並行運行大量的實驗了。

在2016年底,我們將ChAP與持續交付工具Spinnaker集成在一起,這樣微服務就可以在每次新發布時運行混沌工程實驗了。這個新功能有些類似金絲雀,但是在這個場景下我們需要它不間斷運行,不會立即自動優雅降級,因爲這裏我們的目的是要儘可能發現所有未來潛在的系統問題。服務所有者基於在他們的微服務中發現的薄弱環節,我們給予了他們防止降級發生的機會。

6.2 自動創建實驗

如果你已經可以配置定期自動運行實驗,你就處在一個非常好的狀態了。然而,我們認爲還可以追求一個更好的自動化水平:自動設計實驗。

設計混沌工程實驗的挑戰並非來自於定位導致生產環境崩潰的原因,這些信息在我們的故障跟蹤中有。我們真正想要做的是找到那些本不應該讓系統崩潰的事件的原因,包括那些還從未發生過的事件,然後持續不斷的設計實驗來驗證,保證這些事件永遠不會導致系統崩潰。

然而這是非常困難的。導致系統波動的原因空間是非常巨大的,我們不可能有足夠的時間和資源窮舉所有可能導致問題的事件及其組合。

Lineage-Driven Fault Injection (LDFI)

一個值得關注的關於自動創建實驗的研究是一項叫做Lineage-Driven Fault Injection (LDFI)的技術,由加州大學聖克魯茲分校的Peter Alvaro教授開發。LDFI可以識別出可能導致分佈式系統故障的錯誤事件組合。LDFI的工作原理是通過推斷系統正常情況下的行爲來判斷需要注入的候選錯誤事件。

2015年,Peter Alvaro與Netflix的工程師合作來研究是否可以把LDFI應用在我們的系統上。他們成功地在Netflix FIT框架的基礎上開發了一個版本的LDFI,並且識別出了可能導致嚴重故障的一些錯誤事件組合。

有關如何在Netflix應用這項工作的更多信息,請參閱“第七屆ACM雲計算研討會論文集”(SoCC '16)上發表的論文“Internet規模的自動化故障測試研究”http://dx.doi.org/10.1145/2987550.2987555

7. 最小化“爆炸半徑”

1986年4月26日,人類歷史上最嚴重的核事故之一發生在烏克蘭的切爾諾貝利核電站。具有諷刺意味的是,災難是由於一次彈性演習導致的:一次驗證冷卻劑泵冗餘電源的演習。雖然我們大多數人並不從事像核電廠冷卻系統這樣高危的項目工作,但每一次混沌工程實驗的確具備導致生產環境崩潰的風險。混沌工程師的一項專業職責就是要理解和降低生產風險,可以爲實驗而具備良好設計的系統可以阻止大規模的生產事故,僅僅影響到少量的用戶。

不幸的是,我們經常運行本來只會影響一小部分用戶的測試,卻由於級聯故障無意中影響到了更多的用戶。在這些情況下,我們不得不立即中斷實驗。雖然我們絕不想發生這種情況,但隨時遏制和停止實驗的能力是必備的,可以避免造成更大的危機。我們的實驗通過很多方法來探尋故障會造成的未知的和不可預見的影響,所以關鍵在於如何讓這些薄弱環節曝光出來而不會意外造成更大規模的故障。我們稱之爲最小化“爆炸半徑”。

能帶來最大信心的實驗也是風險最大的,是對所有生產流量都影響的實驗。而混沌工程實驗應該只承受謹慎的,可以衡量的風險,並採用漸進的方式,每一步都基於前一步的基礎之上。這種遞進的方式不斷增加對系統的信心,而不會對用戶造成過多不必要的影響。

最小風險的實驗只作用於很少的用戶之上。爲此我們驗證客戶端功能時只向一小部分終端注入故障。這些實驗僅限於影響一小部分用戶或一小部分流程。他們不能代表全部生產流量,但他們是很好的早期指標。例如,如果一個網站無法通過早期實驗,就沒必要影響大量其餘的真實用戶。

當自動化實驗成功之後(或者小量設備驗證沒有涵蓋要測試的功能時),下一步就是運行小規模的擴散實驗。這種實驗會影響一小部分百分比的用戶,因爲我們允許這些流量都遵循正常的路由規則,所以最終它們會在生產服務器上均勻分佈。對於此類實驗,你需要用你定義好的成功指標來過濾所有被影響的用戶,以防實驗的影響被生產環境的噪音掩蓋。小規模擴散實驗的優勢在於它不會觸動到生產環境的例如斷路器閾值,所以你可以驗證每一個單一請求的超時和預案。這可以驗證系統對瞬時異常的彈性。

下一步是進行小規模的集中實驗,通過修改路由策略讓所有實驗覆蓋的用戶流量導向到特定的節點。這些節點會經歷高度集中的故障、延遲等測試。這裏我們會允許斷路器打開,同時會暴露隱藏的資源限制。如果我們發現有無效的預案或者奇怪的鎖競爭等情況導致服務中斷,那麼只有實驗覆蓋的用戶會受到影響。這個實驗模擬大規模生產環境故障,但同時可以把負面影響控制在最小,然而結果卻能帶來高度的信心。

風險最大但最準確的實驗是大規模無自定義路由的實驗。在這個實驗級別,實驗結果應該在你的主控控制檯顯示,同時因爲斷路器和共享資源的限制,實驗可能會影響到不在實驗覆蓋範圍內的用戶。當然,沒有什麼比讓所有生產用戶都參與實驗,能給你更多關於系統可以抵禦特定故障場景的確定性了。

除了不斷擴大實驗範圍,在實驗造成過多危害時及時終止實驗也是必不可少的。有些系統設計會使用降級模式來給用戶帶來稍小的影響,這還好,但是當系統完全中斷服務的時候,就應該立即終止實驗。這可以由之前討論過的“大紅色按鈕”來處理。

我們強烈建議實施自動終止實驗,尤其是在定期自動執行實驗的情況下。關於弄清楚如何構建一個可以實時監控到我們感興趣的指標的系統,並可以隨時實施混沌工程實驗,這完全依賴於你手上的獨特的系統構造,我們留給讀者當做一個課後練習。

爲了讓儘可能高效地應對實驗發生不可預期的情況,我們要避免在高風險的時間段運行實驗。例如我們只在所有人都在辦公室工作的時間段運行實驗。

如果實驗的工具和儀器本身就會對系統和指標產生影響,那麼整個混沌工程的目的就被破壞了。我們要的是建立對系統彈性的信心,記住每次只檢驗一個可控的故障。

譯者簡介

侯傑,TGO 鯤鵬會會員,美利金融技術副總裁,整體負責美利金融技術研發工作。曾在愛點擊,IBM 中國,IBM 澳大利亞擔任研發管理,諮詢管理等職位,帶領團隊負責過多個大規模金融行業信息化項目,和互聯網轉型實踐。畢業於南京大學。

下期預告

接下來我們會討論實踐中的混沌工程以及混沌工程的成熟度模型。

相關閱讀

[Netflix 混沌工程手冊 Part 1:混沌工程簡介](

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章