天天看點

Prometheus的叢集與高可用

  導航:這裡主要是列出一個prometheus一些系統的學習過程,最後按照章節順序檢視,由于寫作該文檔經曆了不同時期,是以在文中有時出現

的雲環境不統一,但是學習具體使用方法即可,在最後的篇章,有一個完整的騰訊雲的實戰案例。

  1.​​什麼是prometheus?​​

  2.​​Prometheus安裝​​

  3.​​Prometheus的Exporter詳解​​

  4.​​Prometheus的PromQL​​

  5.​​Prometheus告警處理​​

  6.​​Prometheus的叢集與高可用​​

  7.​​Prometheus服務發現​​

  8.​​kube-state-metrics 和 metrics-server​​

  9.​​監控kubernetes叢集的方式​​

  10.​​prometheus operator​​

  11.​​Prometheus實戰之聯邦+高可用+持久​​

  12.​​Prometheus實戰之配置彙總​​

  13.​​Grafana簡單用法​​

  14.​​Grafana SQL彙總​​

  15.​​prometheus SQL彙總​​

  參考:

  ​​https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config​​

  ​​https://yunlzheng.gitbook.io/prometheus-book/part-iii-prometheus-shi-zhan/readmd/use-prometheus-monitor-kubernetes​​

  ​​https://www.bookstack.cn/read/prometheus_practice/introduction-README.md​​

  ​​https://www.kancloud.cn/huyipow/prometheus/521184​​

  ​​https://www.qikqiak.com/k8s-book/docs/​​

 

  Prometheus内置了一個基于本地存儲的時間序列資料庫。在Prometheus設計上,使用本地存儲可以降低Prometheus部署和管理的複雜度同時減少高可用(HA)帶來的複雜性。 在預設情況下,使用者隻需要部署多套Prometheus,采集相同的Targets即可實作基本的HA。同時由于Promethus高效的資料處理能力,單個Prometheus Server基本上能夠應對大部分使用者監控規模的需求。

  當然本地存儲也帶來了一些不好的地方,首先就是資料持久化的問題,特别是在像Kubernetes這樣的動态叢集環境下,如果Promthues的執行個體被重新排程,那所有曆史監控資料都會丢失。 其次本地存儲也意味着Prometheus不适合儲存大量曆史資料(一般Prometheus推薦隻保留幾周或者幾個月的資料)。最後本地存儲也導緻Prometheus無法進行彈性擴充。為了适應這方面的需求,Prometheus提供了remote_write和remote_read的特性,支援将資料存儲到遠端和從遠端讀取資料。通過将監控與資料分離,Prometheus能夠更好地進行彈性擴充。

  除了本地存儲方面的問題,由于Prometheus基于Pull模型,當有大量的Target需要采樣本時,單一Prometheus執行個體在資料抓取時可能會出現一些性能問題,聯邦叢集的特性可以讓Prometheus将樣本采集任務劃分到不同的Prometheus執行個體中,并且通過一個統一的中心節點進行聚合,進而可以使Prometheuse可以根據規模進行擴充。

  除了讨論Prometheus自身的高可用,Alertmanager作為Promthues體系中的告警進行中心,本章的最後部分會讨論如何實作Alertmanager的高可用部署。

  本章的主要内容:

  • Prometheus本地存儲機制
  • Prometheus的遠端存儲機制
  • Prometheus聯邦叢集
  • Prometheus高可用部署架構
  • Alertmanager高可用部署架構

1.本地存儲

1.1 本地存儲

  Prometheus 2.x 采用自定義的存儲格式将樣本資料儲存在本地磁盤當中。如下所示,按照兩個小時為一個時間視窗,将兩小時内産生的資料存儲在一個塊(Block)中,每一個塊中包含該時間視窗内的所有樣本資料(chunks),中繼資料檔案(meta.json)以及索引檔案(index)。

t0            t1             t2             now
 ┌───────────┐  ┌───────────┐  ┌───────────┐
 │           │  │           │  │           │                 ┌────────────┐
 │           │  │           │  │  mutable  │ <─── write ──── ┤ Prometheus │
 │           │  │           │  │           │                 └────────────┘
 └───────────┘  └───────────┘  └───────────┘                        ^
       └──────────────┴───────┬──────┘                              │
                              │                                   query
                              │                                     │
                            merge ──────────────────────────────────┘      

  目前時間視窗内正在收集的樣本資料,Prometheus則會直接将資料儲存在記憶體當中。為了確定此期間如果Prometheus發生崩潰或者重新開機時能夠恢複資料,Prometheus啟動時會從寫入日志(WAL)進行重播,進而恢複資料。此期間如果通過API删除時間序列,删除記錄也會儲存在單獨的邏輯檔案當中(tombstone)。

  在檔案系統中這些塊儲存在單獨的目錄當中,Prometheus儲存塊資料的目錄結構如下所示:

