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 | 输入: 内部 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"。其余的指标按正常方式接受。
注意:在本博客中,我们只讨论了基于测量名称删除指标的情况。对于标签(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 标志不会将收集到的指标传递到输出端点。以下是一个测试标志的示例
telegraf --once --config ./file_parsing/telegraf-json-v1.conf
提高 Telegraf 的效率
因此,我们花费了一些时间基于上述黄金法则构建了一个配置。我们启动Telegraf服务,开始收集和输出指标。下一步是优化Telegraf在其运行的宿主机上的整体性能。
这因用例而异,但我希望提供一个步骤清单,帮助您判断应该更改哪些设置。
监控Telegraf
我非常认同“吃自己的狗粮”这句话。如果Telegraf是用来从其他平台抓取和监控指标的,那么它也应该能够监控自身。我们可以使用Telegraf 内部输入插件来完成这项工作。
让我们遵循最佳实践创建一个配置
- 创建一个名为 telegraf-internal-monitor.conf 的配置文件
- 添加Telegraf内部插件和InfluxDB输出插件(记得应用别名和路由)。在此处找到示例。 注意:我选择为该插件启用memstats。这主要是因为一些模板仪表板节点期望从memstats获取指标数据。值得注意的是,此功能主要与调试目的相关,不需要始终启用。
- 多亏了Steven Soroka,有一个用于仪表板化我们正在收集的新指标的InfluxDB模板。在此处找到该模板。
现在我们有一个看起来像这样的仪表板
检查清单
因此,我们已经勾选了我们清单的第一点(了解当前Telegraf代理性能)。接下来呢?好吧,让我们看一下检查清单,然后解释每个项目背后的科学/疯狂。
项目 | 系统影响 |
代理 | |
您的时间间隔适合您正在收集的数据吗? | CPU / 内存 / 带宽 |
您能否为每个输入插件更细粒度地设置时间间隔? | CPU / 内存 |
与您的收集间隔相比,您的刷新间隔合理吗? | CPU / 延迟 |
您考虑过每批发送的指标数量吗? | 网络 / CPU |
您有空间减小缓冲区大小吗?检查以下仪表板单元格
|
内存 |
您定义了收集抖动吗? | CPU / 内存 |
您定义了刷新抖动吗? | 网络 / CPU |
时间间隔
简而言之,间隔参数定义了Telegraf代理何时触发从每个输入插件收集指标。因此,通常我们应该意识到何时以及为什么从特定的输入收集数据。例如,如果我们的端点每小时只更新一次,那么每10秒抓取端点就没有意义。
我的黄金法则是根据端点行为为每个输入插件显式指定收集间隔。问问自己:我需要多频繁地监控此端点的新数据?
您还可以选择定义多个Telegraf实例。我们将在额外积分部分讨论这一点。
刷新间隔
与此并行,您还必须考虑您的刷新间隔。刷新间隔简单地设置了一个触发时间,在此时,所有存储在缓冲区中的指标都写入其输出端点。以下是一些需要考虑的点:
- 如果您正在每小时收集指标,那么在小时前刷新缓冲区就没有意义。
- 根据您的收集吞吐量来实验您的刷新间隔。例如,刷新间隔越接近收集间隔,当时处理需求就越高。
- 刷新间隔越长,您看到指标(例如您的数据库中的指标)的延迟就越高。
批量处理
批量处理控制一次发送到输出插件的指标数量。例如,假设我们的批量大小为20,但我们每个间隔收集30个指标。Telegraf会自动将前20个指标发送到输出端点,因为我们已达到批量大小。剩余的10个指标将在之后发送。值得注意的是,当刷新间隔被触发时,整个缓冲区将分批最大到缓冲区限制。例如,一个80的缓冲区将依次释放4批20个指标的批次。
写入和缓冲区大小
所以,我们不妨直接提及——缓冲区会影响内存使用。对于大多数系统,默认限制不会对系统性能产生明显影响。但话说回来,较小的边缘设备(树莓派、Jetson等)可能需要更谨慎地管理内存。
我的建议是使用内部插件来监控三个指标
- 写入缓冲区大小 - 这一点是不言而喻的。注意任何达到您限制的峰值。这些数据中是否有任何趋势?急剧的峰值可能是由于多个输入插件在相近时间触发。缓慢的上升可能是由于连接到输出插件端点的连接有限。
- 丢失的指标 - 这是一个强烈的指标,表明您的缓冲区大小对于您希望输入的频率来说太小。
- 收集的指标与写入的指标 - 这个单元格提供了收集和写入指标之间整体健康状况的良好指标。如果这两个线图非常相似,那么您有调整缓冲区大小的空间。
如您所见,在下午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或以上,然后我们的条件检查失败。
请注意,您可以使用此插件定义多个检查。需要记住的主要规则是:如果所有条件语句都通过,则健康插件输出“OK”,否则任何一条规则失败则插件输出“不健康”。
多个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代理及其设置的控制感到一些信心。在许多情况下,Telegraf的默认行为和设置将提供您需要的即插即用功能,以使您的项目启动。确保您的Telegraf解决方案随着项目规模的扩大而可扩展、高效和可维护,始终值得记住这些黄金法则。我很乐意听听您对这个问题的看法。我是否忽略了应该在这里的黄金法则?请前往InfluxData的Slack和社区论坛(只需确保@Jay Clifford)。让我们在那里继续讨论!