本期新东方的技术朋友分享了一个硬件监控的方案,使用 Telegraf 做数据采集,使用 Loki 做日志存储,使用 Nightingale 做告警规则配置,玩的挺花的,我们一起来学习一下吧。也欢迎其他技术朋友投稿给我们,一起分享有意思的监控、可观测性方案。
核心要点
- 这套方案适合物理服务器硬件健康监控,核心数据来自服务器带外 SNMP。
- Telegraf 负责采集 SNMP OID,Loki 负责保存字符串型健康状态,Nightingale 负责配置告警规则。
- 服务器厂商和型号不同,MIB 文件和 OID 节点会不同,落地前要先确认设备的健康状态节点。
- 采集结果以
OK、Normal、WARNING、CRITICAL等状态值为主,更适合按日志告警处理,而不是强行转成数值指标。 - 告警产生后可以直接发钉钉、企微,也可以进入 Flashduty 等事件中心做聚合、降噪、排班和升级。
方案概述
物理机可以通过带外开启 SNMP,通过 SNMP 获取 CPU、内存、硬盘、风扇、电源、RAID、温度等硬件模块的健康状态。Telegraf 提供了丰富的 input、output 插件,可以用 inputs.snmp 采集硬件状态信息,再用 outputs.loki 写入 Loki。
Nightingale 侧则把 Loki 作为数据源,通过兼容 Prometheus Querying 接口的方式配置告警规则,产出告警事件。后续告警可以直接发给钉钉、企微,也可以发给 Flashduty 做告警聚合降噪、排班、认领、升级等处理。

这条链路可以拆成四步:
| 环节 | 组件 | 作用 |
|---|---|---|
| 硬件状态暴露 | 带外管理 / SNMP | 从物理机读取硬件模块健康状态 |
| 数据采集 | Telegraf SNMP input | 按 OID 获取单值或表格型状态数据 |
| 数据存储 | Loki | 保存字符串型状态记录,便于后续查询 |
| 告警处理 | Nightingale / Flashduty | 配置告警规则、通知、聚合、认领和升级 |
实操步骤
1. snmp 插件准备
SNMP 监控主要通过各种 OID 节点获取对应信息,分为 get(单值)和 walk(多值)。Telegraf 的 SNMP 插件默认取单值,如果需要采集多个同类对象,可以找对应的 table 类型节点。
# 单值节点
[[inputs.snmp.field]]
name="uptime"
oid=".1.3.6.1.2.1.1.3.0"
# 多值节点,table类型
[[inputs.snmp.table]]
oid = ".1.3.6.1.2.1.31.1.1"
name = "interface"
2. OID节点查找
不同型号的物理机,OID 节点不同,需要通过对应型号的 MIB 文件查找。除了单独查找每个硬件模块的状态 OID,MIB 文件中通常还能找到服务器整体健康状态的 OID 节点,可以先取这个值作为总览。下面以浪潮服务器为例:

