
1. 这不是“加个插件就能用”的事Prometheus 与 SNMP 的真实关系你搜“Prometheus 监控交换机”页面刷出来一堆“docker-compose 一键部署”、“三行配置搞定 SNMP 采集”的教程点进去照着抄结果 metrics 一个没上来target 状态永远是DOWN日志里反复滚动snmp: connect: no route to host或snmp: timeout。我刚入坑那会儿也这样——以为 Prometheus 像 Grafana 那样装上就能连设备结果在 snmp_exporter 的 YAML 文件里调了两天 timeout 和 retries最后发现根本不是参数问题而是压根没搞清 Prometheus 和 SNMP 在整个监控链路里的角色分工。Prometheus 本身不支持 SNMP。这是所有踩坑的起点。它原生只认 HTTP text/plain 或 protobuf 格式的指标暴露接口比如/metrics而 SNMP 是一套独立的、基于 UDP 的网络管理协议工作在 OSI 模型的第7层应用层但走的是完全不同的通信范式。你不能让 Prometheus 直接去“发 GETNEXT 请求”或“解析 ASN.1 编码的 PDU 包”。它需要一个“翻译官”这个角色就是snmp_exporter—— 一个独立运行的、专门负责和 SNMP 设备打交道的代理服务。Prometheus 只负责定期 HTTP GET 这个 exporter 暴露出来的/metrics接口拿到转换好的文本指标再存进自己的 TSDB。整个链路是设备SNMP Agent→ snmp_exporterSNMP Client 指标转换器→ PrometheusHTTP Client TSDB。所以当你看到“Prometheus 监控交换机”这个说法时实际指的是“用 snmp_exporter 作为桥梁把 SNMP 设备的数据喂给 Prometheus”。热搜词里反复出现的prometheus监控交换机、prometheus监控服务器资源、truenas scale 自带ups服务 snmp ups背后全是这个模式。而stm32 snmp trap v2c 代码、华为 snmp实验这类词则指向另一端——设备侧的 SNMP Agent 实现它和 Prometheus 没有直接关系但决定了 snmp_exporter 能否成功采集。至于prometheus是如何从otel-collector收取数据的那是另一个生态OpenTelemetry和 SNMP 完全无关混在一起搜只会让你更迷糊。真正要动手核心就三件事配对 snmp_exporter 的配置文件generator.yml、部署并验证 snmp_exporter 服务、在 Prometheus 里正确配置 scrape job 指向它。下面我们就从这三件事的底层逻辑开始拆解。2. 为什么不能手写 snmp_exporter 配置Generator 工具的不可替代性很多人第一次尝试会直接打开 snmp_exporter 的snmp.yml文件想手动写一个 target 的 OID 列表。比如想监控一台 Cisco 交换机的 CPU 使用率就去查 MIB 手册找到1.3.6.1.4.1.9.9.109.1.1.1.1.8ciscoEnvMonCpuTotal5min然后往配置里硬塞。结果跑起来metrics 里确实多了一行snmp_ciscoEnvMonCpuTotal5min{instance192.168.1.1,jobsnmp}但值永远是0或者NaN。问题出在哪不是 OID 错而是snmp_exporter 不是简单的 OID 抓取器它需要完整的 MIB 解析上下文。SNMP 的 OID 树不是扁平的。一个 OID 比如1.3.6.1.2.1.1.1.0sysDescr返回的是一个字符串而1.3.6.1.2.1.2.2.1.10ifInOctets是一个表格table它下面有无数个实例instance每个实例对应一个网卡接口OID 后缀是.1、.2、.3……。snmp_exporter 必须知道这个 OID 是 scalar标量还是 table表格如果是 table还要知道它的索引列index column是什么比如ifIndex这样才能把原始的 SNMP 响应一堆 OIDvalue 对正确地映射成 Prometheus 的 time series带 label 的指标。这个映射规则就定义在 snmp_exporter 的配置文件里叫module。一个 module 就是一套针对某类设备比如“通用 Linux 主机”、“Cisco IOS”、“Netgear 交换机”的采集策略。手写 module 几乎不可能。原因有三第一MIB 文件本身是 ASN.1 语法写的结构嵌套极深比如IF-MIB里定义ifTable它又依赖IF-INDEX类型而IF-INDEX又定义在IANAifType-MIB里层层引用第二不同厂商对标准 MIB 的实现有差异比如华为的HUAWEI-ENTITY-MIB和 Cisco 的CISCO-ENTITY-SENSOR-MIB虽然都监控温度但 OID 和结构完全不同第三Prometheus 指标要求 label 必须是字符串而 SNMP 的 index 可能是整数或 OID需要walk和get结合才能拼出完整 label比如ifDescr的值GigabitEthernet0/1要作为ifDescrlabel就必须先walkifIndex再对每个 indexget对应的ifDescr。这就是generator工具存在的意义。它不是一个可选的“高级功能”而是snmp_exporter 生态的基石。generator的工作流程是下载目标设备的 MIB 文件.mib 或 .my 文件→ 用 Go 的net/snmp库解析 MIB 的 ASN.1 结构 → 根据用户提供的generator.yml中的modules定义指定要 walk 哪些 OID 子树、哪些 OID 作为 label、如何重命名指标名→ 自动生成符合 snmp_exporter 规范的snmp.yml。整个过程全自动避免了人工解析 MIB 的灾难性错误。你看到的snmp addnewaccessentry addnewview这类命令是 SNMPv3 的访问控制配置它发生在设备侧确保generator或snmp_exporter有权限读取 MIB 数据但它不参与指标转换逻辑。没有generator你就只能用snmpwalk手动调试效率极低且无法规模化。提示generator生成的snmp.yml文件本质是一个巨大的 YAML 映射表里面每一个module下的walk列表都对应一个 SNMPGETBULK请求的 OID 起始点。get列表则对应单个GET请求。retries和timeout参数在这里设置直接影响采集成功率但它们只是“重试机制”不能解决 OID 不存在或权限不足的根本问题。3. 从零开始一次真实的 snmp_exporter 部署与调试全流程我们以监控一台家用级 Netgear GS108Ev3 交换机为例完整走一遍部署、配置、验证的闭环。这台设备出厂默认开启 SNMPv2ccommunity string 是publicIP 是192.168.1.100。整个过程不依赖 Docker全部用二进制方式让你看清每个环节的输入输出。3.1 环境准备与基础验证首先在你的监控服务器假设是 Ubuntu 22.04上安装基础工具sudo apt update sudo apt install -y snmp snmp-mibs-downloadersnmp-mibs-downloader会自动下载 IETF 标准 MIB如IF-MIB、IP-MIB但厂商私有 MIB如 Netgear 的NETGEAR-PRIV-MIB需要手动获取。去 Netgear 官网支持页面搜索 GS108Ev3 的固件包解压后找到MIBs文件夹把NETGEAR-PRIV-MIB.mib下载到本地/tmp/mibs/目录。接下来用snmpwalk做最底层验证确认网络和权限通# 测试基础连通性UDP 161 端口 nc -uz 192.168.1.100 161 echo Port open || echo Port closed # 发送 SNMPv2c GET 请求读取 sysDescr验证 community string snmpget -v2c -c public 192.168.1.100 1.3.6.1.2.1.1.1.0 # 正常响应类似SNMPv2-MIB::sysDescr.0 STRING: Netgear GS108Ev3, 8-port Gigabit Switch, Firmware: V1.0.0.36如果snmpget失败99% 是防火墙或设备 SNMP 设置问题和 Prometheus 无关。此时不要往下走。3.2 下载与编译 generator 工具snmp_exporter 的generator并不随主程序发布需要单独编译# 创建工作目录 mkdir -p ~/snmp-gen cd ~/snmp-gen # 下载源码注意版本匹配这里用 v0.24.0对应 snmp_exporter v0.24.0 wget https://github.com/prometheus/snmp_exporter/archive/refs/tags/v0.24.0.tar.gz tar -xzf v0.24.0.tar.gz cd snmp_exporter-0.24.0/generator # 编译 generator需要 Go 1.19 go build -o generator .编译完成后generator二进制就在当前目录。3.3 编写 generator.yml 并生成 snmp.yml在~/snmp-gen目录下创建generator.ymlmodules: # 定义一个名为 netgear_gs108 的 module netgear_gs108: # 指定要 walk 的 OID 子树这里用 IF-MIB 的 ifTable walk: - 1.3.6.1.2.1.2.2.1 # ifTable - 1.3.6.1.2.1.31.1.1.1 # ifXTable (扩展接口信息) # 指定要 get 的 scalar OID get: - 1.3.6.1.2.1.1.1.0 # sysDescr - 1.3.6.1.2.1.1.3.0 # sysUpTime # 定义指标重命名和 label 映射 metrics: - name: ifIndex oid: 1.3.6.1.2.1.2.2.1.1 type: gauge help: The ifIndex value of the interface. - name: ifDescr oid: 1.3.6.1.2.1.2.2.1.2 type: gauge help: The ifDescr value of the interface. indexes: - labelname: ifDescr type: string - name: ifInOctets oid: 1.3.6.1.2.1.2.2.1.10 type: counter help: The total number of octets received on the interface. indexes: - labelname: ifIndex type: integer这个配置只覆盖了基础接口流量但已经足够说明逻辑。关键点在于indexes字段ifDescr的值字符串被提取为 labelifDescr而ifInOctets的值则按ifIndex整数分组最终生成的指标形如snmp_ifInOctets{ifIndex1,instance192.168.1.100}。然后执行生成# 设置 MIBS 环境变量让 generator 能找到标准和私有 MIB export MIBDIRS/usr/share/snmp/mibs:/tmp/mibs # 运行 generator输出到 snmp.yml ./generator generate成功后当前目录下会生成snmp.yml它是一个数千行的 YAML 文件里面包含了netgear_gs108module 的完整 walk/get 规则和 metrics 映射。3.4 部署 snmp_exporter 并配置 Prometheus下载 snmp_exporter 二进制wget https://github.com/prometheus/snmp_exporter/releases/download/v0.24.0/snmp_exporter-0.24.0.linux-amd64.tar.gz tar -xzf snmp_exporter-0.24.0.linux-amd64.tar.gz cd snmp_exporter-0.24.0.linux-amd64启动服务指定配置文件路径./snmp_exporter --config.file../snmp.yml --web.listen-address:9116服务启动后访问http://localhost:9116/snmp?target192.168.1.100modulenetgear_gs108这是一个调试端点。如果返回text/plain格式的指标如snmp_ifInOctets{ifIndex1} 123456789说明 exporter 本身工作正常。如果返回500 Internal Server Error查看日志大概率是snmp.yml里某个 OID 在设备上不存在或者generator没正确解析 MIB。最后在 Prometheus 的prometheus.yml中添加 jobscrape_configs: - job_name: snmp static_configs: - targets: - 192.168.1.100 # 这是你的交换机 IP metrics_path: /snmp params: module: [netgear_gs108] relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: localhost:9116 # snmp_exporter 的地址重启 Prometheus进入 Web UI 的 Targets 页面你应该能看到snmpjob 下有一个UP状态的 target。至此数据链路打通。4. 指标落地从 raw OID 到可告警的业务视图snmp_exporter 生成的指标是“原始”的比如snmp_ifInOctets是一个 counter 类型的累加值直接画图会是一条直线上升的曲线毫无业务意义。真正的价值在于PromQL 的二次加工。我们以交换机端口流量监控为例展示如何把 raw data 变成 actionable insight。4.1 计算实时速率bpsifInOctets是字节计数器要得到每秒接收的比特数bps需用rate()函数计算 2 分钟内的平均变化率再乘以 8rate(snmp_ifInOctets{instance192.168.1.100}[2m]) * 8这个表达式的结果单位是 bps。但注意rate()的时间窗口[2m]必须大于 Prometheus 的 scrape interval默认 15s否则可能因数据点不足而返回空。如果你的 scrape interval 是 30s[2m]是安全的如果设成了 5s就得用[1m]。4.2 构建端口维度的动态视图snmp_ifInOctets的 label 是ifIndex但运维人员更关心的是端口名比如GigabitEthernet1/0/1。前面generator.yml里我们定义了ifDescr的 indexes所以可以关联rate(snmp_ifInOctets{instance192.168.1.100}[2m]) * 8 and on (instance, ifIndex) group_right snmp_ifDescr{instance192.168.1.100}这个查询会自动把ifIndex1关联到ifDescrGigabitEthernet1/0/1最终图表的 legend 就是端口名而不是数字。4.3 设置有意义的告警阈值监控不是为了看图而是为了发现问题。一个常见的错误是设置固定阈值比如snmp_ifInOctets 10000000001Gbps。但家用交换机的千兆口满速是 125MB/s1000Mbps而ifInOctets是字节1000Mbps 125,000,000 bytes/s。更合理的做法是基于百分比# prometheus.rules.yml groups: - name: snmp_alerts rules: - alert: PortBandwidthUsageHigh expr: | 100 * rate(snmp_ifInOctets{instance192.168.1.100}[2m]) * 8 / (1000 * 1000 * 1000) 80 for: 5m labels: severity: warning device: Netgear GS108Ev3 annotations: summary: Port {{ $labels.ifDescr }} bandwidth usage 80% description: Current usage is {{ $value | printf \%.2f\ }}% of 1Gbps link.这里1000 * 1000 * 1000是 1Gbps 的数值$labels.ifDescr能正确显示端口名因为告警触发时Prometheus 会自动注入匹配到的 label。for: 5m表示持续 5 分钟超过阈值才发告警避免瞬时抖动误报。4.4 处理 SNMPv3 的安全实践热搜词里有snmp trap v2c 代码但生产环境强烈建议用 SNMPv3。它提供认证Authentication和加密Privacy比明文的community string安全得多。snmp_exporter 支持 v3配置在snmp.yml的auth字段modules: cisco_ios_v3: auth: username: monitor password: AuthPass123 # MD5/SHA 认证密码 auth_protocol: sha priv_password: PrivPass456 # AES/DES 加密密码 priv_protocol: aes walk: [...]generator生成时会在snmp.yml的 module 下自动添加auth块。但注意password和priv_password是明文存储的生产环境必须配合 Prometheus 的file_sd_configs或 Vault 等密钥管理工具绝不能硬编码在配置文件里。这也是为什么truenas scale 自带ups服务 snmp ups这类场景如果 UPS 支持 SNMPv3一定要启用否则publiccommunity 就等于把设备控制权暴露在局域网里。5. 常见故障排查从日志、网络到 MIB 的全链路诊断即使严格按照流程操作90% 的失败都发生在以下五个环节。我把它们按发生频率排序并给出实测有效的排查步骤。5.1 Target 状态为 DOWN先查网络和 SNMP 服务这是最表层的问题。Target页面显示DOWN第一反应不是改 Prometheus 配置而是做三件事Ping 设备 IPping 192.168.1.100。不通检查物理连接、VLAN、防火墙。Telnet/NC 测试 UDP 161 端口nc -uz 192.168.1.100 161。返回Connection refused说明设备 SNMP 服务没开去设备 Web 界面或 CLI 开启。snmpget 验证 communitysnmpget -v2c -c public 192.168.1.100 1.3.6.1.2.1.1.1.0。返回Timeout可能是 community 错或设备 ACL 拒绝了你的监控服务器 IP。注意snmpget默认超时是 5 秒而snmp_exporter的默认 timeout 是 10 秒。如果snmpget都超时exporter必然失败。不要跳过这一步。5.2 Target 状态为 UP 但无 metricssnmp_exporter 配置问题Target显示UP但http://localhost:9116/snmp?target...返回空或500。这时要看snmp_exporter的日志# 启动时加 --log.leveldebug 查看详细日志 ./snmp_exporter --config.filesnmp.yml --log.leveldebug常见日志线索Error reading from socket: read udp ...: i/o timeout网络延迟高或设备响应慢增大timeout参数。No such objectgenerator.yml里写的 OID在设备上根本不存在。用snmpwalk -v2c -c public 192.168.1.100 1.3.6.1.2.1.2.2.1看设备实际返回的 OID 树对比snmp.yml。Could not unmarshalsnmp.yml格式错误YAML 缩进不对。用在线 YAML 校验器检查。5.3 Metrics 存在但值异常OID 解析或类型错误指标存在但值是0、-1或NaN。典型场景是ifOperStatus端口状态永远是1up但你知道某个口明明 down 了。这是因为ifOperStatus的 OID 是1.3.6.1.2.1.2.2.1.8它的值是整数1up, 2down但generator默认把它当gauge处理。你需要在generator.yml的metrics里显式指定type: gauge并添加help注释确保snmp_exporter正确解析。5.4 Grafana 图表为空PromQL 或数据源配置错误Prometheus Web UI 能查到数据但 Grafana 里图表空白。检查Grafana 的 Prometheus 数据源 URL 是否指向http://localhost:9090Prometheus 地址而不是http://localhost:9116snmp_exporter 地址。查询语句是否用了错误的 label。比如instance192.168.1.100写成了target192.168.1.100。时间范围是否太小没覆盖到数据点。Grafana 默认是最近 6 小时如果刚部署可能还没采集到数据。5.5 大规模设备管理配置文件的可维护性陷阱当你要监控 50 台交换机、20 台 UPS、10 台打印机时snmp.yml会变成一个巨无霸文件generator.yml里modules也越来越多。此时手动生成和管理配置是灾难。解决方案是模块化 模板化把不同厂商的 MIB 放在不同子目录mibs/cisco/,mibs/huawei/,mibs/netgear/。generator.yml用include引入多个子配置include: [modules/cisco.yml, modules/huawei.yml]。用 Ansible 或 Shell 脚本批量生成snmp.yml并自动部署到各 exporter 实例。我曾经管理过 200 台网络设备就是靠这套模板新增一个设备只需在 inventory 里加一行 IP 和 vendorCI/CD 流水线自动完成 MIB 下载、配置生成、服务重启。这才是可持续的运维。6. 超越基础SNMP 与现代可观测性的融合边界看到热搜词里有prometheus是如何从otel-collector收取数据的有人会问既然 OpenTelemetryOTel是新一代标准为什么还要折腾 SNMP答案是SNMP 不是过时的技术而是不可替代的“最后一公里”协议。OTel Collector 的优势在于统一采集、处理、导出各种语言 SDK 上报的 trace/metrics/logs但它需要在被监控系统上部署 agent 或 instrument 应用代码。而网络设备交换机、路由器、UPS、PDU、IoT 设备STM32 传感器、工业 PLC它们的操作系统是封闭的不支持安装任意 agent唯一开放的标准接口就是 SNMP。stm32 snmp trap v2c 代码这个热词正说明开发者在资源受限的 MCU 上用最小成本实现了 SNMP Agent就是为了对接像 Prometheus 这样的中心监控系统。所以SNMP 和 OTel 不是竞争关系而是互补。一个典型的混合架构是应用层用 OTel SDK 上报业务指标基础设施层Linux 服务器用 node_exporter网络层交换机用 snmp_exporter电源层UPS用 snmp_exporter 或专用 exporter如apcupsd_exporter。所有这些数据最终都汇聚到 Prometheus用同一套 PromQL 查询、告警、可视化。prometheus grafana成为事实上的“可观测性中枢”而 SNMP 是它伸向物理世界的触手。这也解释了为什么prometheus grafana安装部署、prometheus告警规则配置详解这些词热度居高不下——大家要的不是孤立的 SNMP 教程而是如何把 SNMP 数据无缝融入整个 Prometheus 生态。因此学习 SNMP 采集本质上是在学习如何让“哑设备”开口说话并用统一的语言PromQL听懂它们说的话。这不是一个技术点而是一种架构思维。当你能熟练配置 snmp_exporter能读懂 MIB能写出精准的 PromQL你就掌握了将物理世界数字化的关键一环。这比任何 Docker 一键部署的脚本都更接近监控的本质。