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