輕鬆應對上千節點失效,去哪兒網混沌工程自動演練實踐

語言: CN / TW / HK

本文根據朱仕智 老師在 2022 Gdevops全球敏捷運維峰會-廣州站 〗現場演講內容整理而成。 (點選上方【dbaplus社群】公眾號,回覆“220617”可獲取完整PPT)

朱仕智

去哪兒網 高階技術總監

  • 2013年加入去哪兒網,負責過公共業務、國際機票、基礎架構等團隊,擅長高併發高可用高效能的系統設計和落地,多年的技術管理經驗。目前主要負責去哪兒網的基礎架構、基礎平臺、大前端、質量保障,專注公司整體技術演進和雲原生技術落地。

分享概要

一、混沌工程價值探討

二、去哪兒網混沌工程平臺

三、大規模自動演練

四、故障注入攻防演練

一、混沌工程價值探討

因為混沌工程是一個比較新興的技術,所以不可避免地會面臨一個問題:當我們要落地混沌工程時,需要對其進行價值的論證,再決定我們需要投入的人力,以及期望達到的效果。

作為技術行業的從業者,我們經常會了解到各大公司出現的宕機的情況,比如去年韓國電信網路的崩潰,以及Facebook伺服器宕機的事件,國內各大廠商尤其是雲廠商其實都會出現大規模的宕機故障。大部分人看到這些新聞都是吃瓜心態,但是作為技術行業的從業者,這類問題也許某一天就會發生在我們身上,出現在我們的機房和公司裡,並且需要我們去解決。

1、去哪兒網的系統群情況

接下來我簡單介紹去哪兒網的系統群情況。目前線上執行的活躍的應用有3000+個,提供18000+個dubbo rpc的服務介面,有3500+個http的域名註冊,有13000+個mq的主題,公司內部有5種語言的技術棧,以Java和node為主,可見其中關係的複雜程度。只要一個地方出現問題,就有可能會級聯地傳遞到c端,那麼使用者側就會感受到問題。因此我們應該如何治理這些系統群?混沌工程則是一個非常好的手段,後面我將會介紹如何用混沌工程解決這些問題。

2、常見故障原因

在我從業的八九年期間,我們也遇見過大量的基礎設施問題,如微服務的註冊中心zookeeper經常出現抖動的問題,訊息隊列出現問題,甚至機房的網路、斷電也會出現問題。

從故障的原因來看,我們可以將其分成幾大類:

  • 底層的機房、中介軟體、機器問題影響規模相對較大,機房掛掉時整個機房的相關應用都會受到影響;

  • 上層有應用問題,以及整個拓撲裡都會有依賴問題。

不同故障的原因千差萬別,只有拉長時間線才能夠發現其中存在著一定的共性,但是短期內很難通過方法解決一切問題,混沌工程則能夠很好地使其從被動轉成主動的形式。

混沌工程主要解決的問題並不是讓軟體不失敗,而是軟體在失敗的情況下是否還有一定的韌性,是否還能提供高質量的服務,以及能否主動構建失敗並提前將其修復。 因此我認為混沌工程的目標有兩個:

  • 一是通過混沌工程,我們可以 建立對系統抵禦生產環境中失控條件的能力和信心 ,這需要我們提前處理問題,併產生對應的改進和預案。

  • 二是 把不確定的東西變確定 ,實際上無論是質量還是可靠性、穩定性,最可怕的並不是我們能否做什麼,而是我們永遠不知道我們做完之後是否還有問題。

3、混沌工程的收益

我認為混沌工程可以有以下三方面的收益。

1)人

人的不同角色,如從使用者的角度來看,能夠得到穩定的使用者體驗,不會再由於技術問題導致使用者體驗的不佳,甚至於無法得到服務;從測試的角度來看,能夠提前降低甚至有可能阻止故障的發生;從開發和運維的角度來看,可以提升應急的效率。

2)流程

把一個亂序的具體問題具體分析的流程轉化為體系性治理的流程。

3)系統

