分佈式系統中,級聯故障是最可怕的

互聯網服務提供商面臨快速增長挑戰的同時還要管理不斷增長的系統分佈。儘管服務的可靠運行對像 Google、Amazon 和 Co. 等這樣的大公司來說非常重要,但它們的系統還是會一次又一次地出現故障,導致大量中斷和帶來糟糕的客戶體驗。

舉幾個例子,比如深受影響的 Gmail(2012)[1]、AWS DynamoDB(2015)[2] 以及最近的 Facebook(2021)[3]。在這種情況下,人們經常會遇到所謂的級聯故障,導致超出普通系統故障的不良併發症。但是,考慮到他們的預算和技術知識,即使是在線業務的大玩家,爲什麼也不能完全避免這種故障呢?你可以爲自己的系統使用哪些切實可行的風險緩解方法?

這篇文章是想帶你瞭解如何通過防止故障傳播來提高大型分佈式系統的彈性。

—__1 

級聯故障

級聯故障是由於正反饋循環而隨時間增加的故障。典型行爲最初由單個節點或子系統故障觸發。然後它會將負載分散到其他系統的節點上,這反過來又進一步增加系統故障的可能性,從而導致惡性循環或滾雪球效應。

級聯故障的重要性體現在三個方面:首先,它們可以在短時間內導致整個服務停機。其次,受影響的系統不會像處理常見的問題那樣恢復正常,而是會逐漸惡化。最終要依賴人爲干預纔行。最後,在最壞的情況下,級聯故障可能會在沒有警告的情況下突然發生,因爲負載分佈和故障會迅速發生。

這篇文章主要關注點是分佈式計算環境中的級聯故障,但它們也可能發生在其他各種領域,例如,電力傳輸、金融、生物學以及生態系統。因此,它們是一種相當普遍的現象,與自然界中發現的模式有些相似。爲了更好地瞭解計算機科學中的級聯故障是什麼樣的,讓我們看一個具體的案例。

—__2 

案例研究:2015 年的 AWS DynamoDB 中斷

AWS DynamoDB 是一種高可擴展的非關係數據庫服務,分佈在多個數據中心,提供高度一致的讀取操作和 ACID 事務。它被 Netflix、Airbnb 和 IMDb 等多個知名的互聯網公司所使用。

我們要研究的級聯故障示例的事件發生在 2015 年 9 月 20 日,當時 DynamoDB 在美國東部地區超過四個小時不可用。涉及兩個子系統:存儲服務器和元數據服務,兩者都在多個數據中心有副本。存儲服務器向元數據服務請求其數據分區分配的所謂成員資格。如圖 1 所示。

圖 1:存儲服務器和元數據服務

對於成員資格(也用於數據分區的分配)的請求,存在相應的超時時間。如果超時了,則相應的存儲服務器會重試並將其自身排除在服務之外。

該事件的一個不幸的先決條件是 DynamoDB 引入的一個新特性,稱爲全球二級索引(GSI)。這使客戶可以更好地訪問他們的數據,但缺點是會顯著增加元數據表的大小。因此,導致處理時間變得很長。同時不幸的是元數據服務的容量和成員請求的超時時間沒有做出相應的調整。

真正的問題是由於一個短暫的網絡問題導致一些存儲服務器(處理非常大的元數據表)成員資格請求超時,這些服務器變得不可用並且還不斷的重試它們的請求。

這就導致元數據服務超負荷運轉,進而減慢響應速度並導致更多服務器重新提交其成員資格請求,因爲它們也達到了超時時間。結果,元數據服務的狀態進一步惡化。儘管多次嘗試增加資源,系統仍然陷入故障循環數小時。最終,問題只能通過中斷對元數據服務的請求來解決,即服務基本上離線。

結果是美國東部地區發生了廣泛的 DynamoDB 中斷,這是一個典型的級聯故障例子。但是,陷入這種錯誤循環的系統的底層概念和模式是什麼?

—__2 

級聯故障的原因

