企业级日志收集系统——ELKstack

2023年 7月 16日 98.7k 0

1.ELKstack简介

ELKstack 是 Elasticsearch、Logstash、Kibana 三个开源软件的组合而成,形成一款强大的实时日志收集展示系统。

各组件作用如下:

Logstash:日志收集工具,可以从本地磁盘,网络服务(自己监听端口,接受用户日志),消息队列中收集各种各样的日志,然后进行过滤分析,并将日志输入到Elasticsearch中。

Elasticsearch:日志分布式存储/搜索工具,原生支持集群功能,可以将指定时间的日志生成一个索引,加快日志查询和访问。

Kibana:可视化日志web展示工具,对Elasticsearch中存储的日志进行展示,还可以生成炫丽的仪表盘。

2.使用ELKstack对运维工作的好处:

1、应用程序的日志大部分都是输出在服务器的日志文件中,这些日志大多是开发人员来看,然后开发却没有登录服务器的权限,如果开发人员需要查看日志就需要运维到服务器来拿日志,然后交给开发;试想下,一个公司有10个开发,一个开发每天找运维那一次日志,对运维来说就是一个不小的工作量,这样大大影响了运维的工作效率,部署ELKstack之后,开发人员就可以直接登录kibana中进行日志的查看,就不需要通过运维查看日志,这样就减轻了运维的工作。

2、日志种类多,且分散在不同的位置难以查找:如LAMP/LNMP网站出现访故障,这个时候可能就需要通过查询日志来进行分析故障原因,如果需要查看apache的错误日志,就需要登录到Apache服务器查看,如果查看数据库错误日志就需要登录到数据库查询,实现下如果是一个集群环境几十台主机呢?这时如果部署了ELKstack就可以登录到kibana页面进行查看日志,查看不同类型的日志只需要点点鼠标切换一下索引即可。

3.ELK实验架构图

image001

redis消息队列作用说明:
1、防止logstash和Es无法正常通信,从而丢失日志
2、防止日志量过大导致ES无法承受大量写操作从而丢失日志
3、应用程序(php,java)在输出日志时,可以直接输出到消息队列,从而完成日志收集

补充:如果redis使用的消息队列出现扩展瓶颈,可以使用更加强大的kafka,flume来代替。

实验环境说明:

[root@node1 ~]# cat /etc/redhat-release
CentOS release 6.6 (Final)
[root@node1 ~]# uname -rm
2.6.32-504.el6.x86_64 x86_64

使用软件说明:
logstash-1.5.4.tar.gz
elasticsearch-1.7.2.tar.gz
kibana-4.1.2-linux-x64.tar.gz
nginx和redis均为yum安装

部署顺序:
1、Elasticsearch集群配置
2、Logstash客户端配置(直接写入数据到ES集群,写入系统messages日志)
3、Redis消息队列配置(logstash写入数据到消息队列)
4、Kibana部署
5、nginx负载均衡kibana请求
6、案例:同时收集nginx和MySQL慢查询日志
7、kibana报表功能说明

配置需要注意的事项:

1、时间必须同步

2、出了问题,检查日志

4.Elasticsearch集群安装配置

(1)配置java环境,且版本为8,如果使用7可能会出现警告信息

[root@ES1 ~]# yum -y install java-1.8.0
[root@ES1 ~]# java -version
openjdk version "1.8.0_51"
OpenJDK Runtime Environment (build 1.8.0_51-b16)
OpenJDK 64-Bit Server VM (build 25.51-b03, mixed mode)

(2)下载elasticsearch,可以使用我直接给出的地址也可以直接去官方下载最新版,官方地址在文章结尾有给出。

wget https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.2.tar.gz

(3)安装elasticsearch,安装过程很简单,只需要把软件解压到指定目录,然后做一个软链接即可

tar xf elasticsearch-1.7.2.tar.gz
mv elasticsearch-1.7.2 /usr/local/
cd /usr/local/
ln -s /usr/local/elasticsearch-1.7.2/ /usr/local/elasticsearch

(4)修改配置文件,这里的一些路径就看自己的习惯了,推荐将数据目录放到ssd硬盘加快检索速度