面向正確的設計轉向了面向失敗的設計,為整個系統提供了一定的韌性容災能力。對於一些基礎能力而言,可觀測、可灰度、可回滾、可降級都不再是期望,而是實際能夠得到的能力。

二、去哪兒網混沌工程平臺

我們首先提出一個問題: 混沌工程的最佳實踐到底是什麼?

在業界裡大家經常看到的混沌工程,第一反應就是攻防演練,如線上隨機地插拔、隨機地注入故障,而我個人並不是很認同。混沌工程是把不確定的問題變成確定的,雖然人的攻防也可以一定程度上解決這個問題,但是人的攻防需要投入的時間非常多,而且攻防產生的知識,能夠固化、擴充套件的幅度也是有限的,除非能夠把混沌工程的文化在全公司裡從上到下執行得非常徹底。

從這個角度來看,隨機演練不是我們的最終目標,那我們的目標到底是什麼?

我認為需要分成多階段的目標,待會我會按照我們的實踐路線進行講解。需要注意的是,功能支援並不等於實踐模型。目前很多公司在做混沌工程相關的系統,或者在提供同樣的能力。但我認為其中存在很大的偏差,在於 提供了能力並不代表達到了混沌工程的目標。 因為從實踐模型來看,覆蓋面、有效率如何,接下來我會具體講解我們的實踐。

從應用架構層次來看,基本每個公司偏差不大,最底層是機房可用區,往上依次是中介軟體、伺服器層、應用層、依賴層。層次越往下,出問題的概率相對而言越小。在服務治理裡超時熔斷甚至限流發生頻率非常高,但是在機房層面統一的掛掉或斷電的概率則小很多。然而,底層的失效概率小,影響面卻非常大。應用架構的層次和對應的失效情況,很大程度影響了我們實踐的節奏。

混沌工程要達到兩個目標,並不是一蹴而就的,這意味著我們要做大量的工作以確保機制的制定和生效。應用架構層次是我們對應階段劃分投入的人力時一個很好的嚮導。當我們一無所有時,應當優先解決影響面大的問題。如果把服務之間依賴關係做得很好,但機房掛掉或中介軟體掛掉了,整個服務都不可用時,一切都是笑談。

因此, 從實踐路徑出發,我認為可以分成四個節奏:

1)先要做到機房、中介軟體、實體機、虛擬機器級別的關機演練,儘可能消除大面的影響。

2)應用演練在於應用的可用性治理。

3)依賴演練在於拓撲合理性依賴的治理。

4)攻防演練解決的是人面對失效時,是否有對應的預案策略,預案策略的執行時效性與恢復時間。

從左往右的路徑其實是一個 影響面逐漸縮小 的過程。

1、關機演練

去哪兒網的混沌工程平臺建設第一步就是關機演練的支援。能力目標上,我們要求同一機房某業務線所有服務節點掛掉時不受影響,那麼我們單次演練可能需要有上千臺節點的失效。要達到這樣的能力,需要直接利用混沌工程現代化的技術並不多,因為關機演練不是針對應用級別,而是針對機器節點級別,它的關鍵點在於基礎資訊的整合和整個流程的打通。

1)機房聚合資訊查詢

能夠發現應用是否符合標準,即當機房掛掉時,應用是否受到影響。其中有兩個方面的影響,一是應用只在單機房部署,二是單機房掛掉的情況下它的容量是否足夠,從而對應用提前進行改造。

2)自動建立溝通群

如果整個執行週期非常久,我們需要提前制定計劃,將非常明顯的問題進行改造,再進行真正執行的進度周知,以及事後的總結。以上過程都需要大量自動化資訊的周知,我們在內部的IM體系上有一套溝通和通知機制的方式。

3)真實關機

測試機器重啟和應用重啟的時間是否滿足要求,如果只是做一個假的失效,演練的恢復速度快並不意味著真正的故障發生時速度也足夠快。

4)接入告警,告警事件關聯推送

5)虛擬機器開機後關聯服務自動恢復

