愛伊米

Prometheus “活學活用”,大牛總結了一套避坑指南

監控系統的歷史悠久,是一個很成熟的方向,而 Prometheus 作為新生代的開源監控系統,慢慢成為了雲原生體系的事實標準,也證明了其設計很受歡迎。

本文主要分享在 Prometheus 實踐中遇到的一些問題和思考,如果你對 K8S 監控體系或 Prometheus 的設計還不太瞭解,可以先看下容器監控系列。

容器監控系列:https://yasongxu。gitbook。io/container-monitor/

幾點原則

監控是基礎設施,目的是為了解決問題,不要只朝著大而全去做,尤其是不必要的指標採集,浪費人力和儲存資源(To B商業產品例外)。

需要處理的告警才發出來,發出來的告警必須得到處理。

簡單的架構就是最好的架構,業務系統都掛了,監控也不能掛。Google Sre 裡面也說避免使用 Magic 系統,例如機器學習報警閾值、自動修復之類。這一點見仁見智吧,感覺很多公司都在搞智慧 AI 運維。

Prometheus 的侷限

Prometheus 是基於 Metric 的監控,不適用於日誌(Logs)、事件(Event)、呼叫鏈(Tracing)。

Prometheus 預設是 Pull 模型,合理規劃你的網路,儘量不要轉發。

對於叢集化和水平擴充套件,官方和社群都沒有銀彈,需要合理選擇 Federate、Cortex、Thanos等方案。

監控系統一般情況下可用性大於一致性,容忍部分副本資料丟失,保證查詢請求成功。這個後面說 Thanos 去重的時候會提到。

Prometheus 不一定保證資料準確,這裡的不準確一是指 rate、histogram_quantile 等函式會做統計和推斷,產生一些反直覺的結果,這個後面會詳細展開。二來查詢範圍過長要做降取樣,勢必會造成資料精度丟失,不過這是時序資料的特點,也是不同於日誌系統的地方。

K8S 叢集中常用的 exporter一級標題

Prometheus 屬於 CNCF 專案,擁有完整的開源生態,與 Zabbix 這種傳統 agent 監控不同,它提供了豐富的 exporter 來滿足你的各種需求。

你可以在這裡看到官方、非官方的 exporter。如果還是沒滿足你的需求,你還可以自己編寫 exporter,簡單方便、自由開放,這是優點。

Prometheus:https://prometheus。io/docs/instrumenting/exporters/

但是過於開放就會帶來選型、試錯成本。之前只需要在 zabbix agent裡面幾行配置就能完成的事,現在你會需要很多 exporter 搭配才能完成。還要對所有 exporter 維護、監控。尤其是升級 exporter 版本時,很痛苦。非官方exporter 還會有不少 bug。這是使用上的不足,當然也是 Prometheus 的設計原則。

K8S 生態的元件都會提供/metric介面以提供自監控,這裡列下我們正在使用的:

cadvisor: 整合在 Kubelet 中。

kubelet: 10255為非認證埠,10250為認證埠。

apiserver: 6443埠,關心請求數、延遲等。

scheduler: 10251埠。

controller-manager: 10252埠。

etcd: 如etcd 寫入讀取延遲、儲存容量等。

docker: 需要開啟 experimental 實驗特性,配置 metrics-addr,如容器建立耗時等指標。

kube-proxy: 預設 127 暴露,10249埠。外部採集時可以修改為 0。0。0。0 監聽,會暴露:寫入 iptables 規則的耗時等指標。

kube-state-metrics: K8S 官方專案,採集pod、deployment等資源的元資訊。

node-exporter: Prometheus 官方專案,採集機器指標如 CPU、記憶體、磁碟。

blackbox_exporter: Prometheus 官方專案,網路探測,dns、ping、http監控

process-exporter: 採集程序指標

nvidia exporter: 我們有 gpu 任務,需要 gpu 資料監控

node-problem-detector: 即 npd,準確的說不是 exporter,但也會監測機器狀態,上報節點異常打 taint

應用層 exporter: mysql、nginx、mq等,看業務需求。

還有各種場景下的自定義 exporter,如日誌提取後面會再做介紹。

K8S 核心元件監控與 Grafana 面板

k8s 叢集執行中需要關注核心元件的狀態、效能。如 kubelet、apiserver 等,基於上面提到的 exporter 的指標,可以在 Grafana 中繪製如下圖表:

Prometheus “活學活用”,大牛總結了一套避坑指南

Prometheus “活學活用”,大牛總結了一套避坑指南

Prometheus “活學活用”,大牛總結了一套避坑指南

Prometheus “活學活用”,大牛總結了一套避坑指南

模板可以參考dashboards-for-kubernetes-administrators,根據執行情況不斷調整報警閾值。

dashboards-for-kubernetes-administrators:https://povilasv。me/grafana-dashboards-for-kubernetes-administrators

這裡提一下 Grafana 雖然支援了 templates 能力,可以很方便地做多級下拉框選擇,但是不支援templates 模式下配置報警規則,相關issue