./data 
   |- 01BKGV7JBM69T2G1BGBGM6KB12 # 塊
      |- meta.json  # 中繼資料
      |- wal        # 寫入日志
        |- 000002
        |- 000001
   |- 01BKGTZQ1SYQJTR4PB43C8PD98  # 塊
      |- meta.json  #中繼資料
      |- index   # 索引檔案
      |- chunks  # 樣本資料
        |- 000001
      |- tombstones # 邏輯資料
   |- 01BKGTZQ1HHWHV8FBJXW1Y3W0K
      |- meta.json
      |- wal
        |-000001      

  通過時間視窗的形式儲存所有的樣本資料,可以明顯提高Prometheus的查詢效率,當查詢一段時間範圍内的所有樣本資料時,隻需要簡單的從落在該範圍内的塊中查詢資料即可。

  同時該存儲方式可以簡化曆史資料的删除邏輯。隻要一個塊的時間範圍落在了配置的保留範圍之外,直接丢棄該塊即可。

┌────────────┐  ┌────┼─────┐  ┌───────────┐  ┌───────────┐  
 │ 1          │  │ 2  |     │  │ 3         │  │ 4         │ . . .
 └────────────┘  └────┼─────┘  └───────────┘  └───────────┘  
                      |
                      |
             retention boundary      

1.2 本地存儲配置

  使用者可以通過指令行啟動參數的方式修改本地存儲的配置。

啟動參數 預設值 含義
--storage.tsdb.path data/ Base path for metrics storage
--storage.tsdb.retention 15d How long to retain samples in the storage
--storage.tsdb.min-block-duration 2h The timestamp range of head blocks after which they get persisted
--storage.tsdb.max-block-duration 36h The maximum timestamp range of compacted blocks,It's the minimum duration of any persisted block.
--storage.tsdb.no-lockfile false Do not create lockfile in data directory

  在一般情況下,Prometheus中存儲的每一個樣本大概占用1-2位元組大小。如果需要對Prometheus Server的本地磁盤空間做容量規劃時,可以通過以下公式計算:

needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample      

  從上面公式中可以看出在保留時間(retention_time_seconds)和樣本大小(bytes_per_sample)不變的情況下,如果想減少本地磁盤的容量需求,隻能通過減少每秒擷取樣本數(ingested_samples_per_second)的方式。是以有兩種手段,一是減少時間序列的數量,二是增加采集樣本的時間間隔。考慮到Prometheus會對時間序列進行壓縮效率,減少時間序列的數量效果更明顯。

1.3 從失敗中恢複

  如果本地存儲由于某些原因出現了錯誤,最直接的方式就是停止Prometheus并且删除data目錄中的所有記錄。當然也可以嘗試删除那些發生錯誤的塊目錄,不過相應的使用者會丢失該塊中儲存的大概兩個小時的監控記錄。

2.遠端存儲

  Prometheus的本地存儲設計可以減少其自身運維和管理的複雜度,同時能夠滿足大部分使用者監控規模的需求。但是本地存儲也意味着Prometheus無法持久化資料,無法存儲大量曆史資料,同時也無法靈活擴充和遷移。

  為了保持Prometheus的簡單性,Prometheus并沒有嘗試在自身中解決以上問題,而是通過定義兩個标準接口(remote_write/remote_read),讓使用者可以基于這兩個接口對接将資料儲存到任意第三方的存儲服務中,這種方式在Promthues中稱為Remote Storage。

2.1 Remote Write

  使用者可以在Prometheus配置檔案中指定Remote Write(遠端寫)的URL位址,一旦設定了該配置項,Prometheus将采集到的樣本資料通過HTTP的形式發送給擴充卡(Adaptor)。而使用者則可以在擴充卡中對接外部任意的服務。外部服務可以是真正的存儲系統,公有雲的存儲服務,也可以是消息隊列等任意形式。

Prometheus的叢集與高可用

                                      Remote Write

2.2 Remote Read

  如下圖所示,Promthues的Remote Read(遠端讀)也通過了一個擴充卡實作。在遠端讀的流程當中,當使用者發起查詢請求後,Promthues将向remote_read中配置的URL發起查詢請求(matchers,ranges),Adaptor根據請求條件從第三方存儲服務中擷取響應的資料。同時将資料轉換為Promthues的原始樣本資料傳回給Prometheus Server。

  當擷取到樣本資料後,Promthues在本地使用PromQL對樣本資料進行二次處理。

  注意:啟用遠端讀設定後,隻在資料查詢時有效,對于規則檔案的處理,以及Metadata API的處理都隻基于Prometheus本地存儲完成。

Prometheus的叢集與高可用

                                       Remote Read

2.3 配置檔案

  Prometheus配置檔案中添加remote_write和remote_read配置,其中url用于指定遠端讀/寫的HTTP服務位址。如果該URL啟動了認證則可以通過basic_auth進行安全認證配置。對于https的支援需要設定tls_concig。proxy_url主要用于Prometheus無法直接通路擴充卡服務的情況下。

  remote_write和remote_write具體配置如下所示:

remote_write:
    url: <string>
    [ remote_timeout: <duration> | default = 30s ]
    write_relabel_configs:
    [ - <relabel_config> ... ]
    basic_auth:
    [ username: <string> ]
    [ password: <string> ]
    [ bearer_token: <string> ]
    [ bearer_token_file: /path/to/bearer/token/file ]
    tls_config:
    [ <tls_config> ]
    [ proxy_url: <string> ]