[root@ES1 ~]# vim /usr/local/elasticsearch/config/elasticsearch.yml
32 cluster.name: elasticsearch        #组播的名称地址
40 node.name: "linux-ES1"             #节点名称,不能和其他节点重复
47 node.master: true                  #节点能否被选举为master
51 node.data: true                    #节点是否存储数据
107 index.number_of_shards: 5         #索引分片的个数
111 index.number_of_replicas: 1       #分片的副本个数
145 path.conf: /usr/local/elasticsearch/config/      #配置文件的路径
149 path.data: /data/es-data                        #数据目录路径
159 path.work: /data/es-worker                      #工作目录路径
163 path.logs:  /usr/local/elasticsearch/logs/      #日志文件路径
167 path.plugins: /usr/local/elasticsearch/plugins  #插件路径
184 bootstrap.mlockall: true                        #内存不向swap交换

(5)创建相关目录

mkdir /data/es-data -p
mkdir /data/es-worker -p
mkdir /usr/local/elasticsearch/logs
mkdir /usr/local/elasticsearch/plugins

(6)安装启动脚本

由于是使用的tar包安装的ES,默认是没有启动脚本的,如果不愿意自己手动些启动脚本,可以根据如下步骤安装

git clone https://github.com/elastic/elasticsearch-servicewrapper.git
cd elasticsearch-servicewrapper/
mv service/ /usr/local/elasticsearch/bin/
/usr/local/elasticsearch/bin/service/elasticsearch install

修改服务配置文件

[root@ES1 ~]# vim /usr/local/elasticsearch/bin/service/elasticsearch.conf 	
set.default.ES_HOME=/usr/local/elasticsearch    #设置ES的安装路径,必须和安装路径保持一直
set.default.ES_HEAP_SIZE=1024                   #设置分配jvm内存大小

(7)启动ES,并检查是否监听9200和9300端口是否正常监听

[root@ES1 ~]# /etc/init.d/elasticsearch start
[root@ES1 ~]# netstat -lntp | grep -E "9200|9300"
tcp        0      0 :::9300                     :::*                        LISTEN      37416/java          
tcp        0      0 :::9200                     :::*                        LISTEN      37416/java

访问9200端口,检查节点是否正常

image002

 

 

 

 

 

 

 

5.补充:ES2节点配置

配置时只需要保证elasticsearch.yml文件中,node.name和node1节点不同即可,其他步骤都是一样的

[root@ES2 ~]# vim /usr/local/elasticsearch/config/elasticsearch.yml
40 node.name: "linux-ES2"           #节点名称,不能和其他节点重复

这里我已经配置好了ES2节点

image003

 

 

 

 

 

 

 

 

6.配置集群管理插件(head)

官方提供了一个ES集群管理插件,可以非常直观的查看ES的集群状态和索引数据信息,安装方法如下所示:

[root@ES1 ~]# /usr/local/elasticsearch/bin/plugin -i mobz/elasticsearch-head

安装完成,访问安装head插件的ES服务器,后缀如下图所示,即可查看集群信息,由于还没有向ES中写入数据,所以ES可能还看不到有索引,写入数据后就可以在此页面 看到ES存储的日志信息了

image004

这个时候ES集群就配置完成了,下面就可以配置logstash向ES集群中写入数据了。

7.Logstash部署

(1)下载logstash

wget https://download.elastic.co/logstash/logstash/logstash-1.5.4.tar.gz

(2)部署logstash需要java环境

yum -y install java-1.8.0
tar zxf logstash-1.5.4.tar.gz
mv logstash-1.5.4 /usr/local/
ln -s /usr/local/logstash-1.5.4/ /usr/local/logstash

(3)设置启动脚本,在/etc/init.d/logstash中添加如下内容即可, 脚本中给出的logstash配置文件为/etc/logstash如果有需要请手动修改。

#!/bin/sh
# Init script for logstash
# Maintained by Elasticsearch
# Generated by pleaserun.
# Implemented based on LSB Core 3.1:
#   * Sections: 20.2, 20.3
#
### BEGIN INIT INFO
# Provides:          logstash
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: 
# Description:        Starts Logstash as a daemon.
### END INIT INFO

PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

