Categories
程式開發

字節跳動混沌工程實踐總結


本文選自“字節跳動基礎架構實踐”系列文章。
“字節跳動基礎架構實踐”系列文章是由字節跳動基礎架構部門各技術團隊及專家傾力打造的技術乾貨內容,和大家分享團隊在基礎架構發展和演進過程中的實踐經驗與教訓,與各位技術同學一起交流成長。
混沌工程是通過故障注入的方式幫助系統尋找薄弱點,從而提高系統的穩定性。隨著微服務、雲原生相關技術的發展,分佈式系統已經流行在業界各處,但因此也帶來了複雜度急劇上升、故障發生難以預測後果、難以避免與驗證等挑戰。而混沌工程正是通過故障注入等方式為切入點,幫助解決以上問題。本文討論了字節跳動引入混沌工程以來的相關實踐,希望能提供一些參考。

背景

什麼是混沌工程

在生產環境中實際運行分佈式系統,難免會有各種不可預料的突發事件發生。同時,雲原生的發展,不斷推進著微服務的進一步解耦,海量的數據與用戶規模也帶來了基礎設施的大規模分佈式演進。分佈式系統天生有著各種相互依賴,可以出錯的地方數不勝數,處理不好就會導致業務受損,或者是其他各種無法預期的異常行為。

在復雜的分佈式系統中,無法阻止這些故障的發生,我們應該致力於在這些異常行為被觸發之前,盡可能多地識別風險。然後,針對性地進行加固,防範,從而避免故障發生時所帶來的嚴重後果。

混沌工程正是這樣一套通過在生產分佈式系統上進行實驗,主動找出系統中的脆弱環節的方法學。這種通過實證的驗證方法顯然可以為我們打造更具彈性的系統,同時讓我們更透徹的掌握系統運行時的各種行為規律。我們能夠在不斷打造更具彈性(彈性:系統應對故障、從故障中恢復的能力)系統的同時,樹立運行高可用分佈式系統的信心。

實踐混沌工程可以簡單如在生產環境中運行kill -9 來模擬一個服務節點的突然宕機,也可以復雜到在線上挑選一小部分(但足夠代表性)的流量,按一定規則或頻率自動運行一系列實驗。

更多混沌工程相關基礎介紹,在此不再贅述,相關討論已有很多,可參考《混沌工程:Netflix 系統穩定性之道》[1]。

業內實踐

實際上業界主流大廠都有混沌工程實踐的身影,較有代表性的項目如下:

  • Netflix 最早系統化地提出了混沌工程的概念,並出版了混沌工程領域內的首部書籍《混沌工程:Netflix 系統穩定性之道》[1],在本書中提出了混沌工程成熟度模型與應用度模型,並總結了五條高級原則,對於混沌工程的發展具有指導性意義。另外 Netflix 開源了其混沌工程項目 – Chaos Monkey[3]。
  • 阿里巴巴是國內較早開始探索混沌工程並做出開源的公司,其開源項目 ChaosBlade[4]可以結合阿里雲進行 chaos 實驗。
  • PingCap 作為國內優秀的數據庫領域開源公司,其在混沌工程領域一直有投入,並在最近開源了內部混沌工程實踐平台 – Chaos Mesh[5]。
  • Gremlin 為一家混沌工程商業化公司,該公司提供了一個混沌工程實驗平台,通過將其 agent 安裝在雲主機上觸發故障。同時提出了 chaos gameday[2] 的概念。

字節跳動如何實踐

字節跳動各業務線內一直都能看到故障演練的踪跡,也有一些簡單的工具,並演化出了故障演練平台。在發現該平台無法滿足之後,我們開始引入混沌工程的理論概念,重新思考混沌工程。關於混沌工程,我們打算分三部分討論:

  • 故障注入
  • 自動化指標分析
  • 活動實踐落地

在混沌工程的實施過程中,我們發現需要依賴兩個核心的原子能力,分別是故障注入與穩定性檢測。故障注入是混沌工程的基礎無需過多解釋。穩定性檢測能力,可以:1. 降低實驗的時間成本,我們可以依賴自動化指標分析,幫助我們進行輔助判斷,從而尋找更大的產出。 2. 降低實驗的風險成本,我們可以依賴自動化指標分析,進行穩定性判斷,作為 chaos 實驗自動化停止的決策依據。

另外,關於第三部分活動實踐的落地,則是我們在 chaos 實驗中,出於不同目的,或多或少會有一些流程化,事務性的內容,我們希望將其沉澱在平台中。

第一代