remote_read:
    url: <string>
    required_matchers:
    [ <labelname>: <labelvalue> ... ]
    [ remote_timeout: <duration> | default = 30s ]
    [ read_recent: <boolean> | default = false ]
    basic_auth:
    [ username: <string> ]
    [ password: <string> ]
    [ bearer_token: <string> ]
    [ bearer_token_file: /path/to/bearer/token/file ]
    [ <tls_config> ]
    [ proxy_url: <string> ]      

2.4 自定義Remote Storage Adaptor

  實作自定義Remote Storage需要使用者分别建立用于支援remote_read和remote_write的HTTP服務。

Prometheus的叢集與高可用

                           Remote Storage

  目前Prometheus中Remote Storage相關的協定主要通過以下proto檔案進行定義:

syntax = "proto3";
package prometheus;

option go_package = "prompb";

import "types.proto";

message WriteRequest {
  repeated prometheus.TimeSeries timeseries = 1;
}

message ReadRequest {
  repeated Query queries = 1;
}

message ReadResponse {
  // In same order as the request's queries.
  repeated QueryResult results = 1;
}

message Query {
  int64 start_timestamp_ms = 1;
  int64 end_timestamp_ms = 2;
  repeated prometheus.LabelMatcher matchers = 3;
}

message QueryResult {
  // Samples within a time series must be ordered by time.
  repeated prometheus.TimeSeries timeseries = 1;
}      

  以下代碼展示了一個簡單的remote_write服務,建立用于接收remote_write的HTTP服務,将請求内容轉換成WriteRequest後,使用者就可以按照自己的需求進行後續的邏輯處理。

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"

    "github.com/gogo/protobuf/proto"
    "github.com/golang/snappy"
    "github.com/prometheus/common/model"

    "github.com/prometheus/prometheus/prompb"
)

func main() {
    http.HandleFunc("/receive", func(w http.ResponseWriter, r *http.Request) {
        compressed, err := ioutil.ReadAll(r.Body)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        reqBuf, err := snappy.Decode(nil, compressed)
        if err != nil {
            http.Error(w, err.Error(), http.StatusBadRequest)
            return
        }

        var req prompb.WriteRequest
        if err := proto.Unmarshal(reqBuf, &req); err != nil {
            http.Error(w, err.Error(), http.StatusBadRequest)
            return
        }

        for _, ts := range req.Timeseries {
            m := make(model.Metric, len(ts.Labels))
            for _, l := range ts.Labels {
                m[model.LabelName(l.Name)] = model.LabelValue(l.Value)
            }
            fmt.Println(m)

            for _, s := range ts.Samples {
                fmt.Printf("  %f %d\n", s.Value, s.Timestamp)
            }
        }
    })

    http.ListenAndServe(":1234", nil)
}      

2.5 使用Influxdb作為Remote Storage

  目前Prometheus社群也提供了部分對于第三方資料庫的Remote Storage支援:

存儲服務 支援模式
AppOptics write
Chronix write
Cortex: read/write
CrateDB read/write
Gnocchi write
Graphite write
InfluxDB read/write
OpenTSDB write
PostgreSQL/TimescaleDB: read/write
SignalFx write

  這裡将介紹如何使用Influxdb作為Prometheus的Remote Storage,進而確定當Prometheus發生當機或者重新開機之後能夠從Influxdb中恢複和擷取曆史資料。

  這裡使用docker-compose定義并啟動Influxdb資料庫服務,docker-compose.yml定義如下:

version: '2'
services:
  influxdb:
    image: influxdb:1.3.5
    command: -config /etc/influxdb/influxdb.conf
    ports:
      - "8086:8086"
    environment:
      - INFLUXDB_DB=prometheus
      - INFLUXDB_ADMIN_ENABLED=true
      - INFLUXDB_ADMIN_USER=admin
      - INFLUXDB_ADMIN_PASSWORD=admin
      - INFLUXDB_USER=prom
      - INFLUXDB_USER_PASSWORD=prom      

  啟動influxdb服務

$ docker-compose up -d
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
795d0ead87a1        influxdb:1.3.5      "/entrypoint.sh -c..."      

  擷取并啟動Prometheus提供的Remote Storage Adapter:

go get github.com/prometheus/prometheus/documentation/examples/remote_storage/remote_storage_adapter      

  擷取remote_storage_adapter源碼後,go會自動把相關的源碼編譯成可執行檔案,并且儲存在$GOPATH/bin/目錄下。

  啟動remote_storage_adapter并且設定Influxdb相關的認證資訊:

INFLUXDB_PW=prom $GOPATH/bin/remote_storage_adapter -influxdb-url=http://localhost:8086 -influxdb.username=prom -influxdb.database=prometheus -influxdb.retention-policy=autogen      

  修改prometheus.yml添加Remote Storage相關的配置内容:

remote_write:
  - url: "http://localhost:9201/write"

remote_read:
  - url: "http://localhost:9201/read"      

  重新啟動Prometheus能夠擷取資料後,登入到influxdb容器,并驗證資料寫入。如下所示,當資料能夠正常寫入Influxdb後可以看到Prometheus相關的名額。

docker exec -it 795d0ead87a1 influx
Connected to http://localhost:8086 version 1.3.5
InfluxDB shell version: 1.3.5
> auth
username: prom
password:

