為了提升Promthues的服務可用性,通常用戶會部署兩個或者兩個以上的Promthus Server,它們具有完全相同的配置包括Job配置,以及告警配置等。當某一個Prometheus Server發生故障后可以確保Promthues持續可用。
同時基於Alertmanager的告警分組機制即使不同的Prometheus Sever分別發送相同的告警給Alertmanager,Alertmanager也可以自動將這些告警合並為一個通知向receiver發送。
但不幸的是,雖然Alertmanager能夠同時處理多個相同的Prometheus Server所產生的告警。但是由於單個Alertmanager的存在,當前的部署結構存在明顯的單點故障風險,當Alertmanager單點失效后,告警的后續所有業務全部失效。
如下所示,最直接的方式,就是嘗試部署多套Alertmanager。但是由於Alertmanager之間不存在並不了解彼此的存在,因此則會出現告警通知被不同的Alertmanager重復發送多次的問題。
為了解決這一問題,如下所示。Alertmanager引入了Gossip機制。Gossip機制為多個Alertmanager之間提供了信息傳遞的機制。確保及時在多個Alertmanager分別接收到相同告警信息的情況下,也只有一個告警通知被發送給Receiver。
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機制的關鍵在於兩點:
- Silence設置同步:Alertmanager啟動階段基於Pull-based從集群其它節點同步Silence狀態,當有新的Silence產生時使用Push-based方式在集群中傳播Gossip信息。
- 通知發送狀態同步:告警通知發送完成后,基於Push-based同步告警發送狀態。Wait階段可以確保集群狀態一致。
Alertmanager基於Gossip實現的集群機制雖然不能保證所有實例上的數據時刻保持一致,但是實現了CAP理論中的AP系統,即可用性和分區容錯性。同時對於Prometheus Server而言保持了配置了簡單性,Promthues Server之間不需要任何的狀態同步。
搭建本地集群環境
為了能夠讓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
創建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.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://localhost:9093/#/status,可以查看當前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",
多實例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.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",