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)。而用戶則可以在適配器中對接外部任意的服務。外部服務可以是真正的存儲系統,公有雲的存儲服務,也可以是消息隊列等任意形式。

                                       Remote Write

 

2.2 Remote Read

  如下圖所示,Promthues的Remote Read(遠程讀)也通過了一個適配器實現。在遠程讀的流程當中,當用戶發起查詢請求后,Promthues將向remote_read中配置的URL發起查詢請求(matchers,ranges),Adaptor根據請求條件從第三方存儲服務中獲取響應的數據。同時將數據轉換為Promthues的原始樣本數據返回給Prometheus Server。

  當獲取到樣本數據后,Promthues在本地使用PromQL對樣本數據進行二次處理。

  注意:啟用遠程讀設置后,只在數據查詢時有效,對於規則文件的處理,以及Metadata API的處理都只基於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服務。

                           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..."   3 hours ago         Up 3 hours          0.0.0.0:8086->8086/tcp   localhost_influxdb_1

  獲取並啟動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可以正常查詢到本地存儲以刪除的歷史數據記錄。

                    從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 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采集任務進行划分,以支持規模的擴展。

 

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目標即可。

 

                        基本HA

  基本的HA模式只能確保Promthues服務的可用性問題,但是不解決Prometheus Server之間的數據一致性問題以及持久化問題(數據丟失后無法恢復),也無法進行動態的擴展。因此這種部署方式適合監控規模不大,Promthues Server也不會頻繁發生遷移的情況,並且只需要保存短周期監控數據的場景。

 

4.2 基本HA + 遠程存儲

  在基本HA模式的基礎上通過添加Remote Storage存儲支持,將監控數據保存在第三方存儲服務上。

                      HA + Remote Storage

  在解決了Promthues服務可用性的基礎上,同時確保了數據的持久化,當Promthues Server發生宕機或者數據丟失的情況下,可以快速的恢復。 同時Promthues Server可能很好的進行遷移。因此,該方案適用於用戶監控規模不大,但是希望能夠將監控數據持久化,同時能夠確保Promthues Server的可遷移性的場景。

 

4.3 基本HA + 遠程存儲 + 聯邦集群

  當單台Promthues Server無法處理大量的采集任務時,用戶可以考慮基於Prometheus聯邦集群的方式將監控采集任務划分到不同的Promthues實例當中即在任務級別功能分區。

                          基本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實例。通過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部分。如下所示。

                                Alertmanager成為單點

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

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

                                  Alertmanager特性

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

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

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

                                Alertmanager Gossip

 

5.1 Gossip協議

  Gossip是分布式系統中被廣泛使用的協議,用於實現分布式節點之間的信息交換和狀態同步。Gossip協議同步狀態類似於流言或者病毒的傳播,如下所示:

 

                            Gossip分布式協議

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

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

 

                              通知流水線

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

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

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

4.告警發送完成后該Alertmanager進入最后一個階段Gossip,Gossip會通知其他Alertmanager實例當前告警已經發送。其他實例接收到Gossip消息后,則會在自己的數據庫中保存該通知已發送的記錄。

  因此如下所示,Gossip機制的關鍵在於兩點:

                              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" --config.file=/etc/prometheus/alertmanager.yml  --storage.path=/data/alertmanager/

  定義Alertmanager實例a2,其中主服務運行在9094端口,集群服務運行在8002端口。為了將a1,a2組成集群。 a2啟動時需要定義--cluster.peer參數並且指向a1實例的集群服務地址:8001。

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.yml  --storage.path=/data/alertmanager2/

  為了能夠在本地模擬集群環境,這里使用了一個輕量級的多線程管理工具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
# 啟動服務
webhook

  示例結構如下所示:

                        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集群的狀態。

 

                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" http://localhost:9095/api/v1/alerts

  運行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" alert=DiskRunningFull[8320f0a][active]

  查看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 }})"

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

                    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" alert=hostCpuUsageAlert[7d698ac][active]

  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/' --log.level=debug 

  定義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的地址就可以了.

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM