我在使用Prometheus時(shí)都踩過(guò)哪些坑?
Prometheus 是一個(gè)開(kāi)源監(jiān)控系統(tǒng),它本身已經(jīng)成為了云原生中指標(biāo)監(jiān)控的事實(shí)標(biāo)準(zhǔn),幾乎所有 k8s 的核心組件以及其它云原生系統(tǒng)都以 Prometheus 的指標(biāo)格式輸出自己的運(yùn)行時(shí)監(jiān)控信息。我在工作中也比較深入地使用過(guò) Prometheus,最大的感受就是它非常容易維護(hù),突出一個(gè)簡(jiǎn)單省心成本低。當(dāng)然,這當(dāng)中也免不了踩過(guò)一些坑,下面就總結(jié)一下。
假如你沒(méi)有用過(guò) Prometheus,建議先看一遍官方文檔。
接受準(zhǔn)確性與可靠性的權(quán)衡
Prometheus 作為一個(gè)基于指標(biāo)(Metric)的監(jiān)控系統(tǒng),在設(shè)計(jì)上就放棄了一部分?jǐn)?shù)據(jù)準(zhǔn)確性:
比如在兩次采樣的間隔中,內(nèi)存用量有一個(gè)瞬時(shí)小尖峰,那么這次小尖峰我們是觀察不到的;
再比如 QPS、RT、P95、P99 這些值都只能估算,無(wú)法和日志系統(tǒng)一樣做到 100% 準(zhǔn)確,下面也會(huì)講一個(gè)相關(guān)的坑。
放棄一點(diǎn)準(zhǔn)確性得到的是更高的可靠性,這里的可靠性體現(xiàn)為架構(gòu)簡(jiǎn)單、數(shù)據(jù)簡(jiǎn)單、運(yùn)維簡(jiǎn)單。假如你維護(hù)過(guò) ELK 或其它日志架構(gòu)的話,就會(huì)發(fā)現(xiàn)相比于指標(biāo),日志系統(tǒng)想要穩(wěn)定地跑下去需要付出幾十倍的機(jī)器成本與人力成本。既然是權(quán)衡,那就沒(méi)有好或不好,只有適合不適合,我推薦在應(yīng)用 Prometheus 之初就要先考慮清楚這個(gè)問(wèn)題,并且將這個(gè)權(quán)衡明確地告訴使用方。
首先做好自監(jiān)控
不知道你有沒(méi)有考慮過(guò)一個(gè)問(wèn)題,其它系統(tǒng)都用 Prometheus 監(jiān)控起來(lái)了,報(bào)警規(guī)則也設(shè)置好了,那 Prometheus 本身由誰(shuí)來(lái)監(jiān)控?
答案是”另一個(gè)監(jiān)控系統(tǒng)”,而這個(gè)監(jiān)控系統(tǒng)可以是另一個(gè) Prometheus。按照官方的 quickstart 或 helm 部署的 Prometheus 單實(shí)例自己監(jiān)控自己的,我們當(dāng)然不能指望一個(gè)系統(tǒng)掛掉之后自己發(fā)現(xiàn)自己掛了。
因此我強(qiáng)烈建議在上生產(chǎn)環(huán)境之前,一定要確保至少有兩個(gè)獨(dú)立的 Prometheus 實(shí)例互相做交叉監(jiān)控。交叉監(jiān)控的配置也很簡(jiǎn)單,每臺(tái) Prometheus 都拉取其余所有 Prometheus 的指標(biāo)即可。
還有一個(gè)點(diǎn)是警報(bào)系統(tǒng)(Alertmanager),我們?cè)倏紤]一下警報(bào)系統(tǒng)掛掉的情況:這時(shí)候 Prometheus 可以監(jiān)控到警報(bào)系統(tǒng)掛了,但是因?yàn)榫瘓?bào)掛掉了,所以警報(bào)自然就發(fā)不出來(lái),這也是應(yīng)用 Prometheus 之前必須搞定的問(wèn)題。這個(gè)問(wèn)題可以通過(guò)給警報(bào)系統(tǒng)做 HA 來(lái)應(yīng)對(duì)。除此之外還有一個(gè)經(jīng)典的兜底措施叫做 “Dead man’s switch”: 定義一條永遠(yuǎn)會(huì)觸發(fā)的告警,不斷通知,假如哪天這條通知停了,那么說(shuō)明報(bào)警鏈路出問(wèn)題了。
不要使用 NFS 做存儲(chǔ)
如題,Prometheus 維護(hù)者也在 issue 中表示過(guò)不支持 NFS。這點(diǎn)我們有血淚教訓(xùn)(我們?cè)?jīng)有一臺(tái) Prometheus 存儲(chǔ)文件發(fā)生損壞丟失了歷史數(shù)據(jù))。
盡早干掉維度過(guò)高的指標(biāo)
根據(jù)我們的經(jīng)驗(yàn),Prometheus 里有 50% 以上的存儲(chǔ)空間和 80% 以上的計(jì)算資源(CPU、內(nèi)存)都是被那么兩三個(gè)維度超高的指標(biāo)用掉的。而且這類(lèi)維度超高的指標(biāo)由于數(shù)據(jù)量很大,稍微查得野一點(diǎn)就會(huì) OOM 搞死 Prometheus 實(shí)例。
首先要明確這類(lèi)指標(biāo)是對(duì) Prometheus 的濫用,類(lèi)似需求完全應(yīng)該放到日志流或數(shù)倉(cāng)里去算。但是指標(biāo)的接入方關(guān)注的往往是業(yè)務(wù)上夠不夠方便,假如足夠方便的話什么都可以往 label 里塞。這就需要我們防患于未然,一個(gè)有效的辦法是用警報(bào)規(guī)則找出維度過(guò)高的壞指標(biāo),然后在 Scrape 配置里 Drop 掉導(dǎo)致維度過(guò)高的 label。
警報(bào)規(guī)則的例子:
- # 統(tǒng)計(jì)每個(gè)指標(biāo)的時(shí)間序列數(shù),超出 10000 的報(bào)警
- count by (__name__)({__name__=~".+"}) > 10000
“壞指標(biāo)”報(bào)警出來(lái)之后,就可以用 metric_relabel_config 的 drop 操作刪掉有問(wèn)題的 label(比如 userId、email 這些一看就是問(wèn)題戶(hù)),這里的配置方式可以查閱文檔。
對(duì)了,這條的關(guān)鍵詞是盡早,最好就是部署完就搞上這條規(guī)則,否則等哪天 Prometheus 容量滿(mǎn)了再去找業(yè)務(wù)方說(shuō)要?jiǎng)h label,那業(yè)務(wù)方可能就要忍不住扇你了……
Rate 類(lèi)函數(shù) + Recording Rule 的坑
可能你已經(jīng)知道了 PromQL 里要先 rate() 再 sum(),不能 sum() 完再 rate()(不知道也沒(méi)事,馬上講)。但當(dāng) rate() 已經(jīng)同類(lèi)型的函數(shù)如 increase() 和 recording rule 碰到一起時(shí),可能就會(huì)不小心掉到坑里去。
當(dāng)時(shí),我們已經(jīng)有了一個(gè)維度很高的指標(biāo)(只能繼續(xù)維護(hù)了,因?yàn)闆](méi)有盡早干掉),為了讓大家查詢(xún)得更快一點(diǎn),我們?cè)O(shè)計(jì)了一個(gè) Recording Rule,用 sum() 來(lái)去掉維度過(guò)高的 bad_label,得到一個(gè)新指標(biāo)。那么只要不涉及到 bad_label,大家就可以用新指標(biāo)進(jìn)行查詢(xún),Recording Rule 如下:
- sum(old_metric) without (bad_label)
用了一段時(shí)候后,大家發(fā)現(xiàn) new_metric 做 rate() 得到的 QPS 趨勢(shì)圖里經(jīng)常有奇怪的尖峰,但 old_metric 就不會(huì)出現(xiàn)。這時(shí)我們恍然大悟:繞了個(gè)彎踩進(jìn)了 rate() 的坑里。
這背后與 rate() 的實(shí)現(xiàn)方式有關(guān),rate() 在設(shè)計(jì)上假定對(duì)應(yīng)的指標(biāo)是一個(gè) Counter,也就是只有 incr(增加) 和 reset(歸0) 兩種行為。而做了 sum() 或其他聚合之后,得到的就不再是一個(gè) Counter 了,舉個(gè)例子,比如 sum() 的計(jì)算對(duì)象中有一個(gè)歸0了,那整體的和會(huì)下降,而不是歸零,這會(huì)影響 rate() 中判斷 reset(歸0) 的邏輯,從而導(dǎo)致錯(cuò)誤的結(jié)果。寫(xiě) PromQL 時(shí)這個(gè)坑容易避免,但碰到 Recording Rule 就不那么容易了,因?yàn)椴蝗タ磁渲玫脑挻蠹乙蚕氩坏?new_metric 是怎么來(lái)的。
要完全規(guī)避這個(gè)坑,可以遵守一個(gè)原則:Recording Rule 一步到位,直接算出需要的值,避免算出一個(gè)中間結(jié)果再拿去做聚合。
警報(bào)和歷史趨勢(shì)圖未必 Match
最近半年常常被問(wèn)兩個(gè)問(wèn)題:
- 我的歷史趨勢(shì)圖看上去超過(guò)水位線了,警報(bào)為什么沒(méi)報(bào)?
- 我的歷史趨勢(shì)圖看上去挺正常的,警報(bào)為什么報(bào)了?
這其中有一個(gè)原因是:趨勢(shì)圖上每個(gè)采樣點(diǎn)的采樣時(shí)間和警報(bào)規(guī)則每次的計(jì)算時(shí)間不是嚴(yán)格一致的。當(dāng)時(shí)間區(qū)間拉得比較大的時(shí)候,采樣點(diǎn)非常稀疏,不如警報(bào)計(jì)算的間隔來(lái)得密集,這個(gè)現(xiàn)象尤為明顯,比如時(shí)序圖采樣了 0秒,60秒,120秒三個(gè)點(diǎn)。而警報(bào)在15秒,30秒,45秒連續(xù)計(jì)算出了異常,那在圖上就看不出來(lái)。另外,經(jīng)過(guò)越多的聚合以及函數(shù)操作,不同時(shí)間點(diǎn)的數(shù)據(jù)差異會(huì)來(lái)得越明顯,有時(shí)確實(shí)容易混淆。
這個(gè)其實(shí)不是問(wèn)題,碰到時(shí)將趨勢(shì)圖的采樣間隔拉到最小,仔細(xì)比對(duì)一下,就能驗(yàn)證警報(bào)的準(zhǔn)確性。而對(duì)于聚合很復(fù)雜的警報(bào),可以先寫(xiě)一條 Recording Rule, 再針對(duì) Recording Rule 產(chǎn)生的新指標(biāo)來(lái)建警報(bào)。這種范式也能幫助我們更高效地去建分級(jí)警報(bào)(超過(guò)不同閾值對(duì)應(yīng)不同的緊急程度)
group_interval 會(huì)影響 resolved 通知
Alertmanager 里有一個(gè)叫 group_interval 的配置,用于控制同一個(gè) group 內(nèi)的警報(bào)最快多久通知一次。這里有一個(gè)問(wèn)題是 firing(激活) 和 resolved(已消除) 的警報(bào)通知是共享同一個(gè) group 的。也就是說(shuō),假設(shè)我們的 group_interval 是默認(rèn)的 5 分鐘,那么一條警報(bào)激活十幾秒后立馬就消除了,它的消除通知會(huì)在報(bào)警通知的 5 分鐘之后才到,因?yàn)樵诎l(fā)完報(bào)警通知之后,這個(gè) Group 需要等待 5 分鐘的 group_interval 才能進(jìn)行下一次通知。
這個(gè)設(shè)計(jì)讓”警報(bào)消除就立馬發(fā)送消除通知”變得幾乎不可能,因?yàn)榧偃绨?group_interval 變得很小的話,警報(bào)通知就會(huì)過(guò)于頻繁,而調(diào)大的話,就會(huì)拖累到消除通知。
這個(gè)問(wèn)題修改一點(diǎn)源碼即可解決,不過(guò)無(wú)傷大雅,不修也完全沒(méi)問(wèn)題。
最后一條:不要忘記因何而來(lái)
最后一條撒點(diǎn)雞湯:監(jiān)控的核心目標(biāo)還是護(hù)航業(yè)務(wù)穩(wěn)定,保障業(yè)務(wù)的快速迭代,永遠(yuǎn)不要忘記因何而來(lái)。
曾經(jīng)有一端時(shí)間,我們追求”監(jiān)控的覆蓋率”,所有系統(tǒng)所有層面,一定要有指標(biāo),而且具體信息 label 分得越細(xì)越好,最后搞出幾千個(gè)監(jiān)控項(xiàng),不僅搞得眼花繚亂還讓 Prometheus 變慢了。
還有一段時(shí)間,我們追求”警報(bào)的覆蓋率”,事無(wú)巨細(xì)必有要有警報(bào),人人有責(zé)全體收警報(bào)(有些警報(bào)會(huì)發(fā)送給幾十個(gè)人)。最后當(dāng)然你也能預(yù)想到了,告警風(fēng)暴讓大家都對(duì)警報(bào)疲勞了。
這些事情乍看起來(lái)都是在努力工作,但其實(shí)一開(kāi)始的方向就錯(cuò)了,監(jiān)控的目標(biāo)絕對(duì)不是為了達(dá)到 xxx 個(gè)指標(biāo),xxx 條警報(bào)規(guī)則,這些東西有什么意義?
依我看,負(fù)責(zé)監(jiān)控的開(kāi)發(fā)就算不是 SRE 也要有 SRE 的心態(tài)和視野,不要為監(jiān)控系統(tǒng)的功能或覆蓋面負(fù)責(zé)(這樣很可讓導(dǎo)致開(kāi)發(fā)在監(jiān)控里堆砌功能和內(nèi)容,變得越來(lái)越臃腫越來(lái)越不可靠),而要為整個(gè)業(yè)務(wù)的穩(wěn)定性負(fù)責(zé),同時(shí)站在穩(wěn)定性的投入產(chǎn)出比角度去考慮每件事情的性質(zhì)和意義,不要忘記我們因何而來(lái)。