Prometheus 如何「活學活用」?大牛總結的避坑指南來了

做者:徐亞鬆   原文:http://www.xuyasong.com/?p=1921

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

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

幾點原則:nginx

  • 監控是基礎設施,目的是爲了解決問題,不要只朝着大而全去作,尤爲是沒必要要的指標採集,浪費人力和存儲資源(To B商業產品例外)。
  • 須要處理的告警才發出來,發出來的告警必須獲得處理。
  • 簡單的架構就是最好的架構,業務系統都掛了,監控也不能掛。Google Sre 裏面也說避免使用 Magic 系統,例如機器學習報警閾值、自動修復之類。這一點見仁見智吧,感受不少公司都在搞智能 AI 運維。

1、版本的選擇

Prometheus 當前最新版本爲 2.16,Prometheus 還在不斷迭代,所以儘可能用最新版,1.X版本就不用考慮了。git

2.16 版本上有一套實驗 UI,能夠查看 TSDB 的狀態,包括Top 10的 Label、Metric.github

2、Prometheus 的侷限

  • Prometheus 是基於 Metric 的監控,不適用於日誌(Logs)、事件(Event)、調用鏈(Tracing)。
  • Prometheus 默認是 Pull 模型,合理規劃你的網絡,儘可能不要轉發。
  • 對於集羣化和水平擴展,官方和社區都沒有銀彈,須要合理選擇 Federate、Cortex、Thanos等方案。
  • 監控系統通常狀況下可用性大於一致性,容忍部分副本數據丟失,保證查詢請求成功。這個後面說 Thanos 去重的時候會提到。
  • Prometheus 不必定保證數據準確,這裏的不許確一是指 rate、histogram_quantile 等函數會作統計和推斷,產生一些反直覺的結果,這個後面會詳細展開。二來查詢範圍過長要作降採樣,勢必會形成數據精度丟失,不過這是時序數據的特色,也是不一樣於日誌系統的地方。

3、K8S 集羣中經常使用的 exporter

Prometheus 屬於 CNCF 項目,擁有完整的開源生態,與 Zabbix 這種傳統 agent 監控不一樣,它提供了豐富的 exporter 來知足你的各類需求。你能夠在這裏看到官方、非官方的 exporter。若是仍是沒知足你的需求,你還能夠本身編寫 exporter,簡單方便、自由開放,這是優勢。sql

可是過於開放就會帶來選型、試錯成本。以前只須要在 zabbix agent裏面幾行配置就能完成的事,如今你會須要不少 exporter 搭配才能完成。還要對全部 exporter 維護、監控。尤爲是升級 exporter 版本時,很痛苦。非官方exporter 還會有很多 bug。這是使用上的不足,固然也是 Prometheus 的設計原則。docker

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,如日誌提取後面會再作介紹。segmentfault

4、K8S 核心組件監控與 Grafana 面板

k8s 集羣運行中須要關注核心組件的狀態、性能。如 kubelet、apiserver 等,基於上面提到的 exporter 的指標,能夠在 Grafana 中繪製以下圖表:後端

模板能夠參考dashboards-for-kubernetes-administrators,根據運行狀況不斷調整報警閾值。

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

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

It would be grate to add templates support in alerts. Otherwise the feature looks useless a bit.

5、採集組件 All IN One

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

  1. 經過主進程拉起N個 Exporter 進程,仍然能夠跟着社區版本作更新、bug fix。
  2. 用Telegraf來支持各類類型的 Input,N 合 1。

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

6、合理選擇黃金指標

採集的指標有不少,咱們應該關注哪些?Google 在「Sre Handbook」中提出了「四個黃金信號」:延遲、流量、錯誤數、飽和度。實際操做中可使用 Use 或 Red 方法做爲指導,Use 用於資源,Red 用於服務。

  • Use 方法:Utilization、Saturation、Errors。如 Cadvisor 數據
  • Red 方法:Rate、Errors、Duration。如 Apiserver 性能指標

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

  1. 在線服務:如 Web 服務、數據庫等,通常關心請求速率,延遲和錯誤率即 RED 方法
  2. 離線服務:如日誌處理、消息隊列等,通常關注隊列數量、進行中的數量,處理速度以及發生的錯誤即 Use 方法
  3. 批處理任務:和離線任務很像,可是離線任務是長期運行的,批處理任務是按計劃運行的,如持續集成就是批處理任務,對應 K8S 中的 job 或 cronjob, 通常關注所花的時間、錯誤數等,由於運行週期短,極可能還沒采集到就運行結束了,因此通常使用 Pushgateway,改拉爲推。

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

7、K8S 1.16中 Cadvisor 的指標兼容問題

在 K8S 1.16版本,Cadvisor 的指標去掉了 pod_Name 和 container_name 的 label,替換爲了pod 和 container。若是你以前用這兩個 label 作查詢或者 Grafana 繪圖,須要更改下 Sql 了。由於咱們一直支持多個 K8S 版本,就經過 relabel配置繼續保留了原來的**_name。

metric_relabel_configs: - source_labels: [container\]  
 regex: (.+)  
 target_label: container_name  
 replacement: $1  
 action: replace  