首先,要說的是級聯故障的觸發點看起來是多種多樣的。例如,可能是新推出的特性、維護、流量流失、cron 作業、分佈式拒絕服務(DDoS)、限流等。它們的共同點是它們在一組有限資源的上下文中工作,這意味着可能會出現服務器過載、資源耗盡和服務不可用等影響 。讓我們詳細看看這些:

服務器過載

最常見的原因是服務器過載。發生這種情況時,系統性能下降通常會影響系統的其他區域。如圖 2 所示,在初始場景(左)中,來自兩個反向代理的負載分佈在集羣 A 和 B 之間,因此集羣 A 以每秒 1000 個請求的假設最大容量運行。

在第二種情況(右)中,集羣 B 發生故障,整個負載都到達集羣 A,這就會導致集羣 A 過載。集羣 A 現在必須每秒處理 1200 個請求並開始出現異常行爲,導致性能遠遠低於所預期的每秒 1000 個請求。

圖 2:集羣 A 和 B 根據容量(左)接收負載,如果集羣 B 發生故障,集羣 A 接收過載(右)

資源耗盡

服務器的資源是有限的。如果負載增加到某個閾值以上,服務器的性能指標(例如,延遲或錯誤率)就會惡化,這意味着更高的崩潰風險。隨後的影響取決於導致瓶頸的資源類型,例如:

在這種情況下對主要原因進行故障排除通常很痛苦。這是因爲所涉及的組件是相互依賴的,並且根本原因可能隱藏在複雜的事件鏈之後。例如,假設可用於緩存的內存較少,導致緩存命中次數減少,因此後端負載較高,以及此類組合。

服務不可用

當資源耗盡導致服務器崩潰時,流量會轉到其他服務器,從而增加這些服務器崩潰的可能性。這樣一個服務器的崩潰循環就建立了。更壞的情況是這些問題會一直保持在系統中,因爲某些機器仍然處於關閉狀態或正在重新啓動的過程中,而持續增加的流量會阻止它們完全恢復。

一般來說,當我們將流量從不健康節點重新分配到健康節點時,總是存在級聯故障的風險。這可能是編排系統、負載均衡器或任務調度系統的情況。爲了解決級聯故障,我們需要仔細研究所涉及的組件之間的關係。

—__3 

跳出循環——如何修復級聯故障

從 DynamoDB 的案例中可以看出,修復級聯故障非常棘手。尤其是從大型科技公司的角度來看,分佈式系統增加了很多複雜性,這使得跟蹤各種互連變得更加困難。

我們這裏使用一種被稱爲因果循環圖(CLD)的方法來描述這些(級聯)關係。CLD 是一種建模方法,有助於可視化複雜系統中的反饋迴路。圖 3 可視化了 AWS DynamoDB 中斷的 CLD。

解釋如下:箭頭表示初始變量和後續變量之間的動態。例如,如果元數據服務的延遲增加,超時次數就會增加,所需的重試次數也會增加。如果系統中的影響是高度不平衡的,即正負的數量在很大程度上不相等,則存在一個加強循環。這意味着系統可能對級聯故障很敏感。

圖 3:2015 年 AWS DynamoDB 中斷的因果循環圖

現在,針對級聯故障場景,我們有好多種措施可以採用。第一個也是最直觀的選擇是增加資源。在上圖中,可以看到在循環中元數據服務容量引入了減號。如果增加,它會減弱循環的增強,不過,這可能沒有用,正如我們在 AWS 中看到的那樣。除了增加資源外,還可以採用其他策略:

這個可能會讓系統的某些服務不可用並且客戶是能夠感知到的,因此最好首先避免級聯故障。

—__4 

避免級聯故障

有許多方法可以使分佈式系統對級聯故障具有魯棒性。

