日志收集是一个很普遍的需求,各个服务的log日志,打点日志都需要收集起来做离线etl或实时分析。日志收集工具也有很多开源的可供选择,flume,  logstash, filebeat等等。  目前360商业化最主要的日志收集场景是投放引擎端的打点日志收集,中间件服务日志收集,这两种场景都是日志先持久化在本地,然后通过日志收集工具收集。

技术选型

投放引擎对资源占用比较敏感, 所以在收集工具的选择上我们对比下来filebeat相比于基于jvm的工具在资源占用上优势还是很明显的,而且支持的一些特性也是能很好满足我们要求的。如果需要日志汇聚层我们选用的是logstash, 因为与filebeat都是elastic家的产品,集成起来方便。

fliebeat版本选用的是目前stable版本7.14,支持kafka sasl scram授权认证方式, 老的版本不一定支持。logstash版本选用的是目前stable版本7.14。

当前我们主要使用的是filebeat的log input,  kafka output,  得益于filebeat的可扩展性,其他input, output也能很好支持。

filebeat简介

fibeateat 处理java日志 filebeat日志收集_fibeateat 处理java日志

filebeat底层主要依赖libbeat库来完成日志收集,libbeat提供publisher来对接inputs, 进入到libbeat后会经过processors的一系列加工处理。 libbeat封装了retry与ack反馈持久化机制,可以保证日志收集的完整性。

要用好filebeat涉及到很多配置,以下列出比较重要的:

  • processors配置: 可以对event做很多加工, 比如add_fields,add_labels, add_tags, rename, drop_event等等
  • Inputs配置: 定义各种input配置。
  • Moduels: 对各种开源组件的日志收集支持,这些modules针对各个组件的日志做了parse与结构化处理,同时集成了ES, Kibana, 为这些开源组件提供一整套日志收集展示方案。
  • HTTP endpoint: 配置http rest api获取metrics。

接下来介绍filebeat两个比较重要的机制,收集meta持久化机制和反压机制, 正是通过这两个机制filebeat才能保证日志收集的at least once语义。

01

filebeat收集meta持久化机制

这个机制很重要,因为这个涉及到日志收集的完整性保障,日志重放等问题。在filebeat run之后,在其根目录下会有data/registry/filebeat/log.json 文件,  这其中记录了filebeat收集meta持久化信息:

{"op":"set","id":1}
{"k":"filebeat::logs::native::3162611-64768","v":{"id":"native::3162611-64768","prev_id":"","source":"/root/ysptest1.log","offset":0,"ttl":-1,"type":"log","timestamp":[2061828726851,1628151202],"FileStateOS":{"inode":3162611,"device":64768},"identifier_name":"native"}}
{"op":"set","id":2}
{"k":"filebeat::logs::native::3162611-64768","v":{"id":"native::3162611-64768","prev_id":"","timestamp":[2061975276931,1628151203],"ttl":-1,"FileStateOS":{"inode":3162611,"device":64768},"identifier_name":"native","source":"/root/ysptest1.log","offset":165,"type":"log"}}其中记录了input type, source端文件路径以及当前收集到的offset,时间戳, 还有用于唯一标识文件的inode, device信息。ack确认反馈会从output端一直反馈到input端,input端收到ack确认后会负责将meta信息持久化到这个文件中。

通过收集meta持久化机制我们能清楚得知道哪些文件收集完成了,哪些文件正在收集以及正在收集文件的offset, 在必要的情况下,通过修改这个持久化文件可以做到日志重新收集的目的, 或者干脆将data文件夹删除,那么所有本地日志都会重新收集。

02

filebeat反压机制

如果outputs一直发送失败,会有retry机制尝试,然后日志在internel queue堆积,internel queue满了inputs端就会停止收集,queue中日志的ack反馈也会迟迟得不到确认, 文件的offset信息也不会更新.  这样就达到了一个反压的效果。

举个例子如果下游kafka集群不可用, 那么outputs就会失败,慢慢internel queue就会full,inputs收集也会停止。这正是我们想要的效果, 在下游出问题的时候,文件保持在本地磁盘是安全的。

通过filebeat的收集meta持久化机制以及反压机制就可以保证在极端情况下的at least once语义。

日志收集方案

设计目标:

  • 提供统一运维监控管理,降低运维成本。
  • 收集的日志可以同时满足实时,离线需求。
  • 日志收集pipeline支持反压,支持at least once语义,支持日志重放。
  • 跨IDC容灾,支持动态修改agent配置,将日志收集定向到其他IDC。

