基於 Prometheus 的監控神器,簡單靈活!

Linksla發表於2022-11-01

眾所周知,大資料產品作為底層平臺,其運維監控一直是生產實踐的痛點難點,且在穩定執行的基礎之上往往還需要對效能進行評估最佳化,所以其監控系統的建設顯得尤為重要。

Prometheus 作為雲原生時代最火的監控軟體,很多大資料元件或原生或以第三方外掛/exporter 的形式對 Prometheus 做了支援。
我使用的大資料平臺是基於 K8s 執行的,有部署靈活管理方便的優點,更容易與 Prometheus 進行結合。
下面將對設計思路和技術實現進行闡述探討。

一、設計思路

監控系統的核心任務是將暴露出來的指標資料進行抓取,在此之上進行分析、告警,所以有以下幾個要明確的問題:

  1. 監控物件是什麼

  2. 監控物件如何暴露指標資料

  3. 監控系統如何對指標進行抓取

  4. 如何實現告警規則動態配置、管理

監控物件

以 pod(容器)形式執行在 kubernetes 叢集上的各個大資料元件。

指標暴露方式

各元件根據對 Prometheus 的支援程度,可分為 3 種型別的指標暴露方式:

  1. 直接暴露 Prometheus 指標資料 (直接,拉)

  2. 主動將指標資料推送到 prometheus-pushGateway,由 pushGateway 暴露資料(間接,推)

  3. 自定義 exporter 將其他形式的指標資料轉換為符合 Prometheus 標準的格式進行暴露(exporter,直接,拉)

個別元件同時支援多種方式,如 flink 支援直接和間接方式,spark 支援直接方式而且也有第三方 exporter。
大部分元件都有官方/第三方的 exporter,極少數需要自己開發。 一般情況下直接方式就可以了。
需要注意的是,像 flink(spark) on yarn 模式執行的時候,flink 節點是跑在 yarn 容器裡面的。
這種情況下 Prometheus 很難對其直接進行抓取,這種時候就只能用間接方式,主動將資料推送到 pushGateway。 另外那些短暫生命週期的元件也建議用主動 push 到 pushGateway。

指標抓取方式

不管是 exporter 還是 pushGateway,到最後必然是由 Prometheus 主動對這些目標進行抓取。

Prometheus 主要透過 Pull 的方式來抓取目標服務暴露出來的監控介面,
因此需要配置對應的抓取任務來請求監控資料並寫入到 Prometheus 提供的儲存中,

目前 Prometheus 服務提供瞭如下幾個任務的配置:

  • 原生 Job 配置: 提供 Prometheus 原生抓取 Job 的配置。

  • Pod Monitor: 在 K8S 生態下,基於 Prometheus Operator 來抓取 Pod 上對應的監控資料。

  • Service Monitor: 在 K8S 生態下,基於 Prometheus Operator 來抓取 Service 對應 Endpoints 上的監控資料。

參考:https://cloud.tencent.com/document/product/1416/55995

既然都上了 K8s 環境了,一般當然是推薦直接用 podMonitor。配置更簡潔易懂。

podMonitorSelector 的過濾在  prometheus-prometheus.yaml  配置。

prometheus-prometheus.yaml 是核心配置檔案,不宜頻繁修改(會導致 Prometheus 重啟)。

主要配置項為 :serviceMonitorSelector、podMonitorSelector、ruleSelector、alertmanagers。
其中 service 監控選擇器和 pod 監控選擇器預設選擇所有,這裡建議把 ruleSelector 也修改為選擇所有
不過一個 podMonitor 一般只對應一種型別的 pod,在已有 pod 型別較多的情況下,
還可以考慮一種更取巧的方法就是 Prometheus 的 K8s 服務發現功能。即  kubernetes_sd_config

這種屬於原生 Job 配置,建議使用 additional-scrape-config 進行配置。

kubernetes_sd_config 賦予了 Prometheus 透過 kubernetes rest api 感知 K8s 資源的功能,
利用該能力,可以使用原生 Job 配置自動發現 pod,將其作為監控目標。
再利用 Prometheus 的 Relabel 功能可以改寫發現的標籤,進行前置處理、轉換。實現pod篩選,修改抓取配置的效果。
而自動發現的 pod 的標籤的來源又可以是 pod 資源的 label/annotation等。
最終實現的效果如下,
這是一個 pushGateway 的 pod 的配置,則 Prometheus 會透過其 19091埠訪問/metrics 路徑獲取其指標資料

 annotations:    prometheus.io/scrape: "true"    prometheus.io/scheme: "http"    prometheus.io/path: "/metrics"    prometheus.io/port: "19091"

