Fluentd:在Kubernetes上支持多行日志

首先

我认为在Kubernetes中使用fluent进行日志收集是一种相当流行的方法。
实际尝试后,我发现当在Kubernetes上运行的应用程序产生多个业务的日志时,
日志会在Kubernetes中被拆分为多个部分。
我认为在Fluentd中,根据固定的日志格式,可以解析日志内容是其中的一个优点,
但现在这样做可能无法进行解析了。

具体来说,就是这种感觉。

2020-10-04T14:07:22.234Z ERR Authorization error because you have to be a member of following groups
XXXX
YYYY
ZZZZ

这将会被划分成类似这样的感觉。

{"log":"2020-10-04T14:07:22.234Z ERR Authorization error because you have to be a member of following groups\n","stream":"stderr","time":"2020-10-04T14:07:22.234Z"}
{"log":"XXXX\n","stream":"stderr","time":"2020-10-04T14:07:22.434Z"}
{"log":"YYYY\n","stream":"stderr","time":"2020-10-04T14:07:22.634Z"}
{"log":"ZZZZ\n","stream":"stderr","time":"2020-10-04T14:07:22.639Z"}

我认为最终会将日志集中到数据库或者Elasticsearch之类的地方,但这样可读性会下降呢…
我进行了各种调查,但没有找到同时进行日志合并和解析(按照我的想法)的案例,所以我进行了反复尝试,终于写成了一篇文章。
如果能对某人有所帮助,我会很高兴的。

顺便提一下,在Fluent内可以使用Ruby代码,但是我不懂Ruby。
我在最后的解析部分使用了Ruby,但是只是试探性地进行,所以可能会有一些改进点被Ruby专家发现。

如果可以的话,我们非常希望您能给予我们友善且有建设性的意见。

环境

kubectl:v1.16.13
k8s 服务器: AWS EKS
fluentd:1.11.3

流畅的配置

尽快解决方案。
首先,选择要使用的插件。

fluent-plugin-concat (2.4.0)
fluent-plugin-record-reformer (0.9.1)

因为没有时间去调查,所以我只好编辑并使用现有的图片了。

FROM fluent/fluentd-kubernetes-daemonset:v1.11.3-debian-kinesis-1.0
RUN fluent-gem install fluent-plugin-record-reformer

我们将在这里制作的图像上传到AWS ECR,并将其作为Daemonset使用。
这个图像默认会收集kubernetes上的日志,但是不支持合并上述分割的日志,而是按行输出。

我会更改这个。

最初的逻辑

主要的配置文件是/fluentd/etc/fluent.conf。

@include "#{ENV['FLUENTD_SYSTEMD_CONF'] || 'systemd'}.conf"
@include "#{ENV['FLUENTD_PROMETHEUS_CONF'] || 'prometheus'}.conf"
@include kubernetes.conf

Kubernetes上的应用程序日志存储在/var/log/containers/*.log中。
由于kubernets.conf对应上述配置,所以将覆盖此文件。

我正在原始文件中添加以下内容。


    <filter kubernetes.**>
      @type concat
      key log
      multiline_start_regexp /^\d{4}-\d{1,2}-\d{1,2}.*/
      separator ''
      flush_interval 5
      timeout_label @NORMAL
    </filter>

    <match **>
      @type relabel
      @label @NORMAL
    </match>

    <label @NORMAL>
      <match kubernetes.**>
        @type record_reformer
        tag ref.kubernetes
        enable_ruby true
          time ${begin log.scan(/^(\d{4}-\d{1,2}-\d{1,2}T\d{1,2}:\d{1,2}:\d{1,2}.\d{1,3}Z?)/).first.first rescue "" end}
          level ${begin log.scan(/^\d{4}-\d{1,2}-\d{1,2}T\d{1,2}:\d{1,2}:\d{1,2}.\d{1,3}Z (\w{3}?)/).first.first rescue "" end}
          message ${begin log.gsub(/\n/, 'NNN').scan(/^\d{4}-\d{1,2}-\d{1,2}T\d{1,2}:\d{1,2}:\d{1,2}.\d{1,3}Z \w{3} (.*?)$/).first.first.gsub(/NNN/, '\n') rescue log end}
      </match>
      <filter ref.kubernetes>
          @type record_transformer
          enable_ruby
          <record>
            container ${record.dig("kubernetes", "container_name")}
          </record>
      </filter>
      <match **>
          @type kinesis_streams
          @id out_kinesis_streams
          region "ap-northeast-1"
          stream_name "xxxxx"
          include_time_key "xxxxx"
          <buffer>
            @type file
            path /var/log
            flush_at_shutdown true
            flush_interval 1
            chunk_limit_size "1m"
            flush_thread_interval 0.1
            flush_thread_burst_interval 0.01
            flush_thread_count 15
          </buffer>
        </store>
      </match>
    </label>

以下是解释。

1. 请使用 @type 连接起来

这是几乎与Github说明文件中的kubernetes实际应用案例完全相同。
我们利用应用程序日志中的规则性(即使换行,日期始终位于日志的开头),使用multiline_start_regexp来判断multiline的结束。

另外,由于异常常常不符合此格式,因此我们设置了超时和超时标签,并使得不符合此正则表达式的内容能够继续传递到后续步骤中。

将设置为键的日志字段连接在一起,然后流经下一个进程。

2. @类型 记录整理器

由於在前一個步驟中標籤已經被賦予,所以在

我正在使用Ruby的scan方法,通过正则表达式提取值。
另外,不符合前一个多行正则表达式的内容根本不会成为scan的目标,
由于scan(xxx)没有返回值,所以会触发scan(xxx).first.first的异常。
因此,我在其中使用了Ruby的异常处理机制begin rescue end。

最后,由于合并日志中的主要部分,肯定会包括换行符。
在这里,我尝试了正则表达式和扫描等常规方法,但无论如何都无法正确处理换行部分,
如果忽略换行符,就无法正确确定结尾,无法顺利进行。

尽管有些污乱,但使用gsub来转义换行符,最后再恢复换行(因为在本例中想要将换行符发送到kinesis作为换行的原因。如果希望使用空格等其他字符,只需将其更改为空格即可)。

结束

以上是关于我为什么写了这篇文章的原因。
我想很多人都有尝试同样的事情,但无论是用日语还是英语,都没有直接适用的内容。
因此,我进行了各种调查和试错,将其内容写成了这篇文章。

期待您的问题、改善意见等。

请你用中文给一个翻译。

关于 concatenate:fluent-plugin-concat
关于连接后的解析提示:使用FluentD解析Ruby on Rails日志

bannerAds