第三章 告警标准化:设计统一告警模型
标准化是基础。但没有统一模型,标准化就是空谈。
我见过不少团队,告警字段随心定,严重等级全凭感觉。结果呢?告警池乱成一锅粥,自动化处置根本推不下去。说白了,你得先定义清楚「告警长什么样」,才能让机器看懂、让流程跑通。
3.1 核心字段定义:告警的「身份证」
统一告警模型,第一件事就是把字段固定下来。我习惯把字段分成两类:必填字段和选填字段。必填字段缺一不可,否则告警直接丢弃。
下面是我的推荐清单,你可以在自己系统里照着建:
核心字段清单
- 告警ID:全局唯一标识。我通常用UUID或雪花ID,别用自增数字——如果你要跨系统合并,重复ID会让你哭。
- 告警标题:一句话概括问题。例如「CPU使用率超过90% on host-001」。
- 告警内容:详细描述,包含上下文和关键指标。
- 告警来源:哪个监控系统产生的?Prometheus、Zabbix、还是ELK?
- 资源标识:哪个IP、主机名、容器ID或服务名出了问题。
- 严重等级:下文专门展开。
- 事件时间:问题实际发生的时间。
- 接收时间:告警系统收到的时间。
- 标签集合:可扩展的KV键值对,用于路由、过滤和分组。
嗯,这里要注意——告警内容别写太啰嗦。我在项目中遇到过有人把整页日志贴进去,结果下游处理时解析超时。控制在200字以内,关键信息前置。
来看看实际JSON长什么样:
{
"alert_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "磁盘使用率超过80% on db-01",
"content": "主机 db-01 (10.0.1.10) 根分区使用率85.3%,已持续15分钟",
"source": "Prometheus",
"resource_id": "host:db-01",
"severity": "critical",
"event_time": "2025-03-15T14:32:00+08:00",
"receive_time": "2025-03-15T14:32:05+08:00",
"labels": {
"env": "production",
"team": "dba",
"region": "cn-beijing"
}
}
字段定了,团队要达成一致。我建议把这份定义当作「告警契约」,所有产生告警的系统都必须遵守。不遵守的告警,直接进黑名单。
3.2 严重等级:不要只分「严重」和「不严重」
很多团队把告警等级分为P0、P1、P2、P3、P4。但光有等级还不够——你得明确每个等级对应的响应时效和处置动作。否则大家还是按自己理解来。
我个人习惯用这个标准:
| 等级 | 定义 | 举例 | 响应时效 |
|---|---|---|---|
| P0 (紧急) | 核心服务完全不可用,影响大量用户 | 支付网关挂了,数据库主库宕机 | 5分钟内有人处理 |
| P1 (严重) | 核心功能降级,或局部服务不可用 | API响应延迟>5秒,Redis集群部分节点掉线 | 15分钟内有人处理 |
| P2 (一般) | 非核心功能受损,或核心功能有潜在风险 | 磁盘使用率超过80%,次要服务报错率上升 | 1小时内处理 |
| P3 (警告) | 指标有异常,但暂时不影响业务 | CPU负载短期峰值,证书即将过期 | 24小时内处理 |
| P4 (通知) | 信息类,仅记录 | 日常备份完成,版本发布成功 | 无需立即响应 |
我曾经踩过的坑: 某次把所有告警都设为P1,想着「宁可严不可松」。结果值班同学被频繁告警炸到麻木,真正P0的核心宕机反而没人第一时间响应。所以,等级必须清晰、差异化,否则就是噪音。
为什么我在模型里特别强调「响应时效」?因为自动化处置需要它。举个例子,P0告警来了,自动拉群、打电话、启动runbook;P3告警来了,自动创建Jira工单就行。等级定义不清,自动化逻辑就没法写。
3.3 时间戳:千万别只用「当前时间」
很多人写告警模型,时间戳只写一个「当前时间」。这是错的。你想想看——监控系统从采集到传输再到入库,中间可能有延迟。如果你只用接收时间,那问题追溯就会出偏差。
我推荐的方案是三级时间戳体系:
- 事件时间 (event_time):指标实际被采集的时间。这是你分析问题时序的依据。
- 采集时间 (collect_time):代理或系统采集到数据的时刻。通常和事件时间很接近,但有些被动采集(比如SNMP)会有差异。
- 入库时间 (receive_time):告警系统实际写入存储的时间。用于监控系统自身的延迟检测。
我的建议: 所有时间戳统一使用ISO 8601格式(带时区)。我见过有人用Unix秒数、有人用毫秒、还有人用字符串 - 每个系统的解析器都要单独适配。统一格式能省下你大量调试时间。
举个例子,时间戳转换的逻辑我通常会放在告警接收的入口处,统一清洗:
def normalize_timestamp(raw_time):
"""
统一转成带时区的ISO 8601字符串
"""
if isinstance(raw_time, (int, float)):
# 假设是秒级Unix时间戳
dt = datetime.fromtimestamp(raw_time, tz=timezone.utc)
return dt.isoformat()
# 其他格式的兜底逻辑
return raw_time # 如果入参已经是标准格式,直接返回
3.4 标签:告警的「分类和路由钥匙」
字段定义了、等级划定了、时间戳统一了。但真正让告警能被灵活分组、路由和过滤的,是标签 (Labels)。
说白了,标签就是一组Key-Value的元数据。它可以表达任何维度:环境(env)、机房(region)、业务线(service)、团队(team)、甚至告警来源子类(alert_type)。
我过去做的一个经典标签策略是这样的:
| 标签Key | 说明 | 示例值 |
|---|---|---|
| env | 部署环境 | production, staging, dev |
| region | 地域/机房 | cn-beijing, us-east-1 |
| service | 所属服务或业务线 | payment, user, order |
| team | 负责团队 | dba, sre, backend |
| alert_type | 告警类别 | cpu, memory, latency, error_rate |
| runbook | 处置手册链接 | https://wiki/internal/runbook/cpu |
为什么要这样设计?因为在你做自动化处置时,标签就是「条件判断」的依据。比如:
- 如果
env = production且severity = critical,自动创建P0工单并@值班人。 - 如果
service = payment且alert_type = latency,自动触发扩容runbook。 - 如果
team = dba,自动路由到DBA的告警群。
我团队有一个原则:任何告警模型,标签集的扩展性要大于字段集。因为字段是固定的Schema,改起来要动表结构;标签是KV对,随时可以追加。新增一个标签,不需要改数据库。
补充建议: 标签的值尽量标准化。比如环境统一写成「production」而非「pro」、「prod」、「prd」。我曾经因为命名不规范,导致告警路由规则写了30行条件匹配,改了半年才收敛。
好了,现在我们把上面所有内容串成一张图,看看统一告警模型长什么样:
看图就清楚了。字段结构负责「是什么」,等级映射决定「有多急」,时间戳体系记录「什么时候」,标签体系控制「怎么分、怎么转」。四者缺一不可。
我在落地时,通常会把这个模型放到公司的告警网关层。所有上游监控系统(Prometheus、Zabbix、CloudWatch)产生的告警,都必须经过这个网关「翻译」成统一模型,再进入下游处置引擎。这样,后端处理代码就只需要跟这一种模型打交道,复用率极高。
小提示: 如果你们团队已经有一套现成的告警模型,别急着推倒重来。先在网关层做一层模型转换,逐步迁移。我见过一个团队花三个月「重构告警模型」,结果业务方等不及,自己又绕开模型发了告警。