if [ `id -u` -ne 0 ]; then
   echo "You need root privileges to run this script"
   exit 1
fi

name=logstash
pidfile="/var/run/$name.pid"
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$PATH:$JAVA_HOME/bin
LS_USER=logstash
LS_GROUP=logstash
LS_HOME=/usr/local/logstash
LS_HEAP_SIZE="500m"
LS_JAVA_OPTS="-Djava.io.tmpdir=${LS_HOME}"
LS_LOG_DIR=/usr/local/logstash
LS_LOG_FILE="${LS_LOG_DIR}/$name.log"
LS_CONF_FILE=/etc/logstash.conf
LS_OPEN_FILES=16384
LS_NICE=19
LS_OPTS=""

[ -r /etc/default/$name ] && . /etc/default/$name
[ -r /etc/sysconfig/$name ] && . /etc/sysconfig/$name

program=/usr/local/logstash/bin/logstash
args="agent -f ${LS_CONF_FILE} -l ${LS_LOG_FILE} ${LS_OPTS}"

start() {


  JAVA_OPTS=${LS_JAVA_OPTS}
  HOME=${LS_HOME}
  export PATH HOME JAVA_OPTS LS_HEAP_SIZE LS_JAVA_OPTS LS_USE_GC_LOGGING

  # set ulimit as (root, presumably) first, before we drop privileges
  ulimit -n ${LS_OPEN_FILES}

  # Run the program!
  nice -n ${LS_NICE} sh -c "
    cd $LS_HOME
    ulimit -n ${LS_OPEN_FILES}
    exec "$program" $args
  " > "${LS_LOG_DIR}/$name.stdout" 2> "${LS_LOG_DIR}/$name.err" &

  # Generate the pidfile from here. If we instead made the forked process
  # generate it there will be a race condition between the pidfile writing
  # and a process possibly asking for status.
  echo $! > $pidfile

  echo "$name started."
  return 0
}

stop() {
  # Try a few times to kill TERM the program
  if status ; then
    pid=`cat "$pidfile"`
    echo "Killing $name (pid $pid) with SIGTERM"
    kill -TERM $pid
    # Wait for it to exit.
    for i in 1 2 3 4 5 ; do
      echo "Waiting $name (pid $pid) to die..."
      status || break
      sleep 1
    done
    if status ; then
      echo "$name stop failed; still running."
    else
      echo "$name stopped."
    fi
  fi
}

status() {
  if [ -f "$pidfile" ] ; then
    pid=`cat "$pidfile"`
    if kill -0 $pid > /dev/null 2> /dev/null ; then
      # process by this pid is running.
      # It may not be our pid, but that's what you get with just pidfiles.
      # TODO(sissel): Check if this process seems to be the same as the one we
      # expect. It'd be nice to use flock here, but flock uses fork, not exec,
      # so it makes it quite awkward to use in this case.
      return 0
    else
      return 2 # program is dead but pid file exists
    fi
  else
    return 3 # program is not running
  fi
}

force_stop() {
  if status ; then
    stop
    status && kill -KILL `cat "$pidfile"`
  fi
}


case "$1" in
  start)
    status
    code=$?
    if [ $code -eq 0 ]; then
      echo "$name is already running"
    else
      start
      code=$?
    fi
    exit $code
    ;;
  stop) stop ;;
  force-stop) force_stop ;;
  status) 
    status
    code=$?
    if [ $code -eq 0 ] ; then
      echo "$name is running"
    else
      echo "$name is not running"
    fi
    exit $code
    ;;
  restart) 
    
    stop && start 
    ;;
  reload)
    stop && start
    ;;
  *)
    echo "Usage: $SCRIPTNAME {start|stop|force-stop|status|restart}" >&2
    exit 3
  ;;
esac

exit $?

添加为系统服务

[root@client elk-config]# chkconfig --add logstash
[root@client elk-config]# chkconfig logstash on
[root@client elk-config]# chkconfig --list logstash
logstash       	0:off	1:off	2:on	3:on	4:on	5:on	6:off

(4)Logstash向Elasticsearch写入数据

1、设置一个logstash配置文件

[root@client elk]# cat /etc/logstash.conf 
input {         #表示从标准输入中收集日志
  stdin {}
}