podMonitor 是官方支援,簡潔易懂。kubernetes_sd_config+relabel 的方案較複雜,難度較高,但不用寫那麼多的 podMonitor。自行抉擇就行,也可以一起用。

告警設計

告警流程

prometheus 的監控告警基本流程是:

  1. 服務發生異常

  2. 觸發 prometheus 伺服器發出告警資訊(alert)

  3. alertmanager 收到告警資訊

  4. alertmanager 根據預配置的規則對告警資訊進行處理,實現業務邏輯,如分組、抑制、觸發簡訊郵箱等

  5. 當然具體的流程沒那麼簡單,有很多細節需要注意,特別是觸發告警時機,是個重點。

告警的動態配置

kube-prometheus 的告警規則分兩部分:

  1. alertmanager:即對告警資訊的處理策略

  2. alertRule:即具體的告警規則

接入自定義告警平臺

從個人實踐的角度來看,AlertManager處理web hook之外的告警接收外掛,如簡訊、郵箱等只適合測著玩。
生產使用還是要透過web hook將告警資訊傳送到自己的告警平臺。可以根據業務需要對告警資訊做高度定製化處理、記錄等。
另外可以在告警資訊中攜帶具體告警規則等資訊指導告警平臺進行處理。
這裡要做個區分,AlertManager 是告警資訊的前置處理,負責非業務性前置操作,如告警資訊分組、平抑等。
而自定義告警平臺則負責告警資訊的業務處理,如記錄、去敏、傳送到多終端等。
AlertManager 可能收到 1w 條告警資訊,經過處理最終只發了1條到自定義告警平臺。
而自定義告警平臺可以將這1條告警資訊記錄起來,修改內容,同時使用郵箱、簡訊通知到多個負責人。

告警層級標籤設計

監控物件的粒度決定告警的層級,體現在配置上則是告警規則的分組。
分組資訊決定 alertManager 的處理方式。
alertManager 對告警資訊的路由策略是樹狀的,所以可透過多個分組標籤實現多層級路由處理。
具體設計應結合業務需求,不在這裡展開,感興趣的可以看我下面的實現舉例。

二、技術實現

技術實現主要分以下幾部分:

  1. kubernetes 環境下 prometheus 的部署(kube-prometheus)

  2. kube-prometheus 的增強配置:即 kubernetes_sd_config+relabel 方案的實現

  3. bigdata-exporter 的實現

  4. 告警設計例項

1.Kubernetes 環境下 prometheus 的部署

1)kube-prometheus vs prometheus-operator

github 上 coreos 下有兩個專案 :kube-prometheus 和 prometheus-operator

兩者都可以實現 prometheus 的建立及管理。

需要注意的是,kube-prometheus 上的配置操作也是基於 prometheus-operator 的,並提供了大量的預設配置,故這裡使用的是 kube-prometheus 專案的配置。

另外使用前需注意 K8s 版本要求,找到對應的 kube-prometheus 版本,弄清楚對應的 prometheus-operator 版本

如:k8s1.14 版本最高可使用 kube-prometheus 0.3,對應的 prometheus-operator 版本是 0.32

閱讀文件時注意對應版本。

2)kube-prometheus 使用前說明

kube-prometheus 使用 jsonnet 編寫配置模板檔案,生成 K8s 配置清單。

已提供預設清單檔案,在 manifests 資料夾下。

如果需要修改預設清單配置,需要在 Go 環境下使用 jp 編譯清單。

下面都以預設配置為例

2. kubernetes_sd_config+relabel 方案的實現

見:

3. bigdata-exporter的實現

hdfs、yarn、hbase、yarn 等元件都提供了 web 獲取 jmx 指標的方式。

這裡的思路是使用一個 bigdata-exporter,去採集多個元件多個節點的指標資料,並進行轉換,然後以 Prometheus 規定的格式對外公開。

指標資料的轉換規則可以檢視 github 上的一些專案,要注意版本,也可以像我一樣自己寫,更可靠。

bigdata-exporter 如何感知到採集目標?

除了部署 IP 不同,不同元件不同角色的指標對外埠、路徑、內容(解析規則)也都不一樣。
這裡可以參考上面 kubernetes_sd_config+relabel 的方案,做得優雅一些:

  1. 授予 bigdata-exporter 呼叫kubernetes app的能力,

  2. 利用 label 和 annotations 進行篩選和資訊傳遞,確定捕捉目標和途徑。

  3. 使用 role 代表解析內容的型別,根據 role 確定解析規則

  labels:    bigData.metrics.object: pod  annotations:    bigData.metrics/scrape: "true"    bigData.metrics/scheme: "https"    bigData.metrics/path: "/jmx"    bigData.metrics/port: "29871"    bigData.metrics/role: "hdfs-nn,common"

