Telegraf 最佳实践:SNMP 插件

导航至

Telegraf 现在已拥有 300 多个插件,并部署在各种用例中。一月份,我们发布了一篇博客文章,介绍了创建配置和优化 Telegraf Agent 的黄金法则。现在是时候深入了解社区最常用的一些插件了。

在这篇文章中,我们将介绍 SNMP 输入插件。我与我们的 Telegraf 维护者之一 Thomas Casteleyn (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 Agent 收集指标。它支持收集单个 OID 和整个 SNMP 表。
  2. SNMP Trap - 服务输入插件,用于接收 traps 和 informs 请求。Samantha Wang 发布了一篇很棒的博客,帮助您开始使用 SNMP Trap 插件。我强烈建议您查看一下,因为我们将介绍 SNMP 插件。

基础知识

让我们从介绍一些基础知识开始。在本例中,我们将使用 Telegraf 从 Windows 10 VM 收集指标

[[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 Agent(启用 SNMP 的设备)列表。在大多数情况下,您的 Agent 端口将为 161。
  2. timeout - 我用粗体突出显示它,因为我们稍后会讨论它。此参数告诉 Telegraf 等待 Agent 回复的时间。
  3. version - 您的 Agent 正在运行的 SNMP 版本。大多数 Windows Agent 默认运行版本 2 (2c)。
  4. community - 本质上是 SNMP Agent 的密码。在 Windows 中,您可以定义 SNMP 团体字符串。请注意,如果不使用正确的团体字符串,Telegraf 将无法收到来自 Agent 的任何响应。
  5. retries - 如果请求超时,Telegraf 尝试轮询 Agent 的次数。

现在我们已经设置了初始 SNMP 插件参数,让我们开始从我们的 Agent 请求一些指标。在我们的例子中,我们想知道以下内容

  • Agent 的运行时间
  • Agent 的名称
  • Agent 的存储指标

要获取这些指标,一种方法是直接访问包含值的字段,如下所示

[[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 允许我们将返回的值转换为指标标签。运行时间字段的转换是因为运行时间以百分之一秒为单位报告。

我们可用的第二种方法是引用包含我们希望收集的字段的表

[[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。您应该确保适当增加您的 timeout 参数。

分而治之

值得为您监控的每个设备创建一个 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 插件的逻辑中,只有当所有源都返回回复后,才会处理指标。本质上,在这种情况下,您的速度与最慢的 Agent 一样慢。这也意味着,如果一个 Agent 未能在给定的时间内发送回回复,SNMP 插件将进入重试循环(取决于您的配置),这将延迟下一轮收集(对于同一 SNMP 插件的所有 Agent),直到重试循环完成。

分离成单独的插件将 Agent 彼此解耦。这允许首先处理响应时间更快的 Agent,并提高解决方案的整体持久性。由于 Telegraf 的占用空间很小,创建更多插件不会显着增加系统上 Agent 的开销。

有用的元数据

当从多个设备订阅等效字段和表时,能够区分指标以进行进一步处理/查询非常重要。以下是一些您可以包含的重要标签

主要

  • 系统名称 (SNMPv2-MIB::sysName.0)
  • 主机名

可选

  • 位置 (SNMPv2-MIB::sysLocation.0)
  • 技术提供商 / 类型

指标与元数据

在 SNMP 表中找到的字段可以有多种用途。有些字段适合长期监控。其他字段将保持静态/线性,除非手动更新或系统发生重大配置;例如,向设备添加更多存储。这取决于您是否决定完全删除这些字段,还是将它们作为标签包含在内以进行过滤。

这方面的一个很好的例子是 ifAdminStatusifOperStatus

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

处理器插件

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

接口映射: 

上面我们讨论了有用的元数据,如 ifAdminStatus。IfAdminStatus 提供接口的期望状态。一般来说,接口可以置于三种主要状态

  1. 启用
  2. 禁用
  3. 测试中

但是,当我们请求 ifAdminStatus 值时,我们收到的是数值,而不是人类可读的文本。要更改这一点,我们可以使用 Enum 处理器插件

[[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 = “up”)。请注意,如果值不在映射列表中,则标签将使用原始值进行处理。

注意: 这仅在使用已弃用的 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 为单位。

如您所见,hrStorageSizehrStorageUsed 直接与存储在 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 插件时要记住的事项

  1. 在有意义的情况下,使用字段而不是表
  2. 每个插件一个 SNMP Agent
  3. 插件超时可能会因 Agent 速度慢而使您陷入困境
  4. 某些 SNMP 字段非常适合作为元数据标签,而不是指标字段

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

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