Telegraf 最佳实践:配置建议和性能监控
作者:Jay Clifford / 用例, 产品, 开发者
2022年1月18日
导航至
Telegraf 已经达到了成熟的 V1.21.2 版本。感谢社区的反馈和贡献,多年来添加了许多功能。最近,我看到以下问题弹出:
- 如何监控我的 Telegraf 实例的性能?
- 我可以更改哪些配置参数来减少内存消耗?
- 如何维护包含数百个插件的配置文件?
- 如何提高调试的可读性并测试新的配置?
如果这些问题困扰着您,请不要害怕——这篇博客将为您提供帮助!以下是我在构建 Telegraf 解决方案时保持最佳实践的黄金法则。
维护大型且复杂的配置文件
在许多用例中,Telegraf 被部署用于从多个输入源摄取数据,并将数据传递到 InfluxDB 或其他企业平台(如下例所示)。
那么我们如何维护和面向未来地配置 Telegraf 呢?
分而治之
我的第一个黄金法则是将您的配置拆分成可管理的小块。这如何实现?Telegraf 提供了将配置文件添加到 telegraf.d 目录的功能。该目录通常位于
- Linux: /etc/telegraf/telegraf.d/
- Windows: C:\Program Files\Telegraf\telegraf.d (您在服务安装时设置此目录)
- Mac: /usr/local/etc/telegraf.d
注意:您还可以使用 –config-directory 标志来定义您自己的目录位置以存储 Telegraf 配置。所有以 .conf 结尾的文件都被视为 Telegraf 配置。
这样做允许您将大型配置的一部分保留在多个较小的配置中。例如:
- 创建三个配置文件,分别名为:inputs、processors 和 outputs。添加您的插件以匹配相应的配置。
- 为每个使用场景创建一个配置文件。这是我个人在使用 Telegraf 时的偏好。
这就是我将上述图表划分为配置文件的方式:
telegraf.conf |
全局参数 |
opc_ua_machines.conf | modbus_sensors.conf | system_performance.conf |
输入: opc_ua 客户端 x2 输出: influxdb x1,云连接器 x1 | 输入: modbus x1 输出:influxdb x1,云连接器 x1 | 输入: internal x1,cpu x1 等。输出: influxdb x1 |
优点
- 此结构可以扩展到数百个插件,同时保持在一个 Telegraf 实例下运行的场景的可读性和可维护性。
缺点
- 管理员必须记住,Telegraf 不区分配置文件(它们作为一个文件运行)。路由仍然必须在每个配置文件中考虑(我们将在博客文章的后面部分讨论这一点)。
命名插件
让我们从简单的开始,这将为您节省未来的调试时间:命名您的插件。我的第二个黄金法则是为您的每个插件提供描述性的别名。一个例子如下所示:
[[inputs.file]]
alias = "file-2"
files = ["/data/sample.json"]
data_format = "json"
这将在检查 Telegraf 日志时提供更多上下文,使您可以找出有问题的插件。
没有别名
2021-11-29T17:11:04Z E! [inputs.file] 插件错误:找不到文件:/data/sample.json
有别名
2021-11-29T17:11:04Z E! [inputs.file::file-2] 插件错误:找不到文件:/data/sample.json
标签,标签,标签!
在我们深入研究标签的有用性之前,让我们快速浏览一下线路协议的数据结构,以及为什么在使用 Telegraf 时理解其结构很重要。线路协议由 4 个关键组件组成:
- 指标名称: 指标的描述和命名空间
- 标签: 键/值字符串对,通常用于标识指标
- 字段: 键/值对,具有类型,通常包含指标数据
- 时间戳:与字段关联的日期和时间
假设我们正在摄取此 JSON 消息并覆盖插件的指标名称为:
[
{
"Factory" :"US_Factory",
"Machine" :"Chocolate_conveyor",
"Temperature" : 30,
"Speed" : 0.6
},
{
"Factory" :"US_Factory",
"Machine" :"hard_candy_conveyor",
"Temperature" : 40,
"Speed" : 0.4
},
{
"Factory" :"UK_Factory",
"Machine" :"Chocolate_conveyor",
"Temperature" : 28,
"Speed" : 0.5
}
]
以下是当前在线路协议中的结果:
factory_data Factory="US_Factory",Machine="Chocolate_conveyor",Temperature=30,Speed=0.6 1638975030000000000
factory_data Factory="US_Factory",Machine="hard_candy_conveyor",Temperature=40,Speed=0.4 1638975030000000000
factory_data Factory="UK_Factory",Machine="Chocolate_conveyor",Temperature=28,Speed=0.5 1638975030000000000
休斯顿,我想我们遇到问题了(引用阿波罗 13 号宇航员在发现问题时的无线电通讯)。由于我们没有定义标签,因此我们的每个 JSON 属性都已成为一个字段。在某些情况下,这可能不是问题。但在这种情况下,这是一个大问题!想象一下,如果我们将这些指标写入 InfluxDB,由于每个指标共享相同的时间戳,我们将基本上用下一个值覆盖我们的数据。这就是在 Telegraf 管道中尽早分配标签的重要性所在。让我们现在定义这些标签:
factory_data
Factory="US_Factory",Machine="Chocolate_conveyor" Temperature=30,Speed=0.6 1638975030000000000
Factory_data Factory="US_Factory",Machine="hard_candy_conveyor" Temperature=40,Speed=0.4 1638975030000000000
factory_data Factory="UK_Factory",Machine="Chocolate_conveyor" Temperature=28,Speed=0.5 1638975030000000000
如您所见,标签为我们的指标提供了可搜索和可隔离的定义。它是 InfluxDB 中使用的一个极其重要的数据功能。除此之外,Telegraf 还可以利用标签作为许多高级功能的一部分,例如路由,我们将在接下来讨论。
路由插件
我的下一个黄金法则是始终掌控您的数据去向。对于简单的 Telegraf 配置,这实际上不是问题,因为通常您是从一个或多个输入源收集数据,并将数据发送到像 InfluxDB 这样的输出插件。但是,让我们考虑上图所示的示例。我如何确保我的 OPC-UA 数据仅包含在 InfluxDB 的机器存储桶中,而我的 Modbus 数据包含在传感器存储桶中。这就是路由发挥作用的地方。
需要明确的是,路由 = 官方 Telegraf 术语中的过滤。我们基本上是在指标被插件处理之前根据使用的规则删除指标
- namepass:glob 模式字符串数组。仅当指标的指标名称与此列表中的模式匹配时,才会发出该指标。
- namedrop:namepass 的反向操作。如果找到匹配项,则丢弃指标。这在指标通过 namepass 测试后进行测试。
让我们看一下这个例子:
[[outputs.influxdb_v2]]
namepass = ["test"]
alias = "k8"
urls = ["${INFLUX_HOST}"]
token = "${INFLUX_TOKEN}"
organization = "${INFLUX_ORG}"
bucket = "bucket1"
[[outputs.influxdb_v2]]
namedrop = ["test"]
alias = "k8-2"
urls = ["${INFLUX_HOST}"]
token = "${INFLUX_TOKEN}"
organization = "${INFLUX_ORG}"
bucket = "bucket2"
[[inputs.file]]
alias = "file-1"
files = ["./data/sample.json"]
data_format = "json"
tag_keys = ["Driver", "Channel", "Trace", "Notes", "Address", "Instrument"]
name_override = "test"
[[inputs.file]]
alias = "file-2"
files = ["./data/sample.json"]
data_format = "json"
tag_keys = ["Driver", "Channel", "Trace", "Notes", "Address", "Instrument"]
name_override = "test2"
[[inputs.file]]
alias = "file-3"
files = ["./data/sample.json"]
data_format = "json"
tag_keys = ["Driver", "Channel", "Trace", "Notes", "Address", "Instrument"]
name_override = "test3"
分解
- 有三个文件输入插件。每个指标名称都已使用 name_override 覆盖(test,test2,test3)。
- 我们有两个 InfluxDB 输出插件,每个插件都指向不同的存储桶(bucket1,bucket2)。
- 在 bucket1 中,我们只想接受来自 “test” 的指标。在这种情况下,我们使用 namepass。这将删除所有其他指标(test2 和 test3),并且仅将 “test” 写入 InfluxDB。
- 在 bucket2 中,我们将存储除 “test” 之外的所有其他指标,因为我们已经将其存储在 bucket1 中。我们可以使用 namedrop 在 “test” 被 InfluxDB 插件处理之前将其删除。其余指标照常接受。
注意:值得一提的是,在本博客中,我们仅讨论了基于指标名称删除指标。标签也有等效的功能(tagpass,tagdrop)。在此处查找更多信息:此处。
InfluxDB 存储桶按标签路由
因此,目前为了在存储桶之间路由数据,我们为每个存储桶生成一个输出插件。如果所有数据都最终进入同一个 InfluxDB 实例,则这不是特别有效。在存储桶之间路由数据的另一种方法是使用 bucket_tag。让我们看一下配置:
[[outputs.influxdb_v2]]
alias = "k8"
urls = ["${INFLUX_HOST}"]
token = "${INFLUX_TOKEN}"
organization = "${INFLUX_ORG}"
bucket_tag = "bucket"
[[inputs.file]]
alias = "file-1"
files = ["./data/sample.json"]
data_format = "json"
tag_keys = ["Driver", "Channel", "Trace", "Notes", "Address", "Instrument"]
name_override = "test"
[inputs.file.tags]
bucket = "bucket1"
[[inputs.file]]
alias = "file-2"
files = ["./data/sample.json"]
data_format = "json"
tag_keys = ["Driver", "Channel", "Trace", "Notes", "Address", "Instrument"]
name_override = "test2"
[inputs.file.tags]
bucket = "bucket2"
如您所见,对于每个输入文件,我们都定义了一个标签(这是完全可选的,因为您可以使用已定义的标签之一。但是,标签值应等于您的存储桶名称之一)。在我们的 InfluxDB V2 输出插件中,我们没有显式定义存储桶,而是将我们的标签添加到 bucket_tag。此标签的值用于与存储桶名称匹配。
敏感配置参数
一个非常重要的安全功能是考虑在配置中存储凭据信息。如果我告诉一位安全专家我打算将他们企业系统的安全令牌存储在一个配置文件中,我想他们可能会直接在我面前崩溃。缓解这种情况的最佳方法之一是将环境变量用作最佳实践的一部分。这为应用安全最佳实践提供了更多范围,但也为您的 Telegraf 配置提供了更大的灵活性。
这是一个例子:
[[outputs.influxdb_v2]]
alias = "k8"
urls = ["${INFLUX_HOST}"]
token = "${INFLUX_TOKEN}"
organization = "${INFLUX_ORG}"
bucket = "${INFLUX_BUCKET}"
在上面的示例中,连接到我们的 InfluxDB 实例的每个参数都已通过环境变量匿名化。请注意,您还可以部署某种形式的脚本日志,用于被动地更改这些参数,并根据所需的系统状态更改自动重启 Telegraf 服务。
新配置调试
在按下 Telegraf 配置的 “go” 按钮之前感到不安?不用担心——从自动化工作的日子起,我也完全一样。幸运的是,Telegraf 有一些技巧,我们可以在投入生产之前部署这些技巧。
测试标志
测试标志提供了两个绝佳的机会:
- 对您的配置文件进行健全性检查(TOML 解析错误、连接错误等)。
- 它还可以深入了解最终数据结构的外观,这基于您的管道设计。
telegraf --test --config ./file_parsing/telegraf-json-v1.conf
运行上述命令将根据我的 管道 产生以下输出:
> test,Address=femto.febo.com:1298,Channel=1/1,Driver=Symmetricom\ 5115A,Instrument=TSC-5120A,Trace=HP5065A\ vs.\ BVA,host=Jays-MBP Bin\ Density=29,Bin\ Threshold=4,Data\ Type=0,Duration=86400000000,Duration\ Type=5,Input\ Freq=5000000,MJD=56026.95680392361,Sample\ Interval=0.1,Sample\ Rate=1000,Scale\ Factor=1,Stop\ Condition=0,Trace\ History=1 1638543044000000000
> test-list-2,host=Jays-MBP angle_0=0.35431448844127544,angle_1=0.09063859006636221,odometer_0=9.9,odometer_1=9.1,odometer_2=9.5,updated=1634607913.044073,x=566753.985858001,y=566753.9858582001 1638543044000000000
调试标志
运行测试标志后,您有两个选择:直接运行 Telegraf 服务,或使用调试标志运行。使用调试标志会公开管道中每个插件的扩展日志记录。一个很好的用例是尝试了解连接/解析错误。请注意,这就是别名生效的地方。
telegraf --debug --config ./file_parsing/telegraf-json-v1.conf
我们的日志如下所示:
2021-12-08T13:24:13Z I! Starting Telegraf 1.20.3
2021-12-08T13:24:13Z I! Loaded inputs: file (3x)
2021-12-08T13:24:13Z I! Loaded aggregators:
2021-12-08T13:24:13Z I! Loaded processors:
2021-12-08T13:24:13Z I! Loaded outputs: influxdb_v2
2021-12-08T13:24:13Z I! Tags enabled: host=Jays-MacBook-Pro.local
2021-12-08T13:24:13Z I! [agent] Config: Interval:5s, Quiet:false, Hostname:"Jays-MacBook-Pro.local", Flush Interval:10s
2021-12-08T13:24:13Z D! [agent] Initializing plugins
2021-12-08T13:24:13Z D! [agent] Connecting outputs
2021-12-08T13:24:13Z D! [agent] Attempting connection to [outputs.influxdb_v2::k8]
2021-12-08T13:24:13Z D! [agent] Successfully connected to outputs.influxdb_v2::k8
2021-12-08T13:24:13Z D! [agent] Starting service inputs
2021-12-08T13:24:28Z D! [outputs.influxdb_v2::k8] Wrote batch of 9 metrics in 28.325372ms
2021-12-08T13:24:28Z D! [outputs.influxdb_v2::k8] Buffer fullness: 0 / 10000 metrics
此外,我们可以与 –debug 一起定义 –once 标志。–once 将触发 Telegraf 代理运行完整管道一次且仅一次。–test 和 –once 标志之间的主要区别在于,–test 将不会将收集的指标传递到输出端点。以下是测试标志的示例:
telegraf --once --config ./file_parsing/telegraf-json-v1.conf
提高 Telegraf 的效率
因此,我们花了一些时间根据上述黄金法则构建配置。我们启动了 Telegraf 服务,开始摄取和输出指标。下一步是优化 Telegraf 在其运行主机上的整体性能。
对于每个用例,这将有所不同,但我希望提供的是一份步骤清单,这将使您能够对要更改的设置做出明智的判断。
监控 Telegraf
我坚信 “吃自己的狗粮” 这句话。如果 Telegraf 旨在从其他平台抓取和监控指标,那么它也应该能够监控自身。我们可以使用 Telegraf Internal Input Plugin 来做到这一点。
让我们按照我们的最佳实践为其创建一个配置:
- 创建一个名为 telegraf-internal-monitor.conf 的 conf 文件。
- 添加 Telegraf Internal 插件和 InfluxDB 输出插件(记住应用别名和路由)。在此处查找示例。 注意:我选择为此插件启用 memstats。这主要是因为某些模板仪表板节点期望来自 memstats 的指标数据。值得注意的是,此功能主要与调试目的相关,不需要始终启用。
- 感谢 Steven Soroka,有一个 InfluxDB 模板用于仪表盘显示我们正在收集的新指标。在此处查找该模板。
我们现在有了一个看起来像这样的仪表板:
清单
因此,我们已经勾选了列表中的第一点(获得对当前 Telegraf 代理性能的可见性)。那么接下来是什么?好吧,让我们看一下清单,然后解释每个项目背后的科学/疯狂之处。
项目 | 系统影响 |
代理 | |
您的时间间隔是否适合您正在收集的数据? | CPU / 内存 / 带宽 |
您可以为每个输入插件更精细地设置时间间隔吗? | CPU / 内存 |
与您的收集间隔相比,您的刷新间隔是否有意义? | CPU / 延迟 |
您是否考虑过每个批次发送的指标数量? | 网络 / CPU |
您是否有范围减小缓冲区大小?检查以下仪表板单元格:
|
内存 |
您是否定义了收集抖动? | CPU / 内存 |
您是否定义了刷新抖动? | 网络 / CPU |
时间间隔
简而言之,interval 参数定义了 Telegraf 代理何时将触发从每个输入插件收集指标。因此,一般来说,我们应该意识到何时以及为何从特定输入收集数据。例如,如果我们的端点每小时仅更新一次,那么每 10 秒抓取端点就没有意义了。
我的黄金法则是根据端点行为为每个输入插件显式指定收集间隔。问问自己:我需要多久监控一次来自此端点的新数据?
您还可以选择定义多个 Telegraf 实例。我们将在 “额外学分” 部分中讨论这一点。
刷新间隔
与此并行,您还必须考虑您的刷新间隔。刷新间隔只是设置一个触发时间,用于何时将缓冲区中存储的所有指标写入其输出端点。以下是一些需要考虑的点:
- 如果您按小时收集指标,则在小时之前刷新缓冲区是没有意义的。
- 根据您的收集吞吐量试验您的刷新间隔。例如,刷新间隔越接近收集间隔,该时间的处理要求就越高。
- 刷新间隔越长,您在例如数据库中看到指标的延迟就越高。
批处理
批处理控制一次发送到输出插件的指标数量。例如,假设我们的批处理大小为 20,但我们每个间隔收集 30 个指标。Telegraf 将自动将前 20 个指标发送到输出端点,因为我们已达到批处理大小。剩余的 10 个将在之后发送。值得注意的是,当触发刷新间隔时,整个缓冲区将以批次(最多为缓冲区限制)清空。例如,大小为 80 的缓冲区将连续释放 4 批 20 个指标。
写入和缓冲区大小
因此,值得立即提及的是——缓冲区会影响内存使用率。对于大多数系统,默认限制不会对系统性能造成明显影响。话虽如此,较小的边缘设备(Raspberry Pi,Jetsons 等)可能需要更仔细的内存管理。
我的建议是使用内部插件来监控三个指标:
- 写入缓冲区大小 - 这是一个不费脑筋的事情。记录在此计数中任何接近您的限制的峰值。此数据中是否有任何趋势?急剧的峰值可能是由于许多输入插件在近距离内触发造成的。逐渐上升可能是由于与输出插件端点的连接受限造成的。
- 指标已丢弃 - 这强烈表明您的缓冲区大小对于您期望的输入频率来说太小。
- 收集的指标与写入的指标 - 此单元格很好地指示了收集和写入指标之间的整体健康状况。如果这两条折线图彼此密切镜像,那么您就有范围调整缓冲区大小。
如您所见,在下午 4 点之前,我的收集的指标与写入的指标彼此很好地镜像。下午 4 点之后,我将刷新间隔参数加倍,我们之前讨论过。
收集和刷新抖动
Telegraf 提供的另一个重要功能是错开来自不同插件的指标的收集和写入。这是使用收集和刷新抖动完成的。它们的工作方式略有不同,因此让我们讨论一下:
- 收集抖动强制每个输入插件在抖动范围内随机休眠一段时间,然后再进行收集。这可以用于避免许多插件同时查询 sysfs 等内容,这可能会对系统产生可衡量的影响。
- 刷新抖动在抖动范围内将刷新间隔随机化一定时间。这主要是为了避免运行多个 Telegraf 实例的用户造成大量写入峰值。
额外学分
最后,让我们讨论一些可选的优势,这些优势将有助于提高您的 Telegraf 解决方案的监控和持久性。
远程监控
因此,您可能遇到 Telegraf 主机并非始终可访问的用例。在这些情况下,我们仍然希望能够监控 Telegraf 代理的健康状况。为此,我们可以使用 Health Output Plugin(示例配置 在此处)。
[[outputs.health]]
service_address = "http://:8080"
namepass = ["internal_write"]
tagpass = { output = ["influxdb"] }
[[outputs.health.compares]]
field = "buffer_size"
lt = 100.0
让我们分解一下此配置片段:
- 服务地址允许我们指定一个端口用于监听健康输出。我们将使用这个端口来收集我们的健康状态。
- 然后我们使用
namepass
和tagpass
来过滤我们想要操作的特定指标。在本例中,我们只关心来自我们之前讨论的内部插件的 internal_write 指标。我们进一步使用tagpass
过滤 internal_write,仅提供与 InfluxDB 输出插件相关的指标。 - 最后,我们定义一个条件参数,用于比较字段的值。在本例中,我们正在检查 buffer_size,以查看该值是否达到或超过 100,如果达到或超过 100,则我们的条件检查失败。
请注意,您可以使用此插件定义多个检查。要记住的主要规则是:如果所有条件语句都通过,则健康插件输出 OK,否则,如果任何一个规则失败,则插件输出 unhealthy(不健康)。
多个 Telegraf 实例
最后,让我们思考一下在发生故障时我们解决方案的持久性。由于我们的 Telegraf 作为单个进程运行,我们面临着如果我们的一个插件发生故障,可能会中断其余部分稳定性的风险。缓解这种情况的主要方法之一是运行多个 Telegraf 实例。让我们考虑一下我们最初的工业示例
<Telegraf-agent-1> opc_ua_machines.conf |
<Telegraf-agent-2> modbus_sensors.conf |
<Telegraf-agent-3> system_performance.conf |
输入: opc_ua 客户端 x2。内部 x1 输出: influxdb x1,云连接器 x1 |
输入: modbus x1,内部 x1 输出: influxdb x1,云连接器 x1 |
输入: cpu x1,等等 输出: influxdb x1 |
这种架构确保即使一个 modbus_sensor 收集失败,我们也将继续从我们的 opc_ua_machines 和 system_performance 收集数据。实现此目的最简单的方法是通过 Docker 容器。这是一个 docker-compose 示例,可帮助您入门。
结论
我希望这篇博文能让您对 Telegraf Agent 及其设置的控制充满信心。在很多情况下,Telegraf 的默认行为和设置将提供您启动项目所需的即插即用功能。始终牢记这些黄金法则,以确保您的 Telegraf 解决方案在项目扩展时具有可扩展性、效率和可维护性,这是值得的。我很想听听您对此事的看法。我是否遗漏了应该在这里的黄金法则?请前往 InfluxData Slack 和 社区 论坛(只需 @Jay Clifford)。让我们在那里继续讨论!