監控系統的歷史悠久,是一個很成熟的方向,而 Prometheus 作爲新生代的開源監控系統,慢慢成爲了雲原生體系的事實標準,也證明了其設計很受歡迎。
本文主要分享在 Prometheus 實踐中遇到的一些問題和思考,如果你對 K8S 監控體系或 Prometheus 的設計還不太瞭解,可以先看下容器監控系列。
幾點原則:
監控是基礎設施,目的是爲了解決問題,不要只朝着大而全去做,尤其是不必要的指標採集,浪費人力和存儲資源(To B商業產品例外)。
需要處理的告警才發出來,發出來的告警必須得到處理。
簡單的架構就是最好的架構,業務系統都掛了,監控也不能掛。Google Sre 裏面也說避免使用 Magic 系統,例如機器學習報警閾值、自動修復之類。這一點見仁見智吧,感覺很多公司都在搞智能 AI 運維。
一、版本的選擇
Prometheus 當前最新版本爲 2.16,Prometheus 還在不斷迭代,因此儘量用最新版,1.X版本就不用考慮了。
2.16 版本上有一套實驗 UI,可以查看 TSDB 的狀態,包括Top 10的 Label、Metric.
二、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,簡單方便、自由開放,這是優點。
但是過於開放就會帶來選型、試錯成本。之前只需要在 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 中繪製如下圖表:
模板可以參考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.
五、採集組件 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:- 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。
八、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 性能指標、進程指標等。
九、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 才能支持。
十、更改 Prometheus 的顯示時區
Prometheus 爲避免時區混亂,在所有組件中專門使用 Unix Time 和 Utc 進行顯示。不支持在配置文件中設置時區,也不能讀取本機 /etc/timezone 時區。
其實這個限制是不影響使用的:
如果做可視化,Grafana是可以做時區轉換的。
如果是調接口,拿到了數據中的時間戳,你想怎麼處理都可以。
如果因爲 Prometheus 自帶的 UI 不是本地時間,看着不舒服,2.16 版本的新版 Web UI已經引入了Local Timezone 的選項,區別見下圖。
如果你仍然想改 Prometheus 代碼來適應自己的時區,可以參考這篇文章。
關於 timezone 的討論,可以看這個issue。
十一、如何採集 LB 後面的 RS 的 Metric
假如你有一個負載均衡 LB,但網絡上 Prometheus 只能訪問到 LB 本身,訪問不到後面的 RS,應該如何採集 RS 暴露的 Metric?
RS 的服務加 Sidecar Proxy,或者本機增加 Proxy 組件,保證 Prometheus 能訪問到。
LB 增加 /backend1 和 /backend2請求轉發到兩個單獨的後端,再由 Prometheus 訪問 LB 採集。
十二、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 內存佔用分析:
通過 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來查看每個樣本平均佔用多少空間:
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 存儲機制,可以看這篇。
十四、對 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 的時間區間內有數據缺失,他會基於趨勢進行推測,比如:
作者:徐亞松 原文:http://www.xuyasong.com/?p=1921
end
最新整理的 2TB 乾貨資源,包括但不限於:架構師、大數據、Docker、運維、數據庫、redis、MongoDB、電子書、Java基礎、Java實戰項目、ELK Stack、機器學習、BAT面試精講視頻等。只需在公衆號對話框回覆關鍵字:1024 即可免費獲取全部資料。
推薦閱讀 點擊標題可跳轉
3、一文教你使用 Jenkins 設計多環境、多項目持續集成環境!
看完本文有收穫,請分享給更多人
關注「民工哥技術之路」加星標,提升IT技能
好文章,朕「在看」❤️↓↓↓