Prometheus監控學習筆記之360基於Prometheus的在線服務監控實踐

 

0x00 初衷

最近參與的幾個項目,無一例外對監控都有極強的要求,須要對項目中各組件進行詳細監控,如服務端API的請求次數、響應時間、到達率、接口錯誤率、分佈式存儲中的集羣IOPS、節點在線狀況、偏移量等。node

比較常見的方式是寫日誌,將日誌採集到遠端進行分析和繪圖,或寫好本地監控腳本進行數據採集後,經過監控系統客戶端push到監控系統中進行打點。基本上咱們須要的都能覆蓋,但仍然有一些問題在使用上不太舒服,如在大規模請求下日誌採集和分析的效率比較難控制,或push打點的粒度和緯度以及查詢不夠靈活等。web

後來在同事對《Google SRE》這本書中的一些運維思想進行一番安利後,抱着試一試的態度,開始嘗試使用Prometheus作爲幾個項目的監控解決方案。json

 

0x01 Prometheus的特色

  • 多維數據模型(時序數據由 metric 名和一組K/V標籤構成)。api

  • 靈活強大的查詢語句(PromQL)。服務器

  • 不依賴存儲,支持local和remote(OpenTSDB、InfluxDB等)不一樣模型。網絡

  • 採用 HTTP協議,使用Pull模式採集數據。運維

  • 監控目標,能夠採用服務發現或靜態配置的方式。curl

  • 支持多種統計數據模型,圖形化友好(Grafana)。分佈式

 

0x02 數據類型

  • Counter

    Counter表示收集的數據是按照某個趨勢(增長/減小)一直變化的。函數

  • Gauge

    Gauge表示蒐集的數據是瞬時的,能夠任意變高變低。

  • Histogram

    Histogram能夠理解爲直方圖,主要用於表示一段時間範圍內對數據進行採樣,(一般是請求持續時間或響應大小),並可以對其指定區間以及總數進行統計。

  • Summary

    Summary和Histogram十分類似,主要用於表示一段時間範圍內對數據進行採樣,(一般是請求持續時間或響應大小),它直接存儲了 quantile 數據,而不是根據統計區間計算出來的。

 

               Summary和Histogram十分類似,主要用於表示一段時間範圍內對數據進行採樣,(一般是請求持續時間或響應大小),它直接存儲了 quantile 數據,而不是根據統計區間計算出來的。

               在咱們的使用場景中,大部分監控使用Counter來記錄,例如接口請求次數、消息隊列數量、重試操做次數等。比較推薦多使用Counter類型採集,由於Counter類型不會在兩次採集間隔中間丟失信息。

               一小部分使用Gauge,如在線人數、協議流量、包大小等。Gauge模式比較適合記錄無規律變化的數據,並且兩次採集之間可能會丟失某些數值變化的狀況。隨着時間週期的粒度變大,丟失關鍵變化的狀況也會增多。

               還有一小部分使用Histogram和Summary,用於統計平均延遲、請求延遲佔比和分佈率。另外針對Historgram,不管是打點仍是查詢對服務器的CPU消耗比較高,經過查詢時查詢結果的返回耗時會有十分直觀的感覺。

 

0x03 時序數據-打點-查詢

咱們知道每條時序數據都是由 metric(指標名稱),一個或一組label(標籤),以及float64的值組成的。

標準格式爲 <metric name>{<label name>=<label value>,...}

例如:

rpc_invoke_cnt_c{code="0",method="Session.GenToken",job="Center"} 5

rpc_invoke_cnt_c{code="0",method="Relation.GetUserInfo",job="Center"} 12

rpc_invoke_cnt_c{code="0",method="Message.SendGroupMsg",job="Center"} 12

rpc_invoke_cnt_c{code="4",method="Message.SendGroupMsg",job="Center"} 3

rpc_invoke_cnt_c{code="0",method="Tracker.Tracker.Get",job="Center"} 70

這是一組用於統計RPC接口處理次數的監控數據。

其中rpc_invoke_cnt_c爲指標名稱,每條監控數據包含三個標籤:code 表示錯誤碼,service表示該指標所屬的服務,method表示該指標所屬的方法,最後的數字表明監控值。

