Telegraf 最佳实践:SNMP 插件

导航至

Telegraf 现已达到 300 多个插件,并广泛应用于各种用例。在一月份,我们发布了一篇博客文章,介绍了创建配置和优化 Telegraf 代理的黄金法则。现在是时候深入探讨社区最常用的插件之一了。

在这篇文章中,我们将介绍 SNMP 输入插件。我与我们的 Telegraf 维护者之一 Thomas Casteleyn(《Hipska》Hipska)坐下来讨论了他使用 SNMP 输入插件的最佳实践。Thomas 是我们中最活跃的 SNMP 插件用户之一,为他的客户收集了数千台设备和系统的指标。在他的帮助下,我的目标是为您提供一些有用的技巧和窍门,让您从零开始成为英雄!

SNMP-plugin

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 的插件

  1. SNMP - 使用轮询从 SNMP 代理收集指标。它支持收集单个 OID 和整个 SNMP 表。
  2. 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配置可以在这里找到。

让我们分解一下

  1. agents - 您希望监控的SNMP代理(启用SNMP的设备)列表。在大多数情况下,您的代理端口将是161。
  2. timeout - 我将这个参数用粗体突出显示,因为我们稍后将会讨论它。该参数告诉Telegraf等待代理回复的时间长度。
  3. version - 您的代理正在运行的SNMP版本。大多数Windows代理将默认运行版本2(2c)。
  4. community - 实际上是SNMP代理的密码。在Windows中,您可以定义SNMP社区字符串。请注意,当您没有使用正确的社区字符串时,Telegraf将无法从代理处接收任何响应。
  5. 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表中发现的字段可以有多种用途。其中一些随着时间的推移是很有用的。其他字段除非手动更新或对系统进行重大配置(例如,向设备添加更多存储),否则将保持静态/线性;即添加更多存储到设备。是否决定完全删除这些字段或将其作为过滤目的的标签,取决于您。

一个很好的例子是 ifAdminStatusifOperStatus

  • ifAdminStatus - 接口的期望状态。在大多数情况下,除非重新配置,否则不会改变(元数据)。
  • ifOperStatus - 接口当前的操作状态。这是一个值得关注的字段,因为它将提供代理接口的当前状态(指标)。

处理器插件

现在我们已经很好地掌握了配置我们的SNMP输入插件,是时候分享一些我们收集的指标的巧妙后处理了。以下是一些配置片段,有助于提高可读性和值转换

接口映射:

我们之前讨论了有用的元数据,如ifAdminStatus。IfAdminStatus提供了接口的期望状态。通常,接口可以放置在三种主要状态之一

  1. 开启
  2. 关闭
  3. 测试

然而,当我们请求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。在这个表中,有三个关键字段值得我们关注:

  1. hrStorageAllocationUnits - 从此池分配的数据对象的字节大小。
  2. hrStorageSize - 存储大小由这个条目表示,单位是hrStorageAllocationUnits。
  3. hrStorageUsed - 总共使用的存储量。单位为hrStorageAllocationUnits。

如您所见,hrStorageSizehrStorageUsedhrStorageAllocationUnits中存储的值直接相关。我们可以利用这一点来推导每个字段的字节数总和。

[[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插件一起工作时,以下是一些需要注意的事项:

  1. 在适用的情况下,使用字段而非表格
  2. 每个插件一个SNMP代理
  3. 插件超时可能会因为慢速代理而出现问题
  4. 某些SNMP字段作为元数据标签比作为度量字段更好

我们还讨论了一些优秀的处理器插件示例,您可以使用它们开始塑造您的SNMP数据。

通过免费参加InfluxDB大学使用Telegraf收集数据课程来了解更多关于收集数据的信息。