issue:https://github。com/grafana/grafana/issues/9334

官方對這個功能解釋了一堆,可最新版本仍然沒有支援。借用 issue 的一句話吐槽下:

關於 Grafana 的基礎用法,可以看看《容器監控實踐—Grafana》。

採集元件 All IN One

Prometheus 體系中 Exporter 都是獨立的,每個元件各司其職,如機器資源用 Node-Exporter,Gpu 有Nvidia Exporter等等。但是 Exporter 越多,運維壓力越大,尤其是對 Agent做資源控制、版本升級。我們嘗試對一些Exporter進行組合,方案有二:

透過主程序拉起N個 Exporter 程序,仍然可以跟著社群版本做更新、bug fix。

用Telegraf來支援各種型別的 Input,N 合 1。

另外,Node-Exporter 不支援程序監控,可以加一個Process-Exporter,也可以用上邊提到的Telegraf,使用 procstat 的 input來採集程序指標。

合理選擇黃金指標

採集的指標有很多,我們應該關注哪些?Google 在“Sre Handbook”中提出了“四個黃金訊號”:延遲、流量、錯誤數、飽和度。實際操作中可以使用 Use 或 Red 方法作為指導,Use 用於資源,Red 用於服務。

Use 方法:Utilization、Saturation、Errors。如 Cadvisor 資料

Red 方法:Rate、Errors、Duration。如 Apiserver 效能指標

Prometheus 採集中常見的服務分三種:

線上服務:如 Web 服務、資料庫等,一般關心請求速率,延遲和錯誤率即 RED 方法。

離線服務:如日誌處理、訊息佇列等,一般關注佇列數量、進行中的數量,處理速度以及發生的錯誤即 Use 方法。

批處理任務:和離線任務很像,但是離線任務是長期執行的,批處理任務是按計劃執行的,如持續整合就是批處理任務,對應 K8S 中的 job 或 cronjob, 一般關注所花的時間、錯誤數等,因為執行週期短,很可能還沒采集到就執行結束了,所以一般使用 Pushgateway,改拉為推。

對 Use 和 Red 的實際示例可以參考容器監控實踐—K8S常用指標分析這篇文章。

K8S 1.16中 Cadvisor 的指標相容問題

在 K8S 1。16版本,Cadvisor 的指標去掉了 pod_Name 和 container_name 的 label,替換為了pod 和 container。如果你之前用這兩個 label 做查詢或者 Grafana 繪圖,需要更改下 Sql 了。因為我們一直支援多個 K8S 版本,就透過 relabel配置繼續保留了原來的**_name。

注意要用 metric_relabel_configs,不是 relabel_configs,採集後做的replace。

Prometheus採集外部K8S叢集、多叢集

Prometheus 如果部署在K8S叢集內採集是很方便的,用官方給的Yaml就可以,但我們因為許可權和網路需要部署在叢集外,二進位制執行,採集多個 K8S 叢集。

以 Pod 方式執行在叢集內是不需要證書的(In-Cluster 模式),但叢集外需要宣告 token之類的證書,並替換address,即使用 Apiserver Proxy採集,以 Cadvisor採集為例,Job 配置為:

Prometheus “活學活用”,大牛總結了一套避坑指南

Prometheus “活學活用”,大牛總結了一套避坑指南

bearer_token_file 需要提前生成,這個參考官方文件即可。記得 base64 解碼。

對於 cadvisor 來說,__metrics_path__可以轉換為/api/v1/nodes/$/proxy/metrics/cadvisor,代表Apiserver proxy 到 Kubelet,如果網路能通,其實也可以直接把 Kubelet 的10255作為 target,可以直接寫為:

$:10255/metrics/cadvisor,代表直接請求Kubelet,規模大的時候還減輕了 Apiserver 的壓力,即服務發現使用 Apiserver,採集不走 Apiserver。

因為 cadvisor 是暴露主機埠,配置相對簡單,如果是 kube-state-metric 這種 Deployment,以 endpoint 形式暴露,寫法應該是:

Prometheus “活學活用”,大牛總結了一套避坑指南

Prometheus “活學活用”,大牛總結了一套避坑指南

Prometheus “活學活用”,大牛總結了一套避坑指南

對於 endpoint 型別,需要轉換__metrics_path__為/api/v1/namespaces/$/services/$:$/proxy/metrics,需要替換 namespace、svc 名稱埠等,這裡的寫法只適合介面為/metrics的exporter,如果你的 exporter 不是/metrics介面,需要替換這個路徑。或者像我們一樣統一約束都使用這個地址。

這裡的__meta_kubernetes_service_annotation_prometheus_io_port來源就是 exporter 部署時寫的那個 annotation,大多數文章中只提到prometheus。io/scrape: ‘true’,但也可以定義埠、路徑、協議。以方便在採集時做替換處理。

其他的一些 relabel 如kubernetes_namespace 是為了保留原始資訊,方便做 promql 查詢時的篩選條件。

