SNMP_Trap 插件
本文介绍监控数据采集器 Categraf 的 SNMP_Trap 接收插件与高级数据聚合配置
一、插件概述
1.1 插件功能介绍
snmp_trap 是 Categraf 提供的一个服务端插件,用于被动接收来自网络设备(交换机、路由器、防火墙、服务器等)发送的 SNMP Trap(SNMPv1, SNMPv2c, SNMPv3)通知。
与 snmp 轮询插件(Pull 模式)不同,snmp_trap 处于监听状态(Push 模式),当网络设备发生异常或状态变更(例如:端口宕掉、电源故障、BGP 邻居断开)时,设备会主动将告警事件发送到此插件配置的监听端口(默认 UDP 162)。
1.2 核心特性
- 全版本支持:支持 SNMPv1、v2c、v3。对于 v3,支持认证和加密(AuthPriv)。
- 指标聚合能力:支持将一个 Trap 中的多个离散变量(Varbinds)聚合成一个结构化的核心指标,并将部分变量转换为指标标签(Labels),极大地简化了 PromQL 告警规则的编写。
- 上下文继承(Dispersed Fallback):未被聚合规则消耗的变量会自动作为独立指标散落,且会自动继承已提取的公共上下文标签,确保不丢失任何现场数据。
- 最长前缀匹配:在处理复杂的 OID 匹配时,遵循“最长前缀优先”原则,并且强制进行层级边界对齐,防止
1.3.6.1...错误匹配到1.3.6.10...。
二、基本原理与默认指标生成
当 Categraf 收到一个 SNMP Trap 报文时,报文中会携带若干个变量绑定(Varbinds)。如果没有任何聚合配置,插件会为每一个 Varbind 独立生成一条指标,这被称为 散落指标(Dispersed Metrics)。
2.1 默认标签列表
无论是哪种配置,插件都会从 Trap PDU(协议数据单元)中提取以下默认元数据标签:
source:发送 Trap 的设备 IP 地址name:Trap 事件的名称(例如:link_down,如果没有加载 MIB,则显示为数字 OID)mib:事件所属的 MIB 库名称(例如:SNMPv2-MIB)oid:Trap 事件的完整 OID(对应 SNMPv2-MIB::snmpTrapOID.0)version:SNMP 版本(“1”、“2c” 或 “3”)context_name/engine_id:SNMPv3 特有标签
迁移与安全提示:在早期版本中,SNMP v1/v2c 的
community会作为标签保留。出于安全考虑(防止凭证泄露)以及避免标签高基数,该标签已在最新(开源版>=v0.5.9,企业版>=v0.5.24)版本中被彻底移除。如果您现有的告警面板依赖community标签,请尽快修改。
2.2 无聚合的默认行为
假设收到一个 linkDown Trap,里面携带了 3 个变量(接口索引、管理状态、操作状态),如果没有任何聚合规则,会生成类似如下的三条散落指标:
snmp_trap_ifIndex{source="127.0.0.1", version="2c", name="linkDown", ...} = 835
snmp_trap_ifAdminStatus{source="127.0.0.1", version="2c", name="linkDown", ...} = 1
snmp_trap_ifOperStatus{source="127.0.0.1", version="2c", name="linkDown", ...} = 2
问题:这三条指标相互独立,如果你想写一条告警规则:“当接口操作状态为2,且管理状态为1时告警”,在 PromQL 中将非常困难。这就是为什么需要引入 指标聚合。
三、全局指标聚合配置
指标聚合的核心目的,是将那些表述状态的字段变成 Label,从而把同一事件的数据捏合成一条有意义的核心指标。
3.1 fields_to_labels
白名单机制。定义哪些字段(MIB 翻译后的变量名)应该被当做 Label 处理,而不是生成独立的 Metric。
[[instances]]
service_address = "udp://0.0.0.0:162"
fields_to_labels = ["ifIndex", "ifAdminStatus", "ifOperStatus"]
3.2 varbind_mapping
对特定 OID 前缀的变量进行强制重命名,如果重命名后的名字命中了 fields_to_labels 白名单,同样会转为 Label。
[[instances]]
[instances.varbind_mapping]
# 将以该 OID 开头的变量重命名为 ifIndex
".1.3.6.1.2.1.2.2.1.1" = "ifIndex"
注意 (TOML 配置陷阱):因为
[instances.varbind_mapping]声明了一个子 Table,TOML 解析器会将它下方所有的键值对都归属于这个 Map。如果你同时配置了fields_to_labels,请务必确保fields_to_labels写在[instances.varbind_mapping]之前,否则会报incompatible types错误。
四、结构化 Varbind 映射与值转换
如果只需要重命名,可以继续使用 varbind_mapping + fields_to_labels。如果还需要把 OCTET STRING、十六进制字节、IP、MAC 等值转换成更适合展示或告警的格式,推荐使用结构化的 [[instances.varbind]] 配置。
4.1 配置语法
[[instances]]
service_address = "udp://0.0.0.0:162"
[[instances.varbind]]
oid = ".1.3.6.1.4.1.25506.4.2.2.1.102"
name = "device_name"
conversion = "string"
as_label = true
[[instances.varbind]]
oid = ".1.3.6.1.4.1.25506.4.2.2.1.104"
name = "device_ip"
conversion = "string"
as_label = true
[[instances.varbind]]
oid = ".1.3.6.1.4.1.25506.4.2.2.1.200"
name = "mac"
conversion = "hwaddr"
as_label = true
字段说明:
oid:要匹配的 Varbind OID 前缀,使用最长前缀匹配。name:转换后的字段名或标签名。conversion:值转换方式。为空时默认会把[]byte转为字符串。as_label:是否作为标签输出。为false时作为普通字段输出。
4.2 conversion 支持项
| conversion | 说明 |
|---|---|
string |
将字节串或其他值转成字符串,适合设备名、ASCII 形式 IP 等字段 |
int |
转成整数 |
float |
转成浮点数;直接解析失败时,会自动从字符串中提取第一个浮点数 |
float(N) |
转成浮点数后按 10^N 缩放,例如 float(2) 表示除以 100 |
hwaddr |
将 6 字节值转成 MAC 地址 |
ipaddr |
将 4 字节或 16 字节值转成 IPv4/IPv6 地址 |
percent |
将 34% 形式的字符串转成浮点数 34 |
hextoint:BigEndian:uint16 |
将字节串按大端序转成整数,支持 uint16、uint32、uint64 |
hextoint:LittleEndian:uint32 |
将字节串按小端序转成整数,支持 uint16、uint32、uint64 |
ipaddr适用于原始 4/16 字节 IP,例如0A 5F 05 35。如果设备上报的是字符串形式的 IP,例如"10.95.5.53",请使用conversion = "string"。
4.3 convert_rule 条件转换
同一个 Varbind 可能正常时返回 34%,异常时返回 offline。可以在结构化 Varbind 下配置有序转换规则:
[[instances.varbind]]
oid = ".1.3.6.1.4.1.19046.11.1.1.3.2.1.3"
name = "fan_speed"
conversion = "float"
as_label = false
[[instances.varbind.convert_rule]]
match = "offline"
value = -1
规则按照配置顺序匹配,第一条命中的规则生效。支持精确匹配 match、Go 正则 regex、捕获组提取 extract、固定值 value 和规则级 conversion。所有规则均未命中时回退到 Varbind 原有的 conversion,因此上述配置会将 offline 转为 -1,将 34% 转为 34。
也可以不配置 Varbind 级 conversion,让规则覆盖全部已知返回格式:
[[instances.varbind]]
oid = ".1.3.6.1.4.1.19046.11.1.1.3.2.1.3"
name = "fan_speed"
as_label = false
[[instances.varbind.convert_rule]]
match = "offline"
value = -1
[[instances.varbind.convert_rule]]
regex = '^\s*([+-]?[0-9]+(\.[0-9]+)?)\s*%\s*$'
extract = "$1"
conversion = "float"
该配置没有 conversion fallback:offline 转为 -1,34% 由第二条规则转为 34;如果收到 unknown 等未被任何规则覆盖的值,则保留原始字符串。未命中规则不会丢弃 Varbind,也不会自动转换为 0,因此应尽量覆盖设备所有已知返回格式。
特定 Trap 下的规则路径为 [[instances.trap_mapping.varbind.convert_rule]]。特定 Trap 的 Varbind 配置优先于全局 instances.varbind,并且当该 Varbind 同时被 trap_mapping.value 选为核心值时,转换规则仍然生效。规则命中后的严格转换失败会记录警告并保留原始值,不会中断 Trap 监听。
convert_rule支持企业版>= v0.5.30、开源版>= v0.5.15,更早版本不支持该配置。
示例输出:
snmp_trap_hh3c_cfg_eventlog{device_name="access-switch-01",device_ip="10.95.5.53",...} = 1
五、专属映射 (Trap Specific Mappings)
有时候全局配置不够灵活(不同 Trap 事件可能有同名变量但意义不同)。此时可以使用 trap_mapping,它允许你为特定的 Trap OID 定义专属的聚合规则。它在匹配的变量上优先级高于全局配置。
5.1 配置语法
[[instances]]
[[instances.trap_mapping]]
oid = ".1.3.6.1.6.3.1.1.5.3" # 目标 Trap 的 OID 前缀
name = "link_down" # 强制将核心指标名重写为 snmp_trap_link_down
value = ".1.3.6.1.2.1.1.3" # 指定某个变量的值作为该指标的主 Value (可选)
[[instances.trap_mapping.varbind]]
oid = ".1.3.6.1.2.1.2.2.1.1" # 采用最长前缀匹配
name = "ifIndex" # 直接将其转换为名为 ifIndex 的 Label
conversion = "int" # 可选,支持和 [[instances.varbind]] 相同的转换方式
5.2 匹配逻辑要点
- 最长前缀优先:如果有多个
trap_mapping的 OID 命中,或者varbind命中,Categraf 会自动选择 OID 最长的那一条规则,确保匹配最精确。 - 层级边界对齐:为了防止误匹配(例如 OID
1.3.6匹配到1.3.60),插件会强制进行层级边界校验。 - 主 Value 回退:如果定义了
trap_mapping使得指标聚合生效,但没有匹配到value字段指定的主变量,该核心指标的值默认设置为1(非常适合配置> 0的存在性告警)。
六、经典场景教程
假设我们发送如下一个 SNMPv2c 的 Trap(包含了 sysUpTime, trapOID, 以及三个业务字段:接口索引835,管理状态1,操作状态2):
snmptrap -v 2c -c public 127.0.0.1:162 "" \
.1.3.6.1.6.3.1.1.5.3 \
.1.3.6.1.2.1.2.2.1.1.835 i 835 \
.1.3.6.1.2.1.2.2.1.7.835 i 1 \
.1.3.6.1.2.1.2.2.1.8.835 i 2
场景 A:纯聚合指标
我们希望把这 3 个业务字段全部塞进 Label,并把 sysUpTimeInstance 的值作为指标值。
[[instances]]
service_address = "udp://0.0.0.0:162"
fields_to_labels = ["ifAdminStatus", "ifOperStatus"]
[[instances.trap_mapping]]
oid = ".1.3.6.1.6.3.1.1.5.3"
name = "link_down"
value = ".1.3.6.1.2.1.1.3" # 将 sysUpTime 的值作为主值
[[instances.trap_mapping.varbind]]
oid = ".1.3.6.1.2.1.2.2.1.1"
name = "ifIndex"
输出结果: 变量被全部消耗,生成唯一一条高维度的核心指标:
snmp_trap_link_down{source="127.0.0.1",version="2c",oid=".1.3.6.1.6.3.1.1.5.3",name="link_down",mib="SNMPv2-MIB",ifIndex="835",ifAdminStatus="1",ifOperStatus="2"} = 123456
场景 B:上下文继承 (Dispersed Fallback)
如果我们在上文配置中注释掉 value = ".1.3.6.1.2.1.1.3",会发生什么?
此时,核心指标没有指定主值来源,默认被赋值为 1。而未被消耗的 sysUpTimeInstance 会回退成散落指标。
输出结果: 散落指标继承了核心指标收集到的所有业务 Label!
# 1. 核心事件指标(最适合配置 '> 0' 的实时告警)
snmp_trap_link_down{source="127.0.0.1",version="2c",oid=".1.3.6.1.6.3.1.1.5.3",name="link_down",mib="SNMPv2-MIB",ifIndex="835",ifAdminStatus="1",ifOperStatus="2"} = 1
# 2. 散落的回退指标(保留了全量上下文)
snmp_trap_sysUpTimeInstance{source="127.0.0.1",version="2c",oid=".1.3.6.1.6.3.1.1.5.3",name="link_down",mib="SNMPv2-MIB",ifIndex="835",ifAdminStatus="1",ifOperStatus="2"} = 123456
通过这种机制,snmp_trap 既能提供高度聚合的事件监控用于告警,又绝不丢失任何一个原始指标字段的数据!