;最佳实践

Grafana Loki标签的最佳做法

Grafana Loki正在积极开发中,我们正在不断努力提高性能。但这里有一些最新的最佳实践的厂牌,将给你最好的体验洛基。

静态标签很好

主机、应用程序和环境等都是很好的标签。它们对于特定的系统/应用是固定的,并且具有有限的值。使用静态标签可以更容易地从逻辑上查询日志(例如,显示给定应用程序和特定环境的所有日志,或显示特定主机上所有应用程序的所有日志)。

慎用动态标签

太多的标签值组合会导致太多的流。在Loki中,这样做的代价是存储中的大索引和小块,这反过来实际上会降低性能。

为了避免这些问题,在确定需要之前不要为某些东西添加标签!使用过滤器表达式(| = "文本"| ~“正则表达式”,…),用蛮力砸那些木头。它很有效,而且速度很快。

从一开始,我们就使用Promtail管道动态地设置了标签水平.这对我们来说似乎很直观,因为我们通常只想显示的日志水平= "错误";但是,我们现在在编写查询时重新评估它。{应用=“洛基”}| = " =错误”对于我们的许多应用程序来说,验证是否和{应用=“洛基”,水平=“错误”}

这看起来可能令人惊讶,但如果应用程序的音量从中到低,那么这个标签将导致一个应用程序的日志被分成至多5个流,这意味着存储了5x个块。加载块有一个相关的开销。想象一下,如果这个问题是{应用=“洛基”,水平!=“调试”}.这就需要加载了道路块多{应用=“洛基”}! = " =调试”

在上面,我们提到不要添加标签,直到你需要他们,所以你什么时候需要标签? ?再往下一点是关于chunk_target_size.如果将其设置为1MB(这是合理的),它将尝试切割1MB压缩大小的块,这大约是5mb左右的未压缩日志(根据压缩情况可能多达10MB)。如果您的日志有足够的容量在少于max_chunk_age,或许多块在那个时间框架内,您可能需要考虑将它分成使用动态标签的独立流。

您希望避免的是将日志文件分割为流,这会导致数据块被刷新,因为流处于空闲状态或在满之前达到最大时间。的洛基1.4.0,有一个度量可以帮助您理解为什么块被刷新Sum by (reason) (rate(loki_ingester_chunks_flushed_total{cluster="dev"}[1m]))

每个块在刷新时都是满的并不重要,但它将改善操作的许多方面。因此,我们目前的指导方针是尽可能避免动态标签,而是使用过滤器表达式。例如,不要添加水平动态标签,就| = " =调试”代替。

标签值必须总是有界的

如果要动态设置标签,永远不要使用可以有无界或无限值的标签。这总是会给洛基带来大麻烦。

尽量将值限制在尽可能小的集合中。对于Loki的处理能力,我们没有完美的指导,但可以考虑个位数或10位的动态标签值。这对于静态标签来说不那么重要。例如,如果您的环境中有1,000台主机,那么使用带有1,000个值的主机标签就可以了。

注意客户端应用的动态标签

Loki有几个客户选项:Promtail(它还支持systemd日志接收和基于tcp的syslog接收),Fluentd流利一些,一个码头工人插件,和更多!

每种方法都提供了配置应用哪些标签来创建日志流的方法。但是要注意可能会应用什么动态标签。使用Loki系列API来了解您的日志流是什么样子的,并查看是否有可能减少流和基数的方法。系列信息可以通过系列的API,或者你可以使用logcli

在Loki 1.6.0及更新版本中,logcli系列命令添加了——analyze-labels标志,专门用于调试高基数标签:

总流:25017唯一标签:8标签名称在流中发现的唯一值requestId 24653 24979 logStream 1194 25016 logGroup 140 25016 accountId 13 25016 logger 1 25017 source 1 25016 transport 1 25017 format 1 25017

在这个示例中,您可以看到requestId标签在24979个流中有24653个不同的值,这是糟糕的!!

