從github超24小時的故障看異地多活全域性設計的重要性

銀河1號發表於2018-11-16

我們先來回顧一下github這次事故: 2018年10月21日,github 在更換網路裝置時,引發了美國東海岸網路中心和東海岸資料中心的網路連結發生了40秒的中斷,最終導致多個mysql的主叢集由Orchestrator 自動選舉切換到了美國西海岸資料中心對應的叢集,由此引發了資料不一致,直接導致了超過24小時的服務降級,最後仍然還有少量資料需人工排查修復的悲劇。

事件的整個過程參考blog.github.com/2018-10-30-… 這篇文章,在這裡不詳細說明,我們來直接看導致事件的根本原因。

根本原因:缺失異地資料中心之間資料一致性的保障機制

github資料庫做的是本地主備+跨區域資料中心的容災方案,主資料庫叢集和其他資料中心的叢集是非同步同步的,當物理網路發生中斷後,對於github這樣全球性的,大量使用者訪問的系統,時時刻刻都有資料變更,剛剛提交的資料還沒有來得急同步過去,這時按照Orchestrator的機制,必然會重新選舉出新的資料庫寫入主節點。在沒有確定資料全域性一致性的情況,就這麼一切,兩邊資料就沒法一致同步了,更悲劇的是等網路40s後恢復後,在Orchestrator沒有將原有的資料庫主節點置為從節點之前,東海岸本地的應用還在往本地資料庫叢集寫資料,加劇了資料的不一致,發生了腦裂現象。除了資料不一致的問題外,還有個比較嚴重的後果,github 除了資料庫主節點負責單點寫外,其他區域資料中心的資料庫節點負責讀,把東海岸的應用資料庫切都西海岸,會有60ms的網路消耗,就這點耗時,導致很多應用出現了服務超時不可用,對於很多web應用,一個前端請求會觸發後端系統幾十次的請求非常正常。由此可以看出,github在處理跨資料中心的高可用方案上是有較大漏洞,沒有真正考慮一個資料中心不可用,和資料中心之間的網路故障時的兩個重要場景。

沒有引起重視的原因分析

對於這麼重要的場景,為啥github這麼牛的公司沒有實施了?個人猜測的幾個原因(僅個人觀點):

1.github 發展太快也太順利了,之前應當是沒有出過什麼嚴重問題,精力都聚焦到能帶來效益的事情了,這樣的事情沒有引起足夠的重視,技術戰略上沒有特別重視。

2.異地多活的方案非常複雜,不僅是資料庫的跨地域資料中心同步這麼簡單,需要結合公司的實際業務進行全域性整體性的方案設計。

3.投入大,成本高,就拿異地機房之間的網路線路來說,至少需要保障兩個物理上獨立的線路,而這些線路對資料中心來說,至少需要100G 光纖專線。此外其應用系統為了適應異地多活帶來也需要進行架構升級,灰度測試等等。

實際來看這是很多公司都面臨的普遍問題,在業務高速發展和系統可靠性建設上如何找到平衡點的問題。對於很多業務導向的公司,系統出現過嚴重問題,才會重視起來。

異地多活方案如何來做?

計算機的分層思想非常有利於分析一個複雜系統,異地多活涉及到資料,應用系統,基礎軟體,網路,運維繫統等等,非常複雜。採用分層思想能極大的簡化問題,解耦各層,然後全域性看。下面是一個典型分散式是資料中心的分層結構:


從github超24小時的故障看異地多活全域性設計的重要性

讓我們來看每個分層的異地多活方案如何做:

DNS層:本身就是一個全球性的分散式系統,天然就是異地多活的,在這一層做自己的異地多活,更多是依賴DNS機制,系統化的動態切換域名和伺服器的對映。因其DNS變更生效需要幾分鐘到幾十分鐘,對實時性要求高的容災,不能依賴這塊。

CDN層:嚴格來說,和DNS 一樣,也是天然異地多活的,因為CDN更多的是對靜態資源訪問的加速,需提前將靜態資源分發到離消費者更近的節點,掛掉一個節點不會影響資料的不一致,會降低訪問速度,影響使用者體驗。