針對這個例子,咱們共有四個維度(一個指標名稱、三個標籤),這樣咱們即可以利用Prometheus強大的查詢語言PromQL進行極爲複雜的查詢。

 

0x04 PromQL

PromQL(Prometheus Query Language) 是 Prometheus 本身開發的數據查詢 DSL 語言,語言表現力很是豐富,支持條件查詢、操做符,而且內建了大量內置函,供咱們針對監控數據的各類維度進行查詢。

咱們想統計Center組件Router.Logout的頻率,可以使用以下Query語句:

rate(rpc_invoke_cnt_c{method="Relation.GetUserInfo",job="Center"}[1m])

或者基於方法和錯誤碼統計Center的總體RPC請求錯誤頻率:

sum by (method, code)(rate(rpc_invoke_cnt_c{job="Center",code!="0"}[1m]))

若是咱們想統計Center各方法的接口耗時,使用以下Query語句便可:

rate(rpc_invoke_time_h_sum{job="Center"}[1m]) / rate(rpc_invoke_time_h_count{job="Center"}[1m])

更多的內建函數這裏不展開介紹了。函數使用方法和介紹能夠詳細參見官方文檔中的介紹:https://prometheus.io/docs/querying/functions/

另外,配合查詢,在打點時metric和labal名稱的定義也有必定技巧。

好比在咱們的項目中:

  • rpc_invoke_cnt_c 表示rpc調用統計

  • api_req_num_cv 表示httpapi調用統計

  • msg_queue_cnt_c 表示隊列長度統計

儘量使用各服務或者組件通用的名稱定義metric而後經過各類lable進行區分。

最開始咱們的命名方式是這樣的,好比咱們有三個組件center、gateway、message。RPC調用統計的metric相應的命名成了三個:

  • center_rpc_invoke_cnt_c

  • gateway_rpc_invoke_cnt_c

  • message_rpc_invoke_cnt_c

這種命名方式,對於各組件的開發同窗可能讀起來會比較直觀,可是在實際查詢過程當中,這三個metric至關於三個不一樣的監控項。

例如咱們查詢基於method統計全部組件RPC請求錯誤頻率,若是咱們使用通用名稱定義metric名,查詢語句是這樣的

sum by (method, code) (rate(rpc_invoke_cnt_c{code!="0"}[1m]))

但若是咱們各個組件各自定義名稱的話,這條查詢須要寫多條。雖然咱們能夠經過 {__name__=~".*rpc_invoke_cnt_c"} 的方式來規避這個問題,但在實際使用和操做時體驗會差不少。

例如在Grafana中,若是合理命名相對通用的metric名稱,一樣一個Dashboard能夠套用給多個相同業務,只需簡單修改template匹配一下label選擇便可。否則針對各個業務不一樣的metric進行鍼對性的定製繪圖也是一個十分痛苦的過程。

同時經過前面的各種查詢例子也會發現,咱們在使用label時也針對不一樣的含義進行了區分如 method=GroupJoin|GetUserInfo|PreSignGet|... 來區分調用的函數方法,code=0|1|4|1004|...來區分接口返回值,使查詢的分類和結果展現更加方便直觀,而且label在Grafana中是能夠直接做爲變量進行更復雜的模版組合。

更多的metric和label相關的技巧能夠參考官方文檔:https://prometheus.io/docs/practices/naming/

 

0x05 服務發現

在使用初期,參與的幾個項目的Prometheus都是各自獨立部署和維護的。其配置也是按照官方文檔中的標準配置來操做。機器數量少的時候維護簡單,增刪機器以後簡單reload一下便可。例如:

但隨着服務器量級增加,業務整合到同一組Prometheus時,每次上下線實例都是一個十分痛苦的過程,配置文件龐大,列表過長,修改的過程極其容易眼花致使誤操做。因此咱們嘗試使用了Prometheus的服務發現功能。

從配置文檔中不難發現Prometheus對服務發現進行了大量的支持,例如你們喜聞樂見的Consul、etcd和K8S。

<scrape_config>

<tls_config>

<azure_sd_config>

<Consul_sd_config>

<dns_sd_config>

<ec2_sd_config>

<openstack_sd_config>

