面对大规模系统工程,看Facebook如何处理故障排查(一)
作者介紹:Ben Maurer是Facebook的網絡基礎團隊的技術領先者,主要負責整個Facebook面向用戶產品的性能和可靠性。Ben于2010年正式加入Facebook,基礎設施團隊的成員。在加入Facebook之前,他與Luis von Ahn共同創立的驗證碼。最近,本與美國數字服務公司合作,以改進在聯邦政府的技術使用。
數人云今天為大家帶來一篇Ben Maurer分享的“Facebook面對大規模系統工程故障排查實踐”,由于內容較多,所以數人云今天只為大家帶來上半部分,后續內容會在明天發布!
故障是任何大規模工程系統的一部分。Facebook的文化價值之一就是擁抱失敗。這可以從掛在門洛帕克總部墻上的海報上得到體現:“如果你無所畏懼,你會怎樣?”“天佑勇者。”
為了使Facebook的系統在快速變化的情況下保持可靠,專門為其研究了常見的故障模式,并建立抽象理念來解決這些問題。這些理念確保最佳實踐應用于的整個基礎設施。通過建立工具來診斷問題,并創建一種復盤事故的文化來推動并作出改進,防止未來發生故障。
為什么會發生故障?
雖然每一個故障都有一個獨特的故事,但是多數故障都可以歸結為少數的原因。
個別機器故障
單個機器通常會遇到一個孤立的故障,不會影響基礎設施的其余部分。例如,可能一臺機器的硬盤驅動器發生了故障,或者某臺機器上的服務遇到了代碼中的錯誤,內存損壞或等。
避免單個機器故障的關鍵是自動化,自動化工作最好結合已知的故障模式(如硬盤驅動器的S.M.A.R.T.錯誤)與未知問題的搜索(例如,通過交換服務器異常緩慢的響應時間)。當自動化發現一個未知問題,手工調查可以幫助開發更好的工具來檢測和修復問題。
合理工作負荷的變化
遇到突發狀況,Facebook會改變日常的行為習慣,為基礎設施帶來挑戰。例如,在重要的全球事件中,獨特的工作負載可能會以不尋常的方式來考驗其中的基礎設施。當奧巴馬贏得2008美國總統大選時,Facebook頁面活躍度刷新了記錄。如超級杯或者世界杯這樣重大的體育賽事也會引發其發帖數量大大增加。負載測試,包括“灰度發布”即有新功能發布,但是對于使用者不可見,有助于確保新功能能夠處理負載。
在這些事件中收集的統計數據常常為系統的設計提供一個獨特的視角。通常情況下,重大事件導致用戶行為的變化(例如,通過圍繞一個特定的對象創建主題活動)。有關這些更改的數據通常指向設計決策,以便在后續事件中允許更平滑的操作。
人為失誤
鑒于Facebook鼓勵工程師“快速行動,打破常規”-如同裝飾辦公室的另一個海報所示,也許有人會認為,很多錯誤都是人為造成的。根據數據表明,人為失誤是失敗的一個因素。圖1涵蓋了嚴重到足以被認為違反了SLA(服務水平協議)的事件的時間節點數據。由于目標是很嚴格的,所以對網站用戶而言大多數事件是輕微的,不明顯的。圖1a顯示事件在星期六和星期日發生的概率大幅減少,然而也不會影響網站流量。圖1b顯示6個月的時間只有兩周沒有事件:包括圣誕節的一周和員工寫互評的一周。
這兩個數據似乎表明,當Facebook的員工因為忙于其它事情(如周末、節假日以及員工考核等)而沒有積極去改變基礎設施的時候,網站的可靠性反而處于一個比較高的水平。導致我們相信這不是因為Facebook員工過于粗心,而是證明了基礎設施在很大程度上是對非人為的錯誤進行自我修復,如機器故障。
三種容易導致事故的原因
雖然事故有不同的產生原因,但是通過總結發現,有三種常見的原因會使故障擴大并成為大規模的問題。對于每一個成因,都應制定相應的預防措施,以減輕大規模事故。
快速部署配置更改
配置系統往往被設計為能在全球范圍內迅速復制更改。Rapid配置更改是一個功能強大的工具,可以讓工程師快速管理新產品的推出或調整設置。然而,快速配置也意味著當配置不當時會快速引發故障。我們采取了一些方法來防止配置更改導致故障。
讓每個人都使用一個通用的配置系統
使用通用配置系統可以確保程序和工具適用于所有類型的配置。在Facebook,我們發現團隊有時會試圖以一次性的方式來進行配置。避免使用這種方式而采用一種統一的方式來進行配置,從而使配置系統成為一種提高站點可靠性的衡量方法。
靜態驗證配置更改
許多配置系統允許松散類型的配置,如JSON結構。這些類型的配置很容易使工程師犯一些低級錯誤,例如敲錯字段,如果這個字段是必須使用整數的字符串。對于這種類型的錯誤最好的辦法就是使用靜態驗證。一個結構化的格式(例如,在Facebook使用的Thrift)可以提供最基本的驗證。然而,編寫驗證程序來驗證更詳細的要求也是合理的。
運行一個Canary
首先將配置部署到服務的小范圍,可以防止災難性的更改。一個Canary可以采取多種形式。最明顯的是A / B測試,如只對百分之一的用戶推出一個新的配置。多個A / B測試可以同時運行,并且可以使用數據隨時間進度來跟蹤度量。
然而,對于可靠性的目的,A / B測試不滿足我們的所有需求。一個更改部署給少數用戶,但導致了服務器崩潰或內存耗盡的變化,顯然會產生超出測試的有限用戶的影響。A / B測試也費時。工程師們常常希望在沒有使用A / B測試的情況下推出一些微小的變化。為此,Facebook基礎設施自動測試新配置的一小部分服務器。例如,如果我們希望部署一個新的A / B測試給百分之一的用戶,首先部署測試在僅影響很少量服務器的那部分用戶,在一個很短的時間內監測這些服務器,以確保他們不會崩潰或有其他很明顯的問題。這種機制提供了一個適用于所有變更的基本的“健全檢查”以確保它們不會造成大面積的故障。
保持良好的配置
Facebook的配置系統的設計是盡量確保當更新帶來故障時保持良好的配置。開發人員希望創建的配置系統當接收到無效的更新配置時會崩潰。喜歡在這些類型的情況下保留舊的配置,并向系統操作員發出警報,說明該配置無法更新。繼續運行舊有的配置通常優于將錯誤返回給用戶。
使它容易恢復
有時,盡管盡了最大努力,部署的配置依然有問題,快速查找和恢復是解決這類問題的關鍵,配置系統是由版本控制,這使得系統很容易恢復。
核心服務的硬依賴
開發者通常默認配置管理,服務發現,存儲系統等核心業務永遠不會發生故障。可是,這些核心業務的輕微故障都會引起大面積的事故發生。
核心服務的緩存數據
依賴于這些類型的服務通常是不必要的,可以通過緩存數據的方式,以此保證其中一個系統短暫性中斷,而其它服務依舊繼續運行。
提供硬化的API使用核心服務
核心服務是最好的補充公共庫,遵循最佳實踐來使用這些核心服務。例如,庫可以提供良好的api來管理緩存或處理故障。
運行的消防演習
你可能認為能夠在核心服務中斷中生存下來,在嘗試之前,你永遠不會知道。對于這些類型的中斷,我們不得不開發系統的消防演習,從故障注入系統應用到單個服務器中,以此手動觸發整個數據中心的中斷。
增加延遲和資源耗盡
一些故障導致服務的延遲增加到客戶端。這種增加的延遲可能很少(例如,考慮到一個人的配置錯誤,但是依舊服務的能力導致CPU使用量增加),還有就是,它可能是無限的(一個服務線程服務響應陷入癱瘓)。而少量的延遲可以很容易地解決由Facebook的基礎設施、大量的延遲會導致全面故障。幾乎所有的服務對未完成請求的數量都有限制,這個限制可能是由于每個請求服務線程數量有限,也可能是由于基于故障服務中的內存有限。如果一個服務面臨大量的延遲,那么調用它的服務將耗盡他們的資源。這種故障會通過許多層面進入系統服務中,導致系統故障的發生。
資源枯竭是一個極具破壞性的故障模式,由于它允許服務請求的子集用于導致失敗的所有請求失敗。例如,一個服務調用只推出 1%的用戶對新的實驗服務,通常要求這個實驗服務需要1毫秒,但由于在新的服務失敗的請求需要1秒,所以1%的用戶使用這項新服務請求可能會消耗太多的線程,其他99%用戶就不能運行此線程。
如今,我們已經發現了一些技術,可以避免這種類型的積累與較低的誤報率。
控制延遲
在分析以往的事故延遲中,我們發現許多最糟糕的故障涉及大量隊列等待處理的請求。有問題的服務有一個資源限制(如活動線程或內存的數量)和將緩沖請求以保持低于限制使用的請求。由于服務無法跟上傳入請求的速度,隊列會變得越來越大,直到它突破了應用程序定義的限制。為了解決這種情況,我們希望在不影響正常操作和保證可靠性的情況下,來限制隊列的大小。我們研究了一個很相似的bufferbloat,在保證可靠性的同時,使用隊列從而不會造成過度延遲。嘗試了一種codel1(延時)控制算法:
onNewRequest(req, queue):
if(queue.lastEmptyTime() < (now - N seconds)) {
timeout = M ms} else {
timeout = N seconds;}
queue.enqueue(req, timeout)
在該算法中,如果服務不能在最后N毫秒內清空隊列,則隊列中花費的時間僅限于M毫秒。如果服務能夠在最后N毫秒內完成清空隊列,則隊列中所花費的時間僅限于N毫秒。該算法避免站在隊列(由于lastEmptyTime將在遙遠的過去,導致anM-ms排隊超時),一次達到短時間的排隊對于可靠性的目的。雖然它似乎有悖常理,請求時間較短,這個過程允許的迅速丟棄服務,而不是建立在系統無法跟上傳入請求的服務。短的超時可確保服務器總是處在工作的狀態,而不是空閑。
該算法的一個吸引人的特性是,M和N的值往往不需要調整。解決排隊問題的其他方法,如在隊列中設置項目的數量或設置隊列的超時時間的限制,需要在每個服務基礎上進行調整。我們已經發現,M和100毫秒的值是5毫秒,它可以很好的用于N中。Facebook的開源碼library5提供的算法是由thrift4框架實現。
自適應后進先出(后進先出)
大多數服務進程隊列FIFO(先進先出)。當處于高額度處理進程中時,先進命令明顯已經運行了很長時間,以至于用戶可能已經中止了生成請求的操作。當處理先進申請命令時,相比之下這種剛剛抵達的請求命令,首先會消耗少許可能益于用戶的請求命令,服務進程請求程序使用的是應后進先出的方式。在正常工作條件下,要求按照先進先出的順序進行處理,但是當一個隊列正要開始成形時,服務器會切換為LIFO模式,這時,LIFO和CoDel就可以很好的結合在一起,如圖2所示。CoDel超時設置時,阻止長的計算機程序隊列增長,然后具有適應性的先出后進命令在計算機程序隊列設置新的請求模式,然后在數字信號編碼器的作用下,他們兩個能夠發揮最大化的作用。HHVM3,Facebook的PHP運行時,自適應后進先出法的算法得以實現。
并發控制
無論是編碼和自適應的后進先出法都在服務器端運行。服務器通常是執行延遲的最好的措施——服務器更傾向于大量的客戶同時能夠擁有更多的客戶信息。然而,有些故障是如此嚴重,以至于服務器端控件無法啟動。為此,我們在客戶端實施一種權宜之計。每個客戶端會跟蹤每個服務器所未完成的出站請求數量。當發送新請求時,如果對該服務的未執行請求的數目超過可配置的數字,則該請求將立即標記為錯誤。這種機制可防止單個服務壟斷其客戶端的資源。
以上內容是數人云今天為大家帶來的“Facebook面對大規模系統工程故障排查實踐”的上半部分,其中主要涵蓋導致故障的原因、以及可以使用一個通用的系統等相關內容,希望可以對大家有所幫助~明天還會為大家帶來最終的解決方案喲,敬請期待~
作者:Ben Maurer
原文:Fail at Scale Reliability in the face of rapid change
http://queue.acm.org/detail.c...
總結
以上是生活随笔為你收集整理的面对大规模系统工程,看Facebook如何处理故障排查(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SCU 4438 Censor
- 下一篇: 93. Restore IP Addre