如果是多叢集,同樣的配置多寫幾遍就可以了,一般一個叢集可以配置三類job:

role:node 的,包括 cadvisor、 node-exporter、kubelet 的 summary、kube-proxy、docker 等指標。

role:endpoint 的,包括 kube-state-metric 以及其他自定義 Exporter。

普通採集:包括Etcd、Apiserver 效能指標、程序指標等。

GPU 指標的獲取

nvidia-smi可以檢視機器上的 GPU 資源,而Cadvisor 其實暴露了Metric來表示容器使用 GPU 情況,

如果要更詳細的 GPU 資料,可以安裝dcgm exporter,不過K8S 1。13 才能支援。

更改 Prometheus 的顯示時區

Prometheus 為避免時區混亂,在所有元件中專門使用 Unix Time 和 Utc 進行顯示。不支援在配置檔案中設定時區,也不能讀取本機 /etc/timezone 時區。

其實這個限制是不影響使用的:

如果做視覺化,Grafana是可以做時區轉換的。

如果是調介面,拿到了資料中的時間戳,你想怎麼處理都可以。

如果因為 Prometheus 自帶的 UI 不是本地時間,看著不舒服,2。16 版本的新版 Web UI已經引入了Local Timezone 的選項,區別見下圖。

如果你仍然想改 Prometheus 程式碼來適應自己的時區,可以參考《修改原始碼更改prometheus的時區問題》。

2。16 版本:

https://github。com/prometheus/prometheus/commit/d996ba20ec9c7f1808823a047ed9d5ce96be3d8f

修改原始碼更改prometheus的時區問題:

https://zhangguanzhang。github。io/2019/09/05/prometheus-change-timezone/

Prometheus “活學活用”,大牛總結了一套避坑指南

Prometheus “活學活用”,大牛總結了一套避坑指南

關於 timezone 的討論,可以看這個issue。

issue:https://github。com/prometheus/prometheus/issues/500

如何採集 LB 後面的 RS 的 Metric

假如你有一個負載均衡 LB,但網路上 Prometheus 只能訪問到 LB 本身,訪問不到後面的 RS,應該如何採集 RS 暴露的 Metric?

RS 的服務加 Sidecar Proxy,或者本機增加 Proxy 元件,保證 Prometheus 能訪問到。

LB 增加 /backend1 和 /backend2請求轉發到兩個單獨的後端,再由 Prometheus 訪問 LB 採集。

版本的選擇

Prometheus 當前最新版本為 2。16,Prometheus 還在不斷迭代,因此儘量用最新版,1。X版本就不用考慮了。

2。16 版本上有一套實驗 UI,可以檢視 TSDB 的狀態,包括Top 10的 Label、Metric。

Prometheus “活學活用”,大牛總結了一套避坑指南

Prometheus 大記憶體問題

隨著規模變大,Prometheus 需要的 CPU 和記憶體都會升高,記憶體一般先達到瓶頸,這個時候要麼加記憶體,要麼叢集分片減少單機指標。這裡我們先討論單機版 Prometheus 的記憶體問題。

原因:

Prometheus 的記憶體消耗主要是因為每隔2小時做一個 Block 資料落盤,落盤之前所有資料都在記憶體裡面,因此和採集量有關。

載入歷史資料時,是從磁碟到記憶體的,查詢範圍越大,記憶體越大。這裡面有一定的最佳化空間。

一些不合理的查詢條件也會加大記憶體,如 Group 或大範圍 Rate。

我的指標需要多少記憶體:

作者給了一個計算器,設定指標量、採集間隔之類的,計算 Prometheus 需要的理論記憶體值:計算公式。

計算公式:https://www。robustperception。io/how-much-ram-does-prometheus-2-x-need-for-cardinality-and-ingestion

以我們的一個 Prometheus Server為例,本地只保留 2 小時資料,95 萬 Series,大概佔用的記憶體如下:

有什麼最佳化方案:

Sample 數量超過了 200 萬,就不要單例項了,做下分片,然後透過 Victoriametrics,Thanos,Trickster 等方案合併資料。

評估哪些 Metric 和 Label 佔用較多,去掉沒用的指標。2。14 以上可以看 Tsdb 狀態。

查詢時儘量避免大範圍查詢,注意時間範圍和 Step 的比例,慎用 Group。

如果需要關聯查詢,先想想能不能透過 Relabel 的方式給原始資料多加個 Label,一條Sql 能查出來的何必用Join,時序資料庫不是關係資料庫。

Prometheus 記憶體佔用分析:

透過 pprof分析:https://www。robustperception。io/optimising-prometheus-2-6-0-memory-usage-with-pprof

1。X 版本的記憶體:https://www。robustperception。io/how-much-ram-does-my-prometheus-need-for-ingestion

相關 issue:

https://groups。google。com/forum/#!searchin/prometheus-users/memory%7Csort:date/prometheus-users/q4oiVGU6Bxo/uifpXVw3CwAJ

https://github。com/prometheus/prometheus/issues/5723