> use prometheus
> SHOW MEASUREMENTS
name: measurements
name
----
go_gc_duration_seconds
go_gc_duration_seconds_count
go_gc_duration_seconds_sum
go_goroutines
go_info
go_memstats_alloc_bytes
go_memstats_alloc_bytes_total
go_memstats_buck_hash_sys_bytes
go_memstats_frees_total
go_memstats_gc_cpu_fraction
go_memstats_gc_sys_bytes
go_memstats_heap_alloc_bytes
go_memstats_heap_idle_bytes      

  當資料寫入成功後,停止Prometheus服務。同時删除Prometheus的data目錄,模拟Promthues資料丢失的情況後重新開機Prometheus。打開Prometheus UI如果配置正常,Prometheus可以正常查詢到本地存儲以删除的曆史資料記錄。

Prometheus的叢集與高可用

                    從Remote Storage擷取曆史資料

2.6 新版influxdb配置

  在新版influxdb裡面已經有了http 的通路方式,不再需要插件,是以配置隻有以下幾行;

remote_write: 
  - url: "http://influxdb:8086/api/v1/prom/write?u=root&p=huawei&db=prometheus" 

remote_read: 
  - url: "http://influxdb:8086/api/v1/prom/read?u=root&p=huawei&db=prometheus"      

  上述配置配置中也包含了influxdb配置使用者和密碼的情況

3.聯邦叢集

  通過Remote Storage可以分離監控樣本采集和資料存儲,解決Prometheus的持久化問題。這一部分會重點讨論如何利用聯邦叢集特性對Promthues進行擴充,以适應不同監控規模的變化。

3.1 使用聯邦叢集

  對于大部分監控規模而言,我們隻需要在每一個資料中心(例如:EC2可用區,Kubernetes叢集)安裝一個Prometheus Server執行個體,就可以在各個資料中心處理上千規模的叢集。同時将Prometheus Server部署到不同的資料中心可以避免網絡配置的複雜性。

Prometheus的叢集與高可用

                        聯邦叢集

  如上圖所示,在每個資料中心部署單獨的Prometheus Server,用于采集目前資料中心監控資料。并由一個中心的Prometheus Server負責聚合多個資料中心的監控資料。這一特性在Promthues中稱為聯邦叢集。

  聯邦叢集的核心在于每一個Prometheus Server都包含一個用于擷取目前執行個體中監控樣本的接口/federate。對于中心Prometheus Server而言,無論是從其他的Prometheus執行個體還是Exporter執行個體中擷取資料實際上并沒有任何差異。

scrape_configs:
  - job_name: 'federate'
    scrape_interval: 15s
    honor_labels: true
    metrics_path: '/federate'
    params:
      'match[]':
        - '{job="prometheus"}'
        - '{__name__=~"job:.*"}'
        - '{__name__=~"node.*"}'
    static_configs:
      - targets:
        - '192.168.77.11:9090'
        - '192.168.77.12:9090'      

  為了有效的減少不必要的時間序列,通過params參數可以用于指定隻擷取某些時間序列的樣本資料,例如

"http://192.168.77.11:9090/federate?match[]={job%3D"prometheus"}&match[]={__name__%3D~"job%3A.*"}&match[]={__name__%3D~"node.*"}"      

  通過URL中的match[]參數指定我們可以指定需要擷取的時間序列。match[]參數必須是一個瞬時向量選擇器,例如up或者{job="api-server"}。配置多個match[]參數,用于擷取多組時間序列的監控資料。

  horbor_labels配置true可以確定當采集到的監控名額沖突時,能夠自動忽略沖突的監控資料。如果為false時,prometheus會自動将沖突的标簽替換為”exported_“的形式。

3.2 功能分區

  聯邦叢集的特性可以幫助使用者根據不同的監控規模對Promthues部署架構進行調整。例如如下所示,可以在各個資料中心中部署多個Prometheus Server執行個體。每一個Prometheus Server執行個體隻負責采集目前資料中心中的一部分任務(Job),例如可以将不同的監控任務分離到不同的Prometheus執行個體當中,再有中心Prometheus執行個體進行聚合。

Prometheus的叢集與高可用

                       功能分區

  功能分區,即通過聯邦叢集的特性在任務級别對Prometheus采集任務進行劃分,以支援規模的擴充。

4.Prometheus高可用

  Prometheus的本地存儲給Prometheus帶來了簡單高效的使用體驗,可以讓Promthues在單節點的情況下滿足大部分使用者的監控需求。但是本地存儲也同時限制了Prometheus的可擴充性,帶來了資料持久化等一系列的問題。通過Prometheus的Remote Storage特性可以解決這一系列問題,包括Promthues的動态擴充,以及曆史資料的存儲。

  而除了資料持久化問題以外,影響Promthues性能表現的另外一個重要因素就是資料采集任務量,以及單台Promthues能夠處理的時間序列數。是以當監控規模大到Promthues單台無法有效處理的情況下,可以選擇利用Promthues的聯邦叢集的特性,将Promthues的監控任務劃分到不同的執行個體當中。

  這一部分将重點讨論Prometheus的高可用架構,并且根據不同的使用場景介紹了一種常見的高可用方案。

4.1 基本HA:服務可用性

  由于Promthues的Pull機制的設計,為了確定Promthues服務的可用性,使用者隻需要部署多套Prometheus Server執行個體,并且采集相同的Exporter目标即可。