- source_labels: [pod]  
 regex: (.+)  
 target_label: pod_name  
 replacement: $1  
 action: replace

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

8、Prometheus 採集外部 K8S 集羣、多集羣

Prometheus 若是部署在K8S集羣內採集是很方便的,用官方給的Yaml就能夠,但咱們由於權限和網絡須要部署在集羣外,二進制運行,採集多個 K8S 集羣。

以 Pod 方式運行在集羣內是不須要證書的(In-Cluster 模式),但集羣外須要聲明 token之類的證書,並替換address,即便用 Apiserver Proxy採集,以 Cadvisor採集爲例,Job 配置爲:

- job_name: cluster-cadvisor  
 honor_timestamps: true  
 scrape_interval: 30s  
 scrape_timeout: 10s  
 metrics_path: /metrics  
 scheme: https  
 kubernetes_sd_configs:  
 - api_server: https://xx:6443  
 role: node  
 bearer_token_file: token/cluster.token  
 tls_config:  
 insecure_skip_verify: true  
 bearer_token_file: token/cluster.token  
 tls_config:  
 insecure_skip_verify: true  
 relabel_configs:  
 - separator: ;  
 regex: __meta_kubernetes_node_label_(.+)  
 replacement: $1  
 action: labelmap  
 - separator: ;  
 regex: (.*)  
 target_label: __address__  
 replacement: xx:6443  
 action: replace  
 - source_labels: [__meta_kubernetes_node_name]  
 separator: ;  
 regex: (.+)  
 target_label: __metrics_path__  
 replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor  
 action: replace  
 metric_relabel_configs:  
 - source_labels: [container]  
 separator: ;  
 regex: (.+)  
 target_label: container_name  
 replacement: $1  
 action: replace  
 - source_labels: [pod]  
 separator: ;  
 regex: (.+)  
 target_label: pod_name  
 replacement: $1  
 action: replace

bearer_token_file 須要提早生成,這個參考官方文檔便可。記得 base64 解碼。

對於 cadvisor 來講,__metrics_path__能夠轉換爲/api/v1/nodes/{1}/proxy/metrics/cadvisor,表明Apiserver proxy 到 Kubelet,若是網絡能通,其實也能夠直接把 Kubelet 的10255做爲 target,能夠直接寫爲:{1}:10255/metrics/cadvisor,表明直接請求Kubelet,規模大的時候還減輕了 Apiserver 的壓力,即服務發現使用 Apiserver,採集不走 Apiserver

由於 cadvisor 是暴露主機端口,配置相對簡單,若是是 kube-state-metric 這種 Deployment,以 endpoint 形式暴露,寫法應該是:

- job_name: cluster-service-endpoints  
 honor_timestamps: true  
 scrape_interval: 30s  
 scrape_timeout: 10s  
 metrics_path: /metrics  
 scheme: https  
 kubernetes_sd_configs:  
 - api_server: https://xxx:6443  
 role: endpoints  
 bearer_token_file: token/cluster.token  
 tls_config:  
 insecure_skip_verify: true  
 bearer_token_file: token/cluster.token  
 tls_config:  
 insecure_skip_verify: true  
 relabel_configs:  
 - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]  
 separator: ;  
 regex: "true"  
 replacement: $1  
 action: keep  
 - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]  
 separator: ;  
 regex: (https?)  
 target_label: __scheme__  
 replacement: $1  
 action: replace  
 - separator: ;  
 regex: (.*)  
 target_label: __address__  
 replacement: xxx:6443  
 action: replace  
 - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_endpoints_name,  
 __meta_kubernetes_service_annotation_prometheus_io_port]  
 separator: ;  
 regex: (.+);(.+);(.*)  
 target_label: __metrics_path__  
 replacement: /api/v1/namespaces/${1}/services/${2}:${3}/proxy/metrics  
 action: replace  
 - separator: ;  
 regex: __meta_kubernetes_service_label_(.+)  
 replacement: $1  
 action: labelmap  
 - source_labels: [__meta_kubernetes_namespace]  
 separator: ;  
 regex: (.*)  
 target_label: kubernetes_namespace  
 replacement: $1  
 action: replace  
 - source_labels: [__meta_kubernetes_service_name]  
 separator: ;  
 regex: (.*)  
 target_label: kubernetes_name  
 replacement: $1  
 action: replace

對於 endpoint 類型,須要轉換__metrics_path__爲/api/v1/namespaces/{1}/services/{2}:${3}/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 性能指標、進程指標等。

9、GPU 指標的獲取

nvidia-smi能夠查看機器上的 GPU 資源,而Cadvisor 其實暴露了Metric來表示容器使用 GPU 狀況,

container_accelerator_duty_cycle
container_accelerator_memory_total_bytes
container_accelerator_memory_used_bytes

若是要更詳細的 GPU 數據,能夠安裝dcgm exporter,不過K8S 1.13 才能支持。

10、更改 Prometheus 的顯示時區