https://github。com/prometheus/prometheus/issues/1881

Prometheus 容量規劃

容量規劃除了上邊說的記憶體,還有磁碟儲存規劃,這和你的 Prometheus 的架構方案有關。

如果是單機Prometheus,計算本地磁碟使用量。

如果是 Remote-Write,和已有的 Tsdb 共用即可。

如果是 Thanos 方案,本地磁碟可以忽略(2H),計算物件儲存的大小就行。

Prometheus 每2小時將已緩衝在記憶體中的資料壓縮到磁碟上的塊中。包括Chunks、Indexes、Tombstones、Metadata,這些佔用了一部分儲存空間。一般情況下,Prometheus中儲存的每一個樣本大概佔用1-2位元組大小(1。7Byte)。可以透過Promql來檢視每個樣本平均佔用多少空間:

“`bash

rate(prometheus_tsdb_compaction_chunk_size_bytes_sum[1h])

/

rate(prometheus_tsdb_compaction_chunk_samples_sum[1h])

“`

如果大致估算本地磁碟大小,可以透過以下公式:

保留時間(retention_time_seconds)和樣本大小(bytes_per_sample)不變的情況下,如果想減少本地磁碟的容量需求,只能透過減少每秒獲取樣本數(ingested_samples_per_second)的方式。

檢視當前每秒獲取的樣本數:

有兩種手段,一是減少時間序列的數量,二是增加採集樣本的時間間隔。考慮到 Prometheus 會對時間序列進行壓縮,因此減少時間序列的數量效果更明顯。

舉例說明:

採集頻率 30s,機器數量1000,Metric種類6000,1000600026024 約 200 億,30G 左右磁碟。

只採集需要的指標,如 match[], 或者統計下最常使用的指標,效能最差的指標。

以上磁碟容量並沒有把 wal 檔案算進去,wal 檔案(Raw Data)在 Prometheus 官方文件中說明至少會儲存3個 Write-Ahead Log Files,每一個最大為128M(實際執行發現數量會更多)。

因為我們使用了 Thanos 的方案,所以本地磁碟只保留2H 熱資料。Wal 每2小時生成一份Block檔案,Block檔案每2小時上傳物件儲存,本地磁碟基本沒有壓力。

關於 Prometheus 儲存機制,可以看《容器監控實踐—Prometheus儲存機制》。

對 Apiserver 的效能影響

如果你的 Prometheus 使用了 kubernetes_sd_config 做服務發現,請求一般會經過叢集的 Apiserver,隨著規模的變大,需要評估下對 Apiserver效能的影響,尤其是Proxy失敗的時候,會導致CPU 升高。當然了,如果單K8S叢集規模太大,一般都是拆分叢集,不過隨時監測下 Apiserver 的程序變化還是有必要的。

在監控Cadvisor、Docker、Kube-Proxy 的 Metric 時,我們一開始選擇從 Apiserver Proxy 到節點的對應埠,統一設定比較方便,但後來還是改為了直接拉取節點,Apiserver 僅做服務發現。

Rate 的計算邏輯

Prometheus 中的 Counter 型別主要是為了 Rate 而存在的,即計算速率,單純的 Counter 計數意義不大,因為 Counter 一旦重置,總計數就沒有意義了。

Rate 會自動處理 Counter 重置的問題,Counter 一般都是一直變大的,例如一個 Exporter 啟動,然後崩潰了。本來以每秒大約10的速率遞增,但僅運行了半個小時,則速率(x_total [1h])將返回大約每秒5的結果。另外,Counter 的任何減少也會被視為 Counter 重置。例如,如果時間序列的值為[5,10,4,6],則將其視為[5,10,14,16]。

Rate 值很少是精確的。由於針對不同目標的抓取發生在不同的時間,因此隨著時間的流逝會發生抖動,query_range 計算時很少會與抓取時間完美匹配,並且抓取有可能失敗。面對這樣的挑戰,Rate 的設計必須是健壯的。

Rate 並非想要捕獲每個增量,因為有時候增量會丟失,例如例項在抓取間隔中掛掉。如果 Counter 的變化速度很慢,例如每小時僅增加幾次,則可能會導致【假象】。比如出現一個 Counter 時間序列,值為100,Rate 就不知道這些增量是現在的值,還是目標已經運行了好幾年並且才剛剛開始返回。

建議將 Rate 計算的範圍向量的時間至少設為抓取間隔的四倍。這將確保即使抓取速度緩慢,且發生了一次抓取故障,您也始終可以使用兩個樣本。此類問題在實踐中經常出現,因此保持這種彈性非常重要。例如,對於1分鐘的抓取間隔,您可以使用4分鐘的 Rate 計算,但是通常將其四捨五入為5分鐘。

如果 Rate 的時間區間內有資料缺失,他會基於趨勢進行推測,比如:

Prometheus “活學活用”,大牛總結了一套避坑指南

反直覺的 P95 統計

