企业级日志收集系统——ELKstack
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实验架构图

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端口,检查节点是否正常

5.补充:ES2节点配置
配置时只需要保证elasticsearch.yml文件中,node.name和node1节点不同即可,其他步骤都是一样的
[root@ES2 ~]# vim /usr/local/elasticsearch/config/elasticsearch.yml 40 node.name: "linux-ES2" #节点名称,不能和其他节点重复
这里我已经配置好了ES2节点

6.配置集群管理插件(head)
官方提供了一个ES集群管理插件,可以非常直观的查看ES的集群状态和索引数据信息,安装方法如下所示:
[root@ES1 ~]# /usr/local/elasticsearch/bin/plugin -i mobz/elasticsearch-head
安装完成,访问安装head插件的ES服务器,后缀如下图所示,即可查看集群信息,由于还没有向ES中写入数据,所以ES可能还看不到有索引,写入数据后就可以在此页面 看到ES存储的日志信息了

这个时候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中已经写入数据,并且自动建立了一个索引

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

这个时候说明,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端口即可

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

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

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

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

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}"
}
}
}

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

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

(2)创建mysql慢查询索引


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

对于kibana图表展示,只想说如下两点:
1、日志最好是json格式,否则还需要使用filter 的grok使用复杂的正则表达式进行日志内容处理,即加大的运维难度,由加大的服务器运行负担。
2、ELKStask最好用来收集业务日志,对于WEB访问日志的分析,可以使用第三方的分析工具,如百度统计,CNZZ,以及开源工具如piwik,awstats。
14.参考文档
官方文档:https://www.elastic.co/
中文指南:http://kibana.logstash.es/
篇幅问题,很多内容可能描述不是特别清楚,如果有问题,可以直接联系本人qq或者留言。