第一代可以認為是古早時期,此時字節跳動內部有使用一套容災演練平台,作為內部故障平台,架構如下:

字節跳動混沌工程實踐總結 1

容災演練平台架構圖

該平台主要目標是解決故障注入問題,同時提供了基於閾值的簡單指標分析與自動停止。故障主要為通過網絡干擾模擬下游依賴故障,此階段幫助部分業務實現了生產環境的部分容災演練。

但是該平台存在這樣的情況:在故障注入方面,由於早期設計著眼於網絡故障,因而其架構與模型不方便擴展至其他故障形態。缺少明確、統一的故障域描述,也因此對於爆炸半徑缺乏清晰的描述。另外該平台指標分析比較簡單,在實踐活動方面也只是以單純製造故障為主。於是我們開始引入混沌工程理論,重新打造全新混沌工程平台。

第二代

該階段整體還是圍繞故障建設為主,階段目標為:

  • 在故障注入方面,設計可擴展的故障中心,實現精準可控的故障。
  • 在實踐活動方面,確立混沌工程規範,探索最佳實踐。
  • 解耦故障實現與 chaos 實踐活動管理。

系統設計

整體設計如下:

字節跳動混沌工程實踐總結 2

初步階段整體設計

其中用戶自演練平台無需關心故障實現與故障狀態維護,著眼於 chaos 實驗計劃的管理與編排。所有故障相關實現下沉至故障中心,而平台層只需將任務發給故障中心即可。

故障中心抽象了故障模型,並提供了一套聲明式接口,負責對故障聲明進行轉化計算,確定發生故障的容器、物理機 或者 中間件,自動化安裝 agent 並下髮指令。可實現精準控制故障,維護故障狀態。

故障模型

在開始討論如何進行故障注入之前,我們首先需要定義故障模型。對於網絡故障,OS 故障,下游依賴故障,中間件故障等發生在不同層面的故障,我們該如何抽象描述?我們首先確立了一點:

所有故障的發生,都會間接或直接影響某個微服務。而我們的最終目的,是觀察該服務在各外部依賴異常時,服務本身的 resilience 能力如何。

故我們以某個微服務作為觀察目標展開故障的定義。故障模型如下:

字節跳動混沌工程實踐總結 3

故障模型

  • Target – 即上文提及的目標微服務,在開始 chaos 實驗之前,需要明確,對什麼服務注入故障,該服務為主要觀察目標。
  • Scope Filter – 對應混沌工程概念中的爆炸半徑,為了降低實驗風險,我們不會令服務全流量受影響。通常會過濾出某一部署單元,該單元或為某一機房,或為某一集群,甚至精確到實例級別乃至流量級別。
  • Dependency – 依賴,我們認為所謂服務被故障影響,實際是其依賴有異常。該異常可能來自中間件,可能來自某下游服務,也可能來自所依賴的 cpu,磁盤,網絡等。
  • Action – 故障事件,即描述該服務的外部依賴究竟發生了何種故障,比如下游服務返回了拒絕,發生了丟包,或者延時;又比如磁盤發生了寫異常,讀繁忙,寫滿等意外情況。

根據故障模型,進行故障聲明的偽代碼描述如下:

spec. //微服务application A 的 cluster1集群内10%的实例cpu突然满载
    tareget("application A").
    cluster_scope_filter("cluster1").
    percent_scope_filter("10%").
    dependency("cpu").
    action("cpu_burn").
    end_at("2020-04-19 13:36:23")

spec. //服务application B 的 cluster2集群所依赖的下游application C突然延时增加100ms
    tareget("application B").
    cluster_scope_filter("cluster2").
    dependency("application C").
    action("delay, 200ms").
    end_at("2020-04-19 13:36:23")

故障中心設計

我們在以上故障模型基礎上設計了一套聲明式接口。當注入某一故障時,只需按上述模型添加故障聲明即生效;若想終止故障,只需刪除該聲明。

故障中心在收到上述類似聲明後,便開始向內部研發體系平台尋找符合條件的實例,並自動安裝故障 agent,通過將相關指令下發給 agent,實現故障注入的目的。故障中心適當借鑒了 Kubernetes 的架構設計與理念,其架構設計如下:

字節跳動混沌工程實踐總結 4

故障中心架構圖

故障中心由三個核心組件 API Server, Scheduler, Controller,外加一個核心存儲 etcd 組成。其中 API Server 負責包裝 etcd 並對外提供聲明式接口; Scheduler 則負責將故障聲明解析,並根據聲明持續尋找 Target 對應的實例,以及 Dependency 對應的下游實例/中間件/物理設備;在這之後, Controller 將 action 故障解析為可執行指令,下發至對應實例的 agent,或者調用對應中間件的 API,達到精準的故障注入。

