2、告警源接入:常见告警源的接入方式与协议解析
告警源接入,说白了就是让我们的告警平台能听懂各个监控系统在说什么。我刚开始做运维那会儿,公司里监控系统五花八门,Zabbix、Prometheus、ELK各玩各的,告警信息散落在不同群里,值班同学每天光翻消息就得花半小时。后来我们统一做了告警源接入,才算是把散落的拼图拼到了一起。
这一章,我就带你看看几种主流告警源的接入方式和协议解析。嗯,这里要注意,不同告警源的协议差异很大,但核心思路是一致的——把异构的告警数据,转换成统一的告警事件模型。
2.1 告警源接入的核心思路
先说说我的个人习惯。我一般把告警源接入分成三步:
- 接收:通过Webhook、API轮询或Agent方式,拿到原始告警数据
- 解析:把JSON、XML或自定义格式的数据,提取出关键字段
- 标准化:映射到统一的告警模型(告警名称、级别、时间、来源、详情等)
你想想看,如果每个告警源都单独写一套处理逻辑,那维护成本得多高?所以标准化这一步,是绝对不能省的。
核心告警模型字段:
- alert_id:告警唯一ID
- alert_name:告警名称
- severity:级别(critical/warning/info)
- status:状态(firing/resolved)
- source:来源系统
- timestamp:触发时间
- description:描述信息
- labels:标签(主机、服务、地域等)
2.2 Zabbix 接入方式
Zabbix 是我最早接触的监控系统,说实话它的告警机制有点「老派」,但胜在稳定。我在项目中遇到过不少团队还在用 Zabbix 3.x 版本,接入时要注意版本差异。
2.2.1 Webhook 方式(推荐)
Zabbix 从 4.4 版本开始支持 Webhook 类型的告警媒介。配置起来其实不复杂:
# Zabbix Webhook 配置示例(JavaScript脚本)
# 在 Zabbix 前端:管理 → 告警媒介类型 → 创建
var params = JSON.parse(value);
var webhook_url = params.HOOK_URL;
var payload = {
"alert_id": "{EVENT.ID}",
"alert_name": "{TRIGGER.NAME}",
"severity": "{TRIGGER.SEVERITY}",
"status": "{EVENT.STATUS}",
"host": "{HOST.NAME}",
"timestamp": "{EVENT.DATE} {EVENT.TIME}",
"description": "{TRIGGER.DESCRIPTION}"
};
// 发送HTTP POST请求
HttpClient.post(webhook_url, JSON.stringify(payload), {
"Content-Type": "application/json"
});
小技巧:Zabbix 的宏变量里,{TRIGGER.SEVERITY} 返回的是数字(0-5),我一般会在脚本里做个映射,转成 readable 的级别名称,比如 3 对应 "warning"。
2.2.2 数据库轮询方式
有些老版本 Zabbix 不支持 Webhook,那就只能走数据库轮询了。我曾经维护过一个 Zabbix 3.2 的环境,就是靠定时任务查数据库来拿告警的。
# Python 示例:轮询 Zabbix 数据库
import pymysql
import time
def poll_zabbix_alerts():
conn = pymysql.connect(host='zabbix-db', user='zabbix', password='xxx', db='zabbix')
cursor = conn.cursor()
# 查询最近5分钟内的告警
sql = """
SELECT e.eventid, t.description, t.severity, e.clock, h.name
FROM events e
JOIN triggers t ON e.objectid = t.triggerid
JOIN hosts h ON t.hostid = h.hostid
WHERE e.clock > UNIX_TIMESTAMP(NOW() - INTERVAL 5 MINUTE)
AND e.source = 0
"""
cursor.execute(sql)
rows = cursor.fetchall()
for row in rows:
alert = {
"alert_id": row[0],
"alert_name": row[1],
"severity": map_severity(row[2]),
"timestamp": row[3],
"host": row[4]
}
# 发送到告警平台
send_to_alert_platform(alert)
cursor.close()
conn.close()
注意:数据库轮询方式对 Zabbix 数据库有压力,轮询间隔不要太短,我建议至少 30 秒以上。另外,要处理好已恢复告警的标记,避免重复推送。
2.3 Prometheus 接入方式
Prometheus 的告警机制比 Zabbix 现代得多,它通过 Alertmanager 统一管理告警。说白了,Alertmanager 就是 Prometheus 生态里的「告警路由器」。
2.3.1 Alertmanager Webhook Receiver
这是最标准的接入方式。Alertmanager 原生支持 webhook_config,配置起来非常简洁:
# alertmanager.yml 配置
route:
receiver: 'my-webhook'
group_wait: 10s
group_interval: 5m
repeat_interval: 4h
receivers:
- name: 'my-webhook'
webhook_configs:
- url: 'http://your-alert-platform:8080/webhook/prometheus'
send_resolved: true
http_config:
basic_auth:
username: 'admin'
password: 'password123'
Prometheus 发过来的告警数据格式是固定的 JSON,我贴一个典型的 payload:
{
"receiver": "my-webhook",
"status": "firing",
"alerts": [
{
"status": "firing",
"labels": {
"alertname": "HighCpuUsage",
"instance": "192.168.1.100:9090",
"job": "node_exporter",
"severity": "critical"
},
"annotations": {
"summary": "CPU usage above 90%",
"description": "Instance 192.168.1.100 CPU usage is 95%"
},
"startsAt": "2024-01-15T10:30:00Z",
"endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://prometheus:9090/graph?g0..."
}
],
"groupLabels": {"alertname": "HighCpuUsage"},
"commonLabels": {"severity": "critical"},
"commonAnnotations": {"summary": "CPU usage above 90%"},
"externalURL": "http://alertmanager:9093"
}
解析要点:
- status 字段:firing 表示触发,resolved 表示恢复
- labels 里的 alertname 是告警规则名称,severity 是级别
- annotations 里通常放人类可读的描述信息
- startsAt 和 endsAt 是时间戳,注意 endsAt 为 0001-01-01 时表示还在触发中
2.3.2 自定义 Exporter 方式
有些场景下,我们需要把非 Prometheus 体系的告警也接入进来。我做过一个方案:写一个自定义 Exporter,把其他系统的告警数据暴露成 Prometheus metrics,然后通过 Prometheus 自身的告警规则来触发。
# Python 自定义 Exporter 示例
from prometheus_client import start_http_server, Gauge
import time
# 定义告警指标
alert_gauge = Gauge('custom_alert', 'Custom alert from legacy system',
['alert_name', 'severity', 'source'])
def update_alerts():
# 从遗留系统获取告警
alerts = fetch_from_legacy_system()
for alert in alerts:
alert_gauge.labels(
alert_name=alert['name'],
severity=alert['severity'],
source='legacy'
).set(1 if alert['status'] == 'firing' else 0)
if __name__ == '__main__':
start_http_server(8000)
while True:
update_alerts()
time.sleep(30)
2.4 ELK(Elasticsearch)接入方式
ELK 本身不是专门的告警系统,但很多人用它做日志告警。我记得有一次线上故障,就是靠 ELK 的日志告警发现的——某个服务的错误日志在 5 分钟内暴增了 100 倍。
2.4.1 Watcher 告警(X-Pack)
Elasticsearch 的 Watcher 功能可以基于查询结果触发告警。配置一个 Webhook 动作:
# Elasticsearch Watcher 配置示例
PUT _watcher/watch/log_error_watch
{
"trigger": {
"schedule": {
"interval": "5m"
}
},
"input": {
"search": {
"request": {
"indices": ["logs-*"],
"body": {
"query": {
"bool": {
"filter": [
{"range": {"@timestamp": {"gte": "now-5m"}}},
{"term": {"level": "ERROR"}}
]
}
},
"aggs": {
"error_count": {"value_count": {"field": "_index"}}
}
}
}
}
},
"condition": {
"compare": {
"ctx.payload.aggregations.error_count.value": {"gte": 100}
}
},
"actions": {
"webhook": {
"webhook": {
"method": "POST",
"host": "your-alert-platform",
"port": 8080,
"path": "/webhook/elk",
"body": "{\"alert_name\":\"LogErrorSpike\",\"count\":{{ctx.payload.aggregations.error_count.value}},\"timestamp\":\"{{ctx.trigger.scheduled_time}}\"}"
}
}
}
}
避坑指南:Watcher 的 body 里用 Mustache 模板语法,注意转义双引号。我曾经在这里踩过坑,少转义了一个引号,导致 Webhook 一直发不出去。
2.4.2 ElastAlert 方案(开源)
如果没买 X-Pack,可以用 ElastAlert 这个开源工具。它支持多种告警规则类型,配置起来也很灵活:
# ElastAlert 配置文件示例
# config.yaml
rules_folder: rules
run_every:
minutes: 1
buffer_time:
minutes: 5
es_host: elasticsearch.example.com
es_port: 9200
writeback_index: elastalert_status
# rules/error_rate.yaml
name: "High Error Rate"
type: frequency
index: logs-*
num_events: 50
timeframe:
minutes: 5
filter:
- query:
query_string:
query: "level: ERROR"
alert:
- "command"
command: ["curl", "-X", "POST", "-d", "{\"alert_name\":\"HighErrorRate\"}", "http://alert-platform:8080/webhook/elastalert"]
2.5 云监控接入方式
云监控这块,各家有各家的玩法。我以阿里云和腾讯云为例,说说通用的接入思路。
2.5.1 阿里云 CMS
阿里云支持通过 Webhook 回调来推送告警。配置路径:云监控 → 告警规则 → 通知方式 → 选择 Webhook。
# 阿里云 Webhook 回调示例 payload
{
"alertName": "CPU使用率过高",
"alertState": "ALERT",
"curValue": "95.0",
"dimensions": {
"instanceId": "i-xxxxx",
"userId": "123456"
},
"expression": "Average >= 90",
"metricName": "cpu_utilization",
"namespace": "acs_ecs",
"region": "cn-hangzhou",
"timestamp": "2024-01-15 10:30:00"
}
2.5.2 腾讯云 CM
腾讯云的告警回调格式略有不同,但核心字段大同小异:
# 腾讯云 Webhook 回调示例 payload
{
"sessionId": "xxxxx",
"alarmId": "alarm-xxxxx",
"alarmStatus": "1", // 1:触发 0:恢复
"alarmType": "metric",
"alarmObjInfo": {
"region": "ap-guangzhou",
"namespace": "qce/cvm",
"dimensions": {
"InstanceId": "ins-xxxxx"
}
},
"alarmPolicyName": "CPU告警",
"alarmPolicyType": "cvm",
"alarmLevel": "2", // 1:致命 2:严重 3:警告 4:提醒
"metricName": "CPUUsage",
"metricValue": "95.0",
"occurTime": "2024-01-15 10:30:00"
}
云监控接入要点:
- 云厂商的告警格式差异主要在字段命名上,建议写一个适配层做字段映射
- 注意云监控的告警恢复通知,有些云厂商的恢复通知格式和触发通知不一样
- 云监控的 Webhook 地址需要公网可达,如果告警平台在内网,可以考虑用云函数做中转
2.6 告警源接入架构图
下面这张图是我自己总结的告警源接入架构,你可以参考一下:
2.7 接入协议解析要点
最后,我总结一下接入协议解析的几个关键点,这些都是我在实际项目中踩过的坑:
| 协议类型 | 常见格式 | 解析难点 | 我的建议 |
|---|---|---|---|
| HTTP Webhook | JSON / XML | 字段命名不统一,嵌套层级深 | 写一个通用的 JSON Path 提取器 |
| 数据库轮询 | SQL 结果集 | 数据库压力,增量数据识别 | 用时间戳或自增ID做增量标记 |
| Agent 推送 | 自定义二进制/文本 | 协议不标准,需要定制解析 | 优先转成 JSON 再处理 |
| 消息队列 | Kafka/RabbitMQ | 消息顺序,重复消费 | 做好幂等处理,记录 offset |
重要提醒:不管用什么协议接入,一定要做好以下几点:
- 超时处理:Webhook 请求设置 5 秒超时,避免阻塞
- 重试机制:失败后至少重试 3 次,间隔递增
- 日志记录:记录原始 payload,方便排查问题
- 监控告警:接入层本身也要有监控,接入挂了要能发现
嗯,告警源接入这块,说白了就是「翻译」工作。把不同监控系统的语言,翻译成告警平台能理解的语言。只要把协议解析和标准化做好了,后面的事情就顺了。我个人习惯是先接一个告警源跑通全流程,再逐步扩展,这样风险最小。
公众号:蓝海资料掘金营,微信deep3321