histogram_quantile 是 Prometheus 常用的一個函式,比如經常把某個服務的 P95 響應時間來衡量服務質量。不過它到底是什麼意思很難解釋得清,特別是面向非技術的同學,會遇到很多“靈魂拷問”。

我們常說 P95(P99,P90都可以) 響應延遲是 100ms,實際上是指對於收集到的所有響應延遲,有 5% 的請求大於 100ms,95% 的請求小於 100ms。Prometheus 裡面的 histogram_quantile 函式接收的是 0-1 之間的小數,將這個小數乘以 100 就能很容易得到對應的百分位數,比如 0。95 就對應著 P95,而且還可以高於百分位數的精度,比如 0。9999。

當你用 histogram_quantile 畫出響應時間的趨勢圖時,可能會被問:為什麼P95大於或小於我的平均值?

正如中位數可能比平均數大也可能比平均數小,P99 比平均值小也是完全有可能的。通常情況下 P99 幾乎總是比平均值要大的,但是如果資料分佈比較極端,最大的 1% 可能大得離譜從而拉高了平均值。一種可能的例子:

服務 X 由順序的 A,B 兩個步驟完成,其中 X 的 P99 耗時 100Ms,A 過程 P99 耗時 50Ms,那麼推測 B 過程的 P99 耗時情況是?

直覺上來看,因為有 X=A+B,所以答案可能是 50Ms,或者至少應該要小於 50Ms。實際上 B 是可以大於 50Ms 的,只要 A 和 B 最大的 1% 不恰好遇到,B 完全可以有很大的 P99:

所以我們從題目唯一能確定的只有 B 的 P99 應該不能超過 100ms,A 的 P99 耗時 50Ms 這個條件其實沒啥用。

類似的疑問很多,因此對於 histogram_quantile 函式,可能會產生反直覺的一些結果,最好的處理辦法是不斷試驗調整你的 Bucket 的值,保證更多的請求時間落在更細緻的區間內,這樣的請求時間才有統計意義。

慢查詢問題

Prometheus 提供了自定義的 Promql 作為查詢語句,在 Graph 上除錯的時候,會告訴你這條 Sql 的返回時間,如果太慢你就要注意了,可能是你的用法出現了問題。

評估 Prometheus 的整體響應時間,可以用這個預設指標:

一般情況下響應過慢都是Promql 使用不當導致,或者指標規劃有問題,如:

大量使用 join 來組合指標或者增加 label,如將 kube-state-metric 中的一些 meta label和 node-exporter 中的節點屬性 label加入到 cadvisor容器資料裡,像統計 pod 記憶體使用率並按照所屬節點的機器型別分類,或按照所屬 rs 歸類。

範圍查詢時,大的時間範圍 step 值卻很小,導致查詢到的數量過大。

rate 會自動處理 counter 重置的問題,最好由 promql 完成,不要自己拿出來全部元資料在程式中自己做 rate 計算。

在使用 rate 時,range duration要大於等於step,否則會丟失部分資料

prometheus 是有基本預測功能的,如deriv和predict_linear(更準確)可以根據已有資料預測未來趨勢

如果比較複雜且耗時的sql,可以使用 record rule 減少指標數量,並使查詢效率更高,但不要什麼指標都加 record,一半以上的 metric 其實不太會查詢到。同時 label 中的值不要加到 record rule 的 name 中。

step:https://www。robustperception。io/step-and-query_range

部分資料:https://chanjarster。github。io/post/p8s-step-param/

高基數問題 Cardinality

高基數是資料庫避不開的一個話題,對於 Mysql 這種 DB 來講,基數是指特定列或欄位中包含的唯一值的數量。基數越低,列中重複的元素越多。對於時序資料庫而言,就是 tags、label 這種標籤值的數量多少。

比如 Prometheus 中如果有一個指標 http_request_count表示訪問量,method 表示請求方法,originIP 是客戶端 IP,method的列舉值是有限的,但 originIP 卻是無限的,加上其他 label 的排列組合就無窮大了,也沒有任何關聯特徵,因此這種高基數不適合作為 Metric 的 label,真要的提取originIP,應該用日誌的方式,而不是 Metric 監控。

時序資料庫會為這些 Label 建立索引,以提高查詢效能,以便您可以快速找到與所有指定標籤匹配的值。如果值的數量過多,索引是沒有意義的,尤其是做 P95 等計算的時候,要掃描大量 Series 資料。

官方文件中對於Label 的建議

如何檢視當前的Label 分佈情況呢,可以使用 Prometheus提供的Tsdb工具。可以使用命令列檢視,也可以在 2。16 版本以上的 Prometheus Graph 檢視

Prometheus “活學活用”,大牛總結了一套避坑指南

top10 高基數的 metric

高基數的 label

找到最大的 metric 或 job

top10的 metric 數量:按 metric 名字分

top10的 metric 數量:按 job 名字分

Prometheus 重啟慢與熱載入