设计方案:

  • 提供一键agent部署脚本,提供agentManager管理metrcis上报与agent配置修改动态感知
  • 将kafka作为统一日志收集目的地。
  • logstash 作为日志汇聚层, 避免agent过多对kafka带来压力。
  • 监控告警: 获取filebeat,logstash metrics并解析成prom格式上报给prometheus, 通过grafana展示,提供实例级别的监控,对收集延迟,失败及时告警, 对收集的日志count进行统计,方便对数。
  • 产品化集成到ultron平台,基于项目粒度进行统一管理,运维,监控。

以下是日志收集的总体架构示意图:

fibeateat 处理java日志 filebeat日志收集_数据库_02

对于日志收集处理可以分为如下四个层次:

#Q1 日志收集agent层:

我们选用filebeat作为日志收集agent,同时会有一个伴生的agentManager来对agent做管理,抽取metrics并发送到pushgateway,  对配置修改动态感知

#Q2 日志汇聚层:

这是个可选层,分以下两种情况:

  • 对于agent不是很多的case, 直接采用filebeat + kafka的方案是很高效的
  • 对于agent很多的case,我们提供logstash汇聚层来对收集数据汇总然后发送给kafka, 避免对kafka造成连接过多的问题

#Q3 DataBus层:

我们将日志采集统一到kafka, 这样离线实时需求都可以得到满足, 我们在每个IDC都有kafka集群,这样当某个IDC不可用时,动态修改filebeat配置即可完成重新收集,相当于具备了跨IDC容灾的能力。

#Q4 异构传输层:

这一层主要是对收集日志的处理使用, 我们当前通过自研的hamal2落地hdfs/hive为离线etl提供支持, 通过flink/spark/storm/druid/clickhouse等实时消费处理。hamal2是我们基于flink实现的一个异构数据同步传输框架,用于Kafka数据实时入仓入湖。

下面主要介绍两种方案:

  • filebeat —> kafka 直连, 这是目前主要在用的方案。
  • filebeat —> logstash —> kafka  这个方案加入了logstash汇聚层。

filebeat —> kafka 

对于agent不是很多的场景, 直接使用filebeat kafka output写入kafka是高效简洁的方式, 根据我们上面阐述的filebeat收集meta持久化机制和反压机制,在kafka有问题写入不成功的情况下,会触发filebeat反压, 日志收集文件的offset也将停止持久化,这样是符合我们预期的。目前360商业化没有有很多agent的场景, 所以主要使用这种模式。

下面是filebeat-->kafka的简单架构示意图:

fibeateat 处理java日志 filebeat日志收集_kafka_03

filebeat.yml 配置示例:

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /root/test*.log
  fields:
    topic_name: test
- type: log
  enabled: true
  paths:
    - /root/test2*.log
  fields:
    topic_name: test2


output.kafka:
  version: 2.0.0
  hosts: ["xxx:9092", "xxx:9092", "xxx:9092"]
  # message topic selection + partitioning
  topic: '%{[fields.topic_name]}'
  partition.round_robin:
    reachable_only: false


  required_acks: 1
  compression: lz4
  max_message_bytes: 10000000
  sasl.mechanism: SCRAM-SHA-256
  username: xxx
  password: xxx
  worker: 1   # producer实例数
  refresh_frequency: 5m
  codec.format:
    string: '%{[message]}'   # 定义输出的日志,默认是带有很多meta信息的json string


