SNMP 监控难,不是因为配置文件有多长,而是因为很多人一开始就卡在几个问题上:交换机到底应该采哪些 OID?端口流量应该用 32 位计数器还是 64 位计数器?ifDescr、ifName、ifAlias 哪个适合做标签?为什么某个端口字段采集失败后整张表都没有了?Dashboard 上看到流量之后,又该怎么判断带宽、错包和端口状态是否异常?
Categraf 的 snmp 插件可以主动拉取交换机、路由器、防火墙、负载均衡等支持 SNMP 的设备指标。本文先从最常见的网络设备接口监控开始,围绕 IF-MIB 建立一套可直接复用的配置和 PromQL。后续文章再继续展开 SNMP 表格采集、SNMPv3、厂商私有 OID 和排障。
核心要点
- 交换机接口监控优先采 IF-MIB / IF-MIB::ifXTable 中的通用指标,不要一开始就陷入厂商私有 MIB。
- 高速端口优先使用
ifHCInOctets、ifHCOutOctets这类 64 位计数器;ifInOctets、ifOutOctets在高速链路上容易回绕。 - 端口身份建议同时保留
index、ifIndex、ifName、ifDescr,ifAlias可以用来展示业务含义,但要控制命名规范。 - 生产环境不要无脑采集所有端口,应使用
filters过滤空口、虚拟口、无意义接口,避免时序数量膨胀。 - 新版 SNMP 插件支持
partial表格采集策略,可以在部分非关键数值字段失败时保留已成功采集的数据,同时对标签、过滤字段等依赖保持保守处理。 - SNMP Dashboard 没有绝对通用版本,文章中的 PromQL 以本文配置生成的
snmp_interface_*指标为准。
1. 交换机接口监控要回答什么
交换机接口监控最少要回答六类问题。
第一,设备是否还能访问:
snmp_up
snmp_icmp_up
snmp_up 表示 Categraf 能否通过 SNMP 读取设备;snmp_icmp_up 表示 ICMP 探测是否可达。二者不完全等价。SNMP 失败可能是 community、SNMPv3 认证、ACL 或 UDP 161 被拦;ICMP 失败可能只是设备禁 ping。
第二,设备是否重启过:
snmp_uptime
sysUpTime 通常是 TimeTicks,单位是百分之一秒。换算成天:
snmp_uptime / 8640000
第三,端口现在是 up 还是 down:
snmp_interface_ifAdminStatus
snmp_interface_ifOperStatus
ifAdminStatus = 1 表示管理状态开启,ifOperStatus = 1 表示运行状态 up。值班时最关心的是“管理状态开启但运行状态 down”的端口。
第四,端口流量和带宽利用率:
rate(snmp_interface_ifHCInOctets[5m]) * 8
rate(snmp_interface_ifHCOutOctets[5m]) * 8
ifHCInOctets / ifHCOutOctets 是字节计数器,乘以 8 后得到 bps。
第五,端口有没有错包和丢包:
rate(snmp_interface_ifInErrors[5m])
rate(snmp_interface_ifOutErrors[5m])
rate(snmp_interface_ifInDiscards[5m])
rate(snmp_interface_ifOutDiscards[5m])
第六,这个端口到底接了什么业务。仅靠 10.1.2.3 Gi1/0/48 很难排障,最好通过 ifAlias 或外部 CMDB 标签让告警里带上业务含义。
2. 写配置前先用 snmpwalk 验证
不要一上来就改 Categraf。先在 Categraf 所在机器上确认 SNMP 本身可用。
SNMPv2c 示例:
snmpwalk -v2c -c '<COMMUNITY>' 10.10.10.11 1.3.6.1.2.1.1.5.0
snmpwalk -v2c -c '<COMMUNITY>' 10.10.10.11 1.3.6.1.2.1.1.3.0
snmpwalk -v2c -c '<COMMUNITY>' 10.10.10.11 1.3.6.1.2.1.31.1.1.1.1
这三条分别验证:
sysName.0:设备名;sysUpTime.0:设备运行时间;ifName:接口名称列表。
如果这些命令都超时,先排查网络、设备 ACL、community、SNMP 服务开关和 UDP 161。Categraf 不能绕过 SNMP 协议本身的问题。
如果环境有 MIB,可以使用可读名称:
snmpwalk -v2c -c '<COMMUNITY>' 10.10.10.11 IF-MIB::ifName
如果没有 MIB,直接使用数字 OID 更稳。本文配置也尽量使用数字 OID,减少对本机 MIB 文件的依赖。
3. 最小可用配置
SNMP 插件配置文件通常位于:
conf/input.snmp/snmp.toml
下面是一份面向交换机接口监控的配置。生产环境请替换设备地址、community 和标签。
[[instances]]
agents = [
"udp://10.10.10.11:161",
"udp://10.10.10.12:161"
]
timeout = "5s"
retries = 2
version = 2
community = "<COMMUNITY>"
agent_host_tag = "ident"
labels = { region = "cn-east", device_role = "switch" }
# 新版 SNMP 插件支持。旧版本不识别这些配置时,需要先升级或删除。
default_table_error_policy = "partial"
dependency_cache_ttl = "10m"
dependency_cache_max_entries = 10000
# 健康检查参数。异常设备会按 recovery_interval 做恢复探测。
health_check_interval = "60s"
health_check_timeout = "5s"
max_fail_count = 3
recovery_interval = "5m"
[[instances.field]]
oid = "1.3.6.1.2.1.1.3.0" # sysUpTime.0
name = "uptime"
[[instances.field]]
oid = "1.3.6.1.2.1.1.5.0" # sysName.0
name = "source"
is_tag = true
[[instances.table]]
name = "interface"
inherit_tags = ["source"]
index_as_tag = true
error_policy = "partial"
# 先按需过滤。首次接入时可以注释 filters,确认全量端口后再收敛。
filters = [
"A:ifOperStatus:^(1|2)$",
"B:ifDescr:^(GigabitEthernet|Ten-GigabitEthernet|TwentyFiveGigE|FortyGigE|HundredGigE|Eth|ge-|xe-|te-)",
"C:ifAlias:.+"
]
filters_expression = "A && (B || C)"
filters_mode = "strict"
[[instances.table.field]]
oid = "1.3.6.1.2.1.2.2.1.1" # ifIndex
name = "ifIndex"
is_tag = true
[[instances.table.field]]
oid = "1.3.6.1.2.1.2.2.1.2" # ifDescr
name = "ifDescr"
is_tag = true
[[instances.table.field]]
oid = "1.3.6.1.2.1.31.1.1.1.1" # ifName
name = "ifName"
is_tag = true
[[instances.table.field]]
oid = "1.3.6.1.2.1.31.1.1.1.18" # ifAlias
name = "ifAlias"
is_tag = true
[[instances.table.field]]
oid = "1.3.6.1.2.1.2.2.1.7" # ifAdminStatus
name = "ifAdminStatus"
[[instances.table.field]]
oid = "1.3.6.1.2.1.2.2.1.8" # ifOperStatus
name = "ifOperStatus"
[[instances.table.field]]
oid = "1.3.6.1.2.1.31.1.1.1.15" # ifHighSpeed, Mbps
name = "ifHighSpeed"
[[instances.table.field]]
oid = "1.3.6.1.2.1.31.1.1.1.6" # ifHCInOctets
name = "ifHCInOctets"
[[instances.table.field]]
oid = "1.3.6.1.2.1.31.1.1.1.10" # ifHCOutOctets
name = "ifHCOutOctets"
[[instances.table.field]]
oid = "1.3.6.1.2.1.2.2.1.13" # ifInDiscards
name = "ifInDiscards"
[[instances.table.field]]
oid = "1.3.6.1.2.1.2.2.1.14" # ifInErrors
name = "ifInErrors"
[[instances.table.field]]
oid = "1.3.6.1.2.1.2.2.1.19" # ifOutDiscards
name = "ifOutDiscards"
[[instances.table.field]]
oid = "1.3.6.1.2.1.2.2.1.20" # ifOutErrors
name = "ifOutErrors"
这份配置会产生类似下面的指标名:
snmp_up
snmp_icmp_up
snmp_icmp_rtt
snmp_uptime
snmp_interface_ifAdminStatus
snmp_interface_ifOperStatus
snmp_interface_ifHighSpeed
snmp_interface_ifHCInOctets
snmp_interface_ifHCOutOctets
snmp_interface_ifInErrors
snmp_interface_ifOutErrors
snmp_interface_ifInDiscards
snmp_interface_ifOutDiscards
其中 source、index、ifIndex、ifDescr、ifName、ifAlias、ident、region、device_role 会作为标签参与查询和告警。
4. 配置项怎么理解
agents 是设备地址。建议显式写协议和端口,例如 udp://10.10.10.11:161。如果省略协议,默认使用 UDP。
version 和 community 用于 SNMPv2c。SNMPv3 的认证加密配置会在后续文章单独展开。生产网络如果安全要求较高,优先使用 SNMPv3。
agent_host_tag = "ident" 表示把设备地址写入 ident 标签。这样 Dashboard 和告警可以按设备过滤。若团队已有统一标签规范,也可以使用 agent_host、device 或 instance,但要和 Dashboard 查询保持一致。
labels 适合放稳定标签,例如区域、机房、设备角色、环境。不要把接口描述、工单号、临时用途放在实例级标签里。
default_table_error_policy = "partial" 是新版能力。历史行为是 legacy:表格里任一字段失败,当前表可能整体失败。partial 会保留已成功采到的数值字段,但对影响行身份和标签正确性的依赖仍然保守处理,例如 tag 字段、filter 字段、secondary index 和继承标签未知时不会盲目上报。
dependency_cache_ttl 是 partial 模式下的依赖缓存时间。它只缓存标签、过滤字段、secondary-index 映射和顶层继承标签这类依赖值,用于应对短暂读取失败。不要把 TTL 设置得过长,否则设备端口标签发生变化后,旧标签可能保留更久。
5. 为什么优先采这些 OID
下面这组 OID 是交换机接口监控的基础集合。
| 指标 | OID | 用途 |
|---|---|---|
sysName.0 |
1.3.6.1.2.1.1.5.0 |
设备名,适合作为继承标签 |
sysUpTime.0 |
1.3.6.1.2.1.1.3.0 |
设备运行时间,判断重启 |
ifIndex |
1.3.6.1.2.1.2.2.1.1 |
接口索引,端口行身份 |
ifDescr |
1.3.6.1.2.1.2.2.1.2 |
接口描述,常见如 GigabitEthernet1/0/1 |
ifName |
1.3.6.1.2.1.31.1.1.1.1 |
接口名称,通常更短更适合展示 |
ifAlias |
1.3.6.1.2.1.31.1.1.1.18 |
接口别名,适合写业务用途 |
ifAdminStatus |
1.3.6.1.2.1.2.2.1.7 |
管理状态 |
ifOperStatus |
1.3.6.1.2.1.2.2.1.8 |
运行状态 |
ifHighSpeed |
1.3.6.1.2.1.31.1.1.1.15 |
接口带宽,单位 Mbps |
ifHCInOctets |
1.3.6.1.2.1.31.1.1.1.6 |
64 位入方向字节计数器 |
ifHCOutOctets |
1.3.6.1.2.1.31.1.1.1.10 |
64 位出方向字节计数器 |
ifInErrors |
1.3.6.1.2.1.2.2.1.14 |
入方向错包 |
ifOutErrors |
1.3.6.1.2.1.2.2.1.20 |
出方向错包 |
ifInDiscards |
1.3.6.1.2.1.2.2.1.13 |
入方向丢弃 |
ifOutDiscards |
1.3.6.1.2.1.2.2.1.19 |
出方向丢弃 |
很多设备还支持 ifInOctets、ifOutOctets,但它们是 32 位计数器。百兆以下端口问题不大,千兆、万兆或更高速端口上更容易回绕,建议优先使用 ifHC* 64 位计数器。
ifAlias 很有价值,但也要谨慎。如果团队把工单号、临时备注、人员名字频繁写入端口描述,ifAlias 作为标签会造成时序变化。生产环境最好约定稳定格式,例如:
to-core-sw02
to-prod-k8s-node-rack12
to-payment-fw-a
6. 启动和验证
修改配置后,先用测试模式看输出:
./categraf --test --inputs snmp
可以用 rg 过滤关键信息:
./categraf --test --inputs snmp 2>&1 \
| rg 'snmp_(up|icmp_up|uptime|interface_ifHC|interface_ifOperStatus|interface_ifInErrors|interface_ifOutErrors)'
正常情况下应该能看到:
snmp_up ident=10.10.10.11 1
snmp_uptime ident=10.10.10.11,source=sw-core-01 ...
snmp_interface_ifHCInOctets ident=10.10.10.11,ifName=...
snmp_interface_ifOperStatus ident=10.10.10.11,ifName=...
如果 snmp_up = 0,先回到 snmpwalk 验证。若 snmp_up = 1 但没有接口指标,重点检查:
filters是否过滤掉了所有端口;- 设备是否支持
ifHCInOctets和ifHCOutOctets; - Categraf 版本是否支持
default_table_error_policy、error_policy、dependency_cache_ttl; - Categraf 日志中是否有 OID 不存在、超时或权限错误。
正式运行后,在后端查询:
snmp_up
snmp_interface_ifOperStatus
snmp_interface_ifHCInOctets
测试模式只证明采集侧能拿到数据,不证明 remote write 已经写入后端。如果后端查不到,请继续检查 writer 地址、网络、队列和后端数据源。
7. PromQL 怎么看端口流量
入方向流量:
sum by (ident, source, ifName, ifAlias) (
rate(snmp_interface_ifHCInOctets[5m]) * 8
)
出方向流量:
sum by (ident, source, ifName, ifAlias) (
rate(snmp_interface_ifHCOutOctets[5m]) * 8
)
如果只想看某台设备:
sum by (ifName, ifAlias) (
rate(snmp_interface_ifHCOutOctets{ident="10.10.10.11"}[5m]) * 8
)
带宽利用率需要结合 ifHighSpeed。ifHighSpeed 单位是 Mbps,因此要乘以 1000000 转为 bps:
100 *
rate(snmp_interface_ifHCOutOctets[5m]) * 8
/
clamp_min(snmp_interface_ifHighSpeed * 1000000, 1)
入方向同理:
100 *
rate(snmp_interface_ifHCInOctets[5m]) * 8
/
clamp_min(snmp_interface_ifHighSpeed * 1000000, 1)
如果某些设备上 ifHighSpeed = 0 或缺失,带宽利用率就不能直接算,应先修设备侧接口速率,或为这些端口单独补充静态容量标签。
8. PromQL 怎么看端口状态
所有运行状态不是 up 的端口:
snmp_interface_ifOperStatus != 1
真正需要告警的通常不是所有 down 端口,而是“管理状态开启,但运行状态 down”:
snmp_interface_ifAdminStatus == 1
and on (ident, index)
snmp_interface_ifOperStatus != 1
这里使用 index_as_tag = true 生成的 index 标签做向量匹配。如果你的配置没有开启 index_as_tag,可以改用 ifIndex 标签,前提是你把 ifIndex 配成了 is_tag = true。
常见状态值可以这样理解:
| 值 | ifAdminStatus |
ifOperStatus |
|---|---|---|
1 |
up | up |
2 |
down | down |
3 |
testing | testing |
不同设备可能还有更细状态,告警前应在目标设备上验证实际返回值。
9. PromQL 怎么看错包和丢包
入方向错包速率:
rate(snmp_interface_ifInErrors[5m])
出方向错包速率:
rate(snmp_interface_ifOutErrors[5m])
入方向丢弃速率:
rate(snmp_interface_ifInDiscards[5m])
出方向丢弃速率:
rate(snmp_interface_ifOutDiscards[5m])
可以按接口聚合:
sum by (ident, source, ifName, ifAlias) (
rate(snmp_interface_ifInErrors[5m])
+ rate(snmp_interface_ifOutErrors[5m])
+ rate(snmp_interface_ifInDiscards[5m])
+ rate(snmp_interface_ifOutDiscards[5m])
)
错包和丢包不要只看一次瞬时尖刺。建议结合 5 到 15 分钟窗口、业务流量和链路角色判断。核心上联、存储网络、跨机房链路上的持续错包更值得关注;普通空闲端口偶发少量错误,优先级可以低一些。
10. 如何过滤端口
SNMP 监控最容易犯的错误是采集所有端口。很多设备会暴露大量 VLAN、Loopback、Null、Tunnel、Stack、管理口、虚拟口和空口,全部采集会带来三个问题:
- 后端时序数量快速膨胀;
- Dashboard 变量里出现大量无意义端口;
- 告警噪音变多,真正关键端口被淹没。
建议按阶段处理。
第一阶段,先不加过滤,运行测试模式确认设备返回了哪些端口:
./categraf --test --inputs snmp 2>&1 \
| rg 'snmp_interface_if(HCInOctets|OperStatus|InErrors)'
输出里的 ifName、ifDescr、ifAlias 是标签,不是独立指标。检查端口名称时,看这些数值指标后面带的标签即可。
第二阶段,找出接口命名规律。例如核心物理口可能是:
GigabitEthernet1/0/1
Ten-GigabitEthernet1/0/49
HundredGigE1/0/1
ge-0/0/0
xe-0/0/0
Eth-Trunk1
第三阶段,再配置 filters:
filters = [
"A:ifOperStatus:^(1|2)$",
"B:ifDescr:^(GigabitEthernet|Ten-GigabitEthernet|HundredGigE|Eth|ge-|xe-|te-|Eth-Trunk)",
"C:ifAlias:.+"
]
filters_expression = "A && (B || C)"
filters_mode = "strict"
这表示:端口状态要是 up 或 down,并且接口描述像物理口,或者配置了非空 ifAlias。实际环境要按厂商命名调整,不要直接照抄正则。
11. partial 表格采集解决什么问题
SNMP 表格采集经常遇到不完整数据。比如某台设备支持 ifDescr、ifOperStatus、ifHCInOctets,但不支持 ifAlias;或者某个字段偶发超时。历史行为下,表内任一字段失败可能导致整张表失败,用户看到的结果就是“明明大部分 OID 都能 walk,Dashboard 却没有接口指标”。
新版 SNMP 插件增加了表格错误策略:
default_table_error_policy = "partial"
[[instances.table]]
name = "interface"
error_policy = "partial"
partial 的原则是:保留已经成功采集到的数值字段,但不牺牲标签和行身份正确性。
可以保留的场景:
- 某个普通数值字段失败,例如
ifOutDiscards偶发读取失败; - 同一行其他数值字段已经成功,例如流量和状态仍然可用。
仍然会保守跳过的场景:
- tag 字段未知,无法确认这行指标属于哪个端口;
- filter 字段未知,无法判断这行是否应该被采集;
- inherited tag 未知,无法继承设备身份;
- secondary index 依赖未知,可能造成行错配。
这就是所谓 fail closed:宁可少报一部分,也不要报出标签错乱的数据。
如果设备偶发丢字段,可以开启依赖缓存:
dependency_cache_ttl = "10m"
dependency_cache_max_entries = 10000
缓存只保存依赖值,例如 tag、filter 字段、secondary-index 映射和顶层继承标签。它不是把所有指标都缓存起来,也不会用旧流量值冒充新流量值。
12. Dashboard 怎么导入
SNMP 采集内容由用户配置决定,因此不存在完全通用的 Dashboard。Categraf 仓库里提供了一个基础接口大盘:
inputs/snmp/dashboard.json
导入后要重点检查三件事:
- Dashboard 查询的指标名是否和你的配置一致;
- 变量使用的标签是否存在,例如
ident、source、ifName; - 面板是否使用 64 位计数器和正确单位。
如果使用本文配置,PromQL 应以 snmp_interface_* 为准,例如:
rate(snmp_interface_ifHCInOctets[5m]) * 8
rate(snmp_interface_ifHCOutOctets[5m]) * 8
rate(snmp_interface_ifInErrors[5m]) + rate(snmp_interface_ifOutErrors[5m])
如果导入后没有数据,先不要怀疑采集。直接在后端裸查:
snmp_up
snmp_interface_ifHCInOctets
snmp_interface_ifOperStatus
如果裸指标存在,并且这些指标上能看到 ifName、ifDescr、ifAlias 等标签,问题多半在 Dashboard 的数据源、变量、标签名或 PromQL。
13. 告警建议
设备 SNMP 不可达
snmp_up == 0
建议持续 2 到 3 个采集周期再告警。网络设备 SNMP 偶发超时并不少见,直接按单点失败告警会比较吵。
设备 ICMP 不可达
snmp_icmp_up == 0
如果设备禁 ping,不要配置这条告警,或者关闭 disable_icmp_up 相关采集。
端口管理 up 但运行 down
snmp_interface_ifAdminStatus == 1
and on (ident, index)
snmp_interface_ifOperStatus != 1
这条规则应只对关键端口启用。普通接入口经常因为终端关机而 down,不适合全部告警。
端口带宽利用率过高
100 *
rate(snmp_interface_ifHCOutOctets[5m]) * 8
/
clamp_min(snmp_interface_ifHighSpeed * 1000000, 1)
> 80
建议按链路类型分层:核心上联可以 70% 或 80% 预警,接入口可以更宽松。
端口错包或丢包持续增长
sum by (ident, source, ifName, ifAlias) (
rate(snmp_interface_ifInErrors[10m])
+ rate(snmp_interface_ifOutErrors[10m])
+ rate(snmp_interface_ifInDiscards[10m])
+ rate(snmp_interface_ifOutDiscards[10m])
) > 0
这条规则不要一刀切。某些端口少量丢包可能来自限速、队列调度或广播控制,应结合端口角色和基线调整阈值。
设备重启
snmp_uptime < 30000
这里的 30000 约等于 300 秒,因为 sysUpTime 是百分之一秒。也可以用 changes(snmp_uptime[10m]) 辅助判断,但不同设备计数行为可能有差异。
14. 常见问题
为什么用 ifHCInOctets,而不是 ifInOctets?
ifInOctets 是 32 位计数器,高速端口上容易快速回绕。ifHCInOctets 和 ifHCOutOctets 是 64 位计数器,更适合千兆、万兆和更高速链路。
ifAlias 可以作为标签吗?
可以,但前提是它相对稳定。ifAlias 很适合写“接到哪台设备、哪个业务、哪条链路”,但如果经常被人临时修改,会导致标签变化和时序漂移。
SNMP up 正常,但接口指标没有数据?
先注释 filters,用测试模式确认接口表是否有数据。再检查设备是否支持 ifHC* OID,以及 Categraf 日志里是否有某个字段 walk 失败。
为什么 partial 后还是有些行被跳过?
partial 不是“任何错误都继续上报”。如果标签、过滤字段、继承标签等依赖未知,Categraf 会跳过相关行,避免输出标签缺失或错配的数据。
Dashboard 里的指标名和文章不一致怎么办?
以实际配置和后端裸指标为准。SNMP 指标名由 inputName、table.name 和 field.name 共同决定。本文配置下,接口表字段会输出为 snmp_interface_<field>,例如 snmp_interface_ifHCInOctets。
15. 生产建议
SNMP 监控要分层建设。
第一层先做可用性:snmp_up、snmp_icmp_up、snmp_uptime。这能快速发现设备不可达、SNMP 权限变化和设备重启。
第二层做接口流量和状态:ifOperStatus、ifAdminStatus、ifHighSpeed、ifHCInOctets、ifHCOutOctets。这能覆盖绝大多数交换机端口监控需求。
第三层做质量指标:ifInErrors、ifOutErrors、ifInDiscards、ifOutDiscards。这类指标更适合结合端口角色和历史基线告警。
第四层再做厂商私有硬件指标:温度、风扇、电源、CPU、内存、光模块。不要在第一篇配置里把所有厂商 OID 都混进去,否则配置复杂度会迅速失控。
最后,SNMP 配置一定要和设备命名规范一起治理。没有稳定的设备名、端口名和端口描述,再好的采集器也只能生成一堆难以排障的时间序列。