ELK日志分析集群搭建管理(rsyslog->kafka->ELK)

日志分析作为掌握业务情况的一个重要手段,目前使用最多最成熟的莫过于ELK方案,其中也有各种搭配组合,像rsyslog->ES->kibana、rsyslog->Redis->Logstash->ES->kibana、rsyslog->kafka->Logstash->ES->kibana等等,复杂点的有spark的引用。每种方案适合不同的应用场景,没有好坏,我的集群用的是rsyslog->kafka->Logstash->ES->kibanarsyslog->rsyslog中继->kafka->Logstash->ES->kibana方案,目前4台ES每天索引10多亿条日志,包含nginx、redis、php等,运行比较健壮,每条日志的索引在10个字段左右,每天Primary Shard的索引量在500个G左右,考虑到性能和日志性质,我们没要复制分片,日志保留7天。

总结一下,其实就是采集->清洗->索引->展现几个环节,再去考虑各环节中缓存、队列的使用。下面介绍一下此方案集群的搭建和配置。希望对同行有所帮助,也算我积的福德,在ELK探索过程中多谢远川和冯超同学的奉献交流。

一、采集(使用rsyslog)

http://www.rsyslog.com/rhelcentos-rpms/

将yum源配置好后:

  yum  install  rsyslog
  yum  install  rsyslog-kafka

  安装好后对应rsyslog的配置文件如下:

module(load= "imfile" )
module(load= "omkafka" )
$PreserveFQDN on
main_queue(
  queue.workerthreads= "10"      # threads to work on the queue
  queue.dequeueBatchSize= "1000"    # max number of messages to process at once
  queue.size= "50000"          # max queue size
)
##########################nginx log################################
$template nginxlog, "%$myhostname%`%msg%"
if  $syslogfacility-text ==  'local6'  then  {
    action(
        broker=[ "10.13.88.190:9092" , "10.13.88.191:9092" , "10.13.88.192:9092" , "10.13.88.193:9092" ]
        type = "omkafka"
        topic= "cms-nginx"
        template= "nginxlog"
        partitions.auto= "on"
      )
    stop
  }
############################redis log#########################
$template redislog, "%$myhostname%`%msg%"
ruleset(name= "redis7215-log" ) {
    action(
        broker=[ "10.13.88.190:9092" , "10.13.88.191:9092" , "10.13.88.192:9092" , "10.13.88.193:9092" ]
        type = "omkafka"
        topic= "redis-log"
        template= "redislog"
        partitions.auto= "on"
      )
  }
input( type = "imfile"
      File= "/data1/ms/log/front/redis7215.log"
      Tag= ""
      ruleset= "redis7215-log"
      freshStartTail= "on"      #start tailf
      reopenOnTruncate= "on"    #Truncate  reopen
      )
input( type = "imfile"
      File= "/data1/ms/log/front/redis7243.log"
      Tag= ""
      ruleset= "redis7215-log"
      freshStartTail= "on"
      reopenOnTruncate= "on"
      )
############################php curl log#############################
$template phpcurl-log, "%$myhostname%`%msg%"
ruleset(name= "phpcurl-log" ) {
    action(
        broker=[ "10.13.88.190:9092" , "10.13.88.191:9092" , "10.13.88.192:9092" , "10.13.88.193:9092" ]
        type = "omkafka"
        topic= "phpcurl-log"
        template= "phpcurl-log"
        partitions.auto= "on"
      )
  }
input( type = "imfile"
      File= "/data1/ms/log/php_common/php_slow_log"
      Tag= ""
      ruleset= "phpcurl-log"
      freshStartTail= "on"       
      reopenOnTruncate= "on"
      )

为了避免在日志发送错误时,丢在message日志里,瞬间将磁盘占满,同时配置丢弃策略

*.info;mail.none;authpriv.none; cron .none;local6.none    /var/log/messages

目前收集了nginx、redis、php curl三种日志,说一下收集方案。

1、对于nginx

 方案1:采用nginx的rsyslog模块将日志打到local6,对应nginx的配置如下

  ##########elk############################# 
        access_log  syslog:local6 STAT;

然后通过如上rsyslog的配置,将日志直接入kafka队列,kafka集群是4个broker。

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

方案2:线上还有另一个传输方案,rsyslog设置一个中继,通过udp的方式将日志传到中继的rsyslog,由中继rsyslog入kafka,这么做的目的是方便了管理,当时还有个考虑是udp不会堵,但经过多轮测试后,nginx的rsyslog模块也是很健壮,不会堵的。