Prometheus 重啟的時候需要把 Wal 中的內容 Load 到記憶體裡,保留時間越久、Wal 檔案越大,重啟的實際越長,這個是 Prometheus 的機制,沒得辦法,因此能 Reload 的就不要重啟,重啟一定會導致短時間的不可用,而這個時候Prometheus高可用就很重要了。

Prometheus 也曾經對啟動時間做過最佳化,在 2。6 版本中對於Wal的 Load 速度就做過速度的最佳化,希望重啟的時間不超過 1 分鐘。

1分鐘:https://www。robustperception。io/optimising-startup-time-of-prometheus-2-6-0-with-pprof

Prometheus 提供了熱載入能力,不過需要開啟web。enable-lifecycle配置,更改完配置後,curl 下 reload 介面即可。prometheus-operator 中更改了配置會預設觸發 reload,如果你沒有使用operator,又希望可以監聽 configmap 配置變化來 reload 服務,可以試下這個簡單的指令碼。

Prometheus “活學活用”,大牛總結了一套避坑指南

使用時和 prometheus 掛載同一個 configmap,傳入如下引數即可:

你的應用需要暴露多少指標

當你開發自己的服務的時候,你可能會把一些資料暴露 Metric出去,比如特定請求數、Goroutine 數等,指標數量多少合適呢?

雖然指標數量和你的應用規模相關,但也有一些建議(Brian Brazil),比如簡單的服務如快取等,類似 Pushgateway,大約 120 個指標,Prometheus 本身暴露了 700 左右的指標,如果你的應用很大,也儘量不要超過 10000 個指標,需要合理控制你的 Label。

建議(Brian Brazil):https://www。robustperception。io/how-many-metrics-should-an-application-return

node-exporter 的問題

node-exporter 不支援程序監控,這個前面已經提到了。

node-exporter 只支援 unix 系統,windows機器 請使用 wmi_exporter。因此以 yaml 形式不是 node-exporter 的時候,node-selector 要表明os型別。

因為node_exporter是比較老的元件,有一些最佳實踐並沒有merge進去,比如符合Prometheus命名規範,因此建議使用較新的0。16和0。17版本。

命名規範:https://prometheus。io/docs/practices/naming/

一些指標名字的變化:

如果你之前用的舊版本 exporter,在繪製 grafana 的時候指標名稱就會有差別,解決方法有兩種:

一是在機器上啟動兩個版本的node-exporter,都讓prometheus去採集。

二是使用指標轉換器,他會將舊指標名稱轉換為新指標

指標轉換器:https://github。com/prometheus/node_exporter/blob/master/docs/example-16-compatibility-rules。yml

kube-state-metric 的問題

除了文章中提到的作用,kube-state-metric還有一個很重要的使用場景,就是和 cadvisor 指標組合,原始的 cadvisor 中只有 pod 資訊,不知道屬於哪個 deployment 或者 sts,但是和kube-state-metric 中的 kube_pod_info 做 join 查詢之後就可以顯示出來,kube-state-metric的元資料指標,在擴充套件 cadvisor 的 label 中起到了很多作用,prometheus-operator 的很多 record rule 就使用了 kube-state-metric 做組合查詢。

kube-state-metric 中也可以展示 pod 的 label 資訊,可以在拿到 cadvisor 資料後更方便地做 group by,如按照 pod 的執行環境分類。但是 kube-state-metric 不暴露 pod 的 annotation,原因是下面會提到的高基數問題,即 annotation 的內容太多,不適合作為指標暴露。

relabel_configs

relabel_configs 與 metric_relabel_configs

relabel_config 發生在採集之前,metric_relabel_configs 發生在採集之後,合理搭配可以滿足很多場景的配置。

如:

Prometheus 的預測能力

場景1:你的磁碟剩餘空間一直在減少,並且降低的速度比較均勻,你希望知道大概多久之後達到閾值,並希望在某一個時刻報警出來。

場景2:你的 Pod 記憶體使用率一直升高,你希望知道大概多久之後會到達 Limit 值,並在一定時刻報警出來,在被殺掉之前上去排查。

Prometheus 的 Deriv 和 Predict_Linear 方法可以滿足這類需求, Promtheus 提供了基礎的預測能力,基於當前的變化速度,推測一段時間後的值。

以 mem_free 為例,最近一小時的 free 值一直在下降。

Prometheus “活學活用”,大牛總結了一套避坑指南

deriv函式可以顯示指標在一段時間的變化速度:

Prometheus “活學活用”,大牛總結了一套避坑指南

predict_linear方法是預測基於這種速度,最後可以達到的值:

你可以基於設定合理的報警規則,如小於 10 時報警:

predict_linear 與 deriv 的關係: 含義上約等於如下表達式,不過 predict_linear 稍微準確一些。

如果你要基於 Metric做模型預測,可以參考下forecast-prometheus。

forecast-prometheus:https://github。com/nfrumkin/forecast-prometheus

alertmanager 的上層封裝