我們的能力目標需要大量的節點失效,因此我們無法人工進行恢復,當機房真正出現問題時我們也不可能依靠人工逐個應用排查和恢復,因此虛擬機器開機後關聯服務自動恢復的功能非常關鍵。

關機演練的實現有以下幾點:

  • 控制維度: 我們提供了機房、應用、機器三種級別的篩選。

  • 技術角度: 沒有涉及到現在的chaosmesh或chaosblade等技術,更多運用一些雲集群的原生技術,如openstack的API或saltstack,以及一個我們自研的控制面,包含了我們剛才提到的控制、演練、編排的功能。

關機演練效果:

  • 我們執行了49次機房演練,覆蓋的機器去重完之後共有4000+個機器,覆蓋了500+個應用,每次演練基本上能發現10+個問題。

  • 針對單機器級別的關機演練,我們執行了71次,覆蓋了3000+個機器和200+個應用。

當我們執行真正的關機演練時,如果監控圖出現異常情況,就代表機房有問題會影響C端的業務,那麼我們就需要將其提前修復。主動發現問題,則能制定對應的修復計劃,提前修復問題。

2、應用演練

應用演練的能力目標是對所有應用都可以選擇多策略的故障注入,主要解決應用可用性的問題。關鍵點在於:

1)要在線上環境做單應用的可用性演練

因為線上環境與測試環境、演練環境存在區別,包括服務治理的引數和對應的配置,進而服務產生的應對現象也是不同的。如果在測試環境做得很好,線上環境由於配置不同,也很可能導致演練得到的結果是錯誤的。

2)可靠的注入工具

因為我們要對非常核心的應用做失效情況的注入,可靠的工具對我們而言是一個生命線,如果注入工具本身不可靠,很可能會級聯帶來很多問題。

3)豐富的演練策略

4)生效面要可控

不能因為演練導致無差別的攻擊,導致線上出問題。

首先要對注入工具做技術選型。我們雲平臺裡的容器和虛擬機器都需要支援,要求的場景也比較豐富,我們對開源的要求也比較高,因為需要進行二次開發,最終從選型上我們選擇了比較適合的chaosblade。

接下來對chaosblade做一個簡單的介紹。

chaosblade支援的場景非常多,有容器級別、作業系統級別以及語言層面的,比如Java的executor。以上是我們廣泛應用的,尤其是通過Java的技術,我們對其進行了大量的改造。

chaosblade的功能基本上涵蓋了我們日常通用的中介軟體場景,比如在應用服務的層次裡,一般的rpc框架、閘道器、訊息中心件對於我們來說覆蓋面是比較廣的,特別是資料庫、快取等都有一定的支援。

chaosblade基礎功能比較完善,但是在企業場景裡有一些缺失,比如HTTP超時能力、fullGC和日誌擁堵,甚至官方不支援同一個介面的依賴但是不同的呼叫點等區分,以及比如全鏈路的匹配。我們把這些能力實現之後回饋給了官方,因此大家現在再去用最新版本的chaosblade則具備了以上企業場景。

選完chaosblade之後,我們需要進行演練前的準備,首先選定它的資源和策略,在演練進行的過程中把 agent自動化掛載上去,再通過選好的策略自動注入故障,通過流量copy的方式和人工手動觸發的case在C端觸發兩部分進行驗證。目前我們已經實現了全鏈路的場景匹配,因此能夠區分使用者的真實流量和測試人員的人工流量。在恢復階段我們有兩種恢復方式,一是對我們來說是預防底線的告警或超時恢復,如果都執行完之後沒有觸發則採取人工恢復,然後解除安裝agent,最後總結問題並改進完成覆盤。

以上是新建演練效果圖,演練時可以選擇對應的應用、機器以及策略,編排好之後就可以定期執行。

3、依賴演練

應用可靠性不僅受應用本身影響,還會受到依賴的資訊服務影響。大家維護所有應用時都會想到:死道友不死貧道,即所有的同行都可以出現問題但我不能。當我們保持這個心態治理服務時,就需要非常關注依賴關係,被拖死、被波及、被傳遞時,如何解決依賴關係導致的蝴蝶效應;依賴的超時、熔斷、異常、限流是否符合我們的預期;以及強依賴太多,能否降級為弱依賴,以上問題都非常關鍵。