負載均衡層:通過故障轉移策略,可以迅速的將掛掉的伺服器流量切到同城或者異地的資料中心,非常便捷高效,但這個地方一定要確保底層的資料已經同步過去之後,才能切,否則會出現資料不一致的情況。

應用層:需要跨資料中心彈性叢集部署,就單個應用本身而言,沒有什麼難度,難度主要集中在依賴應用上,對於很多業務系統,特別是微服務架構成為主流後,一個業務系統至少會依賴呼叫好幾個其他應用,對於複雜的電商系統來說,一個系統依賴幾十個其他系統的服務很正常。應用層的異地雙活,就不是簡單的單應用的跨資料中心部署,而是需要按照業務單元整體性考慮,單元化的思想是阿里異地雙活的基礎。這塊需要重點考慮的是,業務單元內的系統之間相互呼叫要本地化呼叫,避免跨單元,但對於一些沒法單元化的業務,需要有中心單元化來統一支撐。此外應用系統本身的架構也需要考慮異地多活,特別是要考慮跨地域訪問帶來的時延影響,資料重複消費等問題。

中介軟體,快取層:需要中介軟體本身要支援異地多活,就拿分散式快取redis來說,put 資料,需要支援在本地資料中心,和異地資料中心同時生效,失效也是一樣。這就需要對redis 進行修改。訊息中介軟體,以及其他中介軟體也是類似,對於不支援異地資料中心的版本,都需自行修改原始碼進行支援,對於一般的小公司來說,這個難度非常大。只有top的公司才具備這樣的技術和環境去做這個事情。

資料庫/儲存:資料庫每個廠商對異地資料中心的支援方案有差異,就mysql來說,mysql 目前的版本還不支援異地同步的方案,只支援本地同步副本,對於mysql的同步,只能是利用實時的資料同步工具drc 來做,因為不是非同步同步,仍然存在幾ms-幾百ms的延遲。將流量從故障的資料中心切換到其他資料中心時,一定要先將故障叢集的資料庫設定成只讀叢集,其次確定資料庫已經完全同步資料到對應異地資料中心的資料庫。最後將異地資料庫叢集設定為寫叢集。


從github超24小時的故障看異地多活全域性設計的重要性

而整個過程會有幾分鐘的中斷,服務不可用,遇上罕見的這種故障,幾分鐘的故障恢復時間是可以接受的,支付寶的異地多活也是分鐘級別的。這個地方最怕的就是物理線路故障,如挖掘機挖斷光纖的事情,對於大型金融級的資料中心,物理上有兩條獨立的光纖線路是必須的。遇上物理線路故障,肯定會存在少量資料沒有同步完的情況,這時候,原本到故障dc的寫流量是不能貿然轉移到其他dc的,這時必然要犧牲部分服務的可用性;除非你能接受資料不一致的情況。

看完每層的情況,再來看全域性,當一個DC發生故障時,如何在保證資料完整的情況下,故障轉移了。發生故障不可怕,怕的是丟資料,資料錯亂。所有的策略都是圍繞資料的完整和正確性。全域性設計也是一樣,因此我認為較為合理故障處理策略應當按照下面的步驟來:


從github超24小時的故障看異地多活全域性設計的重要性

我們的原則就是將一切造成資料混亂的可能性降到最低,必然就是先關閉資料庫的寫,這會造成應用層寫資料庫等異常,但這也是我們期望的。

發生整個DC出現大面積故障的事件是比較少的,最常見的故障還是比如某個核心系統庫,部分網路故障. 而這些故障會根據異地多活的方案有不同的一些恢復策略,以資料庫發生異常為例,通常的恢復策略為:

1.切換本地備庫為主庫;從已有備份重新還原做備庫.

2.切換資料庫的代理到異地/同城的對應的資料庫叢集,一定要確保資料是完全同步過去之後才能切,如果用Orchestrator之類的故障自動轉移,需要做對應的資料檢查機制。

總結

對於異地多活,沒有一刀切完全通用的方案,需要根據自己的業務特點,在成本,服務可用性和資料完整性,技術複雜度之間做平衡,找到最適合自己公司的方案;但是最重要的原則就是不管那種方案,確保資料的完整性和一致性是首要考慮的。

更多文章歡迎訪問 http://www.apexyun.com/

聯絡郵箱:public@space-explore.com

(未經同意,請勿轉載)


相關文章