filebeat.config.modules:
  path: ${path.config}/modules.d/*.yml
  reload.enabled: false


# 定义http endpoint, 用于获取metrics
http.enabled: true
http.host: your host
http.port: 5066

简单说明下上面的配置, 这个配置是filebeat直接使用kafka output写入kafka,  filebeat.inputs部分定义了两个log type收集目录,并且通过fields添加了对应的topic名, 在output.kafka中通过topic: '%{[fields.topic_name]}’ 动态定义了topic.

还有比较重要的配置是codec.format,可以重新定义输出日志的格式。

filebeat-->logstash-->kafka 

对于agent有很多的场景, 我们需要加入logstash汇聚层,  因为这可以避免大量filebeat直连kafka,对kafka连接负载造成压力。至于为什么选择logstash, 因为filebeat与logstash都是elastic家的产品,集成起来很方便, filebeat直接有logstash的output.  需要注意的是加入汇聚层对日志的完整性,稳定性保障都会带来挑战。

为了保障日志的完整性,我们依赖logstash的persistent queues(PQ)机制与反压机制:

  • persistent queues(PQ): 默认是不开启的,日志会先写内存queue再output出去,但这种方式在异常情况下会丢数据, 为了保证日志完整性,我们必须开启PQ,开启后所有日志将先持久化到disk然后再output出去, 这样可以做到at least once语义,可以通过配置queue.type来开启PQ:
queue.type: persisted
queue.max_bytes: 8gb
queue.max_events: 3000000
path.queue: /path/to/queuefile
  • 反压机制:  在PQ写满的情况下会反压到上游filebeat,  filebeat再反压到input停止日志收集。这种反压传导机制和flink的有点像。

通过logstash的PQ机制以及反压机制就可以保证在极端情况下整个pipeline的at least once语义。

下面是filebeat-->logstash-->kafka的简单架构示意图:

fibeateat 处理java日志 filebeat日志收集_kafka_04

为了方便logstash的运维管理, 我们将其部署在k8s集群, 由于logstash向外暴露的是tcp端口,我们通过nginx ingress + vip实现4层lvs。这样就可以根据其负载动态扩缩容了,而且可以提供一个统一的域名入口。

filebeat.yml 示例:

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /root/test*.log
  fields:
    topic_name: test
    kafka_cluster: cluster1
- type: log
  enabled: true
  paths:
    - /root/test2*.log
  fields:
    topic_name: test2
    kafka_cluster: cluster2


output.logstash:
  hosts: ["logstash.k8s.domain:5044"]


# 定义http endpoint, 用于获取metrics
http.enabled: true
http.host: your host
http.port: 5066

简单说明下上面的配置, 这个配置是filebeat使用logstash output写入logstash,  filebeat.inputs部分定义了两个log type收集目录,并且通过fields添加了对应的topic_name, kafka_cluster, 其中增加的kafka_cluster是logstash将日志分拣到不同集群使用的。

output.logstash中定义的logstash.k8s.domain:5044 其实是一个lvs域名端口,其后对应了多个logstash实例。这里我们没有使用filebeat的loadbalance设置,因为不是很灵活。

logstash.yml示例

queue.type: persisted
queue.max_bytes: 8gb
queue.max_events: 3000000
path.queue: /path/to/queuefile

logstash-pipeline.conf 示例

input {
    beats {
        port => "5044"
    }
}


filter {
    grok {
        match => { "message" => "%{COMBINEDAPACHELOG}"}
    }
}


output {
    stdout { codec => rubydebug {metadata => true}}
    if [fields][kafka_cluster] == "xxx" {  # 日志分拣
        kafka {
            codec => plain {
                format => '{ message:"%{message}"}'
            }


            bootstrap_servers => "xxx:9092,xxx:9092,xxx:9092"
            topic_id => "%{[fields][topic_name]}"
            compression_type => "lz4"
        }
    }


    if [fields][kafka_cluster] == "xxx" {   # 日志分拣
        kafka {
            codec => plain {
                format => '{ message:"%{message}"}'
            }
            bootstrap_servers => "xxx:9092,xxx:9092,xxx:9092"
            topic_id => "%{[fields][topic_name]}"
            jaas_path => "/root/logstash/kafka-jaas.conf"    # 支持SASL SCRAM
            sasl_mechanism => "SCRAM-SHA-256"
            security_protocol => "SASL_PLAINTEXT"
            compression_type => "lz4"
        }
    }
}

简单说明下上面的配置, 通过filter plugin过滤出日志, output kafka plugin中通过filebeat传过来的fields分拣出日志发往不同的kafka集群, 其中codec可以定义要输出到kafka的日志格式。

监控

filebeat的Monitor是要收费的, 免费的filebeat版本只能从http endpoint来获取metrics,需要自己解析后写入pushgateway, 然后通过prometheus --> grafana展示出来。logstash的Monitor也是收费的,类似filebeat,提供rest api来获取metrics,  需要自己解析后写入pushgateway, 然后通过prometheus --> grafana展示出来。

以下是filebeat监控截图, 主要就是将http endpoint中的metrics都展示出来:

fibeateat 处理java日志 filebeat日志收集_大数据_05

总结

基于filebeat的agent非常轻量级,依赖少,资源占用少,支持丰富的inputs, modules, outputs, 非常易于扩展。  logstash作为汇聚层部署在k8s集群, 通过4层lvs提供统一域名端口, 实现实例弹性扩展。  filebeat, logstash都具有持久化与反压机制, 都支持at least once语义, 从而保障了日志收集的完整性。

参考资料

https://www.elastic.co/guide/en/beats/filebeat/current/index.html

https://www.elastic.co/guide/en/logstash/current/index.html

https://cloud.tencent.com/developer/article/1634020