Prometheus 部署之後很少會改動,尤其是做了服務發現,就不需要頻繁新增 target。但報警的配置是很頻繁的,如修改閾值、修改報警人等。alertmanager 擁有豐富的報警能力如分組、抑制等,但如果你要想把他給業務部門使用,就要做一層封裝了,也就是報警配置臺。使用者喜歡錶單操作,而非晦澀的 yaml,同時他們也並不願意去理解 promql。而且大多數公司內已經有現成的監控平臺,也只有一份簡訊或郵件閘道器,所以最好能使用 webhook 直接整合。

例如: 機器磁碟使用量超過 90% 就報警,rule 應該寫為:disk_used/disk_total > 0。9

如果不加 label 篩選,這條報警會對所有機器生效,但如果你想去掉其中幾臺機器,就得在disk_used和disk_total後面加上。這些操作在 promql 中是很簡單的,但是如果放在表單裡操作,就得和內部的 cmdb 做聯動篩選了。

對於一些簡單的需求,我們使用了 Grafana 的報警能力,所見即所得,直接在圖表下面配置告警即可,報警閾值和狀態很清晰。不過 Grafana 的報警能力很弱,只是實驗功能,可以作為除錯使用。

對於常見的 pod 或應用監控,我們做了一些表單化,如下圖所示:提取了 CPU、記憶體、磁碟 IO 等常見的指標作為選擇項,方便配置。

Prometheus “活學活用”,大牛總結了一套避坑指南

使用 webhook 擴充套件報警能力,改造 alertmanager, 在 send message 時做加密和認證,對接內部已有報警能力,並聯動使用者體系,做限流和許可權控制。

呼叫 alertmanager api 查詢報警事件,進行展示和統計。

對於使用者來說,封裝 alertmanager yaml 會變的易用,但也會限制其能力,在增加報警配置時,研發和運維需要有一定的配合。如新寫了一份自定義的 exporter,要將需要的指標供使用者選擇,並調整好展示和報警用的 promql。還有報警模板、原生 promql 暴露、使用者分組等,需要視使用者需求做權衡。

錯誤的高可用設計

有些人提出過這種型別的方案,想提高其擴充套件性和可用性。

應用程式將 Metric 推到到訊息佇列如 Kafaka,然後經過 Exposer 消費中轉,再被 Prometheus 拉取。產生這種方案的原因一般是有歷史包袱、複用現有元件、想透過 Mq 來提高擴充套件性。

這種方案有幾個問題:

增加了 Queue 元件,多了一層依賴,如果 App與 Queue 之間連線失敗,難道要在 App 本地快取監控資料?

抓取時間可能會不同步,延遲的資料將會被標記為陳舊資料,當然你可以透過新增時間戳來標識,但就失去了對陳舊資料的處理邏輯。

擴充套件性問題:Prometheus 適合大量小目標,而不是一個大目標,如果你把所有資料都放在了 Exposer 中,那麼 Prometheus 的單個 Job 拉取就會成為 CPU 瓶頸。這個和 Pushgateway 有些類似,沒有特別必要的場景,都不是官方建議的方式。

缺少了服務發現和拉取控制,Prom 只知道一個 Exposer,不知道具體是哪些 Target,不知道他們的 UP 時間,無法使用 Scrape_* 等指標做查詢,也無法用scrape_limit做限制。

處理邏輯:https://www。robustperception。io/staleness-and-promql

scrape_limit:https://www。robustperception。io/using-sample_limit-to-avoid-overload

如果你的架構和 Prometheus 的設計理念相悖,可能要重新設計一下方案了,否則擴充套件性和可靠性反而會降低。

prometheus-operator 的場景

如果你是在 K8S 叢集內部署 Prometheus,那大機率會用到 prometheus-operator,他對 Prometheus 的配置做了 CRD 封裝,讓使用者更方便的擴充套件 Prometheus例項,同時 prometheus-operator 還提供了豐富的 Grafana 模板,包括上面提到的 master 元件監控的 Grafana 檢視,operator 啟動之後就可以直接使用,免去了配置面板的煩惱。

operator 的優點很多,就不一一列舉了,只提一下 operator 的侷限:

因為是 operator,所以依賴 K8S 叢集,如果你需要二進位制部署你的 Prometheus,如叢集外部署,就很難用上prometheus-operator了,如多叢集場景。當然你也可以在 K8S 叢集中部署 operator 去監控其他的 K8S 叢集,但這裡面坑不少,需要修改一些配置。

operator 遮蔽了太多細節,這個對使用者是好事,但對於理解 Prometheus 架構就有些 gap 了,比如碰到一些使用者一鍵安裝了operator,但 Grafana 圖表異常後完全不知道如何排查,record rule 和 服務發現還不瞭解的情況下就直接配置,建議在使用 operator 之前,最好熟悉 prometheus 的基礎用法。

operator 方便了 Prometheus 的擴充套件和配置,對於 alertmanager 和 exporter 可以很方便的做到多例項高可用,但是沒有解決 Prometheus 的高可用問題,因為無法處理資料不一致,operator目前的定位也還不是這個方向,和 Thanos、Cortex 等方案的定位是不同的,下面會詳細解釋。