Prometheus的叢集與高可用

                        基本HA

  基本的HA模式隻能確定Promthues服務的可用性問題,但是不解決Prometheus Server之間的資料一緻性問題以及持久化問題(資料丢失後無法恢複),也無法進行動态的擴充。是以這種部署方式适合監控規模不大,Promthues Server也不會頻繁發生遷移的情況,并且隻需要儲存短周期監控資料的場景。

4.2 基本HA + 遠端存儲

  在基本HA模式的基礎上通過添加Remote Storage存儲支援,将監控資料儲存在第三方存儲服務上。

Prometheus的叢集與高可用

                      HA + Remote Storage

  在解決了Promthues服務可用性的基礎上,同時確定了資料的持久化,當Promthues Server發生當機或者資料丢失的情況下,可以快速的恢複。 同時Promthues Server可能很好的進行遷移。是以,該方案适用于使用者監控規模不大,但是希望能夠将監控資料持久化,同時能夠確定Promthues Server的可遷移性的場景。

4.3 基本HA + 遠端存儲 + 聯邦叢集

  當單台Promthues Server無法處理大量的采集任務時,使用者可以考慮基于Prometheus聯邦叢集的方式将監控采集任務劃分到不同的Promthues執行個體當中即在任務級别功能分區。

Prometheus的叢集與高可用

                          基本HA + 遠端存儲 + 聯邦叢集

  這種部署方式一般适用于兩種場景:

   場景一:單資料中心 + 大量的采集任務

  這種場景下Promthues的性能瓶頸主要在于大量的采集任務,是以使用者需要利用Prometheus聯邦叢集的特性,将不同類型的采集任務劃分到不同的Promthues子服務中,進而實作功能分區。例如一個Promthues Server負責采集基礎設施相關的監控名額,另外一個Prometheus Server負責采集應用監控名額。再有上層Prometheus Server實作對資料的彙聚。

  場景二:多資料中心

  這種模式也适合與多資料中心的情況,當Promthues Server無法直接與資料中心中的Exporter進行通訊時,在每一個資料中部署一個單獨的Promthues Server負責目前資料中心的采集任務是一個不錯的方式。這樣可以避免使用者進行大量的網絡配置,隻需要確定主Promthues Server執行個體能夠與目前資料中心的Prometheus Server通訊即可。 中心Promthues Server負責實作對多資料中心資料的聚合。

4.4 按照執行個體進行功能分區

  這時在考慮另外一種極端情況,即單個采集任務的Target數也變得非常巨大。這時簡單通過聯邦叢集進行功能分區,Prometheus Server也無法有效處理時。這種情況隻能考慮繼續在執行個體級别進行功能劃分。

Prometheus的叢集與高可用

                          執行個體級别功能分區

  如上圖所示,将統一任務的不同執行個體的監控資料采集任務劃分到不同的Prometheus執行個體。通過relabel設定,我們可以確定目前Prometheus Server隻收集目前采集任務的一部分執行個體的監控名額。

global:
  external_labels:
    slave: 1  # This is the 2nd slave. This prevents clashes between slaves.
scrape_configs:
  - job_name: some_job
    relabel_configs:
    - source_labels: [__address__]
      modulus:       4
      target_label:  __tmp_hash
      action:        hashmod
    - source_labels: [__tmp_hash]
      regex:         ^1$
      action:        keep      

  并且通過目前資料中心的一個中心Prometheus Server将監控資料進行聚合到任務級别。

- scrape_config:
  - job_name: slaves
    honor_labels: true
    metrics_path: /federate
    params:
      match[]:
        - '{__name__=~"^slave:.*"}'   # Request all slave-level time series
    static_configs:
      - targets:
        - slave0:9090
        - slave1:9090
        - slave3:9090
        - slave4:9090      

4.5 高可用方案選擇

  上面的部分,根據不同的場景示範了3種不同的高可用部署方案。當然對于Promthues部署方案需要使用者根據監控規模以及自身的需求進行動态調整,下表展示了Promethues和高可用有關3個選項各自解決的問題,使用者可以根據自己的需求靈活選擇。

選項\需求 服務可用性 資料持久化 水準擴充
主備HA v x x
遠端存儲 x v x
聯邦叢集 x x v

5.Alertmanager高可用

  在上一小節中我們主要讨論了Prometheus Server自身的高可用問題。而接下來,重點将放在告警處理也就是Alertmanager部分。如下所示。

Prometheus的叢集與高可用

                                Alertmanager成為單點

  為了提升Promthues的服務可用性,通常使用者會部署兩個或者兩個以上的Promthus Server,它們具有完全相同的配置包括Job配置,以及告警配置等。當某一個Prometheus Server發生故障後可以確定Promthues持續可用。

  同時基于Alertmanager的告警分組機制即使不同的Prometheus Sever分别發送相同的告警給Alertmanager,Alertmanager也可以自動将這些告警合并為一個通知向receiver發送。