一方面,大型互聯網公司已經在思考如何防止系統陷入級聯錯誤,比如通過對錯誤進行隔離,爲此市面上已經開發出來許多工具和框架。例如,Hystrix(來自 Netflix),一個延遲和容錯庫,或者 Sentinel。對於前者,Netflix 已經做出了進一步的發展,即自適應併發限制(可以在此處閱讀更多內容 [4])。但總的來說,這些工具都是將外部調用包裝成某種數據結構,試圖抽象出關鍵點。

另一方面,就是目前正在發展的技術,有一些複雜的解決方案,例如,實現所謂的 sidecar 代理,諸如 Istio 這樣的服務網格。其他的一些示例比如 Envoy 或 Haproxy。

除了這些解決方案之外,我們還要牢記某些系統設計概念。例如,嘗試減少系統中同步調用的數量。通過應用發佈 - 訂閱模式設計(比如使用 Kafka)從編排(orchestration)模式轉變爲協調(choreography)模式。面對不斷增加的流量,這種解決方案通常會更健壯。其他方法例如,執行容量規劃(取決於用例)也可能有所幫助。這通常意味着實施自動供應和部署、自動擴展和自動修復的解決方案。在這種情況下,對 SLA 和 SLO 的密切監控就顯得很重要。

現在,爲了更好地理解底層解決方案的方法,我們可以看看分佈式系統中的典型反模式,在級聯故障的情況下應該避免這些反模式。Laura Nolan 提出了其中的六項,我們會就風險緩解策略方面進行討論。

反模式 1:接受數量不受限制的請求

隊列 / 線程池中的任務數量應該是受限的。這可以在請求過多的情況下控制服務器何時以及如何慢下來(slow down)。該設置應該在服務器可以達到峯值負載的範圍內,但不要太多從而導致它阻塞。在這種情況下,對於系統和用戶來說,快速失敗總比長時間掛起要好。在代理或負載均衡器方面,通常是通過速率限制策略來實現,例如,用來避免 DDoS 和其他形式的服務器過載。

但是還有其他許多方面要考慮的,例如,在隊列管理的上下文中,因爲大多數服務器在線程池前面都有一個隊列來處理請求。如果數量增加超過隊列的容量,請求將被拒絕。隊列中等待的大量請求會佔用更多內存並增加延遲。如果請求的數量接近恆定,那麼一個小隊列或不需要隊列就可以了。這意味着如果流量增加,請求會被立即拒絕。如果預期會有更大的偏差,則應使用更長的隊列。

此外,爲了保護服務器免受過度負載的影響,減載和優雅降級的概念是可行的選擇。負載脫落用於在過載的情況下儘可能地保持服務器的性能。這是通過簡單地返回 HTTP 503(服務不可用)狀態碼來確定請求優先級的方法丟棄流量來實現的。

一個更復雜的變體是優雅降級,它會逐漸切換到較低質量的查詢響應。這些可能會運行得更快或更有效。但是,這一定是一個經過深思熟慮的解決方案,因爲它會給系統增加很多複雜性。

反模式 2:危險的(客戶端)重試行爲

爲了減少系統的工作量,確保避免過度的重試行爲是很重要的。指數退避是一種合適的方法,它的做法是重試的時間間隔連續增加。還可以使用所謂的抖動(jitter)機制,即在重試間隔中添加隨機噪聲。這可以防止系統被累積的 “負載波” 擊中,這也稱爲重試放大(參見圖 4)。

圖 4:重試放大的典型模式

此外,還有一種稱爲熔斷器的設計模式。熔斷器可以被認爲是一種開關。在初始狀態下,來自上游服務的命令被允許傳遞給下游服務。如果錯誤增加,熔斷器會切換到打開狀態,系統會快速出現故障。這意味着上游服務出錯,允許下游服務恢復。一段時間後,請求再次逐漸增加。例如,在 Hystrix(上面已經提到)中,實現了某種熔斷器模式。

減輕危險重試行爲的另一種方法是設置服務器端重試預算,設置每分鐘可以重試請求的數量。超出預算的所有內容都將被丟棄。但是,我們要綜合全局來看。一定要避免在軟件架構的多個級別上執行重試,因爲這可能會呈指數級增長。