2、对于redis、php curl的日志

通过rsyslog的imfile模块,直接对文件监听,配置见上面的rsyslog配置,在日志轮转时通过超链接的方式进行新文件的连接,对应的超连接计划任务如下,每天0点5分执行:

5 0 * * * root sh  /usr/local/script/php_slow_log .sh  &>  /dev/null

对应的php_slow_log.sh的脚本如下:

#!/bin/bash
DATE=` date  +%F`
ln  -sf  /data1/ms/log/php_common/curl- $DATE  /data1/ms/log/php_common/php_slow_log

备注:

a、rsyslog向4个kafka的broker推送消息时,是以轮训的方式;

b、rsyslog通过udp或tcp向外转发日志时,会默认加上时间、主机名、主机ip的属性。

二、队列(kafka+zookeeper)

 队列用的是kafka,kafka集群使用zookeeper管理,我们用了4台服务器混装了4个kafka和3个zookeeper,kafka和zookeeper的安装地址如下:

http://kafka.apache.org/downloads 注意:下载Binary downloads版本,别下错了,解压后就能用

http://zookeeper.apache.org/  注意:安装过程很简单,按照文档来即可,不在说明

1、关于kafaka

a、配置比较简单,基本默认即可,常调整的配置项如下:

配置文件:server.properties
broker. id =190  #id
num.partitions=20  #默认kafka的partion数量 
log. dirs = /data1/kafka-logs  #日志文件存放目录
log.retention.hours=3  #日志保留时间长短
zookeeper.connect=10.13.88.190:2181,10.13.88.191:2181,10.13.88.192:2181  #zookeeper指定
delete.topic. enable = true  #topic是可以删除的

b、安装后测试(假设kafka和zookeeper都装了):

开两个终端,两个终端分别运行如下命令

启动:. /bin/kafka-server-start .sh  /usr/local/kafka/config/server .properties &
终端1:. /bin/kafka-console-producer .sh --broker-list localhost:9092 --topic  test
终端2:. /bin/kafka-console-consumer .sh  --zookeeper localhost:2181 --from-beginning --topic  test

注意两个终端的topic要一个名字,这时你在终端1输入任何数据,在终端2是同步的,证明你安装成功。

c、kafka常用管理命令

创建topic:. /bin/kafka-topics .sh --create --topic  test  --replication-factor 1 --partitions 32  --zookeeper  localhost:2181
删除topic:. /bin/kafka-topics .sh --delete --topic  test  --zookeeper localhost:2181
查看topic列表:. /bin/kafka-topics .sh  --list  --zookeeper localhost:2181
查看某个topic详细:. /bin/kafka-topics .sh --describe --topic  test  --zookeeper localhosts:2181
监控某个topic的消费:. /bin/kafka-console-consumer .sh --zookeeper localhost:2181 --topic  test
指定消费组查看消费情况:. /bin/kafka-consumer-offset-checker .sh --zookeeper localhost:2181  --group  test

备注:topic下partitions的数量决定了并发消费的数量,在设置上要根据消息的QPS和硬盘情况合理配置。

2、关于zookeeper

a、配置比较简单,大多数默认项,最好奇数个,半数以上zookeeper存活可用

配置文件:zoo.cfg
dataDir= /data1/zookeeper
server.1=10.13.88.190:3888:4888
server.2=10.13.88.191:3889:4888
server.3=10.13.88.192:3889:4888

注意:要在数据目录手动建立myid,myid的值是server后面的数字,数字是有范围限制的1~255

b、zookeeper的常用管理命令

zookeeper我主要是看下它的整体状态,写了个简单脚本获取zookeeper的状态,执行结果如下:

wKiom1iAezLzwcflAAAlWrHKv18771.png

脚本内容如下:

#!/bin/sh
#writer:gaolixu
[ -z $1 ] &&  echo  "Please specify zoo.cfg like /usr/local/zookeeper/conf/zoo.cfg "  &&  exit
cat  $1 | grep  "^server"  | awk  -F '[:|=]'  '{print $2}'  |
while  read  line
do
echo  - ne  "$line\t"
echo  stat|nc -w 2 $line 2181  | egrep  "^(Node|Zxid|Mode|Connections)"  | tr  "\n"  "\t"
echo  stat|nc -w 2 $line 2181  | egrep  "^(Node|Zxid|Mode|Connections)"  &> /dev/null  ||  echo  -n  "host is done."
echo
done
使用方式:zkstat.sh   /配置文件zoo.cfg的位置

