the site subtitle

fluent 云原生的下的日志收集工具

2018.09.25

Fluentd 是另一个 Ruby 语言编写的日志收集系统。和 Logstash 不同的是,Fluentd 是基于 MRI 实现的,并不是利用多线程,而是利用事件驱动。单个日志处理的流程如下,当然也可以多个日志流一同处理。

这里穿插一下Docker 的日志格式,默认的是json-file,分为以下三个字段,当然也有别的driver,但是会导致docker logs不能使用,也就没有尝试。

  "log": "10.244.0.1 - - [21/Dec/2018:13:06:52 +0000] \"GET / HTTP/1.1\" 200,"
  "stream": "stdout",
  "time": "2018-12-21T13:06:52.182042579Z"
}

配置

fluentd配置主要由以下5部分组成

  • source:确定输入源
  • match: 确定输出目的地
  • filter:确定 event 处理流
  • system:设置系统范围的配置
  • label:将内部路由的输出和过滤器分组
  • @include:包括其它文件

下面以k8s项目日志收集配置为实例,介绍一下相关配置。

数据源

<source>
  @id fluentd-containers.log
  @type tail
  path /var/log/containers/demo.log
  pos_file /var/log/es-containers.log.pos
  tag raw.kubernetes.*
  read_from_head true
  <parse>
    @type multi_format
    <pattern>
      format json
      time_key time
      time_format %Y-%m-%dT%H:%M:%S.%NZ
    </pattern>
    <pattern>
      format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
      time_format %Y-%m-%dT%H:%M:%S.%N%:z
    </pattern>
  </parse>
</source>

下一步解析json

<match raw.kubernetes.**>
  @id raw.kubernetes
  @type detect_exceptions
  remove_tag_prefix raw
  message log
  stream stream
  multiline_flush_interval 5
  max_bytes 500000
  max_lines 1000
</match>

# 加上kubernetes的metadata信息
<filter kubernetes.**>
  @type kubernetes_metadata
  verify_ssl false
</filter>

# 再次使用match 配置输出方向

<match **>
  @id elasticsearch
  @type elasticsearch
  @log_level info
  type_name fluentd
  include_tag_key true
  host 118.178.178.140
  port 9200
  logstash_format true
  <buffer>
  @type file
  path /var/log/fluentd-buffers/kubernetes.system.buffer
  flush_mode interval
  retry_type exponential_backoff
  flush_thread_count 2
  flush_interval 5s
  retry_forever
  retry_max_interval 30
  chunk_limit_size 2M
  queue_limit_length 8
  overflow_action block
  </buffer>
</match>

fluent解析之后的格式会带上pod的相关信息,之后发到ES,下面是ES中对应的一条数据:

{
  "_index": "logstash-2018.12.21",
  "_type": "fluentd",
  "_id": "WnlC0WcBd0poFKe5TXTB",
  "_version": 1,
  "_score": 1,
  "_source": {
    "log": "10.244.0.1 - - [21/Dec/2018:11:35:20 +0000] GET / HTTP/1.1",
    "stream": "stdout",
    "docker": {
      "container_id": "873ddbe58783f2e733e19a3b53e453"
    },
    "kubernetes": {
      "container_name": "nginx",
      "namespace_name": "default",
      "pod_name": "nginx-6665cb456b-hslrv",
      "pod_id": "3027acd2-0514-11e9-891e-00163e0d0afe",
      "labels": {
        "pod-template-hash": "6665cb456b",
        "run": "nginx"
      },
      "host": "prod-consul-2",
      "master_url": "https://10.96.0.1:443/api",
      "namespace_id": "0753cc1f-0513-11e9-891e-00163e0d0afe"
    },
    "@timestamp": "2018-12-21T11:35:20.417931350+00:00",
    "tag": "kubernetes.var.log.containers.nginx-6665cb456b-hb19a3b53e453.log"
  }
}

其他

fluent的缺点?

  • ruby也有全局锁的东西(GIL),所以在多核机器上只能利用一个CPU,所以规模大了之后可能会成为瓶颈,另外可以参考Ref#1。
  • 当你修改一个规则的时候,需要重新部署,而且不能区分应用,这种方式其实并不是很适合k8s,banzaicloud开源了一个logoperator,定义了新的CRD,可以基于label的方式来采集日志,还是可以的。https://github.com/banzaicloud/logging-operator
  • 采集容器内非标准输出的日志,阿里云有一个log-pilot可以根据ENV的方式来采集绝对路径下的日志,但是有一些问题,不过可以参考下。https://github.com/AliyunContainerService/log-pilot/

不断产生日志的脚本,或许有更好的方式?

while true; do echo '{"log":"2019-01-29T10:35:27,234+08:00 [main] INFO ","stream":"stderr","time":"2019-01-12T03:36:43.390762996Z"}' >> /var/log/containers/demo.log && sleep 1;done

Ref

Fluentd语法速记

https://lintingbin2009.github.io/2018/05/01/fluentd%E8%AF%AD%E6%B3%95%E9%80%9F%E8%AE%B0/