Telegraf 最佳实践:SNMP 插件
作者:Jay Clifford / 用例,产品,开发者
2022年6月1日
导航至
Telegraf 现已达到 300 多个插件,并广泛应用于各种用例。在一月份,我们发布了一篇博客文章,介绍了创建配置和优化 Telegraf 代理的黄金法则。现在是时候深入探讨社区最常用的插件之一了。
在这篇文章中,我们将介绍 SNMP 输入插件。我与我们的 Telegraf 维护者之一 Thomas Casteleyn(《Hipska》Hipska)坐下来讨论了他使用 SNMP 输入插件的最佳实践。Thomas 是我们中最活跃的 SNMP 插件用户之一,为他的客户收集了数千台设备和系统的指标。在他的帮助下,我的目标是为您提供一些有用的技巧和窍门,让您从零开始成为英雄!
SNMP 输入插件是什么?
如果您正在阅读这篇博客,您可能已经知道 SNMP 是什么,但让我们为每个人澄清一下。以下是一些事实
- SNMP 代表简单网络管理协议(Simple Network Management Protocol)。
- 自 1988 年以来一直存在(它经受了时间的考验,不会很快消失)。
- 该协议是为了满足监控和管理 IP 设备的需求而引入的。
- 例如设备:路由器、交换机、防火墙、负载均衡器、服务器和闭路电视摄像头等。
- SNMP 有三个版本(1、2c & 3)。了解这一点很重要,因为根据制造商和设备年龄的不同,您可能会有混合版本。
- V1 和 2c 使用社区字符串。V3 需要用户名和密码。V3 还提供了加密功能。
- 端口:SNMPv3 161、162(trap)| SNMPv3 安全 10161、10162(trap)
如果您想了解更多关于 SNMP 的信息,我强烈推荐您观看 CertBros 的这个视频。
在 Telegraf 中,我们有两个基于 SNMP 的插件
- SNMP - 使用轮询从 SNMP 代理收集指标。它支持收集单个 OID 和整个 SNMP 表。
- SNMP Trap - 服务输入插件,用于接收陷阱和通知请求。由Samantha Wang发布了一篇精彩的博客,介绍了如何开始使用SNMP Trap插件。我强烈推荐您查看,因为我们将会介绍SNMP插件。
基础知识
让我们先从一些基础知识开始。在这个例子中,我们将使用Telegraf从Windows 10虚拟机收集指标。
[[inputs.snmp]]
## Agent addresses to retrieve values from.
agents = ["udp://127.0.0.1:161"]
## Timeout for each request.
timeout = "15s"
## SNMP version; can be 1, 2, or 3.
version = 2
## SNMP community string.
community = "public"
## Number of retries to attempt.
retries = 1
[[inputs.snmp.field]]
oid = "SNMPv2-MIB::sysUpTime.0"
name = "uptime"
conversion = "float(2)"
[[inputs.snmp.field]]
oid = "SNMPv2-MIB::sysName.0"
name = "source"
is_tag = true
[[inputs.snmp.table]]
oid = "HOST-RESOURCES-MIB::hrStorageTable"
name = "hrStorageTable"
inherit_tags = ["source"]
完整的Telegraf配置可以在这里找到。
让我们分解一下
- agents - 您希望监控的SNMP代理(启用SNMP的设备)列表。在大多数情况下,您的代理端口将是161。
- timeout - 我将这个参数用粗体突出显示,因为我们稍后将会讨论它。该参数告诉Telegraf等待代理回复的时间长度。
- version - 您的代理正在运行的SNMP版本。大多数Windows代理将默认运行版本2(2c)。
- community - 实际上是SNMP代理的密码。在Windows中,您可以定义SNMP社区字符串。请注意,当您没有使用正确的社区字符串时,Telegraf将无法从代理处接收任何响应。
- retries - 当请求超时时,Telegraf将尝试轮询代理的次数。
现在我们已经设置了初始的SNMP插件参数,让我们开始从我们的代理请求一些指标。在我们的案例中,我们想知道以下内容
- 代理的运行时间
- 代理的名称
- 代理的存储指标
为了获取这些指标,一种方法是可以直接访问包含值的字段,如下所示
[[inputs.snmp.field]]
oid = "SNMPv2-MIB::sysUpTime.0"
name = "uptime"
conversion = "float(2)"
[[inputs.snmp.field]]
oid = "SNMPv2-MIB::sysName.0"
name = "source"
is_tag = true
在这个例子中,我们直接引用了系统和系统名称的OID(对象标识符)。请注意,is_tag允许我们将返回的值转换为标签。 uptime字段的转换是因为运行时间以百秒为单位报告。
我们可以使用的第二种方法是引用包含我们希望收集的字段的表
[[inputs.snmp.table]]
oid = "HOST-RESOURCES-MIB::hrStorageTable"
name = "hrStorageTable"
inherit_tags = ["source"]
与直接字段引用类似,我们指定表的OID。然后我们可以提供一个测量名称给表。最后,inherit_tags允许我们的表采用其他字段的标签。使用一次系统名称是有意义的,其命名为“source”。
最佳实践、技巧与窍门
好的,我们现在知道了什么是SNMP,并使用Telegraf的SNMP插件设置了一个基本示例。这些信息足够我们行事,但在规模上是否可行?Thomas给了我一些很好的规则,当构建我的Telegraf SNMP配置时可以遵循。在下一节中,我们将它们分解成您可以直接在SNMP插件上采取的操作,然后在使用处理器插件后对指标采取的操作。
SNMP插件
字段与表
尽可能直接指定字段,而不是表。在SNMP表中可能存在任意字段(如元数据),这些字段在每个间隔中不需要监控。这可能会使请求一般较慢并返回结果。
在某些情况下,提取整个表是有意义的,可以节省时间和提高配置的可读性。值得注意的是,某些设备可能需要长达1分钟的时间才能将大SNMP表返回给Telegraf。您应该确保适当地增加超时参数。
分而治之
为每个您正在监控的设备创建一个SNMP输入插件,而不是将它们组合到一个插件中,这是值得的。
例如
# Local Device
[[inputs.snmp]]
## Agent addresses to retrieve values from.
agents = ["udp://127.0.0.1:161"]
## Timeout for each request.
timeout = "5s"
[[inputs.snmp.table]]
oid = "HOST-RESOURCES-MIB::hrStorageTable"
name = "hrStorageTable"
inherit_tags = ["source"]
# Remote device known to be slow
[[inputs.snmp]]
## Agent addresses to retrieve values from.
agents = ["udp://192.168.1.5:161"]
## Timeout for each request.
timeout = "60s"
[[inputs.snmp.table]]
oid = "HOST-RESOURCES-MIB::hrStorageTable"
name = "hrStorageTable"
inherit_tags = ["source"]
在SNMP插件的逻辑中,只有在所有源都返回响应后,才会处理指标。本质上,在这种情况下,你的速度取决于最慢的代理。这也意味着,如果某个代理在给定时间内未能发送回响应,SNMP插件将进入重试周期(取决于您的配置),这会延迟下一轮收集(对于同一SNMP插件的所有代理)直到重试周期完成。
将它们分离成单独的插件可以将代理彼此解耦。这使得响应时间较快的代理可以先被处理,同时也提高了您解决方案的整体耐用性。由于Telegraf的占用空间很小,创建更多的插件不会显著增加您系统上的代理开销。
有用的元数据
当从多个设备订阅等效字段和表时,能够区分指标以进行进一步处理/查询很重要。以下是一些您可以包含的重要标签
主要
- 系统名称(SNMPv2-MIB::sysName.0)
- 主机名
可选
- 位置(SNMPv2-MIB::sysLocation.0)
- 技术提供商/类型
指标与元数据
在SNMP表中发现的字段可以有多种用途。其中一些随着时间的推移是很有用的。其他字段除非手动更新或对系统进行重大配置(例如,向设备添加更多存储),否则将保持静态/线性;即添加更多存储到设备。是否决定完全删除这些字段或将其作为过滤目的的标签,取决于您。
一个很好的例子是 ifAdminStatus 与 ifOperStatus
- ifAdminStatus - 接口的期望状态。在大多数情况下,除非重新配置,否则不会改变(元数据)。
- ifOperStatus - 接口当前的操作状态。这是一个值得关注的字段,因为它将提供代理接口的当前状态(指标)。
处理器插件
现在我们已经很好地掌握了配置我们的SNMP输入插件,是时候分享一些我们收集的指标的巧妙后处理了。以下是一些配置片段,有助于提高可读性和值转换
接口映射:
我们之前讨论了有用的元数据,如ifAdminStatus。IfAdminStatus提供了接口的期望状态。通常,接口可以放置在三种主要状态之一
- 开启
- 关闭
- 测试
然而,当我们请求ifAdminStatus值时,我们接收到的数值而不是可读的文本。为了改变这一点,我们可以使用枚举处理器插件:
[[inputs.snmp]]
## Agent addresses to retrieve values from.
agents = ["udp://127.0.0.1:161"]
[[inputs.snmp.field]]
oid = "SNMPv2-MIB::sysName.0"
name = "source"
is_tag = true
[[inputs.snmp.table]]
oid = "IF-MIB::ifTable"
name = "interface"
inherit_tags = ["source"]
[[inputs.snmp.table.field]]
oid = "IF-MIB::ifAdminStatus"
is_tag = true
# Translate tag values for interface table
[[processors.enum]]
namepass = ["interface"]
# Translate IF-MIB::ifAdminStatus
[[processors.enum.mapping]]
## Name of the tag to map
tag = "ifAdminStatus"
## Table of mappings
[processors.enum.mapping.value_mappings]
1 = "up"
2 = "down"
3 = "testing"
完整的Telegraf配置可以在这里找到。
如您所见,我们使用namepass仅处理名为interface的测量。我们寻找我们的IfAdminStatus标签,然后提供新的映射到包含的值(1 = “开启”)。请注意,如果值不在映射列表中,则标签将使用原始值处理。
注意:这仅在使用已弃用的netsnmp转换器时需要。当使用较新且更快的gosmi转换器时,可以通过以下配置实现
[[inputs.snmp]]
## Agent addresses to retrieve values from.
agents = ["udp://127.0.0.1:161"]
[[inputs.snmp.field]]
oid = "SNMPv2-MIB::sysName.0"
name = "source"
is_tag = true
[[inputs.snmp.table]]
oid = "IF-MIB::ifTable"
name = "interface"
inherit_tags = ["source"]
[[inputs.snmp.table.field]]
oid = "IF-MIB::ifAdminStatus"
is_tag = true
conversion = "enum"
存储解析:
最后,让我们讨论如何使用Starlark处理器插件来推导SNMP字段的替代值。一个很好的例子是监控hrStorageTable。在这个表中,有三个关键字段值得我们关注:
- hrStorageAllocationUnits - 从此池分配的数据对象的字节大小。
- hrStorageSize - 存储大小由这个条目表示,单位是hrStorageAllocationUnits。
- hrStorageUsed - 总共使用的存储量。单位为hrStorageAllocationUnits。
如您所见,hrStorageSize和hrStorageUsed与hrStorageAllocationUnits中存储的值直接相关。我们可以利用这一点来推导每个字段的字节数总和。
[[inputs.snmp]]
## Agent addresses to retrieve values from.
agents = ["udp://127.0.0.1:161"]
[[inputs.snmp.field]]
oid = "SNMPv2-MIB::sysUpTime.0"
name = "uptime"
conversion = "float(2)"
[[inputs.snmp.field]]
oid = "SNMPv2-MIB::sysName.0"
name = "source"
is_tag = true
[[inputs.snmp.table]]
oid = "HOST-RESOURCES-MIB::hrStorageTable"
name = "hrStorageTable"
inherit_tags = ["source"]
# Parse HOST-RESOURCES-MIB::hrStorageAllocationUnits
[[processors.starlark]]
alias = "hrStorageTable_processor"
namepass = ["hrStorageTable"]
## Source of the Starlark script.
source = '''
def apply(metric):
units = metric.fields.pop("hrStorageAllocationUnits", 0)
if units and "hrStorageSize" in metric.fields:
metric.fields["hrStorageSize"] *= units
if units and "hrStorageUsed" in metric.fields:
metric.fields["hrStorageUsed"] *= units
return metric
'''
完整的Telegraf配置可以在这里找到。
总结
因此,总结一下,当与SNMP插件一起工作时,以下是一些需要注意的事项:
- 在适用的情况下,使用字段而非表格
- 每个插件一个SNMP代理
- 插件超时可能会因为慢速代理而出现问题
- 某些SNMP字段作为元数据标签比作为度量字段更好
我们还讨论了一些优秀的处理器插件示例,您可以使用它们开始塑造您的SNMP数据。
通过免费参加InfluxDB大学使用Telegraf收集数据课程来了解更多关于收集数据的信息。