告警源与采集:常见告警源、采集方式、协议与适配

好了,咱们进入第二章。告警源与采集,说白了就是回答一个问题:告警到底从哪儿来?怎么把这些消息“拽”到我们的监控平台里?

我入行那会儿,监控还很简单——写个 Shell 脚本,cron 跑一下,出问题就往群里丢一条消息。现在不一样了,云原生、微服务、IoT 设备……告警源五花八门,采集方式也各不相同。搞不清这些,后面的规则优化就是空中楼阁。

常见的告警源类型

别以为告警源只有“服务器”和“网络设备”。你想想看,一个现代业务系统,有多少层?

  • 基础设施层:物理机、虚拟机、交换机、防火墙、路由器、存储阵列。协议?SNMP、IPMI、Redfish。老一点的还走 syslog。
  • 平台层:Kubernetes、Docker、数据库(MySQL、PostgreSQL、Redis)、消息队列(Kafka、RocketMQ)、负载均衡器。这些一般通过 API 或 Agent 采集。
  • 应用层:业务代码本身的异常、QPS 毛刺、慢调用、错误日志。通常走日志采集(比如 Filebeat→Kafka→Logstash)或者 APM Agent。
  • 外部依赖:云厂商 API(AWS CloudWatch、阿里云 CMS)、第三方服务状态页、CDN 节点、DNS 解析健康检查。这些得靠定时探测或 Webhook 回调。
  • 安全与合规:WAF、IDS/IPS、漏洞扫描器、堡垒机审计日志。告警量巨大,而且很多是重复的。

小经验:我见过一个团队,把 CDN 的403频率告警和应用层的登录失败告警混在一起处理,结果半夜一个人被几百条告警冲昏头了。其实它们根本不是一个维度的——基础设施告警归基础设施团队,应用告警归开发团队,这个分层后面讲规则时会反复用到。

采集方式:主动 vs 被动

告警采集不是只有一种姿势。按照数据流向,可以分成两大类:

采集方式 典型实现 优点 缺点
主动式(Pull) Prometheus 拉取 metrics;定时探测(Heartbeat);API 轮询 采集端控制节奏;无侵入;安全边界清晰 被采集端需要暴露端口;时效性取决于轮询间隔
被动式(Push) Agent 上报(Telegraf→InfluxDB);syslog 推送;Webhook 回调 实时性好;被采集端不需要暴露端口;适合大量短连接场景 需要确保 Agent 存活和数据完整性;可能产生流量洪峰
混合式 边缘 Agent 采集后推送到中间层,再由中心 Pull 兼顾实时性与可管理性 架构复杂,需要处理数据去重

我个人习惯,对于云原生场景尽量用 Pull:原因很简单,推模式下如果被采集端挂了,你连“它挂了”的告警都可能丢——因为你依赖它的存活来发送消息。而 Pull 模式下,采集器自己发现超时,反而能更快产生告警。

常用协议与适配

每种告警源用什么协议,没有一个普适的答案。我整理了一份常见的对照表,你可以参考:

协议 适用场景 适配难点
SNMP Trap 网络设备、UPS、机房动环 MIB 文件解析;某些厂家的私有 OID 不文档
Syslog 防火墙、Linux 系统日志、交换机 严重级别不对齐;时间戳时区问题;日志截断
HTTP/HTTPS Webhook 云监控告警推送、CI/CD 事件、PagerDuty 不可靠交付——需要幂等处理和重试机制
JDBC/ODBC 数据库监控、业务表中记录的错误状态 查询间隔与数据库负载平衡;连接池耗尽风险
JMX Java 应用(JVM 内存、线程、GC) 端口暴露安全性;跨网络访问常常要开防火墙
gRPC / Protobuf 微服务之间自定义告警上报 Schema 版本管理;重连与背压控制

曾经踩过的坑:有一次接第三方监控平台的 Webhook,对方文档说“推送一次”,结果实际推送了三次(重试机制没对齐)。我们这边直接创建了三条重复告警,值班同事愣了半小时。后来我在适配层做了去重:根据告警 ID + 时间窗口做幂等。

集中告警网关的适配策略

告警源多了,每个都直连告警平台?那你的平台代码会变成一锅粥。我的做法是引入一个告警网关(或者叫告警适配器层)。

这个网关要做三件事:

  1. 协议转内统一格式——不管收到的是 SNMP Trap 还是 JSON Webhook,都转成内部的 AlertEvent 对象。
  2. 字段清洗与补全——例如 syslog 的严重级别是 0-7,转成内部枚举(critical、warning、info)。告警源名字自动根据 IP 或 hostname 查 CMDB 补全。
  3. 质量挡板——对于持续抖动产生的同源告警(比如 5 秒内发了 30 条相同的),直接丢弃或者降频。
# 简单示例:告警适配器的内部结构
class AlertEvent:
    def __init__(self, source, raw_payload, protocol):
        self.source = source          # 告警源标识,如 "prometheus"
        self.raw = raw_payload        # 原始字节
        self.protocol = protocol      # "snmp", "syslog", "webhook" 等
        self.severity = None          # 内部标准化后的级别
        self.title = ""
        self.message = ""
        self.occurred_at = None
        self.tags = {}

def normalize(record: AlertEvent):
    if record.protocol == "syslog":
        # syslog severity: 0=emergency,7=debug
        mapping = {0:"critical",1:"critical",2:"critical",
                   3:"error",4:"warning",5:"notice",6:"info",7:"debug"}
        record.severity = mapping.get(record.raw.get("severity", 6), "info")
        record.title = record.raw.get("tag", "unknown")
    elif record.protocol == "webhook":
        # 假设 body 里有 level 字段
        record.severity = record.raw.get("level", "warning")

适配层的一个设计原则:不要试图在网关里做规则判断,规则统一交给后面的“告警规则引擎”。网关只做协议适配和基本清洗。我见过有人把告警抑制逻辑也塞进适配器,结果改一次抑制策略就要重启网关,太痛苦了。

整体采集架构图

下面这张图展示了我推荐的一种常用告警采集架构。注意数据流是从左到右,网关在所有来源与核心引擎之间起到了协议适配和流量整形的作用。

告警采集与适配架构 SNMP 设备 Syslog Webhook Prometheus 自研 Agent 告警网关 协议适配 字段清洗 流量整形 去重/降频 → 统一 AlertEvent 规则引擎 关联分析 抑制/合并 动作路由 通知渠道 关键点:网关只负责格式统一和基础清洗,不做业务规则判断;规则全部下沉到右侧引擎,便于热更新和维护。

适配工作落地建议

最后,结合我的实战经验,给几点落地建议:

  • 别在适配层做太多智能判断。你想想看,如果网关同时还要做规则匹配,那它就成了单点瓶颈。每次改规则就得重启网关,线上肯定炸。
  • 每一个告警源都要打上唯一标识。比如 source_id: "prometheus-cluster-prod",规则引擎可以基于这个 ID 做差异化处理。我见过有团队只用 IP 来区分,换了 IP 什么旧数据都查不到了。
  • 日志级别要统一。不同源的“严重”含义可能不同——syslog 的 emerg 和 Prometheus 的 critical 分别映射到内部什么等级?提前定义好枚举。我曾经因为没对齐,导致某次实际是 warning 的告警被当成 critical 直接拉电话会议,全组白忙活。
  • 监控网关本身。网关挂了,所有告警都停了。所以我建议你给网关挂一个心跳探测(甚至可以另走一个独立通道),网关健康状态本身就是最高级别的告警。