因此,依賴演練的能力目標是對應用層的外部依賴進行多策略演練,並且斷言它是強依賴還是弱依賴。依賴演練的關鍵點在於:

1)做好應用元資料採集

如果沒有應用元資料的採集,則無法得知應用所有的外部依賴。我們將所有rpc和服務註冊相關的、資料依賴和redis以及一些定時任務等資訊採集到一起,將通過日誌、註冊中心、DB元資料或應用暴露出來的元資料,甚至還有trace資訊進行彙總,產生了一個最全的應用元資料。

2)視覺化應用拓撲結構

便於我們觀測到該應用被誰調了,我調了誰,對於我們做生效面控制非常有效。

3)區分不同場景的同一個依賴

即剛才提到的不同調用點的同一個介面的依賴。

4)標註強弱依賴關係

將採集到的資訊進行分析聚合和儲存,並對資訊進行強弱依賴關係標註。以上是強弱依賴關係標註的介面效果圖,我們能夠區分的型別多且細,對應的目標也非常多。

依賴演練存在一個邏輯的閉環:當我們的服務治理收集了依賴關係之後,需要讓使用者標註依賴關係,相當於得知使用者期望的依賴,我們再進行強弱依賴演練,並且得到演練結果進行修正,檢視是否符合使用者預期,從而產生對比差距。如使用者期望它是一個弱依賴,實際線上執行它是一個強依賴,那麼就能產生我們改進的計劃集,對其進行修復。

依賴演練效果:

我們執行了1200+次演練,依賴的介面涉及3000+個,我們在去年五一前做的依賴演練發現了136個問題,且問題型別各不一致,如程式碼編寫將一個弱依賴寫成了一個強依賴,以及配置問題、容量問題等。

4、平臺架構

關機演練、應用演練與依賴演練做完之後,我們平臺架構如上圖。

  • 下面的資源層我們支援三種形態:實體機、虛擬機器、容器,不同的操作有不同的手段。

  • 演練系統裡我們也有較為豐富的功能:演練生成、演練編排,以及對應的執行、標註等。

  • 上面是應用管理平臺和依賴資訊平臺,還有一些監控等APM之類的支援。

三、大規模自動演練

雖然我們已經具備了大規模自動演練對應的能力,但是當我們想要做到非常大覆蓋面的演練時,我們需要思考一個問題: 常態化演練的成本收益比是否合理?

如果只是節假日演練或者一年演練一兩次,那麼就需要權衡投入。當我們常態化演練時,不可避免地要考慮人工成本,正如我前面提到的演練次數裡,都需要人工進行一定的參與,那麼就會耗費一些人工成本。當我們常態化之後就要考慮自動化,能做到自動化就能替換人工成本。

此外, 質量類工作的價效比天然要求更高 ,因為質量類工作不像創造性工作,防禦型工作要求覆蓋面全,如果在每個點上的價效比不夠高,那麼全域性損耗就會非常大,這對我們來說是一個非常好的指導。

自動化要達到一個目標,就是持續可靠,我們要使其常態化地執行和自動地演練,主要涉及到兩個點:

  • 一是我們希望它常用常新,減少人工成本,並且把覆蓋面提升到最大。

其中的關鍵點在於它自動化執行的流量和斷言。人工參與的目的就是斷言最後結果和制定具體的計劃。

  • 二是可用的環境。

我們將週期性自動演練的機制分成兩類:

  • 增量演練 ,每天把新增的依賴全部演練一遍。

  • 全量演練 ,設定週期重複執行。

因為我們所有的程式碼和架構都在腐化的過程中,中間會有很多變更,比如呼叫點的程式碼變更,則有可能導致強弱依賴的關係被轉化,因此我們必須週期性地進行驗證。

1、自動演練流程

我們自動演練的流程如上圖。