實驗選擇的原則

在 chaos 實驗中,我們考慮到風險以及各業務不同特點對 chaos 理念的接受程度不同,所以定義了實驗選擇的原則,可供各業務線根據實際情況自行決定,其原則如下:

  • 從線下到生產
  • 從小到大
  • 從面向過去到面向未來
  • 從工作日到休息日

從線下到生產

此條指的是環境的選擇。

一般認為,混沌工程只有在生產環境實驗才有意義;但我們認為一種比較溫和的實驗步驟是從線下逐漸走到生產。這也是綜合考慮,從線下開始著手會讓各方都比較放心。不過對於分佈式系統而言,部署不同、流量不同都會帶來不一樣的結果,唯有在生產進行實驗才能真正驗證。一條比較好的路徑是:

測試環境-> 預發布環境 -> 預覽環境特定流量 -> 生產集群生產流量

從小到大

此條指的是故障範圍的選擇。

我們推薦故障應該從小範圍,較溫和的開始。當建立了足夠的信心之後,再進一步擴大故障範圍。一條比較好的路徑是:

可控流量 -> 單個接口 -> 單機 -> 單集群 -> 單機房 -> 全鏈路

從面向過去到面向未來

此條指的是故障類型的選擇。

我們認為曾發生過的故障是實驗優先級最高的。而人類歷史告訴我們,人們總是會在一個地方反复跌倒;生產發生過的故障,很有可能再次發生;且同樣可能會在其他鏈路上發生類似故障。因此一條較好的路徑是:

重現歷史事故的故障 -> 來自歷史事故的故障類型 & 相似鏈路 -> 各種隨機故障 & 全鏈路。

從工作日到休息日

此條指的是混沌工程實驗時間的選擇。

休息日代指任意時間。我們推薦實驗時間從工作日開始嘗試,最優的是工作日下午 3 點左右(各業務根據自身高低峰期再行考慮)。這個時間段,相關人員一般都在工作崗位上,有任何情況都能及時處理。混沌工程的早期目標就是為了在可控的環境中提前暴露問題。當然,隨著混沌工程不斷走向成熟,我們將會慢慢開始嘗試在任意時間進行實驗。一條比較好的路徑是:

工作日下午 -> 工作日晚上 -> 休息日 -> 隨機時間

實驗過程設計

在該階段,我們為業務系統設計了 chaos 實驗過程的最佳實踐。按此過程,chaos 實驗將會更有目的,觀察到的內容亦更有意義。

實驗前

0. ⚠️ 在開始你的第一個混沌實驗之前,請確保你的服務已經應用了彈性模式,並準備好處理可能出現的錯誤,否則不要隨意嘗試。

  1. 準備故障注入的能力
    a. 字節跳動混沌工程實驗平台的故障模擬能力
    b. 聯繫各依賴方手動製造故障
  2. 選定本次實驗的假設,如:
    a. 不會因為某個下游服務掛了而影響業務。
    b. 不會因為 redis 網絡抖動而影響業務。
    c. 不會因為某個 pod 突然被殺而影響業務。
    d. 當某個核心下游依賴掛了之後,降級方案必須有效,且副作用可接受。
  3. 選定能體現實驗假設的指標,並觀察。
  4. 選定能反應服務損失的指標,並設定底線。
  5. 在組織內溝通到位。
    實驗中
  6. 執行期間要密切關注相關指標,因為可能需要隨時終止實驗。
  7. 牢記實驗的假定,收集相關指標稍後可輔助分析實驗結果。
  8. 在實驗過程中,可能會根據指標的波動情況,隨時調整實驗參數(故障範圍與烈度),多嘗試幾次,會有更好的效果。
    實驗後

    根據指標和業務表現,分析本次實驗所能帶來的成果。根據經驗反饋,一般會獲得以下相關成果:

  • 找到了脆弱點,並獲得改進
  • 驗證了降級/預案,增強信心
  • 找到了系統性能拐點
  • 梳理出一波無效告警,優化告警效率

總結

在這一階段,我們對故障中心完全重構,在架構上使得故障注入更加簡單可控;在模型抽像上,故障注入的擴展性更強。在這一階段,我們梳理了 chaos 實驗選擇與流程的最佳實踐。在下一代產品中除了繼續豐富故障能力外,將會著眼於補齊指標分析能力,以及進一步沉澱有更大產出的實踐活動。

第三代