正常取值为"OK"、“Normal”,有报警的话取值为"WARNING"、“CRITICAL”。
采集OID=‘INSPUR-MIB::serverSystemHealthTable’(转换数字为’.1.3.6.1.4.1.37945.2.1.2.13.1’),可以先用snmpwalk命令看下取值:
#正常节点
snmpwalk -v3 1.1.1.1 INSPUR-MIB::serverSystemHealthTable
INSPUR-MIB::serverCurPowerState."" = STRING: "Power On"
INSPUR-MIB::serverUIDState."" = STRING: "UID Off"
INSPUR-MIB::serverCPUState."" = STRING: "OK"
INSPUR-MIB::serverMemoryState."" = STRING: "OK"
INSPUR-MIB::serverHDDState."" = STRING: "OK"
INSPUR-MIB::serverFANState."" = STRING: "OK"
INSPUR-MIB::serverPSUState."" = STRING: "OK"
INSPUR-MIB::serverRAIDState."" = STRING: "OK"
INSPUR-MIB::serverTempState."" = STRING: "OK"
INSPUR-MIB::serverHealthState."" = STRING: "OK"
#异常节点
snmpwalk -v3 2.2.2.2 INSPUR-MIB::serverSystemHealthTable
INSPUR-MIB::serverCurPowerState."" = STRING: "Power On"
INSPUR-MIB::serverUIDState."" = STRING: "UID Off"
INSPUR-MIB::serverCPUState."" = STRING: "OK"
INSPUR-MIB::serverMemoryState."" = STRING: "WARNING"
INSPUR-MIB::serverHDDState."" = STRING: "OK"
INSPUR-MIB::serverFANState."" = STRING: "OK"
INSPUR-MIB::serverPSUState."" = STRING: "OK"
INSPUR-MIB::serverRAIDState."" = STRING: "OK"
INSPUR-MIB::serverTempState."" = STRING: "OK"
INSPUR-MIB::serverHealthState."" = STRING: "WARNING"
INSPUR-MIB::serverCPUStandardStatus."" = STRING: "Normal"
INSPUR-MIB::serverMemoryStandardStatus."" = STRING: "Warning"
INSPUR-MIB::serverHDDStandardStatus."" = STRING: "Normal"
INSPUR-MIB::serverFANStandardStatus."" = STRING: "Normal"
INSPUR-MIB::serverPSUStandardStatus."" = STRING: "Normal"
INSPUR-MIB::serverRAIDStandardStatus."" = STRING: "Normal"
INSPUR-MIB::serverTempStandardStatus."" = STRING: "Normal"
INSPUR-MIB::serverHealthStandardStatus."" = STRING: "Warning"
通过采集状态可以看出,2.2.2.2 这台机器的 memory 已经处于报警状态。
3. telegraf配置
针对找出的OID节点,编辑telegraf配置文件:
[[inputs.snmp]]
agents = ["udp://1.1.1.1:161","udp://2.2.2.2:161"]
timeout = "20s"
version = 3
sec_name = "xxx"
auth_protocol = "MD5"
auth_password = "xxx"
sec_level = "authPriv"
priv_protocol = "DES"
priv_password = "xxx"
[[inputs.snmp.table]]
oid = "INSPUR-MIB::serverSystemHealthTable"
name = "lc_syshealth" #自定义指标名字
测试telegraf插件:
[root]# /opt/telegraf/telegraf --config ./telegraf-lctest.conf --test --input-filter snmp
2023-03-16T01:58:55Z I! Starting Telegraf 1.20.4
> lc_syshealth,agent_host=1.1.1.1,serverCurPowerState=Power\ On serverCPUState="OK",serverFANState="OK",serverHDDState="OK",serverHealthState="OK",serverMemoryState="OK",serverPSUState="OK",serverRAIDState="OK",serverTempState="OK",serverUIDState="UID Off" 1678931943000000000
> lc_syshealth,agent_host=2.2.2.2,serverCurPowerState=Power\ On serverCPUStandardStatus="Normal",serverCPUState="OK",serverFANStandardStatus="Normal",serverFANState="OK",serverHDDStandardStatus="Normal",serverHDDState="OK",serverHealthStandardStatus="Warning",serverHealthState="WARNING",serverMemoryStandardStatus="Warning",serverMemoryState="WARNING",serverPSUStandardStatus="Normal",serverPSUState="OK",serverRAIDStandardStatus="Normal",serverRAIDState="OK",serverTempStandardStatus="Normal",serverTempState="OK",serverUIDState="UID Off" 1678931955000000000
4. 信息录入和报警规则
从采集结果看,硬件健康状态多为 string 类型,不适合直接作为数值型 metrics 处理。更稳妥的方式是把结果写入 Elasticsearch 或 Loki 等日志系统,再基于日志查询配置告警。本文采用 Loki,Telegraf 的 Loki output 插件配置如下:
[[outputs.loki]]
domain = "http://xxx"
endpoint = "/loki/api/v1/push"
data_format = "json"
n9e添加loki的数据源,v6直接在页面添加数据源(系统配置-数据源)即可,v5的话则是在配置文件,v5需要同时修改webapi.conf和server.conf,下面是 webapi.conf 的配置样例:
[[Clusters]]
Name = "loki"
Prom = "http://xxx/loki"
BasicAuthUser = ""
BasicAuthPass = ""
Timeout = 30000
DialTimeout = 10000
TLSHandshakeTimeout = 30000
ExpectContinueTimeout = 1000
IdleConnTimeout = 90000
KeepAlive = 30000
MaxConnsPerHost = 0
MaxIdleConns = 100
MaxIdleConnsPerHost = 100
在n9e上配置告警规则:

将告警对接到统一的告警通道就可以展示和报警了,下图是我们的告警事件截图,当然,您也可以直接发给钉钉、企微,或者 Flashduty 等事件中心。

落地注意事项
- 先确认 MIB 和 OID:不同服务器厂商、型号、固件版本的 OID 可能不同,不要直接复制其他环境的配置上线。
- 区分整体状态和模块状态:整体健康状态适合做总告警,CPU、内存、硬盘、风扇、电源等模块状态适合帮助定位故障部件。
- 字符串状态适合日志告警:
OK、WARNING、CRITICAL这类值不必强行转换成数值指标,写入 Loki 后按状态字段查询即可。 - 告警要进入统一流程:硬件告警最好和业务告警统一进入事件中心,避免短信、群消息、电话各自为政。
- SNMP v3 参数要妥善管理:示例里的认证、加密协议和密码需要按企业安全规范管理,不建议散落在多人可见的配置中。
FAQ
Q1:为什么不用 Prometheus 直接采集这些硬件状态?
A:可以采集,但本文场景下状态值主要是字符串,例如 OK、WARNING、CRITICAL。写入 Loki 后用日志查询做告警,更贴合这种数据形态。
Q2:只采集服务器整体健康状态够不够? A:整体健康状态适合快速发现问题,但故障定位还需要模块级状态。建议整体状态做总入口,模块状态用于判断是内存、硬盘、风扇、电源还是 RAID 异常。
Q3:这套方案适合哪些团队? A:适合仍然管理较多物理服务器、需要硬件带外监控,并且已经有 Nightingale、Loki 或统一告警流程的团队。