<file_sd_config>

<gce_sd_config>

<kubernetes_sd_config>

<marathon_sd_config>

<nerve_sd_config>

<serverset_sd_config>

<triton_sd_config>

詳細的服務發現配置請參照官網: https://prometheus.io/docs/prometheus/latest/configuration/configuration/

因爲最近參與的幾個項目深度使用公司內部的配置管理服務gokeeper,雖然不是Prometheus原生支持,可是經過簡單適配也是一樣能知足服務發現的需求。咱們最終選擇經過file_sd_config進行服務發現的配置。

file_sd_config 接受json格式的配置文件進行服務發現。每次json文件的內容發生變動,Prometheus會自動刷新target列表,不須要手動觸發reload操做。因此針對咱們的gokeeper編寫了一個小工具,定時到gokeeper中採集服務分類及分類中的服務器列表,並按照file_sd_config的要求生成對應的json格式。

下面是一個測試服務生成的json文件樣例。

[

    {

        "targets": [

            "10.10.10.1:65160",

            "10.10.10.2:65160"

        ],

        "labels": {

            "job":"Center",

            "service":"qtest"

        }

    },

    {

        "targets": [

            "10.10.10.3:65110",

            "10.10.10.4:65110"

        ],

        "labels": {

            "job":"Gateway",

            "service":"qtest"

        }

    }

]

Prometheus配置文件中將file_sd_configs的路徑指向json文件便可。

-job_name: 'qtest'

    scrape_interval: 5s

    file_sd_configs:

      - files: ['/usr/local/prometheus/qtestgroups/*.json']

若是用etcd做爲服務發現組件也可使用此種方式,結合confd配合模版和file_sd_configs能夠極大地減小配置維護的複雜度。只須要關注一下Prometheus後臺採集任務的分組和在線狀況是否符合指望便可。社區比較推崇Consul做爲服務發現組件,也有很是直接的內部配置支持。

 

0x06 高可用

高可用目前暫時沒有太好的方案。官方給出的方案能夠對數據作Shard,而後經過federation來實現高可用方案,可是邊緣節點和Global節點依然是單點,須要自行決定是否每一層都要使用雙節點重複採集進行保活。

使用方法比較簡單,例如咱們一個機房有三個Prometheus節點用於Shard,咱們但願Global節點採集歸檔數據用於繪圖。首先須要在Shard節點進行一些配置。

Prometheus.yml:

global:

  external_labels:

  slave: 0 #給每個節點指定一個編號 三臺分別標記爲0,1,2

 

rule_files:

  - node_rules/zep.test.rules  #指定rulefile的路徑

 

scrape_configs:

  - job_name: myjob

    file_sd_configs:

    - files: ['/usr/local/prometheus/qtestgroups/*.json']

    relabel_configs:

    - source_labels: [__address__]

      modulus:       3   # 3節點

      target_label:  __tmp_hash

      action:        hashmod

    - source_labels: [__tmp_hash]

      regex:         ^0$ # 表示第一個節點

      action:        keep

編輯規則文件:

node_rules/zep.test.rules:

job:rpc_invoke_cnt:rate:1m=rate(rpc_invoke_cnt_c{code!="0"}[1m])

在這裏job:rpc_invoke_cnt:rate:1m 將做爲metric名,用來存放查詢語句的結果。

在Global節點Prometheus.yml也須要進行修改。

 -job_name: slaves

    honor_labels: true

    scrape_interval: 5s

    metrics_path: /federate

    params:

      match[]:

         - '{__name__=~"job:.*"}'

    static_configs:

      - targets:

         - 10.10.10.150:9090

         - 10.10.10.151:9090

         - 10.10.10.152:9090

在這裏咱們只採集了執行規則後的數據用於繪圖,不建議將Shard節點的全部數據採集過來存儲再進行查詢和報警的操做。這樣不但會使Shard節點計算和查詢的壓力增大(經過HTTP讀取原始數據會形成大量IO和網絡開銷),同時全部數據寫入Global節點也會使其很快達到單Prometheus節點的承載能力上限。

