摘要:雖然多數時候,我們的系統都做了容錯保護,但我們還是需要能盡快的發現故障,才好進行故障轉移。而這個故障其實是知道的,所以故障檢測的原理很簡單,從發起請求的這一端來觀察,如果發現有問題,那就是有故障了。于是作者開發了這套系統,來對故障進行檢測。
作者:唐劉
在對 TiDB 進行 Chaos 實踐的時候,我一直在思考如何更好的發現 TiDB 整個系統的故障。最開始,我們參考的就是 Chaos Engineering 里面的方式,觀察系統的穩定狀態,注入一個錯誤,然后看 metrics 上面有啥異常,這樣等實際環境中出現類似的 metrics,我們就知道發現了什么故障。
但這套機制其實依賴于如何去注入錯誤,雖然現在我們已經有了很多種錯誤注入的方式,但總有一些實際的情況我們沒有料到。所以后來我們又考慮了另外的一種方式,也就是直接對 metrics 歷史進行學習,如果某一段時間 metrics 出現了不正常的波動,那么我們就能報警。但這個對我們現階段來說難度還是有點大,只使用了幾種策略,對 QPS,Latency 這些進行了學習,并不能很好的定位到具體出了什么樣的問題。
所以我一直在思考如何更好的去發現系統的故障。最近,剛好看到了 OSDI 2018 一篇 Paper,Capturing and Enhancing In Situ System Observability for Failure Detection,眼睛一亮,覺得這種方式也是可以來實踐的。
大家都知道,在生產環境中,故障是無處不在,隨時可能發生的,譬如硬件問題,軟件自身的 bug,或者運維使用了一個錯誤的配置這些。雖然多數時候,我們的系統都做了容錯保護,但我們還是需要能盡快的發現故障,才好進行故障轉移。
但現實世界并沒有那么美好,很多時候,故障并不是很明顯的,譬如整個進程掛掉,機器壞掉這些,它們處于一種時好時壞的狀態,我們通常稱為「Gray Failure」,譬如磁盤變慢了,網絡時不時丟包。這些故障都非常隱蔽,很難被發現。如果單純的依賴外部的工具,其實很難檢測出來。
上面是作者舉的一個 ZooKeeper 的例子,client 已經完全不能跟 Leader 進行交互了,但是 Leader 卻仍然能夠給 Follower 發送心跳,同時也能響應外面 Monitor 發過來的探活命令。
如果從外面的 Monitor 看來,這個 ZooKeeper 集群還是正常的,但其實它已經有故障了。而這個故障其實 client 是知道的,所以故障檢測的原理很簡單,從發起請求的這一端來觀察,如果發現有問題,那就是有故障了。而這也是這篇論文的中心思想。
在論文里面,作者認為,任何嚴重的 Gray Failure 都是能夠被觀察到的,如果發起請求的這邊遇到了錯誤,自然下一件事情就是將這個錯誤給匯報出去,這樣我們就知道某個地方出現了故障。于是作者開發了 Panorama 這套系統,來對故障進行檢測。
整體架構先來說說 Panorama 一些專業術語。
Panorama 整體結構如下:
Panorama 通過一些方式,譬如靜態分析代碼進行代碼注入等,將 Observer 跟要觀察的 Subject 進行綁定,Observer 會將 Subject 的一些信息記錄并且匯報給本地的一個 Local Observation Store(LOS)。本地一個決策引擎就會分析 LOS 里面的數據來判斷這個組件的狀態。如果多個 LOS 里面都有對某個 Subject 的 observation,那么 LOS 會相互交換,用來讓中央的 verdict 更好的去判斷這個 component 的狀態。
故障判定而用來判斷一個 component 是不是有故障也比較容易,采用的是一種大多數 bounded-look-back 算法。對于一個 subject,它可能會有很多 observations,首先我們會對這些 observations 按照 observer 進行分組,對每組多帶帶進行分析。在每個組里面,observations 會按照時間從后往前檢查,并且按照 context 進行聚合。如果一個被觀察的 observation 的 status 跟記錄前面相同 context 的 observation status 狀態不一樣,就繼續 loop-back,直到遇到一個新的 status。對于一個 context,如果最后的狀態是 unhealthy 或者 healthy 的狀態沒有達到多數,就會被認為是 unhealthy 的。
通過這種方式,我們在每組里面得到了每個 context 的狀態,然后又會在多個組里面進行決策,也就是最常用的大多數原則,哪個狀態最多,那么這個 context 對應的狀態就是哪一個。這里我們需要額外處理下 PENDING 這個狀態,如果當前狀態是 HEALTHY 而之前老的狀態是 PENDING,那么 PENDING 就會變成 HEALTHY,而如果一直是 PENDING 狀態并超過了某個閾值,就會退化成 UNHEALTHY。
Observability這里再來說說 Observability 的模式。對于分布式系統來說,不同 component 之間的交互并不是同步的,我們會面臨如下幾種情況:
如果兩個組件 C1 和 C2 是同步交互,那么當 C1 給 C2 發送請求,我們就完全能在 C1 這一端知道這次請求成功還是失敗了,但是對于非同步的情況,我們可能面臨一個問題,就是 C1 給 C2 發了請求,但其實這個請求是放到了異步消息隊列里面,但 C1 覺得是成功了,可是后面的異步隊列卻失敗了。所以 Panorama 需要有機制能正確處理上面多種情況。
為了能更好的從 component 上面得到有用的 observations,Panorama 會用一個離線工具對代碼進行靜態分析,發現一些關鍵的地方,注入鉤子,這樣就能去匯報 observations 了。
通常運行時錯誤是非常有用的能證明有故障的證據,但是,并不是所有的錯誤都需要匯報,Panorama 僅僅會關系跨 component 邊界產生的錯誤,因為這也是通過發起請求端能觀察到的。Panorama 對于這種跨域的函數調用稱為 observation boundaries。對于 Panorama 來說,第一件事情就是定位 observation boundaries。通常有兩種 boundaries,進程間交互和線程間交互。進程間交互通常就是 socket I/O,RPC,而線程間則是在一個進程里面跨越線程的調用。這些 Panorama 都需要分析出來。
當定位了 observation boundaries 之后,下一件事情就是確定 observer 和 subject 的標識。譬如對于進程間交互的 boundaries,observer 的標識就可能是這個進程在系統里面的唯一標識,而對于 subject,我們可以用 method 名字,或者是函數的一個參數,類里面的一個字段來標識。
然后我們需要去確定 observation points,也就是觀測點。通常這些點就是代碼處理異常的地方,另外可能就是一些正常處理返回結果但會對外報錯的地方。
上面就是一個簡單分析代碼得到 observation points 的例子,但這個仍然是同步的,對于 indirection 的,還需要額外處理。
對于異步請求,我們知道,通過發出去之后,會異步的處理結果,所以這里分為了兩步,叫做 ob-origin 和 ob-sink。如下:
對于 ob-origin,代碼分析的時候會先給這個 observation 設置成 PENDING 狀態,只有對應的 ob-sink 調用并且返回了正確的結果,才會設置成 HEALTHY。因為 ob-origin 和 ob-sink 是異步的,所以代碼分析的時候會加上一個特殊的字段,包含 subject 的標識和 context,這樣就能讓 ob-origin 和 ob-sink 對應起來。
小結上面大概介紹了 Panorama 的架構以及一些關鍵的知識點是如何實現的,簡單來說,就是在一些關鍵代碼路徑上面注入 hook,然后通過 hook 對外將相關的狀態給匯報出去,在外面會有其他的分析程序對拿到的數據進行分析從而判定系統是否在正常工作。它其實跟加 metrics 很像,但 metrics 只能看出哪里出現了問題,對于想更細致定位具體的某一個問題以及它的上下文環境,倒不是特別的方便。這點來說 Panorama 的價值還是挺大的。
Panorama 的代碼已經開源,總的來說還是挺簡單的,但我沒找到核心的代碼分析,注入 hook 這些,有點遺憾。但理解了大概原理,其實先強制在代碼寫死也未嘗不可。另一個比較可行的辦法就是進行在代碼里面把日志添加詳細,這樣就不用代碼注入了,而是在外面寫一個程序來分析日志,其實 Panorama 代碼里面提供了日志分析的功能,為 ZooKeeper 來設計的,但作者自己也說到,分析日志的效果比不上直接在代碼里面進行注入。
那對我們來說,有啥可以參考的呢?首先當然是這一套故障檢查的理念,既然 Panorama 已經做出來并且能發現故障量,自然我們也可以在 TiDB 里面實施。因為我們已經有在 Go 和 Rust 代碼里面使用 fail 來進行錯誤注入的經驗,所以早期手寫監控代碼也未嘗不可,但也可以直接完善日志,提供一個程序來分析日志就成。如果你對這塊感興趣,想把 Panorama 相關的東西應用到 TiDB 中來,歡迎聯系我 tl@pingcap.com。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/17823.html
摘要:目標發布目前有兩個主要功能針對局部變量類型推斷這將刪除大部分對象實例化所需的冗長的包含手動類型信息整合源樹的庫即不同的庫將被合并成一個單一的存儲庫。特別是,承諾為局部變量實例化引入類型推斷機制,并將現有的存儲庫合并到一個存儲庫中。 JDK 10 何時發布? JDK 10 是 Java 10 標準版的部分實現,將于 2018 年 3 月 20 日發布,改進的關鍵點包括一個本地類型推斷、一...
摘要:本屆大會議題數量接近,比去年規模較大的北美峰會多出了近一倍。同時還在華為伙伴公有云等云平臺上創建集群并接入了他們的平臺,以便于快速響應技術峰會等大型活動期間暴漲的計算量。Kubernetes,云原生,service mesh,這些驚人的全球增長趨勢,令人欣喜之余迫不及待想要看看云原生在未來究竟會發展出怎樣一派繁榮的景象。 容器領域最具影響力的技術峰會之一 KubeCon + Cloud...
摘要:自年月舉辦以來,規模持續增大。本屆大會議題數量接近,比去年規模較大的北美峰會多出了近一倍。同時還在華為伙伴公有云等云平臺上創建集群并接入了他們的平臺,以便于快速響應技術峰會等大型活動期間暴漲的計算量。 Kubernetes,云原生,service mesh,這些驚人的全球增長趨勢,令人欣喜之余迫不及待想要看看...
摘要:與異常相關的編程藝術異常代替返回狀態碼我們經常需要編寫一些工具類的函數,往往在這些函數的處理流程中,會產生很多的狀態而這些狀態也是調用者需要得到的信息。 編程中經常會需要使用到異常處理的情況,在閱讀了一些資料后,整理了關于異常處理的一些小技巧記錄如下。 如何自定義異常 定義異常類 在實際編程中,有時會發現Python提供的內建異常的不夠用,我們需要在特殊業務場景下的異常。這時就需要我們...
閱讀 785·2023-04-26 00:30
閱讀 2689·2021-11-23 09:51
閱讀 1045·2021-11-02 14:38
閱讀 2560·2021-09-07 10:23
閱讀 2243·2021-08-21 14:09
閱讀 1362·2019-08-30 10:57
閱讀 1603·2019-08-29 11:20
閱讀 1149·2019-08-26 13:53