| 2022-08-02 11:57
學習為什麼 Python 中的可觀測性很重要,以及如何在你的軟件開發生命周期中實現它。
你寫的應用會執行很多代碼,而且是以一種基本上看不到的方式執行。所以你是怎麼知道:
可觀測性是一種能力,可以通過查看數據來告訴你,你的代碼在做什麼。在這篇文章中,主要關注的問題是分布式系統中的服務器代碼。並不是說客戶端應用代碼的可觀測性不重要,只是說客戶端往往不是用 Python 寫的。也不是說可觀測性對數據科學不重要,而是在數據科學領域的可觀測性工具(大多是 Juptyter 和快速反饋)是不同的。
所以,為什麼可觀測性重要呢?在軟件開發生命周期(SDLC)中,可觀測性是一個關鍵的部分。
交付一個應用不是結束,這只是一個新周期的開始。在這個周期中,第一個階段是確認這個新版本運行正常。否則的話,很有可能需要回滾。哪些功能正常運行?哪些功能有細微的錯誤?你需要知道發生了什麼,才能知道接下來要怎麼做。這些東西有時候會以奇怪的方式不能正常運行。不管是天災,還是底層基礎設施的問題,或者應用進入了一種奇怪的狀態,這些東西可能在任何時間以任何理由停止工作。
在標准 SDLC 之外,你需要知道一切都在運行中。如果沒有,有辦法知道是怎麼不能運行的,這是非常關鍵的。
可觀測性的第一部分是獲得反饋。當代碼給出它正在做什麼的信息時,反饋可以在很多方面提供幫助。在模擬環境或測試環境中,反饋有助於發現問題,更重要的是,以更快的方式對它們進行分類。這可以改善在驗證步驟中的工具和交流。
當進行金絲雀部署或更改特性標志時,你需要知道是否要繼續,還是等更長時間,或者回滾,反饋就顯得很重要了。
有時候你懷疑有些東西不太對。也許是一個依賴服務有問題,或者是社交網站爆出了大量你的網站的問題。也許在相關的系統中有復雜的操作,然後你想確保你的系統能完美處理。在這些情況下,你就想把可觀測性系統的數據整合到控制面板上。
當寫一個應用的時候,這些控制面板需要是設計標准的一部分。只有當你的應用能把數據共享給這些控制面板,它們才會把這些數據顯示出來。
看控制面板超過 15 分鐘就像看著油漆變干一樣。任何人都不應該遭受這種折磨。對於這種任務,我們要有報警系統。報警系統將可觀測性數據與預期數據進行對比,當它們不匹配的時候就發出通知。完全深入研究時間管理超出了本文的范圍。然而,從兩方面來說,可觀測應用是報警友好的:
高質量警報有三個特點:
這三個特點是互相有沖突的。你可以通過提高監測的標准來減少錯誤警報,代價是增加了漏報。你也可以通過降低監測的門檻來減少漏報,代價是增加錯報。通過收集更多數據,你也可以同時減少錯報和漏報,而代價是降低了及時性。
同時改善這三個參數就更難了。這就要求高質量的可觀測性數據。更高質量的數據可以同時改善這三個特點。
有的人喜歡嘲笑用打印來調試的方法。但是,在一個大多數軟件都不在你本機運行的世界裡,你所能做的只有打印調試。日志記錄就是打印調試的一種形式。盡管它有很多缺點,但 Python 日志庫提供了標准化的日志記錄。更重要的是,它意味著你可以通過這些庫去記錄日志。
應用程序要負責配置日志的記錄方式。諷刺地是,在應用程序對配置日志負責了多年以後,現在越來越不是這樣了。在現代容器編排環境中,現代應用程序記錄標准錯誤和標准輸出,並且信任編排系統可以合理的處理日志。
然而,你不應該依賴庫,或者說,其他任何地方。如果你想讓操作的人知道發生了什麼,使用日志,而不是打印。
日志記錄的一個最重要功能就是 日志級別。不同的日志級別可以讓你合理的過濾並分流日志。但是這只有在日志級別保持一致的情況下才能做到。最後,你應該在整個應用程序中保持日志級別的一致性。
選擇不兼容語義的庫可以通過在應用層面的適當配置來追溯修復,這只需要通過使用 Python 中最重要的通用風格做到:getLogger(__name-_)
。
大多數合理的庫都會遵循這個約定。過濾器可以在日志對象發出之前就地修改它們。你可以給處理程序附加一個過濾器,這個處理程序會根據名稱修改消息,使其具有合適的級別。
import logging
LOGGER=logging.getLogger(__name__)
考慮到這一點,你現在必須明確日志級別的語義。這其中有很多選項,但是下面這些是我的最愛:
Error
:發送一個即時警告。應用程序處於一個需要操作人員引起注意的狀態。(這意味著包含 Critical
和 Error
)Warning
:我喜歡把這些稱作“工作時間警報”。這種情況下,應該有人在一個工作日內關注一下。Info
:這是在正常工作流程中發出的。如果懷疑有問題的時候,這個是用來幫助人們了解應用程序在做什麼的。Debug
:默認情況下,這個不應該在生產環境中出現。在模擬環境或開發環境下,可以發出來,也可以不發。如果需要更多的信息,在生產環境也可以特地被打開。任何情況下都不要在日志中包含個人身份信息(PII)或密碼。無論日志級別是什麼,都是如此,比如級別更改,激活調試級別等等。日志聚合系統很少是 PII 安全的,特別是隨著 PII 法規的不斷發展(HIPAA、GDPR 等等)。
現代系統幾乎都是分布式的。冗余、擴展性,有時是管轄權需要更多的水平分布。微服務意味著垂直分布。登錄到每個機器去查看日志已經是不現實的了。出於合理的控制原因,允許開發人員登錄到機器中會給予他們更多的權限,這不是個好主意。
所有的日志都應該被發到一個聚合器。有一些商業的方案,你可以配置一個 ELK 棧,或者也可以使用其他的數據庫(SQL 或則 no-SQL)。作為一個真正的低技術解決方案,你可以將日志寫入文件,然後將它們發送到對象存儲中。有很多解決方案,但是最重要的事情是選擇一個,並且將所有東西聚合到一起。
在將所有東西記錄到一個地方後,會有很多日志。具體的聚合器可以定義如何寫查詢,但是無論是通過從存儲中搜索還是寫 NoSQL 查詢,記錄查詢以匹配源和細節都是很有用的。
指標抓取是一個服務器拉取模型。指標服務器定時和應用程序連接,並且拉取指標。
最後,這意味著服務器需要連接和找到所有相關的應用服務器。
如果你的指標聚合器是 Prometheus,那麼 格式做為一個端點是很有用的。但是,即使聚合器不是 Prometheus,也是很有用的。幾乎所有的系統都包含與 Prometheus 端點兼容的墊片
使用客戶端 Python 庫給你的應用程序加一個 Prometheus 墊片,這將使它能夠被大多數的指標聚合器所抓取。當 Prometheus 發現一個服務器,它就期望找到一個指標端點。這經常是應用程序路由的一部分,通常在 /metrics
路徑下。不管 Web 應用的平台是什麼,如果你能在一個端點下運行一個定制類型的定制字節流,Prometheus 就可以將它抓取。
對於大多數流行的框架,總有一個中間件插件或者類似的東西收集指標,如延遲和錯誤率。通常這還不夠。你需要收集定制的應用數據:比如,每個端點的緩存命中/缺失率,數據庫延遲,等等。
Prometheus 支持多個數據類型。一個重要且巧妙的類型就是計數器。計數器總是在前進 —— 但有一點需要注意。
當應用重置,計數器會歸零。計數器中的這些“歷時”通過將計數器“創建時間”作為元數據發送來管理。Prometheus 知道不去比較兩個不同歷時的計數器。
儀表值會簡單很多:它們測量瞬時值。用它們來測量會上下起伏的數據:比如,分配的總內存大小,緩存大小,等等。
枚舉值對於整個應用程序的狀態是很有用的,盡管它們可以以更精細的方式被收集。比如,你正使用一個功能門控框架,一個有多個狀態(比如,使用中、關閉、屏蔽 等)的功能,也許使用枚舉會更有用。
分析不同於指標,因為它們要對應連續的事件。比如,在網絡服務器中,事件是一個外部請求及其產生的工作。特別是,在事件完成之前事件分析是不能被發送的。
事件包含特定的指標:延遲,數量,以及可能產生的對其他服務請求的細節,等等。
現在一個可能的選擇是將日志結構化。發送事件只發送帶有正確格式的有效載荷的日志。這個數據可以從日志聚合器請求,然後解析,並且放入一個合適的系統,這樣可以對它的可見性。
你可以使用日志來追蹤錯誤,也可以用分析來追蹤錯誤。但是一個專門的錯誤系統還是值得的。一個為錯誤而優化的系統可以發送更多的錯誤,因為錯誤畢竟還是罕見的。這樣它就可以發送正確的數據,並且用這些數據,它能做更多智能的事情。Python 中的錯誤追蹤系統通常和一般的異常處理關聯,然後收集數據,並且把它發到一個專門的錯誤聚合器。
很多情況下,自己運行 Sentry 是正確的做法。當錯誤發生時,就說明有些東西就出問題了。可靠地刪除敏感數據是不可能的,因為一定有會出現敏感數據被發送到不應該的地方。
通常,這種工作量並不會很大:異常並不常出現。最後,這個系統並不需要很高的質量,也不需要高可靠性的備份。昨天的錯誤應該已經修復了,希望如此,如果沒有,你還會發現的!
可觀測的系統開發起來更快,因為它們可以給你提供反饋。它們運行起來也更安全,因為當出問題的時候,它們也會更早的讓你知道。最後,因為有反饋回路,可觀測性也有助於圍繞它構建可重復的過程。可觀測性可以讓你了解你的應用程序。而更了解它們,就勝利了一半。
構建所有的可觀測層是一件困難的事情。總會讓人感覺是在浪費的工作,或者更像是“可以有,但是不急”。
之後再做這個可以嗎?也許吧,但是不應該。正確的構建可觀測性可以加速後面所有階段的開發:測試、監控,甚至是培訓新人。在一個和科技行業一樣動蕩的行業,減少培訓新人的工作量絕對是值得的。
事實上,可觀測性很重要,所以盡早把它寫出來,然後就可以在整個過程中進行維護。反過來,它也會幫你維護你的軟件。
via:
作者: 選題: 譯者: 校對:
本文由 原創編譯, 榮譽推出