prometheus+alertmanager+webhook實現自定義監控報警系統
1.概述
上一篇文章prometheus+grafana+mtail+node_exporter實現機器負載及業務監控介紹了使用mtail和node_exporter實現的prometheus無埋點監控機器負載和業務的監控系統,本文是在其基礎上實現自定義報警功能。
Prometheus+Alertmanager 的警報分為兩個部分:
Prometheus負責中配置警報規則,將警報發送到Alertmanager
。Alertmanager 負責管理這些警報,包括沉默,抑制,合並和發送通知
。
Alertmanager 發送通知有多種方式,其內部集成了郵箱、Slack、企業微信等三種方式,也提供了webhook的方式來擴展報警通知方式,網上也有大量例子實現對第三方軟件的集成,如釘釘等。本文介紹郵件報警方式和通過使用java來搭建webhook自定義通知報警的方式。
本文內容主要分為四塊:
- prometheus報警規則配置
- alertmanager配置及部署
- 關聯prometheus和alertmanager
- 配置報警通知方式
2.prometheus配置報警規則
prometheus.yml屬性配置
屬性 | 描述 |
---|---|
scrape_interval | 樣本采集周期,默認為1分鍾采集一次 |
evaluation_interval | 告警規則計算周期,默認為1分鍾計算一次 |
rule_files | 指定告警規則的文件 |
scrape_configs | job的配置項,里面可配多組job任務 |
job_name | 任務名稱,需要唯一性 |
static_configs | job_name 的配置選項,一般使用file_sd_configs 熱加載配置 |
file_sd_configs | job_name 的動態配置選項,使用此配置可以實現配置文件的熱加載 |
files: | file_sd_configs 配置的服務發現的文件路徑列表,支持.json,.yml或.yaml,路徑最后一層支持通配符* |
refresh_interval | file_sd_configs 中的files 重新加載的周期,默認5分鍾 |
此處我們使用rule_files
屬性來設置告警文件
# my global config global: scrape_interval: 15s # 采樣周期 evaluation_interval: 15s # 告警規則計算周期 # Alertmanager configuration alerting: alertmanagers: - static_configs: - targets: # - alertmanager:9093 # 報警規則文件可以指定多個,並且可以使用通配符* rule_files: - "rules/*_rules.yml" # - "second_rules.yml" # 采集job配置 scrape_configs: - job_name: 'prometheus' #使用file_sd_configs熱加載配置文件 file_sd_configs: #指定1分鍾加載一次配置 - refresh_interval: 1m files: - config_prometheus.json - job_name: 'linux_base' file_sd_configs: - refresh_interval: 1m files: - config_exporter.json - job_name: 'service_base' file_sd_configs: - refresh_interval: 1m files: - config_mtail.json - job_name: 'grafana' file_sd_configs: - refresh_interval: 1m files: - config_grafana.json
設置報警規則,rules/host_rules.yml
groups: # 報警組組名稱 - name: hostStatsAlert #報警組規則 rules: #告警名稱,需唯一 - alert: hostCpuUsageAlert #promQL表達式 expr: sum(avg without (cpu)(irate(node_cpu_seconds_total{mode!='idle'}[5m]))) by (instance) > 0.85 #滿足此表達式持續時間超過for規定的時間才會觸發此報警 for: 1m labels: #嚴重級別 severity: page annotations: #發出的告警標題 summary: "實例 {{ $labels.instance }} CPU 使用率過高" #發出的告警內容 description: "實例{{ $labels.instance }} CPU 使用率超過 85% (當前值為: {{ $value }})" - alert: hostMemUsageAlert expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)/node_memory_MemTotal_bytes > 0.85 for: 1m labels: severity: page annotations: summary: "實例 {{ $labels.instance }} 內存使用率過高" description: "實例 {{ $labels.instance }} 內存使用率 85% (當前值為: {{ $value }})"
采集任務job配置config_exporter.json配置如下(其他job配置文件類似):
[ { "targets": [ "192.168.113.13:9100"], "labels": { "env": "prod" } }, { "targets": [ "10.12.141.150:9100"], "labels": { "env": "local" } }, { "targets": [ "10.12.141.151:9100"], "labels": { "env": "local2" } } ]
配置完報警規則重啟prometheus,訪問http://ip:9090/,點擊alerts就能看到配置的報警信息了,如下:
3.alertmanager配置及部署
3.1.alertmanager配置
alertmanager會定義一個基於標簽匹配規則的路由樹,用以接收到報警后根據不同的標簽匹配不同的路由,來將報警發送給不同的receiver。如果定義一個路由route則需要接收並處理所有的報警,如果需要區分不同的報警發送給不同的receiver,則需要配置多個子級route來處理不同的報警,而根route此時必須能夠接收處理所有的報警。
#根路由 route: #頂級路由配置的接收者(匹配不到子級路由,會使用根路由發送報警) receiver: 'default-receiver' #設置等待時間,在此等待時間內如果接收到多個報警,則會合並成一個通知發送給receiver group_wait: 30s #兩次報警通知的時間間隔,如:5m,表示發送報警通知后,如果5分鍾內再次接收到報警則不會發送通知 group_interval: 5m #發送相同告警的時間間隔,如:4h,表示4小時內不會發送相同的報警 repeat_interval: 4h #分組規則,如果滿足group_by中包含的標簽,則這些報警會合並為一個通知發給receiver group_by: [cluster, alertname] routes: #子路由的接收者 - receiver: 'database-pager' group_wait: 10s #默認為false。false:配置到滿足條件的子節點點后直接返回,true:匹配到子節點后還會繼續遍歷后續子節點 continue:false #正則匹配,驗證當前標簽service的值是否滿足當前正則的條件 match_re: service: mysql|cassandra #子路由的接收者 - receiver: 'frontend-pager' group_by: [product, environment] #字符串匹配,匹配當前標簽team的值為frontend的報警 match: team: frontend #定義所有接收者 receivers: #接收者名稱 - name: 'default-receiver' #接收者為webhook類型 webhook_configs: #webhook的接收地址 - url: 'http://127.0.0.1:5001/' - name: 'database-pager' webhook_configs: - url: 'http://127.0.0.1:5002/' - name: 'frontend-pager' webhook_configs: - url: 'http://127.0.0.1:5003/'
3.2.alertmanager部署
建議下載最新版本,最新版下載地址請戳此處(可能需要FQ),我使用的版本為alertmanager-0.20.0.linux-amd64.tar.gz,我的版本csdn下載地址請戳。
將其解壓到/usr/local后,進入目錄/usr/local/alertmanager-0.20.0.linux-amd64,會有個默認配置文件:alertmanager.yml,內容如下:
global: resolve_timeout: 5m route: group_by: ['alertname'] group_wait: 10s group_interval: 10s repeat_interval: 1h receiver: 'web.hook' receivers: - name: 'web.hook' webhook_configs: - url: 'http://127.0.0.1:5001/' inhibit_rules: - source_match: severity: 'critical' target_match: severity: 'warning' equal: ['alertname', 'dev', 'instance']
使用默認配置在后台啟動:
nohup ./alertmanager > alertmanager.out 2>&1 &
查看是否啟動成功:
ps -ef | grep alertmanager
啟動后訪問http://ip:9093/查看是否啟動成功,點擊status查看alertmanager及其配置,訪問如下(我的配置有改動,請以自己頁面展示為准):
4.prometheus關聯alertmanager
prometheus.yml中的alerting標簽下配置上alertmanager的地址即可,配置如下:
# Alertmanager configuration alerting: alertmanagers: - static_configs: - targets: ['192.168.199.23:9093']
添加后重啟prometheus即可。
5.配置報警通知方式
5.1.alertmanager郵箱報警demo
global: #超時時間 resolve_timeout: 5m #smtp地址需要加端口 smtp_smarthost: 'smtp.126.com:25' smtp_from: 'xxx@126.com' #發件人郵箱賬號 smtp_auth_username: 'xxx@126.com' #賬號對應的授權碼(不是密碼),阿里雲個人版郵箱目前好像沒有授權碼,126郵箱授權碼可以在“設置”里面找到 smtp_auth_password: '1qaz2wsx' smtp_require_tls: false route: group_by: ['alertname'] group_wait: 10s group_interval: 1m repeat_interval: 4h receiver: 'mail' receivers: - name: 'mail' email_configs: - to: 'xxx@aliyun.com'
設置后如果有通知,即可收到郵件如下:
5.2.alertmanager使用webhook(java)報警demo
global: resolve_timeout: 5m route: group_by: ['alertname'] group_wait: 10s group_interval: 1m repeat_interval: 4h receiver: 'webhook' receivers: - name: 'webhook' webhook_configs: - url: 'http://192.168.199.152/demo'
使用webhook方式,alertmanager會給配置的webhook地址發送一個http類型的post請求,參數為json字符串(字符串類型),如下(此處格式化為json了):
{ "receiver":"webhook", "status":"resolved", "alerts":[ { "status":"resolved", "labels":{ "alertname":"hostCpuUsageAlert", "instance":"192.168.199.24:9100", "severity":"page" }, "annotations":{ "description":"192.168.199.24:9100 CPU 使用率超過 85% (當前值為: 0.9973333333333395)", "summary":"機器 192.168.199.24:9100 CPU 使用率過高" }, "startsAt":"2020-02-29T19:45:21.799548092+08:00", "endsAt":"2020-02-29T19:49:21.799548092+08:00", "generatorURL":"http://localhost.localdomain:9090/graph?g0.expr=sum+by%28instance%29+%28avg+without%28cpu%29+%28irate%28node_cpu_seconds_total%7Bmode%21%3D%22idle%22%7D%5B5m%5D%29%29%29+%3E+0.85&g0.tab=1", "fingerprint":"368e9616d542ab48" } ], "groupLabels":{ "alertname":"hostCpuUsageAlert" }, "commonLabels":{ "alertname":"hostCpuUsageAlert", "instance":"192.168.199.24:9100", "severity":"page" }, "commonAnnotations":{ "description":"192.168.199.24:9100 CPU 使用率超過 85% (當前值為: 0.9973333333333395)", "summary":"機器 192.168.199.24:9100 CPU 使用率過高" }, "externalURL":"http://localhost.localdomain:9093", "version":"4", "groupKey":"{}:{alertname="hostCpuUsageAlert"}" }
此時需要使用java(其他任何語言都可以,反正只要能處理http的請求就行)搭建個http的請求處理器來處理報警通知,如下:
package com.demo.demo1.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Slf4j @Controller @RequestMapping("/") public class AlertController { @RequestMapping(value = "/demo", produces = "application/json;charset=UTF-8") @ResponseBody public String pstn(@RequestBody String json) { log.debug("alert notify params: {}", json); Map<String, Object> result = new HashMap<>(); result.put("msg", "報警失敗"); result.put("code", 0); if(StringUtils.isBlank(json)){ return JSON.toJSONString(result); } JSONObject jo = JSON.parseObject(json); JSONObject commonAnnotations = jo.getJSONObject("commonAnnotations"); String status = jo.getString("status"); if (commonAnnotations == null) { return JSON.toJSONString(result); } String subject = commonAnnotations.getString("summary"); String content = commonAnnotations.getString("description"); List<String> emailusers = new ArrayList<>(); emailusers.add("xxx@aliyun.com"); List<String> users = new ArrayList<>(); users.add("158*****5043"); try { boolean success = Util.email(subject, content, emailusers); if (success) { result.put("msg", "報警成功")