ELK漫谈

近几年,ELK听的我耳朵起茧子了,是人是鬼,一说到数据采集就一定会提到ELK,包括我目前所在的公司。我用flume用了好些年了,所以一直对ELK没有过多的关注,主要原因是因为flume用了之后没有发现有什么不能满足我的地方。如果说flume有什么优点,那就是结构清晰明了,source, channel, sink 分别对应,从哪里来,放那里去,通过正规表达式分割字段,配置一看就明白。要说flume缺点,那肯定也有. flume这个玩意有些东西默认不支持,需要自己二次开发。 比如flume在读取单个文件的时候,我们通常使用tail,但是不支持position记录,再比如,web日志每天晚上会rotate, flume也不支持变量文件名,比如今天日志是log.20180227, 明天就是log.20180228了,这个是不支持的。也许你们会说,那用spool或者日志名不要改变,是可以,但总归这是个缺点,一个产品毕竟也做不到那么完美,很多东西提供了接口,自己二次开发来完成自己需要的功能。

因为ELK说的太多了,因此我想看看到底这个东西有什么地方值得推崇的地方,花了几天把filebeat, logstash的官方文档全部看完了,谈不上精通,但是已经比较了解了。既然要谈ELK,就应该先说说历史,我也是从网上看到的,好像最开始作者写了logstash来采集日志,结构包含input, filter, output,其实就是类似flume的结构。 后来被elastic收购了,作者自然也加入了elastic,考虑到logstash采集性能不是太好,elastic公司又用go语言写了filebeat来替代logstash的采集功能。至于filter, output还是由logstash来完成,当然filebeat本身也有output功能。

filebeat简单理解就是一个单独的日志采集器,包含2个采集type, stdin和log, 大概4种常用的output (ES,KAFKA,REDIS...).一个filebeat实例一次只能支持一个output格式, 采集的文件可以设置多个,文件名可以使用通配符等等,从input的角度来说,够用了。 问题在于一个filebeat实例如果采集不同格式日志怎么办? 因为output只支持一种啊,2个不同格式的日志一起采集,output设置为ES,那就意味着不同格式的日志进入了ES同一个INDEX,这么说也许你会不同意,你会说我可以通过when contains条件来建立不同索引,也许你还会说,我可以设置tag, fileds,这样就可以在同一个索引中区别不同数据,或者你还会说,我可以启动多个filebeat实例啊。 OK,我不想和你争论这个了。 另外 filebeat还有module的功能,大概是说你如果要使用filebeat来分割字段也是可以的,设置好 module的正规表达式,比如 slow log, 默认支持解析的,这样就不需要发送到logstash,这个功能我没有使用过,不太确定。

由于filebeat没有像logstash这么强大的分割字段的功能,因此采集文件的行直接存储在ES里面就是一个message,如果你能接受这个,那么实际也就不需要logstash了,大部分人还是希望能够把日志根据字段进行拆分,存储的到ES的时候有对应的字段。比如mysql slow log,包含SQL text, start time, userhost等等,如果直接一整条存储,你也很难进行分析。 当然如果你的output设置为 kafka, 然后自己通过程序来进行拆分也可以,总之实现字段拆分的办法太多了。

因为filebeat在开发的时候就仅仅是用作采集,所以不包含filter的功能,这也就能够说明为什么我们要引入logstash.   filebeat采集的文件行,传递给logstash, logstash进行字段拆分,过滤,字段改名等等,再输出到ES等外部存储。说到这里好像没啥可说的了。那就就先来看看实际的操作吧。

filebeat, logstash没有什么安装不安装,就是解压缩,然后修改配置文件启动就可以,我这里以mysql slow log为例子。

filebeat配置:

#=========================== Filebeat prospectors =============================

filebeat.prospectors:
- type: log

  enabled: true

  fields: 
    appid: 112
  tags: ["slowlog"]
  
  close_removed: true
  close_rename: true
  clean_removed: true
  clean_inactive: "48h"
  scan_frequency: "3s"
  ignore_older: "24h"
  fields_under_root: true
  paths:
    - /var/log/slow_log.CSV

  queue.mem:
    events: 4096
    flush.min_events: 512
    flush.timeout: "3s"

#============================= Filebeat modules ===============================

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

  reload.enabled: true

  reload.period: 10s

#==================== Elasticsearch template setting ==========================

setup.template.settings:
  index.number_of_shards: 3
  #index.codec: best_compression
  #_source.enabled: false

setup.template.name: slowlog
setup.template.fields: "/data/filebeat/slowlog.yml"
setup.template.pattern: "slowlog-*"



#-------------------------- Elasticsearch output ------------------------------
#output.elasticsearch:
#  hosts: ["10.215.4.166:9200","10.215.4.167:9200"]
#  index: slowlog

output.logstash:
  hosts: ["10.215.4.166:5044"]

setup.kibana:
   host: "10.10.192.88:5601"

采集/var/log/slow_log.CSV, output设置为logstash,端口是5044.  这里我要提一下,很多人采集慢查询非要去采集慢查询文件,设置mutil lines的方式来采集,MySQL的慢查询如果存储到表,会直接有一个CSV文件,每个慢查询就是一行,你们又何必通过mutil lines采集呢? 通过单行采集不好吗?