这是一个不应该被贴上标签的完美例子,requestId应该作为标签删除,而应该使用过滤器表达式查询特定的日志requestId.例如,如果requestId在日志行中找到一个键=值对,你可以这样写一个查询:{logGroup = " group1 "} | = " requestId = 32422355 "

配置高速缓存

Loki可以在多个级别缓存数据,这可以极大地提高性能。这方面的细节将在以后的帖子中公布。

日志的时间排序

洛基接受无序写入默认情况下.本节确定Loki的最佳实践配置为接受乱序写入。

许多人使用Loki时遇到的一个问题是,他们的客户端接收到无序的日志条目错误。这是因为洛基身上有一条硬性规定:

  • 对于任何单个日志流,日志必须始终以递增的时间顺序发送。如果接收到的日志的时间戳比该流接收到的最新日志的时间戳要早,则该日志将被丢弃。

从这句话中可以剖析出一些东西。第一个限制是每个流。让我们看一个例子:

我是一个syslog日志!00:00:01我是一个syslog!

如果洛基收到这两行是为同一流,一切都会好起来。但是这个例子呢:

我是一个syslog日志!00:00:02我是一个syslog日志!00:00:01我是一个syslog!<-不按顺序拒绝!

我们能做些什么呢?如果这是因为这些日志的来源来自不同的系统呢?我们可以用每个系统唯一的附加标签来解决这个问题:

{job="syslog", instance="host1"} 00:00:00我是一个syslog!{job="syslog", instance="host1"} 00:00:02 i'm a syslog!{job="syslog", instance="host2"} 00:00:01 i'm a syslog!<-接受,这是一个新的流!{job="syslog", instance="host1"} 00:00:03 i'm a syslog!<- accept, still in order for stream 1 {job="syslog", instance="host2"} 00:00:02 i'm a syslog!<-已接受,仍然是为了流2

但是,如果应用程序本身生成的日志是无序的呢?嗯,恐怕这是个问题。如果您正在从日志行提取时间戳,使用类似于Promtail管道阶段,你可以执行此操作,并让Promtail为日志行分配时间戳。或者您可以在应用程序本身中修复它。

同样值得注意的是,Loki push API的批处理特性可能会导致接收到一些无序错误的实例,这实际上是假阳性。(也许有一批成功了一部分,并且到场了;或者任何之前成功的条目将返回一个无序的条目;否则任何新的东西都会被接受。)

使用chunk_target_size

使用chunk_target_size指示洛基尝试填满目标的所有块压缩1.5 mb的大小。这些较大的数据块对洛基来说处理起来更有效率。

其他配置变量会影响块的填充程度。洛基有一个默认max_chunk_age1 h和chunk_idle_period30m,以限制使用的内存量,以及在进程崩溃时暴露丢失的日志。

根据所使用的压缩方式(我们一直在使用snappy,它的可压缩性较低,但性能较快),您需要5-10x或7.5-10MB的原始日志数据来填充1.5MB的块。记住,一个块是每个流的,那么您将日志文件分成的流越多,内存中驻留的块就越多,它们就越有可能在被填满之前由于碰到上面提到的超时而被刷新。

许多小的,未填充的块会对Loki产生负面影响。我们一直在努力改善这一点,可能会考虑在某些情况下使用压实机来改善这一点。但是,总的来说,指导原则应该保持不变:尽最大努力把大块的内容填满。

如果您有一个应用程序的日志记录速度足够快,可以快速填充这些块(远远小于max_chunk_age),那么使用动态标签将其分解为单独的流就变得更加合理了。

使用-print-config-stderr-log-config-reverse-order

Loki和Promtail具有在启动时将整个配置对象转储到stderr或日志文件的标志。

-print-config-stderr在从命令行调用Loki时工作得很好,因为您可以快速输出整个Loki配置。

-log-config-reverse-order是我们在所有环境中使用洛基的标志。配置项被颠倒了,这样当在Grafana的Explore中查看时,配置的顺序从上到下是正确的。