4.告警設計示例

這裡以組和例項兩個維度為例,用 groupId 和 instanceId 表示。

1) alertManager配置示例

以下是 alertmanager 的規則配置,有兩個接收者
其中 test.web.hook 指向自定義告警平臺
default 是空白接收者,不做處理。
路由策略是根據 groupId,instanceId 分組,對節點磁碟使用率、kafka佇列堆積兩個組處理,instanceId還沒有展開。
舊版本是用 secret 的 data 欄位,需要將配置內容轉成 base64 編碼格式。
新版本直接用 stringData 欄位。推薦用 stringData 欄位配置。
其實只要看一下 kube-prometheus 的 alertmanager-secret.yaml 檔案就知道怎麼回事了。

使用data欄位的配置方法:

寫好 config 檔案,以 alertmanager.yaml 命名(不能使用其他名稱)。

執行以下命令,即可更新 secret。

    kubectl -n monitoring create secret generic alertmanager-main --from-file=alertmanager.yaml --dry-run -o yaml  |  kubectl -n=monitoring apply -f -

    global:    resolve_timeout: 5mreceivers:  - name: 'default'  - name: 'test.web.hook'    webhook_configs:      - url: '[groupId,instanceId]  routes:    - receiver: 'test.web.hook'      continue: true      match:        groupId: node-disk-usage    - receiver: 'test.web.hook'      continue: true      match:        groupId: kafka-topic-highstore

    2)alertRule配置示例

    組代表一個型別的所有目標:即所有節點

    例項則代表具體的某個節點

    disk-usage.yaml.ftl 磁碟使用率告警配置示例如下:
    注意: $ 為監控的磁碟路徑,$為使用率閾值,需自行替換
    labels 中的 userIds 和 receivers 為傳遞給自定義告警平臺的引數,以指導告警平臺如何操作。

    在這個任務中,我們的目標是組粒度的(所有節點),所以不需要設定 instanceId。

    apiVersion: monitoring.coreos.com/v1kind: PrometheusRulemetadata:   creationTimestamp: null   labels:      role: alert-rules   name: node-disk-usage   namespace: monitoringspec:   groups:      - name: node-disk-usage        rules:           - alert: node-disk-usage             expr: 100*(1-node_filesystem_avail_bytes{mountpoint="${path}"}/node_filesystem_size_bytes{mountpoint="${path}"} ) > ${thresholdValue}             for: 1m             labels:                groupId: node-disk-usage                userIds: super                receivers: SMS             annotations:                title: "磁碟警告:節點{{$labels.instance}}的 ${path} 目錄使用率已達到{{$value}}%"                content: "磁碟警告:節點{{$labels.instance}}的 ${path} 目錄使用率已達到{{$value}}%"

    kafka-topic-highstore.yaml.ftl kafka 佇列消費堆積告警配置示例如下:
    我們只關心個別佇列的消費情況,所以這裡的粒度為 instance。

    注意 $ 為佇列名, $ 為消費組名稱, $ 為堆積數量閾值

      apiVersion: monitoring.coreos.com/v1kind: PrometheusRulemetadata:  creationTimestamp: null  labels:    role: alert-rules  name: kafka-topic-highstore-${uniqueName}  namespace: monitoringspec:  groups:    - name: kafka-topic-highstore      rules:      - alert: kafka-topic-highstore-${uniqueName}        expr: sum(kafka_consumergroup_lag{exporterType="kafka",consumergroup="${consumergroup}"}) > ${thresholdValue}        for: 1m        labels:          groupId: kafka-topic-highstore          instanceId: ${uniqueName}          userIds: super          receivers: SMS        annotations:          title: "KAFKA警告:消費組${consumergroup}的堆積數量達到:{{$value}}"          content: "KAFKA警告:消費組${consumergroup}的堆積數量達到:{{$value}}"

      三、其他

      告警流程示例

      這裡以兩個節點 node1 和 node2 配置了磁碟空間監控為例,空間使用到達閾值則觸發告警。

      (測試過程中可透過生成、刪除指定體積的檔案來控制空間佔用)

      其中配置項如下:

      • 告警規則: node-disk-usage

        • for 為 1m

      • 告警中心 : alertManager

        • group_wait: 30s

        • group_interval: 5m

        • repeat_interval: 10m

      收到的告警簡訊內容及時間線如下:

      10:23:14 收到第一次警報:node1 於 10:22:44 進入異常10:28:14 收到第二次警報:node1 於 10:22:44 進入異常 node2 於 10:24:44 進入異常10:38:29 收到第三次警報:node1 於 10:22:44 進入異常 node2 於 10:24:44 進入異常10:48:44 收到第四次警報:node1 於 10:22:44 進入異常 node2 於 10:24:44 進入異常10:58:44 收到第五次警報:恢復告警 node1 於 10:22:44 進入異常,並於 10:55:44 恢復 node2 於 10:24:44 進入異常,並於 10:49:14 恢復

      總共收到 5 次簡訊:
      第 1 次是 node1 異常,
      第 2 到 4 次是 node1 和 node2 都異常,因為屬於同個分組 group,所以合併傳送。

      第 5 次是已經恢復正常了。

      根據簡訊內容和時間,整理出告警邏輯時間線如下:

        
        node1 等待 for 1 分鐘 後警報進入 group
        
        node1 記錄異常時間為 10:22:44,實際異常狀態至少在 10:22:44 的一分鐘前
        
        
        
        group 等待 group_wait 30s 後傳送第一次告警 firing:node1
        node2 等待 for 1 分鐘 後警報進入 group node2 記錄異常時間為 10:24:44,實際異常狀態至少在 10:24:44 的一分鐘前,此時 group 中有兩個異常目標 node1 和 node2。
        group 等待 group_interval 5m 後傳送第二次告警 firing:node1,node2 注意:因為 group 發生了變化,所以這裡用的是 group_interval。
        group 等待 repeat_interval 10m 後傳送第三次告警 firing:node1,node2 注意:因為 group 沒有變化,屬於重複告警,用的是 repeat_interval。
        group 等待 repeat_interval 10m 後傳送第四次告警 firing:node1,node2 同上一次。
        第四次告警後的 前 5 分鐘:node2 恢復正常
        第四次告警後的 後 5 分鐘:node1 恢復正常
        group 等待 repeat_interval 10m 後傳送第五次告警 resolved:node1,node2 注意,這裡 node1,node2 都恢復正常用的也是 repeat_interval。

        綜上:

        • for 是告警規則個體的監控配置,用來衡量服務多久檢測不透過才算異常。
        • group_wait:初次傳送告警的等待時間
          用於 group 建立後的等待,這個值通常設定較小,在幾分鐘以內。
        • group_interval:同一個組其他新發生的告警傳送時間間隔
          是 group 內容發生變化後的告警間隔。
        • repeat_interval:重複傳送同一個告警的時間間隔
          group 內容沒有變化且上一次發生成功時用的發生間隔。

        需要注意,恢復正常不屬於 group 變化,用的是 repeat_interval。這有點反直覺,且個人認為不是很合理,不知道是不是測試有問題,也沒有找到比較好的資料說明。希望知道的可以指教一下。

        exporter 的位置
        exporter 可以以 sidecar 的形式和原容器放在同一個 pod 內(1 對 1),也可以以獨立部署的形式存在(1 對 1/1 對多)。
        這個視具體情況而定,技術上沒什麼不同,sidecar 可以繫結生命週期,視為對原有元件的補充。獨立部署則耦合度更低,更靈活。像單節點的 mysql,用 sidecar 則只需要 1 個 pod,不會太複雜。
        而如果像多節點的 kafka 叢集,用獨立部署則只需要一個 exporter 就可以實現對多個節點的採集監控。
        這裡出於減小耦合、節省資源的目的,我主要使用的是獨立部署形式。

        使用 promtool 檢查指標格式是否正確

        promtool 使用方法:

        # 進入pod$ kubectl -n=monitoring exec -it prometheus-k8s-0  sh# 檢視幫助$ promtool -h# 檢查指標格式$ curl -s | promtool check metrics

        比方說 指標 name、labelname 不能使用小數點

        使用 port-forward 臨時提供 Prometheus 外部訪問

        # prometheus$ nohup  kubectl port-forward --address 0.0.0.0 service/prometheus-k8s 19090:9090 -n=monitoring &# grafana$ nohup kubectl port-forward --address 0.0.0.0 service/grafana 13000:3000 -n=monitoring &# alertmanager$ nohup  kubectl port-forward --address 0.0.0.0 service/alertmanager-main 9093:9093 -n=monitoring &

        用  jobs -l  可以檢視

        來源:


        來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70013542/viewspace-2921405/,如需轉載,請註明出處,否則將追究法律責任。

        相關文章