第一步是做控制面,可以獲取到應用的資訊。

第二步是對其進行故障的注入。

第三步是觸發自動化測試,從而產生兩份流量,這兩份流量會打向我們的基準環境和對應測試環境,測試環境的程式碼配置等與基準環境是相同的,然後自動化平臺會對兩者返回的結果做一個斷言,進而得到一個結論:當我注入這個問題時,流量是否依然正常。自動化檢測完結果之後就能產生依賴關係的判斷。如果注入了故障從而產生了問題,那就意味著它是個強依賴。

通過自動演練流程,引入了自動化測試平臺之後,我們就能做到不需要人工觸發流量和判斷。

2、演練結論

從效果資料上看,不符合開發預期的依賴非常多,遠超大家的想象,在抽樣中達到了73%,而符合預期的只佔了27%。主要問題有不合理的強依賴、單應用演練口徑過嚴、自動化測試平臺覆蓋不足、無法完全線上化等。

如果是單應用通過介面自動化平臺做斷言,可能會產生一個問題,就是結論可能過嚴。比如A依賴了B再依賴C,對於 B調C,可能是一個強依賴,但它並不會傳導到使用者端,對使用者側可能是沒有影響的,只是一個區域性的強依賴,那麼就會放大這個問題。

因此我們考慮是否可以全鏈路進行演練,經過試驗是可行的。

3、全鏈路演練

從資訊來看,一個功能入口對應後面的一個過程,過程裡的強弱依賴關係是非常複雜的,可能有一條完整的強依賴,也可能有中間部分的強依賴,或者一些是純弱依賴,這些關係決定了我們的方案。

與剛才說的單應用斷言不同, 做全鏈路斷言需要引入一個全鏈路壓測的演練系統。 我們內部有一個全鏈路壓測平臺,能夠做到用例自動生成、資料隔離,且執行成本非常低,比如執行完一個機票業務線所有核心場景可能只需要半天,相當於我們只需要0.5pd的人力就能覆蓋上千個應用。

從斷言來看, 我們有一個斷言的邏輯: 當入口產生呼叫流量時,我們就會對它進行一個斷言,觀測我們的核心指標尤其是使用者側的核心指標是否存在問題,如果存在問題,我們則會中斷演練,並且記錄對應的結果;如果不產生對應的報警,我們還會從另外一個角度看是否對C端的功能有其它影響。我們會對人工主動標註的告警事件和系統自動分析的雷達事件進行統一,進而得到一個結論,就是當全鏈路注入問題時,對於入口來說是一個強依賴還是一個弱依賴,這是斷言的邏輯。

從流程來看,與剛才的自動演練流程有點相似,只是把介面自動化的測試平臺轉化為全鏈路壓測平臺,但是它的斷言與自動化的斷言邏輯不同。

需要注意的是, 我們需要對鏈路上的命中率做一定的優化。

命中率是指從入口發下去的流量是否一定會經過我們想測試的依賴,比如我想測e系統打到g系統的呼叫,入口的流量發出來不一定都能達到e的依賴鏈路裡,它打到了b系統,再打到e系統,再打到了f系統,這就需要一個非常有效的機制提前發現,演練的流量與我們預期的依賴是否達到。這需要依賴到其它大量的資訊,如果我們沒用精準的方式,而是隨機地挑選入口流量,那麼鏈路上的命中率只有40%左右,我們希望能夠將命中率提到90%以上。

我們針對命中率做了一個精準的策略: 利用依賴關係已經產生的實際資料,即線上APM的trace資料反查能夠經過鏈路的請求條件和流量,比如當我們想要找e和f的呼叫鏈路時,可以從b系統入口打到e系統再打到f系統,這就是我們想要找的其中一個鏈路,再逆向給出一個結論,從而得到對應trace的入口資訊,再得到入口的請求條件資訊,通過請求條件做用例的自動構造,就能得到一部分非常有效的用例,從而把我們的命中率提升到了90%以上。