高可用方案

Prometheus 高可用有幾種方案:

基本 HA:即兩套 Prometheus 採集完全一樣的資料,外邊掛負載均衡

HA + 遠端儲存:除了基礎的多副本 Prometheus,還透過 Remote Write 寫入到遠端儲存,解決儲存持久化問題

聯邦叢集:即 Federation,按照功能進行分割槽,不同的 Shard 採集不同的資料,由Global節點來統一存放,解決監控資料規模的問題。

使用 Thanos 或者 Victoriametrics,來解決全域性查詢、多副本資料 Join 問題。

就算使用官方建議的多副本 + 聯邦,仍然會遇到一些問題:

官方建議資料做 Shard,然後透過Federation來實現高可用,

但是邊緣節點和Global節點依然是單點,需要自行決定是否每一層都要使用雙節點重複採集進行保活。也就是仍然會有單機瓶頸。

另外部分敏感報警儘量不要透過Global節點觸發,畢竟從Shard節點到Global節點傳輸鏈路的穩定性會影響資料到達的效率,進而導致報警實效降低。

例如服務Updown狀態,Api請求異常這類報警我們都放在Shard節點進行報警。

本質原因是,Prometheus 的本地儲存沒有資料同步能力,要在保證可用性的前提下,再保持資料一致性是比較困難的,基礎的 HA Proxy 滿足不了要求,比如:

叢集的後端有 A 和 B 兩個例項,A 和 B 之間沒有資料同步。A 宕機一段時間,丟失了一部分資料,如果負載均衡正常輪詢,請求打到A 上時,資料就會異常。

如果 A 和 B 的啟動時間不同,時鐘不同,那麼採集同樣的資料時間戳也不同,就不是多副本同樣資料的概念了

就算用了遠端儲存,A 和 B 不能推送到同一個 TSDB,如果每人推送自己的 TSDB,資料查詢走哪邊就是問題了。

因此解決方案是在儲存、查詢兩個角度上保證資料的一致:

儲存角度:如果使用 Remote Write 遠端儲存, A 和 B後面可以都加一個 Adapter,Adapter做選主邏輯,只有一份資料能推送到 TSDB,這樣可以保證一個異常,另一個也能推送成功,資料不丟,同時遠端儲存只有一份,是共享資料。

查詢角度:上邊的方案實現很複雜且有一定風險,因此現在的大多數方案在查詢層面做文章,比如 Thanos 或者 Victoriametrics,仍然是兩份資料,但是查詢時做資料去重和 Join。只是 Thanos 是透過 Sidecar 把資料放在物件儲存,Victoriametrics 是把資料 Remote Write 到自己的 Server 例項,但查詢層 Thanos-Query 和 Victor 的 Promxy的邏輯基本一致。

儲存方案:https://blog。timescale。com/blog/prometheus-ha-postgresql-8de68d19b6f5/

我們採用了 Thanos 來支援多地域監控資料。

高可用 Prometheus:Thanos 實踐:

容器日誌與事件

本文主要是 Prometheus 監控內容, 這裡只簡單介紹下 K8S 中的日誌、事件處理方案,以及和 Prometheus 的搭配。

日誌處理:

日誌採集與推送:一般是Fluentd/Fluent-Bit/Filebeat等採集推送到 ES、物件儲存、kafaka,日誌就該交給專業的 EFK 來做,分為容器標準輸出、容器內日誌。

日誌解析轉 metric:可以提取一些日誌轉為 Prometheus 格式的指標,如解析特定字串出現次數,解析 Nginx 日誌得到 QPS 、請求延遲等。常用方案是 mtail 或者 grok

日誌採集方案:

sidecar 方式:和業務容器共享日誌目錄,由 sidecar 完成日誌推送,一般用於多租戶場景。

daemonset 方式:機器上執行採集程序,統一推送出去。

需要注意的點:對於容器標準輸出,預設日誌路徑是/var/lib/docker/containers/xxx, kubelet 會將改日誌軟鏈到/var/log/pods,同時還有一份/var/log/containers 是對/var/log/pods的軟鏈。不過不同的 K8S 版本,日誌的目錄格式有所變化,採集時根據版本做區分:

1。15 及以下:/var/log/pods//

1。15 以上:var/log/pods//

事件:在這裡特指 K8S Events,Events 在排查叢集問題時也很關鍵,不過預設情況下只保留 1h,因此需要對 Events 做持久化。一般 Events 處理方式有兩種:

使用 kube-eventer 之類的元件採集 Events 並推送到 ES

使用 event_exporter 之類的元件將Events 轉化為 Prometheus Metric,同類型的還有谷歌雲的 stackdriver 下的 event-exporter

kube-eventer :https://github。com/AliyunContainerService/kube-eventer

event_exporter:https://github。com/caicloud/event_exporter

event-exporter:

https://github。com/GoogleCloudPlatform/k8s-stackdriver/tree/master/event-exporter