在完成了初步階段的實踐之後,chaos 的核心能力-故障注入已經具備,同時字節跳動各業務線也陸續開始了 chaos 之旅。於是該階段,我們的目標是:

  1. 在自動化指標分析方面,補齊指標分析能力。
  2. 在故障注入方面,豐富故障的類型。
  3. 在實踐活動方面,沉澱上一階段總結的實踐活動,並進一步探索實踐形式,挖掘更大的價值產出。

系統設計

基於以上目的,整體設計如下:

字節跳動混沌工程實踐總結 5

成熟階段系統設計

在原子能力層,添加自動化指標觀察能力。通過引入機器學習,我們做到了基於指標歷史規律的無閾值異常檢測。

在平台層,添加自動化強弱依賴梳理,與紅藍對抗模塊。

自動化指標觀察

在 chaos 實驗中,相關指標的梳理與收集是一件繁瑣且耗費心力的活動。我們觀察了 chaos 實驗過程之後,總結了三類指標,如下:

  • 故障指標 – 確定故障是否注入成功 。
  • 止損指標 – 確保系統不會因故障無法承受而造成過大損失。
  • 觀察指標 – 觀察細節,觀察故障導致了哪些關聯異常。

故障指標

  • 指標定義 – 故障指標源於故障,因故障觸發了指標波動。比如我們注入了故障:redis 延時增加 30ms,那麼該指標則為目標服務 -> redis 之間的均值延時 pct99 延時等。該指標可幫助用戶直觀看到故障的產生與結束。
  • 如何處理 – 對於此類指標,僅做展示即可,可以保證用戶清晰看到故障何時開始,何時結束。
  • 獲得途徑 – 來自故障,平台製造故障時,平台知道會受影響的直接指標是什麼。

止損指標

  • 指標定義 – 止損指標對於目標服務/目標業務至關重要,表示該次演練所能承受的最大限度,指標可能來自服務本身相關(比如對外錯誤率),亦可能來自關聯較遠的業務指標(比如每分鐘點播量),甚至可能來自兜底服務的指標(比如兜底服務的最大承受指標);也可能是以上提到的各類指標某種組合。
  • 如何處理 – 對於此類指標,需要非常精準地標識關鍵閾值,一旦波動到閾值時,表示到達了損失底線,任何操作必須馬上停止,故障需要恢復。

觀察指標

  • 指標定義 – 觀察指標,對於在 chaos 實驗的時候發現新的問題,有很大的輔助作用。觀察指標應該是一切與服務和故障相關的指標。比如服務本身的SRE 四項黃金指標(latency, traffic, errors, and saturation),比如故障可能影響的關聯指標(redis 延時故障,是否會導致redis 其他指標的變化?redis qps,reids errors,降級服務的qps 變化),比如關聯告警記錄,關聯日誌。
  • 如何處理 – 此類指標,沒有明確的閾值,但往往此類指標在事後分析的時候最容易發現各種潛在問題,我們在處理此類指標的時候引入了機器學習方法,與指標歷史規律進行對比,可做到自動化異常檢測。

因此指標觀察的核心是智能指標篩选和無閾值異常檢測。另外配合一套基於經驗的人工規則,我們可以面向不同的實踐活動做各類自動化判斷或者輔助決策。

紅藍對抗實踐

字節跳動的紅藍對抗實踐,吸收自 Gremlin 介紹的 chaos gameday[2]。在字節跳動內部多次實踐中,我們也不斷因地制宜調整,最終發展成為字節跳動特色的紅藍對抗實踐。紅藍對抗的實施目標是幫助業務系統進行全面摸底,也可認為是對業務系統的穩定性建設目標的一次集中驗證。

目前紅藍對抗已多次幫助字節跳動推薦中台進行全面摸底,發現了從監控、告警到兜底、降級、熔斷策略等各方面的問題。

流程設計

在開啟紅藍對抗之前,紅藍雙方的溝通特別關鍵。紅軍(即防守方)需要進行諸多決策,比如評估有信心參與對抗的服務與範圍,比如評估近期業務迭代節奏,權衡業務迭代與穩定性建設。我們遵循如下流程進行實踐對抗前的活動正在完成平台化沉澱:

字節跳動混沌工程實踐總結 6

紅藍對抗執行前流程圖

紅藍對抗一旦開啟後,主要操作將由藍方主導,除非發生預期外情況(這一般也意味著防守失敗)或者需要操作預案開關,否則紅方在此過程中,基本處於 stand by 狀態。主要流程如下:

字節跳動混沌工程實踐總結 7