Prometheus 爲避免時區混亂,在全部組件中專門使用 Unix Time 和 Utc 進行顯示。不支持在配置文件中設置時區,也不能讀取本機 /etc/timezone 時區。

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

  • 若是作可視化,Grafana是能夠作時區轉換的。
  • 若是是調接口,拿到了數據中的時間戳,你想怎麼處理均可以。
  • 若是由於 Prometheus 自帶的 UI 不是本地時間,看着不舒服,2.16 版本的新版 Web UI已經引入了Local Timezone 的選項,區別見下圖。
  • 若是你仍然想改 Prometheus 代碼來適應本身的時區,能夠參考這篇文章。

關於 timezone 的討論,能夠看這個issue。

11、如何採集 LB 後面的 RS 的 Metric

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

  • RS 的服務加 Sidecar Proxy,或者本機增長 Proxy 組件,保證 Prometheus 能訪問到。
  • LB 增長 /backend1 和 /backend2請求轉發到兩個單獨的後端,再由 Prometheus 訪問 LB 採集。

12、Prometheus 大內存問題

隨着規模變大,Prometheus 須要的 CPU 和內存都會升高,內存通常先達到瓶頸,這個時候要麼加內存,要麼集羣分片減小單機指標。這裏咱們先討論單機版 Prometheus 的內存問題。

緣由:

  • Prometheus 的內存消耗主要是由於每隔2小時作一個 Block 數據落盤,落盤以前全部數據都在內存裏面,所以和採集量有關。
  • 加載歷史數據時,是從磁盤到內存的,查詢範圍越大,內存越大。

    這裏面有必定的優化空間。

  • 一些不合理的查詢條件也會加大內存,如 Group 或大範圍 Rate。

個人指標須要多少內存:

  • 做者給了一個計算器,設置指標量、採集間隔之類的,計算 Prometheus 須要的理論內存值:計算公式

以咱們的一個 Prometheus Server爲例,本地只保留 2 小時數據,95 萬 Series,大概佔用的內存以下:

有什麼優化方案:

  • Sample 數量超過了 200 萬,就不要單實例了,作下分片,而後經過 Victoriametrics,Thanos,Trickster 等方案合併數據。
  • 評估哪些 Metric 和 Label 佔用較多,去掉沒用的指標。

    2.14 以上能夠看 Tsdb 狀態

  • 查詢時儘可能避免大範圍查詢,注意時間範圍和 Step 的比例,慎用 Group。
  • 若是須要關聯查詢,先想一想能不能經過 Relabel 的方式給原始數據多加個 Label,一條Sql 能查出來的何須用Join,時序數據庫不是關係數據庫。

Prometheus 內存佔用分析:

相關 issue:

十3、Prometheus 容量規劃

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

  • 若是是單機Prometheus,計算本地磁盤使用量。
  • 若是是 Remote-Write,和已有的 Tsdb 共用便可。
  • 若是是 Thanos 方案,本地磁盤能夠忽略(2H),計算對象存儲的大小就行。

Prometheus 每2小時將已緩衝在內存中的數據壓縮到磁盤上的塊中。包括Chunks、Indexes、Tombstones、Metadata,這些佔用了一部分存儲空間。通常狀況下,Prometheus 中存儲的每個樣本大概佔用1-2字節大小(1.7Byte)。能夠經過Promql來查看每一個樣本平均佔用多少空間:

rate(prometheus_tsdb_compaction_chunk_size_bytes_sum[1h])/  rate(prometheus_tsdb_compaction_chunk_samples_sum[1h]){instance="0.0.0.0:8890", job="prometheus"}  1.252747585939941

若是大體估算本地磁盤大小,能夠經過如下公式:

磁盤大小=保留時間*每秒獲取樣本數*樣本大小

保留時間(retention_time_seconds)和樣本大小(bytes_per_sample)不變的狀況下,若是想減小本地磁盤的容量需求,只能經過減小每秒獲取樣本數(ingested_samples_per_second)的方式。

查看當前每秒獲取的樣本數:

rate(prometheus_tsdb_head_samples_appended_total[1h])

有兩種手段,一是減小時間序列的數量,二是增長採集樣本的時間間隔。考慮到 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 存儲機制,能夠看這篇。

十4、對 Apiserver 的性能影響

若是你的 Prometheus 使用了 kubernetes_sd_config 作服務發現,請求通常會通過集羣的 Apiserver,隨着規模的變大,須要評估下對 Apiserver性能的影響,尤爲是Proxy失敗的時候,會致使CPU 升高。固然了,若是單K8S集羣規模太大,通常都是拆分集羣,不過隨時監測下 Apiserver 的進程變化仍是有必要的。

在監控Cadvisor、Docker、Kube-Proxy 的 Metric 時,咱們一開始選擇從 Apiserver Proxy 到節點的對應端口,統一設置比較方便,但後來仍是改成了直接拉取節點,Apiserver 僅作服務發現。

十5、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 的時間區間內有數據缺失,他會基於趨勢進行推測,好比:

若有錯誤或其它問題,歡迎小夥伴留言評論、指正。若有幫助,歡迎點贊+轉發分享。

歡迎你們關注民工哥的公衆號:民工哥技術之路
image.png

相關文章
相關標籤/搜索