output {        
  elasticsearch  {  #表示将日志输出到ES中
    host => ["172.16.4.102:9200","172.16.4.103:9200"]   #可以指定多台主机,也可以指定集群中的单台主机
    protocol => "http"
  }
}

2、手动启动logstash并写入数据

[root@client elk]# /usr/local/logstash/bin/logstash -f /etc/logstash.conf 
Logstash startup completed
hello world    #这里是自己手动写入的内容

3、写入完成之后,查看ES中已经写入数据,并且自动建立了一个索引

image005

在基本查询选项中选择指定的索引就可以看到写入的日志内容

image006

这个时候说明,logstash结合elasticsearch是可以正常工作的,下面就以一个实际例子还说明如何,收集系统日志。

8.Logstash收集系统日志

修改logstash配置文件为如下所示内容,并启动logstash服务就可以在head中看到messages的日志已经写入到了ES中,并且创建了索引

[root@node5 ~]# vim /etc/logstash.conf 
input {
  file {         #表示从文件中读取日志
    path => "/var/log/messages"    #文件路径
    start_position => "beginning"  #表示从文件开始处(第一行)读取日志进行收集
 }
}
output {
  elasticsearch  {  #表示输出到ES中
    host => ["172.16.4.102:9200","172.16.4.103:9200"]
    protocol => "http"
    index => "system-messages-%{+YYYY-MM}"   #表示创建的索引名称,%{+YYYY-MM}表示安装日期创建索引,这是指定日期格式
  }
}

收集成功如下图所示,自动生成了system-messages的索引

系统日志

logstash结合elasticsearch  写入日志没有问题的话,下一步就是logstash向redis中写入日志,当然如果日志的写入量不是很高,也可以不需要使用消息队列,直接使用logstash向elasticsearch 写入日志。

9.Redis消息队列配置

(1)安装redis,没有特殊需求使用yum安装即可

[root@redis ~]# yum -y install redis

(2)配置redis:redis默认是监听127.0.0.1,是不允许其他主机访问的,如果允许其他主机访问就需要配置监听外部ip地址或者0.0.0.0。

[root@redis ~]# vim /etc/redis.conf
bind  0.0.0.0

(3)启动redis

[root@redis ~]# /etc/init.d/redis start
[root@node4 ~]# netstat -lntp | grep redis
tcp        0      0 0.0.0.0:6379                0.0.0.0:*                   LISTEN      36660/redis-server

(4)收集系统日志写入到redis中

[root@client ~]# cat /etc/logstash.conf 
input {
  file {
    path => "/var/log/messages"
    start_position => "beginning" 
 }
}
output {
  redis {   #表示写入到redis中 
    host => "172.16.4.104"    #指明redis服务器地址
    data_type => "list"       #指定数据类型为list
    key => "system-messages"  #指定存入的键
  }
}

设置完成,重启logstash服务,登录redis查询是否写入

redis 127.0.0.1:6379> KEYS *       #查看redis中的键,已经创建了一个system-messages的键
1) "system-messages"
redis 127.0.0.1:6379> TYPE system-messages   #查询键的类型为list
list
redis 127.0.0.1:6379> LINDEX system-messages -1  #查询键的值
"{"message":"Oct 18 13:55:56 config-03 yum[38732]: Installed: zsh-4.3.11-4.el6.centos.x86_64","@version":"1","@timestamp":"2015-10-18T06:57:54.581Z","host":"0.0.0.0","path":"/var/log/messages","type":"system"}"

注意:由于这里还没有使用logstash从redis中取出数据,所以这里是可以看到消息队列中的数据,如果是生产环境,可能使用KEYS *看不到消息队列中有数据,说明logstash已经从消息队列中取走了数据。

(5)从redis读取日志写入到ES

说明:此logstash我部署在redis服务器上,当然也可以部署在ES上,或者其他机器上看个人需求了

[root@node4 ~]# cat /etc/logstash.conf 
input {
  redis {   #表示从redis中取出数据
    host => "172.16.4.104"   
    data_type => "list"
    key => "system-messages"   #指定去system-messages这个键中取出数据
  }
}