另外部分敏感報警儘可能不要經過global節點觸發,畢竟從Shard節點到Global節點傳輸鏈路的穩定性會影響數據到達的效率,進而致使報警實效下降。例如服務updown狀態,API請求異常這類報警咱們都放在s hard節點進行報警。

此外咱們還編寫了一個實驗性質的Prometheus Proxy工具,代替Global節點接收查詢請求,而後將查詢語句拆解,到各shard節點抓取基礎數據,而後再在Proxy這裏進行Prometheus內建的函數和聚合操做,最後將計算數據拋給查詢客戶端。這樣即可以直接節約掉Global節點和大量存儲資源,而且Proxy節點因爲不須要存儲數據,僅接受請求和計算數據,橫向擴展十分方便。

固然問題仍是有的,因爲每次查詢Proxy到shard節點拉取的都是未經計算的原始數據,當查詢的metric數據量比較大時,網絡和磁盤IO開銷巨大。所以在繪圖時咱們對查詢語句限制比較嚴格,基本不容許進行無label限制的模糊查詢。

 

0x07 報警

Prometheus的報警功能目前來看相對計較簡單。主要是利用Alertmanager這個組件。已經實現了報警組分類,按標籤內容發送不一樣報警組、報警合併、報警靜音等基礎功能。配合rules_file中編輯的查詢觸發條件,Prometheus會主動通知Alertmanager而後發出報警。因爲咱們公司內使用的自研的Qalarm報警系統,接口比較豐富,和Alertmanager的webhook簡單對接便可使用。

Alertmanager也內建了一部分報警方式,如Email和第三方的Slack,初期咱們的存儲集羣報警使用的就是Slack,響應速度仍是很不錯的。

須要注意的是,若是報警已經觸發,可是因爲一些緣由,好比刪除業務監控節點,使報警恢復的規則一直不能觸發,那麼已出發的報警會按照Alertmanager配置的週期一直重複發送,要麼從後臺silence掉,要麼想辦法使報警恢復。例如前段時間咱們縮容Ceph集羣,操做前沒有關閉報警,觸發了幾個osddown的報警,報警刷新週期2小時,那麼每過兩小時Alertmanager都會發來一組osddown的報警短信。

對應編號的osd因爲已經刪掉已經不能再寫入up對應的監控值,索性停掉osddown報警項,直接重啓ceph_exporter,再調用Prometheus API刪掉對應osd編號的osdupdown監控項,隨後在啓用osddown報警項才使報警恢復。

以下圖的報警詳情頁面,紅色的是已觸發的報警,綠色的是未觸發報警:

 

0x08 繪圖展現

對於頁面展現,咱們使用的是Grafana,以下面兩張圖,是兩個不一樣服務的Dashboard,能夠作很是多的定製化,同時Grafana的template也能夠做爲參數傳到查詢語句中,對多維度定製查詢提供了極大的便利。

 

 

0x09 Q&A

Q1:Promethues Alertmanager,能結合案例來一個麼?

A1:直接演示一條報警規則吧。

ALERT SlowRequest

  IF ceph_slow_requests{service="ceph"} > 10

  FOR 1m

  LABELS { qalarm = "true" }

  ANNOTATIONS {

    summary = "Ceph Slow Requests",

    description = "slow requests count: {{ $value }} - Region:{{ $labels.group }}",

  }

這條規則在查詢到ceph slow_request > 10而且超過1分鐘時觸發一條報警。

 

Q2:exporter的編寫及使用方法,以及 promethues 如何結合 grafana使用和promethues 是如何進行報警的。

A2:exporter的編寫能夠單獨拿出來寫一篇文章了。咱們這邊主要使用的Prometheus Golang SDK,在項目中註冊打點,並經過Http接口暴露出來。報警沒有結合Grafana,不過大多數Grafana中使用的查詢語句,簡單修改便可成爲Alertmanager的報警規則。

 

Q3:刪除配置文件job,可是經過查詢還有job記錄,怎麼刪除job記錄? 

A3:直接經過Prometheus接口處理便可

curl -X "DELETE" "http://prometheus:9090/api/v1/series?match[]={job="your job"}"

和查詢接口的使用方式相似

 

以上轉載 https://mp.weixin.qq.com/s/lcjZzjptxrUBN1999k_rXw

相關文章
相關標籤/搜索