最後,需要注意的是,重試應該是冪等的並且沒有副作用。無狀態調用 在系統複雜性方面也是有益的。

反模式 3:因輸入錯誤而崩潰

系統應確保服務器不會因輸入錯誤而崩潰。此類崩潰與重試行爲相結合,可能導致災難性後果,例如,一臺服務器接着一臺相繼崩潰。在這方面,尤其應仔細檢查來自外部的輸入。使用模糊測試是檢測這些類型問題的好方法。

反模式 4:基於鄰近的故障轉移

確保不要把所有流量都重定向到最近的數據中心,因爲它也可能會過載。此處適用的邏輯與集羣中單個服務器的故障相同,也就是一臺機器接着一臺發生故障。

因此,爲了提高系統的彈性,必須在故障轉移期間以受控方式重定向負載,這意味着必須考慮每個數據中心的最大容量。基於 IP-Anycast 的 DNS 方式最終會將流量轉發到最近的數據中心,這可能會出現問題。

反模式 5:失敗引起的工作

故障通常給系統帶來額外的工作。特別是,故障發生在少數幾個節點上,最終可能會給剩餘其他節點帶來大量的額外工作(例如,副本)。這可能會帶來有害的反饋循環。一種常見的緩解策略是延遲或限制副本數量。

反模式 6:啓動時間長

一般而言,在開始時處理過程通常較慢。這是因爲實例需要做初始化過程和運行時優化。故障轉移後,服務和系統經常由於負載過重而崩潰。爲了防止這種情況,我們希望系統可以更快的啓動。

此外,緩存在系統啓動時通常是空的。這使得查詢變得更加昂貴,因爲它們必須去原始地方拿數據。因此,崩潰的風險高於系統在穩定模式下運行時的風險,因此請確保保持緩存可用。

除了這六個反模式之外,還有其他系統組件或參數需要檢查。

例如,可以查看請求或 RPC 調用的截止日期(deadline)。一般來說,很難設定好的截止日期。但是在級聯故障的情況下,經常遇到的一個常見問題是客戶端超過了許多設定的截止日期,這意味着資源的大量浪費。

AWS DynamoDB 示例從一開始也是這種情況。通常情況下服務器應該檢查請求離截止日期是否還有時間剩餘,從而可以避免工作的浪費。一種常見的策略是所謂的期限傳播。也就是請求樹的頂部有一個絕對的截止日期。再往下的服務器只得到前一個服務器完成計算後剩下的時間值。例如,服務器 A 的期限爲 20 秒,計算需要 5 秒,那麼服務器 B 的期限爲 15 秒,依此類推。

—__5 

結論

級聯故障是分佈式系統中一種即可怕又特殊的現象。這是因爲有時必須採取違反直覺的路徑來避免它們,例如實際上旨在減少錯誤的定製化工作,比如看似智能的負載平衡,可能會增加完全失敗的風險。

有時,最好的策略就是向客戶顯示一條錯誤消息,而不是實施複雜的重試邏輯並冒着 DDoS 攻擊系統的風險。但是,有時候又不得不做出妥協。測試、容量規劃和在系統設計中應用某些模式有助於提高系統的彈性。

畢竟,大型科技公司的經驗教訓和事後分析爲進一步採取行動以避免未來出現級聯故障提供了很好的指導。但是,最新技術和趨勢也值得關注。

相關鏈接:

  1. http://static.googleusercontent.com/media/www.google.com/en/us/appsstatus/dashboard/ir/plibxfjh8whr44h.pdf

  2. https://www.sentinelone.com/blog/irreversible-failures-lessons-from-the-dynamodb-outage-2/

  3. https://www.theguardian.com/technology/live/2021/oct/04/facebook-down-instagram-whatsapp-not

  4. https://netflixtechblog.medium.com/performance-under-load-3e6fa9a60581

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/J-fQ0X4uwDL6TLNyED7dRQ