紅藍對抗執行中流程圖

在對抗結束後的複盤回顧是關鍵環節,通過將紅藍對抗過程中所記錄的數據匯總。可清晰地看到對抗整體效果,一目了然地了解此次計劃中目標業務系統的穩定性建設情況。

字節跳動混沌工程實踐總結 8

單場紅藍對抗數據匯總

另外,我們將過程中發現的問題匯總記錄,並保留對抗時候的完整記錄。這使得發現問題可追踪,剖析問題有現場。

字節跳動混沌工程實踐總結 9

單場紅藍對抗結果匯總

強弱依賴自動化梳理實踐

服務的強弱依賴信息對於服務治理,容災體系的設計都至關重要,而強弱依賴的真實情況只能在故障發生時才能得到驗證。故我們開啟了強弱依賴的驗證工作,並隨著實踐打磨,不斷提高強弱依賴梳理的自動化程度。在通過引入機器學習幫助我們進行無閾值指標異常檢測之後,強弱依賴梳理過程已基本實現全自動化。

目前強弱依賴梳理已基本覆蓋抖音與火山的核心場景,為其服務治理與容災體系設計都提供了巨大的輸入。

強弱依賴自動化梳理的整體流程如下:

字節跳動混沌工程實踐總結 10

強弱依賴自動化梳理流程

總結

在該階段,我們補齊了指標分析能力,通過引入機器學習,很大程度降低了指標分析成本。

基於自動化指標分析能力,我們嘗試結合新的實踐活動挖掘了更多的產出。紅藍對抗活動幫助業務系統對自身穩定性有個更全面的了解。而強弱依賴分析則幫助業務系統對自身的穩定性細節有了更深的認識。

未來階段

面向基礎設施的混沌工程

以上關於混沌工程的討論,主要集中在業務層對於故障的 resilience 能力建設。不過,基礎設施的混沌工程建設其實更為重要。特別是各類計算,存儲組件在互聯網企業作為上層業務的基石,其穩定性的保證是上層業務穩定性保證的前提。

然而,越靠近基礎設施的故障模擬,也越有挑戰。例如存儲組件所依賴的核心,磁盤,其故障模擬會一步步需要深入到 OS 內核態,乃至物理層進行故障模擬。

另外,存儲組件的數據驗證也是一個較大的話題,其中包括了分佈式存儲在故障下是否能保證其承諾的一致性特性,以及如何驗證該一致性[6]。

這也是我們即將開始嘗試探索的一個全新方向,如何面向基礎設施服務進行混沌工程。

IAAS With Chaos

在面向基礎設施的混沌工程中我們提到了,基礎設施服務的依賴過於底層,我們也在思考,是否可以通過 OpenStack 構建一個 IAAS 集群,在該集群中還原生產等效的部署模型。之後通過 OpenStack API,在虛擬化層進行更深入故障模擬。由此提供一個自帶 chaos 特性的 IAAS。

全自動隨機的 Chaos 實驗

隨著紅藍對抗實踐的普及,紅藍對抗平台將會逐漸積累足夠的業務防守目標,該目標描述了業務系統所能承受的最大故障能力。那麼我們將可以開始嘗試在防守目標範圍內,開始不定期的自動化進行隨機故障注入,以達到隨時驗證其穩定性的目的。

故障智能診斷

我們也在思考,通過混沌工程的主動故障注入能力,是否能夠積累足夠數量級的故障與指標。從而訓練出指標的某種 pattern 特徵與故障的對應關係。這將可助力於生產排障,達到故障智能診斷的目的[7]。

結尾

混沌工程的早期探索,其實在行業內一直有,曾經是以故障測試、容災演練等身份存在。而隨著微服務架構的不斷發展,以及分佈式系統的不斷龐大,混沌工程開始嶄露頭角,越來越被重視。當 Netflix 正式提出混沌工程概念後,相關理論也開始飛快豐富。 Netflix 的實踐也證明了混沌工程在穩定性領域所帶來的巨大意義。

同時,我們在不斷挖掘,通過混沌工程手段能否有更大產出。相信隨著互聯網逐漸轉變成一項基礎設施服務,其穩定性將被不斷強調。而我們要做的只是直面故障,不懼故障,避免黑天鵝事件的發生。在此也歡迎各位加入我們,共同進行混沌工程實踐,推動混沌工程領域發展。

參考文獻

本文轉載自公眾號字節跳動技術團隊(ID:toutiaotechblog)。

原文鏈接

https://mp.weixin.qq.com/s/kZ_sDdrbc-_trVLNCWXyYw