output {
  elasticsearch  {
    host => ["172.16.4.102:9200","172.16.4.103:9200"]
    protocol => "http"
    index => "system-messages-%{+YYYY-MM}"
  }
}

此处配置完成之后,就已经完成了日志的收集以及存储的配置,那么就剩下WEB展示的配置了,只需要部署完成kibana就可以实现最后的日志展示功能。

10.Kibana部署

说明:我这里是在两个ES节点部署kibana并且使用nginx实现负载均衡,如果没有特殊的需求可以只部署单台节点

(1)安装kibana,每个ES节点部署一个

tar zxf kibana-4.1.2-linux-x64.tar.gz
mv kibana-4.1.2-linux-x64 /usr/local/
cd /usr/local/
ln -s kibana-4.1.2-linux-x64/ kibana

(2)配置kibana,只需要指定ES地址其他配置保持默认即可。

[root@node2 ~]# vim /usr/local/kibana/config/kibana.yml
elasticsearch_url: http://172.16.4.102:9200         #指定elasticsearch地址,各自ES节点的kibana指向各自节点ES

(3)添加kibana启动脚本

#!/bin/bash
#chkconfig:35 13 91
#description:this is kibana servers
. /etc/rc.d/init.d/functions

start(){
   /usr/local/kibana/bin/kibana >/dev/null  2>&1 & 
  if [ `ps -ef | grep -v grep | grep "kibana" | wc -l` -gt 0 ];then
    action "starting kibana:" /bin/true
    sleep 1
   else
    action "starting kibana:" /bin/false
    sleep 1
   fi
}
stop(){
  kibanapid=`ps aux | grep "kibana" | grep -v grep | awk '{print $2}'`
  kill -9 $kibanapid 
  if [ `ps -ef | grep -v grep | grep "kibana" | wc -l` -lt 1 ];then 
    action "stopping kibana:" /bin/true
    sleep 1
  else
    action "stopping kibana:" /bin/false
    sleep 1
  fi
}

case "$1" in
  start)
   start
  ;;
 stop)
  stop
  ;;
 restart)
  $0 stop;
  $0 start;
  ;;
*)
  echo $"Usage: $0 {start|stop|restart}"
  ;;
esac

添加到系统服务:

[root@ES1 local]# chmod +x /etc/init.d/kibana
[root@ES1 local]# chkconfig --add kibana
[root@ES1 local]# chkconfig kibana on
[root@ES1 local]# chkconfig --list kibana
kibana         	0:off	1:off	2:on	3:on	4:on	5:on	6:off

(4)启动kibana并登录web页面创建系统日志索引,并查询日志

1、启动kibana,并检查是否监听5601端口

[root@ES1 local]# /etc/init.d/kibana start
starting kibana:                                           [  OK  ]
[root@ES1 local]# netstat -lntp | grep 5601
tcp        0      0 0.0.0.0:5601                0.0.0.0:*                   LISTEN      40660/node

2、登录web页面,直接访问kibana的5601端口即可

image008

配置完成之后就可以在Discover中看到写入的日志

image009

查询指定字段的日志,如选择侧边栏的messages点击add就可以只查询messages字段的日志了

image010

还可以根据时间查询日志,如下中文显示是我使用网页翻译显示的

image011 image012

也可以根据搜索框直接查询想要的内容,下图所示为查看日志中包含yum的内容。

image013

11.Nginx反向代理kibana使请求负载均衡

(1)安装nginx

[root@nginx ~]# yum -y install nginx

(2)配置nginx负载均衡,配置文件如下所示:

http {
    upstream kibana {      #定义后端主机组
      server 172.16.4.102:5601 weight=1 max_fails=2 fail_timeout=2;
      server 172.16.4.103:5601 weight=1 max_fails=2 fail_timeout=2;
    }    }

  server {
    listen       80;
    server_name  localhost;
    location / {           #定义反向代理,将访问自己的请求,都转发到kibana服务器
        proxy_pass http://kibana/;
        index  index.html index.htm;
    }
}
}

设置完成,访问nginx监听的ip地址即可负载均衡kibana。

由于kiaba不支持用户认证,如果需要用户认证,也可以在nginx处添加认证。

此处ELKstack + redis日志收集系统就部署完成了,需要收集日志只需要设置根据需求编写logstash配置文件即可,下面是一个配置示例