zookeeper是相当稳定的,基本不用管。

备注:zookeeper配置文件里不能有汉字,否则启动不起来。

三、清洗(logstash)

 logstash用做清洗,并且将处理好的日志推送到es里,安装过程很简单详见网址:

https://www.elastic.co/guide/en/logstash/current/installing-logstash.html#package-repositories

我线上的nginx的配置文件如下:

input {
  kafka {
    zk_connect =>  "10.13.88.190:2181,10.13.88.191:2181,10.13.88.192:2181"
    topic_id =>  "cms-nginx"
    group_id =>  "cms-nginx"
    consumer_threads => 1
    reset_beginning =>  false
    decorate_events =>  false
  }
}
filter {
  ruby {
        init =>  "@kname = ['host-name','front','http_x_up_calling_line_id','request','http_user_agent','status','remote_addr_1','id','http_referer','request_time','body_bytes_sent','http_deviceid','http_x_forwarded_for','domain','cookie']"
        code =>  "event.append(Hash[@kname.zip(event['message'].split('`'))]) "
        remove_field => [ "@version" , "_score" , "id" , "tags" , "key" , "message" , "http_deviceid" , "http_x_up_calling_line_id" , "" , "cookie" ]
        }
     
    if  [front] {
        grok {
        match => [ "front" , "%{HTTPDATE:logdate}" ]
        }
        date  {
        match => [ "logdate" "dd/MMM/yyyy:HH:mm:ss Z" ]
        target =>  "@timestamp"
        remove_field => [ "front" , "logdate" ]
        }
    }
    if  [request] {
        ruby {
            init =>  "@kname = ['method','uri','verb']"
            code =>  "event.append(Hash[@kname.zip(event['request'].split(' '))])"
            remove_field => [  "request" , "method" , "verb" ]
                }
        }
    if  [remote_addr_1] {
        grok {
              match => [  "remote_addr_1" "%{IPV4:remote_addr}"  ]
              remove_field => [ "remote_addr_1" ]
                }
        }
    mutate {
        convert => [
            "body_bytes_sent"  "integer" ,
                  "status"  "integer" ,           
            "request_time"  "float"  ]
    }
}
output {
        elasticsearch {
                hosts => [ "10.39.40.94:9200" , "10.39.40.95:9200" , "10.39.40.96:9200" , "10.39.40.97:9200" ]
                workers => 1
                index =>  "logstash-cms-nginx-%{+YYYY.MM.dd.hh}"
                }
        #stdout { codec => dots 
                #workers => 5
                #}                           #测试性能时使用
        #stdout { codec => rubydebug }       #调试时使用
}
启动命令:. /bin/logstash  -w 4 -b 1000 -f  /etc/logstash/conf .d /kafka_logstash_cms_nginx .conf &
-w 后面的worker数是根据cpu的核心数大概算一下,我这里一台服务器开三个logstash,每个起4个worker

  配置文件看着很长,其实阅读性很好,很易懂上手编写,无非就是定义切割点,如果大切割点下需要继续切割,就加if判断,继续切割,吐个槽里面threads和workers的数量好像不管用,我压测时去看线程数对不上,看的方法是top -H -p logstash的pid。

  再就是看看哪些需要计算的变成数字型,还有个timestamp的处理,这个可以看看上面的代码,对于nginx打印的时间符合ISO8601标准,可以用他做es的时间索引,这样有个好处,如果某个环节慢索引赶不上的话,日志不会错序。时间标准详细可见:http://udn.yyuap.com/doc/logstash-best-practice-cn/filter/date.html

备注:

a、尽量去掉没用的字段,精简索引,非常重要;

b、nginx打印出来的时间是标准化的,可以用它传到es作为timestamp建索引;

c、对于响应时间、响应内容大小、状态码要转换成数字类型,方便在kibana里做计算等操作;

d、切割双引号可以使用如下配置

code =>  "event.append(Hash[@kname.zip(event['message'].split(34.chr))])"

e、抓包后发现,logstash向es推数据是轮训的,从zookeeper消费数据不是轮训,可能是1个个用,有压力或问题后再去启用后面的zookeeper。

f、尽量按照官方如下写法建立多个索引向es推送,防止单个索引巨大,search时计算不出来

index =>  "logstash-cms-nginx-%{+YYYY.MM.dd.hh}"

g、测试性能方法如下

由于没有现成工具,我们用了打点计量的方式进行压测,摘掉es后将输出变为一个点,每处理一条信息打一个点,然后将打出的点用pv命令统计出字节流量,反推出logstash的吞吐量。

  cp一个配置文件,修改output如下:

 
output {
        stdout { codec => dots 
        workers => 1
        }
        }
 

  同时为了不影响线上业务,修改group_id,这样的话测试消费和线上消费互不影响,配置文件修改如下:

  kafka {
    zk_connect =>  "10.13.88.190:2181,10.13.88.191:2181,10.13.88.192:2181"
    topic_id =>  "nginx"
    group_id =>  "test001" 
    consumer_threads => 12
    reset_beginning =>  false
    decorate_events => flase
  }
 

测试时执行命令:/opt/logstash/bin/logstash -f /tmp/kafka_test.conf |pv -abt > /dev/null

压测结果如下:

wKiom1h_UVHyuE0MAAASrG8qSoo997.png

 

每个点是一个byte,等到数据稳定后,计算每s的吞吐量为2.93*1024=3000,也就是这一个logstash最大吞吐量为能处理3000条信息每s。

四、索引(es 2.X 版本)

  es是硬盘io和cpu消耗比较重的部分,硬优化有ssd,软优化牵涉到java层面的GC调优、index调优、进程池调优、merge调优等,目前跑的还是比较好的,也有说将index和search分开的,防止search太大影响index索引,没去尝试,10多亿条日志,在目前的架构下性能还可以。

es的安装也是比较简单详见:https://www.elastic.co/guide/en/elasticsearch/reference/current/rpm.html

a、我们线上添加的进程调优如下,不加这个配置,跑一段时间就会有出现某一台es负载特别高的情况,而且难以恢复,甚至死机,加后运行健壮,先看调整前后对比图:

wKiom1ir-tHzMHEkAAJiuYUEivM335.png

wKioL1ir-tOBCRtmAAB30f9bJQs439.png

wKioL1ir-t-jEShaAAMg-1Aq3_c192.pngwKiom1ir-tbx2m4eAAD0_qFZ0KA646.png

bootstrap.memory_lock: false   # centos 6版本内核不支持

bootstrap.system_call_filter:  false
indices.store.throttle. type : merge  #merge调优
indices.store.throttle.max_bytes_per_sec: 50m
index.merge.scheduler.max_thread_count: 1
index.merge.policy.max_merged_segment: 15gb
index.refresh_interval: -1
index.translog.flush_threshold_ops: 100000  #tranlog达到多少条进行数据平衡,默认为5000 
index.translog.flush_threshold_size: 512m
threadpool.bulk. type : fixed
threadpool.bulk.size: 12    #12核机器,核心数
threadpool.bulk.queue_size: 1000
threadpool.index. type : fixed
threadpool.index.size: 12    #12核机器,核心数
threadpool.index.queue_size: 1000
threadpool.get. type : fixed
threadpool.get.size: 12    #12核机器,核心数
threadpool.get.queue_size: 1000
threadpool.search. type : fixed
threadpool.search.size: 24    #12核机器,核心数2倍
threadpool.search.queue_size: 500
index.indexing.slowlog.threshold.index.warn: 10s
index.indexing.slowlog.threshold.index.info: 5s
index.indexing.slowlog.threshold.index.debug: 2s
index.indexing.slowlog.threshold.index.trace: 500ms
index.search.slowlog.threshold.fetch.warn: 1s
index.search.slowlog.threshold.fetch.info: 800ms
index.search.slowlog.threshold.fetch.debug: 500ms
index.search.slowlog.threshold.fetch.trace: 200ms

另外的一些配置中项如下:

cluster.name: yz-search    #集群名称
node.name: yz-search94    #当前节点名称
index.number_of_shards: 4  #每个index的shards数,这个根据磁盘数量等去算,默认5
index.number_of_replicas: 0  #要不要复制分片
index.refresh_interval: 60s  #刷新时间
path.logs:  /data1/LogData/logs  #日志存放地点
network.host: 10.39.40.94    #绑定ip地址
http.port: 9200    #http管理端口
discovery.zen. ping .unicast.hosts: [ "10.39.40.94:9300" , "10.39.40.95:9300" , "10.39.40.96:9300" , "10.39.40.97:9300" #用来发现集群存在的es主机
discovery.zen.minimum_master_nodes: 2  #最少有几个主机就可以进行master选举

b、heap数的设置,官方说是不能超过30G,我们64G的内存,设置是25G,GC方式采用G1

wKioL1iAkfKQIWmrAABFAZ-2iRo137.png

c、常用es的集群管理命令,当然只是看信息的可以浏览器里直接输入查看

curl http: //10 .39.40.94:9200 /_cat/nodes ? v      #节点概况
curl http: //10 .39.40.94:9200 /_cat/shards ? v    #查看shards的信息
curl http: //10 .39.40.94:9200 /_cat/indices ? v    #查看索引信息,如果新推的日志,可以看这个确认是否索引成功   
curl -X DELETE  "http://10.39.40.94:9200/索引名称"  #删除指定历史索引,速度很快

对于我们线上的日志,默认保存7天,每天晚上清除一次,清除的脚本如下:

#!/bin/bash
DATES=` date  +%Y.%m.%d -d '-7 day' `
curl -X DELETE

ES升级5.2.1详见:ELK之ES2.4.1双实例平滑升级至5.2.1问题解决及supervisor管理记

五、展现(kibana)

展现kibana没什么可说的,直接安装后,配置好es的地址就可以用,安装很简单有rpm包,前端可以用nginx做个代理,做限制,安装详见:https://www.elastic.co/downloads/kibana

安装后模型搭建也比较人性化,用几次就熟练了。

备注:像logstash、kafka这种加&号启动的服务(有些启动后自己fork新进程然后退出的其实不合适)可以用supervisor管理,比较方便。配置相当简单,可以在浏览器看状态。

wKiom1iAl-6DujGSAAAgL7sN9I4746.png

wKiom1iAmByyQ4IKAABxxX37B24468.png

supervisor的配置文件如下:

[unix_http_server]
file = /tmp/supervisor .sock   ; (the path to the socket  file )
[inet_http_server]         ; inet (TCP) server disabled by default
port=*:9001        ; (ip_address:port specifier, *:port  for  all iface)
#username=cms              ; (default is no username (open server))
#password=123               ; (default is no password (open server))
[supervisord]
logfile= /tmp/supervisord .log ; (main log  file ;default $CWD /supervisord .log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile= /tmp/supervisord .pid ; (supervisord pidfile;default supervisord.pid)
nodaemon= false                ; (start  in  foreground  if  true ;default  false )
minfds=1024                  ; (min. avail startup  file  descriptors;default 1024)
minprocs=200                 ; (min. avail process descriptors;default 200)
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix: ///tmp/supervisor .sock ; use a unix: //  URL  for  a unix socket
serverurl=http: // *:9001 ; use an http: //  url to specify an inet socket
#username=cms              ; should be same as http_username if set
#password=123                ; should be same as http_password if set
[program:logstash_cms_php_slow_log]
command = /opt/logstash/bin/logstash  -w 4 -b 1000 -f  /etc/logstash/conf .d /kafka_logstash_cms_php .conf    ; the program (relative uses PATH, can take args)
process_name=%(process_num)d ; process_name  expr  (default %(program_name)s)
numprocs=1                    ; number of processes copies to start (def 1)
umask =022                     ;  umask  for  process (default None)
priority=999                  ; the relative start priority (default 999)
autostart= true                ; start at supervisord start (default:  true )
redirect_stderr= true          ; redirect proc stderr to stdout (default  false )
stdout_logfile= /var/log/logstash_php_log        ; stdout log path, NONE  for  none; default AUTO
[program:logstash_cms_nginx_log]
command = /opt/logstash/bin/logstash  -w 4  -b 1000  -f  /etc/logstash/conf .d /kafka_logstash_cms_nginx .conf    ; the program (relative uses PATH, can take args)
process_name=%(process_num)d ; process_name  expr  (default %(program_name)s)
numprocs=3                    ; number of processes copies to start (def 1)
umask =022                     ;  umask  for  process (default None)
priority=999                  ; the relative start priority (default 999)
autostart= true                ; start at supervisord start (default:  true )
redirect_stderr= true          ; redirect proc stderr to stdout (default  false )
stdout_logfile= /var/log/logstash_nginx_log        ; stdout log path, NONE  for  none; default AUTO
;[include]
;files = relative /directory/ *.ini

猜你喜欢

转载自www.linuxidc.com/Linux/2017-03/142225.htm