捕獲和增強原生系統的可觀測性來發現錯誤

在對 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 一些專業術語

Name 解釋
Component 要觀察的組件,可以是一個進程,或者是一個線程
Subject 一個唄監控的 component
Observer 一個用來監控 subject 的 component
Status 一個 subject 的健康狀態
Observation 一個 observer 確定一個 subject 狀態的證據
Context 當一個 observer 確定 observation 時候的上下文
Verdict 一個用來確定 subject 狀態的決定,會通過多個 observation 來彙總得出

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 中來,歡迎聯繫我 [email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章