12.案例:Logstash同时收集nginx日志和mysql慢查询日志

说明:这里的日志是我复制生产日志,不是本机生成的日志。

(1)为了方便nginx日志的统计搜索,这里设置nginx访问日志格式为json

说明:如果想实现日志的报表展示,最好将业务日志直接以json格式输出,这样可以极大减轻cpu负载,也省得运维需要写负载的filter过滤正则。

    log_format json '{"@timestamp":"$time_iso8601",'
                    '"@version":"1",'
                    '"client":"$remote_addr",'
                    '"url":"$uri",'
                    '"status":"$status",'
                    '"domain":"$host",'
                    '"host":"$server_addr",'
                    '"size":$body_bytes_sent,'
                    '"responsetime":$request_time,'
                    '"referer": "$http_referer",'
                    '"ua": "$http_user_agent"'
                    '}';
      access_log  /var/log/access_json.log  json;

(2)收集nginx和mysql慢查询日志,保存到redis消息队列中

input {
  file {
    path => "/var/log/access_json.log"
    type => "nginx-access"     #指定日志类型,以便在一个配置文件中收集多个日志,用来区别输出
    start_position => "beginning"
    codec => "json"
  }
  file {
   path => "/root/slow-mysql.log"
   type => "slow-mysql"        #指定日志类型,以便在一个配置文件中收集多个日志,用来区别输出
   start_position => "beginning" 
   codec => multiline {
     pattern => "^# User@Host"
     negate => true
     what => "previous"
 }
 }
}

output {
   if [type] == "nginx-access" {  #对input中的输入进行判断,如果日志类型为nginx-access则执行以下输出,否则不执行
   redis {
      host => "172.16.4.104"
      data_type => "list"
      key => "nginx-access"    
   }
  }
   if [type] == "slow-mysql" {
   redis {
      host => "172.16.4.104"
      data_type => "list"
      key => "slow-mysql"    
   }
  }
}

Logstash 从redis消息队列读取日志存储到ES中,此处的logstash部署在redis消息队列上,当然也可以部署在ES上

input {
  redis {
    type => "nginx-access"
    host => "172.16.4.104"
    data_type => "list"
    key => "nginx-access"
  }
 redis {
    type => "slow-mysql"
    host => "172.16.4.104"
    data_type => "list"
    key => "slow-mysql"
   }
}

output {
  if [type] == "nginx-access" {
   elasticsearch {
      host => ["172.16.4.102:9200","172.16.4.103:9200"]
      protocol => "http"
      index => "nginx-access-%{+YYYY.MM}"
   }
  }
  if [type] == "slow-mysql" {
   elasticsearch {
      host => ["172.16.4.102:9200","172.16.4.103:9200"]
      protocol => "http"
      index => "slow-mysql-%{+YYYY.MM}"
   }
  }
}

image014

(4)Kibana创建对应日志的索引,用来查询日志

1、创建nginx索引

image015

2、这个时候就可以看到nginx日志了

image016

(2)创建mysql慢查询索引

image017

image018

13.图表展示

限于文章篇幅,这里不想做过多的描述图标生产的过程,如果各位有需要可以参考给出的文档,如下是效果图:

图表生成

对于kibana图表展示,只想说如下两点:

1、日志最好是json格式,否则还需要使用filter 的grok使用复杂的正则表达式进行日志内容处理,即加大的运维难度,由加大的服务器运行负担。

2、ELKStask最好用来收集业务日志,对于WEB访问日志的分析,可以使用第三方的分析工具,如百度统计,CNZZ,以及开源工具如piwik,awstats。

14.参考文档

官方文档:https://www.elastic.co/

中文指南:http://kibana.logstash.es/

篇幅问题,很多内容可能描述不是特别清楚,如果有问题,可以直接联系本人qq或者留言。

相关文章

对接alertmanager创建钉钉卡片(1)
手把手教你搭建OpenFalcon监控系统
无需任何魔法即可使用 Ansible 的神奇变量“hostvars”
openobseve HA本地单集群模式
基于k8s上loggie/vector/openobserve日志收集
openobseve单节点和查询语法

发布评论