上面配置设置好了,就可以启动filebeat了。

logstash配置:

input {
        beats {
                type => "slowlog"
                port => "5044"
        }
 
        beats {
               type => "nginx"
               port => "5045"
       }
}

filter {
     
        if [type] == "slowlog" {

        grok {
                match => {
                        "message" => "%{QS:start_time},%{QS:user_host},%{QS:query_time},%{QS:lock_time},%{INT:rows_sent},%{INT:rows_examined},%{QS:db},%{INT:last_insert_id},%{INT:insert_id},%{INT:server_id},%{QS:sql_text},%{INT:thread_id}"
                }

        }
        mutate {
                remove_field => "thread_id"
        }
        mutate {
                remove_field => "insert_id"
        }

       } else {
                grok {
                match => {
             "message" => "%{QS:start_time},%{QS:user_host},%{QS:query_time},%{QS:lock_time},%{INT:rows_sent},%{INT:rows_examined},%{QS:db},%{INT:last_insert_id},%{INT:insert_id},%{INT:server_id},%{QS:sql_text},%{INT:thread_id}"
                }

        }

      }
}

output {

        if [type] == "slowlog" {
        elasticsearch {
            hosts => ["10.215.4.166:9200", "10.215.4.167:9200"] 
            index => "slow_log"
            }
        } else {
        elasticsearch {
            hosts => ["10.215.4.166:9200", "10.215.4.167:9200"]    
            index => "slowlog" 
             }
        }
}

这里你们看到有2个input, 2个grok, 2个 output, 这是我做测试用,你们只管那个5044端口的配置即可。logstash设置的input为beat, 实际就是filebeat,filebeat设置的logstash端口是5044,logstash配置也设置了一个5044用来对应起来。然后就是正规表达式来拆分慢查询。http://grokdebug.herokuapp.com/  这个是grok正规表达式的验证网站,确认拆分正确。

扫描二维码关注公众号,回复: 902894 查看本文章

启动filebeat和logstash, 通过ES pugin-head 查看索引数据:




这是一个很常用,但是很实用的例子。

生产环境有很多种日志,也许这些不同格式的日志在一台服务器上。一个filebeat实例处理这种问题实际没有太好的办法,启动多个实例可能是比较好的办法,一个实例对应一种日志,logstash通过不同端口来对应不同的 input。因为历史原因导致filebeat和logstash结合起来感觉就像东拼西凑一样,filebeat和logstash有一些功能又重叠,让人看着乱78糟。你要么就作为采集,要么就作为转换,2个东西都支持采集,转换,存储,啥玩意,那还不如做成一个东西。

flume 配置:

a1.sources =  r1
a1.sinks =  k1
a1.channels  = c1

a1.sources.r1.type = org.apache.flume.source.external.MultilineExecSource
a1.sources.r1.multilineRegex = "(.*?)",".* 
a1.sources.r1.command = tail -F /opt/mysql_data/mysql/slow_log.CSV
a1.sources.r1.channels = c1
a1.sources.r1.interceptors  = i1 i2 i3 i4
a1.sources.r1.interceptors.i1.type  = timestamp
a1.sources.r1.interceptors.i2.type  = host
a1.sources.r1.interceptors.i2.hostHeader  = machine_ip
a1.sources.r1.interceptors.i2.useIP  = true
a1.sources.r1.interceptors.i3.type  = host
a1.sources.r1.interceptors.i3.hostHeader  = machine_host
a1.sources.r1.interceptors.i3.useIP  = false
a1.sources.r1.interceptors.i4.type  =static
a1.sources.r1.interceptors.i4.key = dbtype
a1.sources.r1.interceptors.i4.value = mysql
a1.channels.c1.type=file
a1.channels.c1.write-timeout=10
a1.channels.c1.keep-alive=10
a1.channels.c1.checkpointDir=/data/opbin/flume-ng/c1/checkpoint
a1.channels.c1.dataDirs=/data/opbin/flume-ng/c1/data
a1.channels.c1.maxFileSize= 268435456

a1.sinks.k1.type = org.apache.flume.sink.hbase.HBaseSink
a1.sinks.k1.table = db_slow_query
a1.sinks.k1.columnFamily = cf
a1.sinks.k1.batchSize = 100
a1.sinks.k1.serializer = org.apache.flume.sink.hbase.external.HostRegexHbaseEventSerializer
a1.sinks.k1.channel = c1
a1.sinks.k1.serializer.regex = "((\\d{4})-(\\d{2})-(\\d{2})\\s(\\d{2}).*?)","(.*?)","(.*?)","(.*?)",(.*?),(.*?),"(.*?)",(.*?),(.*?),(.*?),"(.*?)"
a1.sinks.k1.serializer.colNames = start_time,year,month,day,hour,user_host,query_time,lock_time,rows_sent,rows_examined,db,last_insert_id,insert_id,server_id,sql_text
a1.sinks.k1.serializer.regexIgnoreCase = true
a1.sinks.k1.serializer.depositHeaders = true

对比filebeat+logstash, 再看看flume配置,我个人是更喜欢 flume,  没那么多麻烦事,又是通过type 来区分,又是通过多实例, flume就source, channel, sink 一 一对应即可。

猜你喜欢

转载自blog.csdn.net/tom_fans/article/details/79393015
elk