全鏈路自動演練的效果 來看,目前覆蓋了去哪兒網的55個核心入口和80%以上的核心應用,僅剩最後的人工成本,只要對彙總的報告進行最終的分析,產生一個節假日混沌工程的報告即可。

四、故障注入攻防演練

第四個階段是故障注入攻防演練。攻防演練的主要目的在於提升人面向失效條件時的處理速度,包括預案和問題。

  • 首先通過各個系統的開發處理失效條件很難積累經驗。

因為一個開發真正能遇到線上故障的概率並不高,公司成百上千人的研發團隊會對故障進行稀釋,因此人為的經驗很難積累,那麼我們只能提前創造機會,當他真正面臨線上問題時才能給出解決方案。

  • 二是故障原因種類繁多。

  • 三是沒有預案。

  • 四是恢復和驗證比較困難。

2020年,我們故障處理時間的中位數是54分鐘和39分鐘,2021年也是51分鐘,因此在線上的真實故障裡處理的時間還是比較長的。

1、攻防演練流程

我們設計了一個攻防演練的流程:

流程依賴於大量的基礎設施系統,包含故障平臺、混沌工程平臺、監控告警平臺、日誌平臺、trace平臺等。首先做攻擊點的規劃,彙總歷史故障原因,優先把出現次數多的原因規劃為對應的攻擊點;其次故障隨機注入,注入點與時間隨機,不同的介面依賴不提前告知開發;然後監控告警觸發,防守方排查問題,需要上報對應的結論、耗時等排查結果;最後對其進行一定的計分公式,覆盤分析。

故障注入流程與全鏈路依賴流程相似,只需要把流量真正注入即可,不需要斷言它的強弱依賴。

從積分例子可以看出,大部分問題1~3分鐘即可解決,通過這個方式演練 能夠有效提升排查和處理問題的速度

2、攻防演練的關鍵點

1)培養混沌文化

混沌工程要做對應的宣導,開發對於攻防演練是非常關心的。

2)時間和策略隨機

3)抹除資訊干擾

比如不能在異常棧裡暴露注入的資訊,不能出現chaosblade等對應的關鍵字,以及一些流量標識也需要抹除。

Q&A

Q1:如何確定應用級災備的演練和驗證範圍?

A1: 應用級的災備需要多可用區,確保每個應用不能只部署在一個可用區,在資訊聚合時可以提前發現並改造。改造後真實演練就是分享中提到的機房大規模關機演練,從雲平臺元資訊中能夠方便地查詢出某業務線所有應用例項,然後進行真實關機演練。觀測對應的核心業務指標,如果業務正常,說明災備、容量的情況滿足。 對於驗證範圍,單應用的演練只需要在當前應用介面級別測試即可,方式可以多種,比如介面自動化測試、人工觸發用例測試。

Q2:越底層的故障影響越大,請問您有沒有用混沌工程做過底層的網路故障?

A2: 底層網路故障我們主要通過大規模關機演練替代,因為在交換機和機器上的網路通訊失敗並不是很可怕的事情,在問題點上恢復後影響基本也就自動恢復了。如果實在有必要演練網路問題,可以進行交換機拔線測試。

Q3:混沌工程在推進過程中,如何衡量業務層面的需求?

A3: 業務層面需要區分不同角色:對於業務負責人來說,混沌工程的結果需要利於業務,比如保證使用者體驗、保障業務平穩執行;對於技術負責人來說,混沌工程能夠提升人員處理線上問題的速度;對於穩定性保障人員,例如QA,混沌工程能夠讓不確定性轉化為高確定性。此外,如果問題中的需求是指混沌工程推進跟業務專案有人力衝突,可以參考分享中的實踐路徑,機房關機演練、應用演練、依賴演練其實並不太多的人力資源,攻防演練則需要給業務需求專案讓步。

Q4:關鍵系統的混沌工程實驗思路有何不同?

A4: 在我們實踐過程中,關鍵系統並沒有特殊對待,一切都要追求高效和高覆蓋。對於關鍵系統也是真實關機、真實注入、真實線上環境。