控體系搭建實戰(zhàn):從指標(biāo)采集到告警定位)
1. 從零搭建 Elasticsearch 監(jiān)控體系為什么裸奔的集群遲早要出事剛接手一個搜索集群的時候我最怕看到的不是報錯日志而是一切正常這四個字。因為 Elasticsearch 這東西它不像傳統(tǒng)數(shù)據(jù)庫那樣CPU 打滿了會立刻告警、連接數(shù)爆了會直接拒絕服務(wù)。它更像一個慢性病患者——今天寫入慢一點明天查詢抖一下你覺得還能用等到某天早上打開 Kibana 發(fā)現(xiàn)索引變紅、分片分配失敗、節(jié)點掉出集群那基本就是 ICU 搶救的節(jié)奏了。elasticsearch-monitor這個項目說白了就是給 ES 集群裝上一套體檢監(jiān)護(hù)系統(tǒng)。它要解決的核心問題有三個第一讓你在集群還沒掛之前就知道它快撐不住了第二出問題的時候能快速定位是哪個節(jié)點、哪個索引、哪類操作在搗鬼第三把歷史數(shù)據(jù)留下來方便做容量規(guī)劃和性能趨勢分析。適合誰來參考如果你手頭維護(hù)著至少一個 ES 集群不管是三節(jié)點的測試環(huán)境還是幾十個節(jié)點的大集群只要你不希望半夜被電話叫醒這套東西就值得花時間搭起來。我見過太多團(tuán)隊的做法是ES 部署完Kibana 能打開就覺得監(jiān)控已經(jīng)有了。實際上 Kibana 自帶的 Monitoring 面板只能算體溫計它能告訴你發(fā)燒了但不會告訴你為什么發(fā)燒、該吃什么藥。真正要做的監(jiān)控體系得覆蓋指標(biāo)采集、日志聚合、告警規(guī)則、可視化看板、以及事后排查的完整鏈路。下面我就按自己實際搭建和踩坑的順序把整套東西拆開講清楚。2. 監(jiān)控指標(biāo)到底該盯哪些別被幾百個 metrics 晃花了眼2.1 集群級指標(biāo)先看大局有沒有崩ES 通過_cluster/health和_cluster/stats暴露的指標(biāo)有上百個但真正需要設(shè)置告警的我認(rèn)為不超過十五個。集群健康狀態(tài)status是最粗粒度的信號green/yellow/red 三檔但這里有個坑yellow 在很多單副本場景下是常態(tài)如果你把 yellow 直接設(shè)成告警運維群會被轟炸到麻木。我的做法是yellow 持續(xù)超過 10 分鐘才觸發(fā)低優(yōu)先級通知red 則立即電話告警。分片相關(guān)指標(biāo)里unassigned_shards是最關(guān)鍵的。它一旦大于 0 且持續(xù)增長說明有分片分配不出去常見原因是磁盤水位線觸發(fā)、節(jié)點掉線、或者分片分配規(guī)則配置沖突。另一個容易被忽略的是initializing_shards和relocating_shards短時間有值是正常的但如果某個分片長時間處于 initializing 狀態(tài)大概率是恢復(fù)過程卡住了需要人工介入。節(jié)點數(shù)量number_of_nodes和數(shù)據(jù)節(jié)點數(shù)量number_of_data_nodes要對比著看。如果兩者差值突然變大說明有數(shù)據(jù)節(jié)點掉線但主節(jié)點還在這時候集群可能還能響應(yīng)但數(shù)據(jù)完整性已經(jīng)受威脅了。2.2 節(jié)點級指標(biāo)找出拖后腿的那一臺節(jié)點級指標(biāo)我重點關(guān)注四類JVM、磁盤、線程池、以及網(wǎng)絡(luò)。JVM 這塊heap_used_percent超過 75% 就要警惕超過 85% 基本離 OOM 不遠(yuǎn)了。但光看堆使用率不夠還要看 GC 情況。老年代 GC 頻率和耗時是更靈敏的指標(biāo)——如果 full GC 每分鐘超過一次或者單次耗時超過 1 秒說明堆內(nèi)存已經(jīng)嚴(yán)重不足這時候調(diào)大堆或者優(yōu)化查詢才是正事重啟只能續(xù)命幾個小時。磁盤方面ES 默認(rèn)的低水位線是 85%高水位線是 90%。一旦某個節(jié)點磁盤使用率超過 85%ES 就不會再往這個節(jié)點分配新分片了超過 90%它會嘗試把現(xiàn)有分片遷走。所以磁盤告警閾值我一般設(shè)在 80%留出緩沖時間。這里有個經(jīng)驗磁盤使用率的增長往往不是線性的日志類索引在業(yè)務(wù)高峰期可能一天漲 10%所以容量規(guī)劃要按峰值算不能按平均值。線程池指標(biāo)里write和search兩個隊列的 rejected 數(shù)量是核心。rejected 大于 0 說明線程池滿了請求被拒絕用戶側(cè)會直接感知到失敗。search隊列的 rejected 通常意味著查詢太慢或者并發(fā)太高write隊列的 rejected 則可能是批量寫入過大或者磁盤 IO 跟不上。2.3 索引級指標(biāo)定位到具體的問題索引集群和節(jié)點都正常但業(yè)務(wù)方反饋搜索變慢了這時候就得下鉆到索引級別。我重點看三個指標(biāo)索引的search.query_total和search.query_time_in_millis的比值平均查詢耗時、indexing.index_total和indexing.index_time_in_millis的比值平均寫入耗時、以及segments.count和segments.memory_in_bytes。平均查詢耗時突然飆升常見原因有某個大查詢打滿了緩存、分片數(shù)過多導(dǎo)致協(xié)調(diào)節(jié)點壓力大、或者有慢查詢在拖后腿。平均寫入耗時變長可能是 refresh 頻率太高、translog 刷盤慢、或者 merge 壓力大。segments 數(shù)量過多單分片超過 50 個會導(dǎo)致查詢變慢因為每個 segment 都要單獨搜索再合并結(jié)果。下面這張表是我在實際運維中總結(jié)的指標(biāo)-閾值-動作對照表可以直接拿去配告警指標(biāo)警告閾值嚴(yán)重閾值建議動作cluster.statusyellow 持續(xù) 10minred檢查分片分配、節(jié)點狀態(tài)unassigned_shards 0 持續(xù) 5min 10檢查磁盤水位、分配規(guī)則heap_used_percent 75% 85%排查大查詢、考慮擴(kuò)容disk_used_percent 80% 85%清理舊索引、擴(kuò)容磁盤thread_pool.search.rejected 0 100/min優(yōu)化查詢、增加節(jié)點thread_pool.write.rejected 0 100/min降低批量大小、檢查磁盤index.search.avg_time 100ms 500ms分析慢查詢、優(yōu)化映射3. 采集方案選型Exporter、API 輪詢還是 Agent 直采3.1 三種采集方式的本質(zhì)區(qū)別給 ES 做監(jiān)控采集方式無非三種用現(xiàn)成的 exporter 拉指標(biāo)、自己寫腳本輪詢 REST API、或者在每個節(jié)點上部署 agent 直接讀本地指標(biāo)。Exporter 方案最省事社區(qū)有成熟的 elasticsearch_exporter它通過調(diào)用 ES 的_nodes/stats、_cluster/health、_cluster/stats等接口把指標(biāo)轉(zhuǎn)成 Prometheus 格式暴露出來。優(yōu)點是開箱即用指標(biāo)覆蓋全和 Prometheus 生態(tài)無縫集成。缺點是它只能看到 ES 主動暴露的指標(biāo)像操作系統(tǒng)層面的 CPU、內(nèi)存、磁盤 IO 這些拿不到而且如果 ES 集群本身響應(yīng)慢exporter 的采集也會超時。自己寫腳本輪詢的好處是靈活你想采什么就采什么想怎么聚合就怎么聚合。比如你可以寫個 Python 腳本每 30 秒調(diào)一次_cat/indices把每個索引的文檔數(shù)、存儲大小、健康狀態(tài)存到自己的監(jiān)控庫里。但缺點是維護(hù)成本高ES 版本升級接口變了你得跟著改而且輪詢頻率高了會給集群增加額外負(fù)擔(dān)。Agent 直采適合對指標(biāo)精度要求高的場景。在每個 ES 節(jié)點上跑一個輕量 agent直接讀/proc下的系統(tǒng)指標(biāo)同時通過本地回環(huán)調(diào)用 ES 的 API。這樣既能拿到系統(tǒng)層指標(biāo)又能避免網(wǎng)絡(luò)開銷。但部署和升級麻煩節(jié)點多了之后 agent 的生命周期管理是個問題。3.2 我為什么最終選了 Exporter 自定義腳本的組合純 exporter 方案有個硬傷它拿不到操作系統(tǒng)的磁盤 IO 和網(wǎng)絡(luò)指標(biāo)而這兩個恰恰是排查 ES 性能問題的重要線索。比如你發(fā)現(xiàn)寫入變慢了如果只看 ES 的 indexing 指標(biāo)你只能知道慢了但不知道是磁盤 IO 飽和了還是網(wǎng)絡(luò)帶寬打滿了。所以我的做法是用 exporter 采集 ES 自身的指標(biāo)用 node_exporter 采集系統(tǒng)指標(biāo)再寫一個輕量腳本采集一些 exporter 不提供的業(yè)務(wù)指標(biāo)比如特定索引的文檔增長速率。這里有個細(xì)節(jié)要注意exporter 的采集頻率不要設(shè)得太高。我見過有人設(shè)成 5 秒一次結(jié)果 exporter 自己成了集群的負(fù)載來源。ES 的_nodes/stats接口在節(jié)點多的時候響應(yīng)很慢頻繁調(diào)用會占用大量協(xié)調(diào)節(jié)點資源。我的經(jīng)驗值是 30 秒一次對于大多數(shù)場景足夠了。如果確實需要更高精度可以針對關(guān)鍵指標(biāo)單獨提高頻率而不是全量拉取。3.3 采集端的資源隔離不管用哪種采集方式都要記住一個原則監(jiān)控系統(tǒng)本身不能成為被監(jiān)控系統(tǒng)的負(fù)擔(dān)。我一般會把 exporter 部署在獨立的監(jiān)控節(jié)點上不要和 ES 數(shù)據(jù)節(jié)點混部。如果資源實在緊張至少要給 exporter 設(shè)置 CPU 和內(nèi)存限制防止它失控。另外采集超時時間要設(shè)置合理。ES 集群在壓力大的時候API 響應(yīng)可能從幾十毫秒變成幾秒。如果 exporter 的超時設(shè)得太短會頻繁采集失敗導(dǎo)致監(jiān)控數(shù)據(jù)出現(xiàn)空洞設(shè)得太長又會拖慢整個采集周期。我的建議是超時時間設(shè)為采集間隔的三分之一左右比如 30 秒采集一次超時設(shè) 10 秒。4. 告警規(guī)則設(shè)計怎么做到既不漏報又不騷擾4.1 告警分級的核心邏輯告警設(shè)計最怕兩種極端一種是狼來了天天報警但都是誤報最后大家都不看了另一種是沉默的羔羊出大事了卻一聲不吭。要避免這兩種情況核心是做好分級。我把告警分成三級P0 是必須立即處理的比如集群 red、所有數(shù)據(jù)節(jié)點掉線、磁盤使用率超過 95%P1 是需要在工作時間內(nèi)處理的比如 yellow 持續(xù)超過 30 分鐘、堆使用率超過 85%、查詢拒絕率超過 1%P2 是記錄備查的比如單次查詢超時、segment 數(shù)量偏多。P0 走電話短信P1 走即時通訊工具P2 只寫日志和看板。分級的關(guān)鍵在于持續(xù)時間這個維度。瞬時尖峰大多數(shù)時候是正常的比如業(yè)務(wù)高峰期查詢量突增導(dǎo)致隊列短暫排隊這不需要告警。但如果某個指標(biāo)持續(xù)超過閾值一段時間那就說明系統(tǒng)真的有問題了。我一般給 P0 設(shè)置 1 分鐘的持續(xù)時間P1 設(shè)置 5 分鐘P2 設(shè)置 15 分鐘。4.2 那些年我踩過的告警坑第一個坑是磁盤使用率告警風(fēng)暴。有段時間我們集群每天凌晨都會收到一堆磁盤告警查了半天發(fā)現(xiàn)是日志索引的定時滾動任務(wù)在凌晨執(zhí)行滾動瞬間會創(chuàng)建新索引導(dǎo)致磁盤使用率短暫上升。后來我把磁盤告警改成了預(yù)測 4 小時后會超過閾值才觸發(fā)而不是看當(dāng)前值。這個用 Prometheus 的predict_linear函數(shù)就能實現(xiàn)效果立竿見影。第二個坑是JVM 堆告警誤報。ES 的堆使用率是波動的GC 之后會降下來GC 之前會升上去。如果只看瞬時值你會收到大量無意義的告警。正確的做法是看GC 后堆使用率或者看一段時間內(nèi)的平均值。Prometheus 里可以用avg_over_time來做平滑。第三個坑是分片告警漏報。我們曾經(jīng)遇到過某個索引的主分片分配失敗但集群狀態(tài)還是 yellow因為副本分片是正常的。如果只監(jiān)控集群 status這個情況就漏掉了。后來我加了一條規(guī)則監(jiān)控unassigned_shards的變化率如果 5 分鐘內(nèi)從 0 變成大于 0就觸發(fā)告警不管集群狀態(tài)是什么顏色。4.3 告警收斂與靜默策略節(jié)點批量重啟或者滾動升級的時候告警會集中爆發(fā)。這時候需要做告警收斂把同一時間窗口內(nèi)的同類告警合并成一條。Prometheus 的 Alertmanager 支持分組group_by和抑制inhibit規(guī)則可以把集群 red和節(jié)點掉線這兩類告警關(guān)聯(lián)起來只發(fā)一條根因告警。靜默策略也很重要。計劃內(nèi)的維護(hù)操作提前設(shè)置靜默窗口避免誤報。但靜默時間不要太長我一般設(shè) 2 小時到期自動恢復(fù)。如果維護(hù)沒做完手動續(xù)期這樣能防止靜默了但忘了取消導(dǎo)致真正的問題被掩蓋。5. 可視化看板讓數(shù)據(jù)自己說話5.1 看板分層設(shè)計我習(xí)慣把 ES 監(jiān)控看板分成三層全局概覽、節(jié)點詳情、索引詳情。全局概覽放在最上面一屏之內(nèi)要能看到集群健康狀態(tài)、節(jié)點數(shù)量、分片總數(shù)、總文檔數(shù)、總存儲大小、當(dāng)前寫入和查詢 QPS、以及最近 24 小時的告警數(shù)量。這一層是給值班人員看的目的是一眼判斷有沒有問題。節(jié)點詳情層展示每個節(jié)點的 CPU、內(nèi)存、磁盤、JVM 堆、GC 次數(shù)、線程池隊列長度。這一層是給運維人員排查用的目的是定位到具體哪臺機(jī)器有問題。索引詳情層展示每個索引的文檔數(shù)、存儲大小、查詢耗時、寫入耗時、segment 數(shù)量。這一層是給開發(fā)人員優(yōu)化用的目的是找到哪個索引拖慢了整體性能。5.2 那些容易被忽略但很有用的圖表除了常規(guī)的折線圖我強烈建議加幾個非典型圖表。第一個是分片分布熱力圖。用表格形式展示每個節(jié)點上的分片數(shù)量顏色越深表示分片越多。這個圖能快速發(fā)現(xiàn)分片分布不均的問題。我曾經(jīng)遇到過一個節(jié)點上分了 200 多個分片而其他節(jié)點只有幾十個導(dǎo)致那個節(jié)點查詢特別慢。熱力圖一眼就能看出來。第二個是查詢耗時 P99 曲線。平均值會騙人P99 才能反映真實用戶體驗。如果 P99 突然飆升但平均值沒變說明有少量請求遇到了嚴(yán)重延遲這往往是慢查詢或者 GC 導(dǎo)致的。第三個是索引增長趨勢圖。把每天的索引存儲大小畫成曲線再疊加一條線性預(yù)測線能直觀看到什么時候會觸發(fā)磁盤水位線。這個圖對于容量規(guī)劃特別有用比看當(dāng)前使用率有價值得多。5.3 看板的性能優(yōu)化看板本身也會消耗資源。如果看板查詢的數(shù)據(jù)量太大每次刷新都會給 ES 造成壓力。我的做法是對于時間范圍超過 7 天的圖表使用預(yù)聚合數(shù)據(jù)比如 Prometheus 的 recording rules而不是直接查原始指標(biāo)。另外看板的自動刷新間隔不要設(shè)得太短30 秒足夠了設(shè)成 5 秒除了讓屏幕閃得更快之外沒有任何實際意義。6. 從告警到定位一次真實的排查過程復(fù)盤6.1 問題現(xiàn)象某天下午告警群收到一條 P1 告警集群查詢拒絕率超過 1%持續(xù) 5 分鐘。同時業(yè)務(wù)方反饋搜索接口偶爾超時。登錄看板一看thread_pool.search.rejected在幾分鐘內(nèi)從 0 漲到了 300 多search隊列長度也滿了。6.2 排查鏈路第一步看集群和節(jié)點狀態(tài)。集群是 green所有節(jié)點都在線磁盤和 JVM 都正常。這說明不是資源耗盡導(dǎo)致的。第二步看索引級別的查詢指標(biāo)。發(fā)現(xiàn)有一個索引的平均查詢耗時從平時的 20ms 漲到了 800msP99 更是超過了 5 秒。問題定位到這個索引上。第三步看這個索引的 segment 數(shù)量。發(fā)現(xiàn)單分片的 segment 數(shù)量達(dá)到了 120 多個遠(yuǎn)超正常水平。segment 太多會導(dǎo)致查詢時需要遍歷大量小文件性能急劇下降。第四步查為什么 segment 沒有合并???merge 線程池的指標(biāo)發(fā)現(xiàn)thread_pool.force_merge隊列里有大量任務(wù)積壓。再查操作記錄發(fā)現(xiàn)前一天有人手動觸發(fā)了一次全量 force merge但沒等它完成就下班了導(dǎo)致 merge 任務(wù)一直排隊新的寫入又不斷產(chǎn)生新 segment形成了惡性循環(huán)。6.3 解決方案與后續(xù)預(yù)防當(dāng)時的緊急處理是先取消積壓的 force merge 任務(wù)讓 ES 自己按默認(rèn)策略做后臺 merge。然后對那個索引做了一次滾動重啟rollover把寫入切到新索引舊索引不再寫入后 segment 數(shù)量自然穩(wěn)定下來。最后對舊索引做了一次 force merge 到 1 個 segment查詢耗時恢復(fù)到 15ms。后續(xù)預(yù)防措施有三條第一force merge 操作必須走審批流程且只能在業(yè)務(wù)低峰期執(zhí)行第二給 force merge 線程池的隊列長度設(shè)置告警超過 10 就通知第三把 segment 數(shù)量納入日常巡檢指標(biāo)單分片超過 50 個就預(yù)警。這個案例告訴我們監(jiān)控不只是看有沒有掛更要看有沒有在變壞。很多故障不是突然發(fā)生的而是有一個漸進(jìn)的過程只是我們沒有在早期發(fā)現(xiàn)而已。7. 監(jiān)控系統(tǒng)自身的維護(hù)別讓保安先倒下監(jiān)控系統(tǒng)跑起來之后它自己也需要被監(jiān)控。我見過不止一次因為 exporter 掛了或者 Prometheus 磁盤滿了導(dǎo)致整個監(jiān)控體系失效而運維人員渾然不知。最基本的要給監(jiān)控組件本身加上存活檢測。exporter 的/metrics接口如果連續(xù) 3 次采集失敗就觸發(fā)告警。Prometheus 的磁盤使用率也要監(jiān)控因為它的數(shù)據(jù)是只增不減的默認(rèn)保留 15 天如果磁盤規(guī)劃不足很容易寫滿。另外監(jiān)控數(shù)據(jù)的保留策略要合理。ES 的指標(biāo)數(shù)據(jù)量很大如果全量保留一年存儲成本會很高。我的做法是原始指標(biāo)保留 15 天5 分鐘粒度的降采樣數(shù)據(jù)保留 90 天1 小時粒度的降采樣數(shù)據(jù)保留 1 年。這樣既能滿足短期排查需求又能支持長期的趨勢分析。還有一個容易被忽略的點監(jiān)控配置的版本管理。告警規(guī)則、看板配置、采集配置這些都應(yīng)該納入版本控制。每次修改都要有記錄出問題的時候可以快速回滾。我見過有人直接在 Grafana 界面上改看板改錯了想恢復(fù)都找不到之前的版本只能憑記憶重建。最后分享一個我自己的小習(xí)慣每個月做一次監(jiān)控盲區(qū)檢查。具體做法是隨機(jī)挑一個歷史故障案例假設(shè)它現(xiàn)在重新發(fā)生看看現(xiàn)有的監(jiān)控體系能不能在 5 分鐘內(nèi)發(fā)現(xiàn)并定位。如果發(fā)現(xiàn)某個環(huán)節(jié)缺失就補上對應(yīng)的指標(biāo)或告警。這個習(xí)慣幫我堵住了不少漏洞推薦你也試試。