Logstash:使用并行 Logstash 管道提高持久队列吞吐量

默认情况下,Logstash 管道阶段(输入→管道工作器)之间使用内存中有界队列来缓冲事件。 但是,为了防止异常终止期间的数据丢失,Logstash 具有持久性队列功能,可以启用该功能以将消息队列存储在磁盘上。 队列位于输入和过滤器阶段之间,如下所示:

input → persistent queue → filter + output

根据持久队列博客文章,Logstash 持久队列应该对整体吞吐量产生很小的影响。 尽管对于管道受 CPU 约束的用例通常是这样,但在其他情况下,持久队列可能会导致 Logstash 性能大幅下降。 因此,在此博客文章中,我将讨论何时会发生这种情况以及使用哪种技术来减少启用 Logstash 持久队列可能对性能造成的影响。

 

此博客的动机

在最近的一次咨询活动中,持续的队列导致速度下降了大约75%,从 40K 事件/秒降低到 10K 事件/秒。 调查表明,磁盘或 CPU 都没有饱和,标准的 Logstash 调优技术(例如测试不同的批处理大小和添加更多的工作线程)无法弥补这种减慢。

 

为什么持久队列可能会影响 Logstash 性能

如果为管道启用了持久队列,则 Logstash 将为该管道运行一个单线程的持久队列-持久队列不会在单个管道内的多个线程之间运行。 换句话说,单个管道只能使用单个线程来驱动磁盘。 即使管道有多个输入,也是如此,因为单个管道中的其他输入不会增加磁盘 I/O 线程。

因为启用持久队列将同步磁盘 I/O(这会增加等待时间)添加到 Logstash 管道中,所以即使关联服务器上的所有资源都没有饱和,持久队列也可能会降低吞吐量。

 

改善整体表现的解决方案

如果 Logstash 无法使磁盘饱和,则吞吐量可能会由于同步磁盘 I/O 导致的等待时间而受到限制。在这种情况下,并行运行的其他持久队列线程可能能够更加有效地驱动磁盘,这将增加总体吞吐量。

这可以通过在单个 Logstash 进程中并行运行多个(相同的)Logstash 管道,然后对管道之间的输入数据流进行负载平衡来实现。例如,如果我们将 Filebeat 用作 Logstash 的输入源,则可以通过在 Filebeat 中指定多个 Logstash 输出来实现跨多个 Logstash 管道的负载平衡。

下面,我们提供一个标准(未优化)Logstash 持久队列实现的示例,然后是一个改进的实现,该实现由两个并行运行的管道组成。

 

一个简单的 Logstash 管道

下面提供了一个从 Filebeat 接收数据的 Logstash 管道的示例。如上所述,由于这只是单个管道,因此其持久队列是单线程的,因此其吞吐量可能会受到同步磁盘 I/O 引起的等待时间的限制。

Logstash pipeline:

input {
  beats {
    port => 5044
  }
}

filter {
  # Custom filters go here
}

output {
  elasticsearch {
    hosts => ["http://<elasticsearch hostname>:9200"]
    index => "<index name>" 
  }
}

下面提供了一个示例 Filebeat 配置,该配置可以将数据驱动到上述 Logstash 管道中:

Filebeat configuration:

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/*.log

output.logstash:
  hosts: ["<logstash hostname>:5044"]

并行运行多个 Logstash 管道

为了增加驱动磁盘的持久队列线程的总数,我们可以在单个 Logstash 进程中运行多个 Logstash 管道。 例如,Logstash 可以并行执行以下两个管道:

Pipeline #1:

input {
  beats {
    port => 5044
  }
}
filter {
  # Custom filters go here
}
output {
  elasticsearch {
    hosts => ["http://<elasticsearch hostname>:9200"]
    index => "<index name>" 
  }
}

Pipeline #2:

input {
  beats {
    port => 5045 # Different than Pipeline #1
  }
}
filter {
  # Same as Pipeline #1
}
output { 
  # Same as Pipeline #1
}

给定上述 Logstash 管道,可以将 Filebeat 配置为在这些管道之间进行负载平衡。 如果我们的 Logstash 实例正在按照我们刚才描述的方式运行,其中 pipeline#1 在端口 5044 上侦听,流水线 pipeline #2 在端口 5045 上侦听,那么我们的文件信号实例可以配置为在这两个 Logstash pipeline 之间保持平衡,如下所示:

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/*.log

output.logstash:
  hosts: ["<logstash hostname>:5044", "<logstash hostname>:5045"]
  loadbalance: true

结果

在上述咨询服务中,启用持久队列最初导致性能下降75%,我们创建了四个并行运行的相同 Logstash 管道,并平衡了这4个管道的filebeat 输出。 这导致性能提高高达3万个事件/秒,与没有持久队列的情况相比仅差25%。 此时,磁盘已饱和(根据需要),无法进一步提高性能。

 

结论

即使基础服务器的资源未达到饱和,Logstash 持久队列中磁盘 I/O 的单线程性质也可能导致性能下降。 要解决此问题,可以并行执行多个相同的 Logstash 管道,并且可以在这些管道之间平衡输入数据。 这样可以增加可以同时写入磁盘的并行线程数量,从而可以提高 Logstash 的整体吞吐量。 在您自己的集群中尝试一下,或者启动 Elasticsearch Service 的14天免费试用,如果你有任何疑问,请在我们的讨论论坛中进行探讨。