Prometheus的叢集與高可用

                                  Alertmanager特性

  但不幸的是,雖然Alertmanager能夠同時處理多個相同的Prometheus Server所産生的告警。但是由于單個Alertmanager的存在,目前的部署結構存在明顯的單點故障風險,當Alertmanager單點失效後,告警的後續所有業務全部失效。

  如下所示,最直接的方式,就是嘗試部署多套Alertmanager。但是由于Alertmanager之間不存在并不了解彼此的存在,是以則會出現告警通知被不同的Alertmanager重複發送多次的問題。

Prometheus的叢集與高可用

  為了解決這一問題,如下所示。Alertmanager引入了Gossip機制。Gossip機制為多個Alertmanager之間提供了資訊傳遞的機制。確定及時在多個Alertmanager分别接收到相同告警資訊的情況下,也隻有一個告警通知被發送給Receiver。

Prometheus的叢集與高可用

                                Alertmanager Gossip

5.1 Gossip協定

  Gossip是分布式系統中被廣泛使用的協定,用于實作分布式節點之間的資訊交換和狀态同步。Gossip協定同步狀态類似于流言或者病毒的傳播,如下所示:

Prometheus的叢集與高可用

                            Gossip分布式協定

  一般來說Gossip有兩種實作方式分别為Push-based和Pull-based。在Push-based當叢集中某一節點A完成一個工作後,随機的從其它節點B并向其發送相應的消息,節點B接收到消息後在重複完成相同的工作,直到傳播到叢集中的所有節點。而Pull-based的實作中節點A會随機的向節點B發起詢問是否有新的狀态需要同步,如果有則傳回。

  在簡單了解了Gossip協定之後,我們來看Alertmanager是如何基于Gossip協定實作叢集高可用的。如下所示,當Alertmanager接收到來自Prometheus的告警消息後,會按照以下流程對告警進行處理:

Prometheus的叢集與高可用

                              通知流水線

1.在第一個階段Silence中,Alertmanager會判斷目前通知是否比對到任何的靜默規則,如果沒有則進入下一個階段,否則則中斷流水線不發送通知。

2.在第二個階段Wait中,Alertmanager會根據目前Alertmanager在叢集中所在的順序(index)等待index * 5s的時間。

3.目前Alertmanager等待階段結束後,Dedup階段則會判斷目前Alertmanager資料庫中該通知是否已經發送,如果已經發送則中斷流水線,不發送告警,否則則進入下一階段Send對外發送告警通知。

4.告警發送完成後該Alertmanager進入最後一個階段Gossip,Gossip會通知其他Alertmanager執行個體目前告警已經發送。其他執行個體接收到Gossip消息後,則會在自己的資料庫中儲存該通知已發送的記錄。

  是以如下所示,Gossip機制的關鍵在于兩點:

Prometheus的叢集與高可用

                              Gossip機制

  • Silence設定同步:Alertmanager啟動階段基于Pull-based從叢集其它節點同步Silence狀态,當有新的Silence産生時使用Push-based方式在叢集中傳播Gossip資訊。
  • 通知發送狀态同步:告警通知發送完成後,基于Push-based同步告警發送狀态。Wait階段可以確定叢集狀态一緻。

  Alertmanager基于Gossip實作的叢集機制雖然不能保證所有執行個體上的資料時刻保持一緻,但是實作了CAP理論中的AP系統,即可用性和分區容錯性。同時對于Prometheus Server而言保持了配置了簡單性,Promthues Server之間不需要任何的狀态同步。

5.2 搭建本地叢集環境

  為了能夠讓Alertmanager節點之間進行通訊,需要在Alertmanager啟動時設定相應的參數。其中主要的參數包括:

  • --cluster.listen-address string: 目前執行個體叢集服務監聽位址
  • --cluster.peer value: 初始化時關聯的其它執行個體的叢集服務位址

  例如:

  定義Alertmanager執行個體a1,其中Alertmanager的服務運作在9093端口,叢集服務位址運作在8001端口。

alertmanager  --web.listen-address=":9093" --cluster.listen-address="127.0.0.1:8001"      

  定義Alertmanager執行個體a2,其中主服務運作在9094端口,叢集服務運作在8002端口。為了将a1,a2組成叢集。 a2啟動時需要定義--cluster.peer參數并且指向a1執行個體的叢集服務位址:8001。

alertmanager  --web.listen-address=":9094" --cluster.listen-address="127.0.0.1:8002"      

  為了能夠在本地模拟叢集環境,這裡使用了一個輕量級的多線程管理工具goreman。使用以下指令可以在本地安裝goreman指令行工具。

go get github.com/mattn/goreman      

5.3 建立Alertmanager叢集

  建立Alertmanager配置檔案/etc/prometheus/alertmanager-ha.yml, 為了驗證Alertmanager的叢集行為,這裡在本地啟動一個webhook服務用于列印Alertmanager發送的告警通知資訊。

route:
  receiver: 'default-receiver'
receivers:
  - name: default-receiver
    webhook_configs:
    - url: 'http://127.0.0.1:5001/'      

  本地webhook服務可以直接從Github擷取。

# 擷取alertmanager提供的webhook示例,如果該目錄下定義了main函數,go get會自動将其編譯成可執行檔案
go get github.com/prometheus/alertmanager/examples/webhook
# 設定環境變量指向GOPATH的bin目錄
export PATH=$GOPATH/bin:$PATH
# 啟動服務      

  示例結構如下所示:

Prometheus的叢集與高可用

                        Alertmanager HA部署結構

  建立alertmanager.procfile檔案,并且定義了三個Alertmanager節點(a1,a2,a3)以及用于接收告警通知的webhook服務:

a1: alertmanager  --web.listen-address=":9093" --cluster.listen-address="127.0.0.1:8001" --config.file=/etc/prometheus/alertmanager-ha.yml  --storage.path=/data/alertmanager/ --log.level=debug
a2: alertmanager  --web.listen-address=":9094" --cluster.listen-address="127.0.0.1:8002" --cluster.peer=127.0.0.1:8001 --config.file=/etc/prometheus/alertmanager-ha.yml  --storage.path=/data/alertmanager2/ --log.level=debug
a3: alertmanager  --web.listen-address=":9095" --cluster.listen-address="127.0.0.1:8003" --cluster.peer=127.0.0.1:8001 --config.file=/etc/prometheus/alertmanager-ha.yml  --storage.path=/data/alertmanager2/ --log.level=debug

webhook: webhook      

  在Procfile檔案所在目錄,執行goreman start指令,啟動所有程序:

$ goreman -f alertmanager.procfile start
10:27:57      a1 | level=debug ts=2018-03-12T02:27:57.399166371Z caller=cluster.go:125 component=cluster msg="joined cluster" peers=0
10:27:57      a3 | level=info ts=2018-03-12T02:27:57.40004678Z caller=main.go:346 msg=Listening address=:9095
10:27:57      a1 | level=info ts=2018-03-12T02:27:57.400212246Z caller=main.go:271 msg="Loading configuration file" file=/etc/prometheus/alertmanager.yml
10:27:57      a1 | level=info ts=2018-03-12T02:27:57.405638714Z caller=main.go:346 msg=Listening address=:9093      

  啟動完成後通路任意Alertmanager節點​​http://ip:9093/#/status​​,可以檢視目前Alertmanager叢集的狀态。

Prometheus的叢集與高可用

                Alertmanager叢集狀态

  當叢集中的Alertmanager節點不在一台主機時,通常需要使用--cluster.advertise-address參數指定目前節點所在網絡位址。

  注意:由于goreman不保證程序之間的啟動順序,如果叢集狀态未達到預期,可以使用goreman -f alertmanager.procfile run restart a2重新開機a2,a3服務。

  當Alertmanager叢集啟動完成後,可以使用send-alerts.sh腳本對叢集進行簡單測試,這裡利用curl分别向3個Alertmanager執行個體發送告警資訊。

alerts1='[
  {
    "labels": {
       "alertname": "DiskRunningFull",
       "dev": "sda1",
       "instance": "example1"
     },
     "annotations": {
        "info": "The disk sda1 is running full",
        "summary": "please check the instance example1"
      }
  },
  {
    "labels": {
       "alertname": "DiskRunningFull",
       "dev": "sdb2",
       "instance": "example2"
     },
     "annotations": {
        "info": "The disk sdb2 is running full",
        "summary": "please check the instance example2"
      }
  },
  {
    "labels": {
       "alertname": "DiskRunningFull",
       "dev": "sda1",
       "instance": "example3",
       "severity": "critical"
     }
  },
  {
    "labels": {
       "alertname": "DiskRunningFull",
       "dev": "sda1",
       "instance": "example3",
       "severity": "warning"
     }
  }
]'

curl -XPOST -d"$alerts1" http://localhost:9093/api/v1/alerts
curl -XPOST -d"$alerts1" http://localhost:9094/api/v1/alerts
curl -XPOST -d"$alerts1"      

  運作send-alerts.sh後,檢視alertmanager日志,可以看到以下輸出,3個Alertmanager執行個體分别接收到模拟的告警資訊:

10:43:36      a1 | level=debug ts=2018-03-12T02:43:36.853370185Z caller=dispatch.go:188 component=dispatcher msg="Received alert" alert=DiskRunningFull[6543bc1][active]
10:43:36      a2 | level=debug ts=2018-03-12T02:43:36.871180749Z caller=dispatch.go:188 component=dispatcher msg="Received alert" alert=DiskRunningFull[8320f0a][active]
10:43:36      a3 | level=debug ts=2018-03-12T02:43:36.894923811Z caller=dispatch.go:188 component=dispatcher msg="Received alert"      

  檢視webhook日志隻接收到一個告警通知:

10:44:06 webhook | 2018/03/12 10:44:06 {
10:44:06 webhook |  >  "receiver": "default-receiver",
10:44:06 webhook |  >  "status": "firing",
10:44:06 webhook |  >  "alerts": [
10:44:06 webhook |  >    {
10:44:06 webhook |  >      "status": "firing",
10:44:06 webhook |  >      "labels": {
10:44:06 webhook |  >        "alertname": "DiskRunningFull",      

5.4 多執行個體Prometheus與Alertmanager叢集

  由于Gossip機制的實作,在Promthues和Alertmanager執行個體之間不要使用任何的負載均衡,需要確定Promthues将告警發送到所有的Alertmanager執行個體中:

alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - 127.0.0.1:9093
      - 127.0.0.1:9094
      - 127.0.0.1:9095      

  建立Promthues叢集配置檔案/etc/prometheus/prometheus-ha.yml,完整内容如下:

global:
  scrape_interval: 15s
  scrape_timeout: 10s
  evaluation_interval: 15s
rule_files:
  - /etc/prometheus/rules/*.rules
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - 127.0.0.1:9093
      - 127.0.0.1:9094
      - 127.0.0.1:9095
scrape_configs:
- job_name: prometheus
  static_configs:
  - targets:
    - localhost:9090
- job_name: 'node'
  static_configs:
  - targets: ['localhost:9100']      

  同時定義告警規則檔案/etc/prometheus/rules/hoststats-alert.rules,如下所示:

groups:
- name: hostStatsAlert
  rules:
  - alert: hostCpuUsageAlert
    expr: sum(avg without (cpu)(irate(node_cpu{mode!='idle'}[5m]))) by (instance) * 100 > 50
    for: 1m
    labels:
      severity: page
    annotations:
      summary: "Instance {{ $labels.instance }} CPU usgae high"
      description: "{{ $labels.instance }} CPU usage above 50% (current value: {{ $value }})"
  - alert: hostMemUsageAlert
    expr: (node_memory_MemTotal - node_memory_MemAvailable)/node_memory_MemTotal * 100 > 85
    for: 1m
    labels:
      severity: page
    annotations:
      summary: "Instance {{ $labels.instance }} MEM usgae high"
      description: "{{ $labels.instance }} MEM usage above 85% (current value: {{ $value }})"      

  本示例部署結構如下所示:

Prometheus的叢集與高可用

                    Promthues與Alertmanager HA部署結構

  建立prometheus.procfile檔案,建立兩個Promthues節點,分别監聽9090和9091端口:

p1: prometheus --config.file=/etc/prometheus/prometheus-ha.yml --storage.tsdb.path=/data/prometheus/ --web.listen-address="127.0.0.1:9090"
p2: prometheus --config.file=/etc/prometheus/prometheus-ha.yml --storage.tsdb.path=/data/prometheus2/ --web.listen-address="127.0.0.1:9091"

node_exporter: node_exporter -web.listen-address="0.0.0.0:9100"      

  使用goreman啟動多節點Promthues:

goreman -f prometheus.procfile -p 8556 start      

  Promthues啟動完成後,手動拉高系統CPU使用率:

cat /dev/zero>/dev/null      

  注意,對于多核主機,如果CPU達不到預期,運作多個指令。

  當CPU使用率達到告警規則觸發條件,兩個Prometheus執行個體告警分别被觸發。檢視Alertmanager輸出日志:

11:14:41      a3 | level=debug ts=2018-03-12T03:14:41.945493505Z caller=dispatch.go:188 component=dispatcher msg="Received alert" alert=hostCpuUsageAlert[7d698ac][active]
11:14:41      a1 | level=debug ts=2018-03-12T03:14:41.945534548Z caller=dispatch.go:188 component=dispatcher msg="Received alert" alert=hostCpuUsageAlert[7d698ac][active]
11:14:41      a2 | level=debug ts=2018-03-12T03:14:41.945687812Z caller=dispatch.go:188 component=dispatcher msg="Received alert"      

  3個Alertmanager執行個體分别接收到來自不同Prometheus執行個體的告警資訊。而Webhook服務隻接收到來自Alertmanager叢集的一條告警通知:

11:15:11 webhook | 2018/03/12 11:15:11 {
11:15:11 webhook |  >  "receiver": "default-receiver",
11:15:11 webhook |  >  "status": "firing",
11:15:11 webhook |  >  "alerts": [
11:15:11 webhook |  >    {
11:15:11 webhook |  >      "status": "firing",
11:15:11 webhook |  >      "labels": {
11:15:11 webhook |  >        "alertname": "hostCpuUsageAlert",      

5.5 一個生産環境的多執行個體Prometheus與Alertmanager叢集案例配置

主機名 Ip位址
Prometheus01 10.10.1.10
Prometheus02 10.10.1.5

  定義Alertmanager執行個體a1,其中Alertmanager的服務運作在9093端口,叢集服務位址運作在8001端口。

/opt/alertmanager/alertmanager  --web.listen-address="10.10.1.10:9093" --cluster.listen-address="10.10.1.10:8001" --config.file=/opt/alertmanager/alertmanager.yml --web.external-url='http://alertmanager.mobage.cn:9093/'      

  定義Alertmanager執行個體a2

/opt/alertmanager/alertmanager --web.listen-address="10.10.1.5:9093" --cluster.listen-address="10.10.1.5:8001" --cluster.peer="10.10.1.10:8001"  --config.file=/opt/alertmanager/alertmanager.yml --web.external-url='http://alertmanager.mobage.cn:9093/'      

  由于Gossip機制的實作,在Promthues和Alertmanager執行個體之間不要使用任何的負載均衡,需要確定Promthues将告警發送到所有的Alertmanager執行個體中:

  Prometheus 配置

alerting:  
  alertmanagers:  
    - static_configs:  
      - targets: ["10.10.1.10:9093","10.10.1.5:9093"]      

  以上,Prometheus配置完全一樣,隻要在alertmanager裡面寫上多台alertmanager的位址就可以了.

  具體更詳細配置會在具體案例中展現.

作者:​​小家電維修​​

轉世燕還故榻,為你銜來二月的花。