背景:
鉴于当前测试环境在日志管理方面遭遇的困境,如日志无法持久化保存、服务重启导致的历史记录丢失,以及数据库日志查询效率低下等问题,我计划在本地搭建一套由Filebeat、Kafka、Elasticsearch、Logstash和Kibana组成的日志分析系统(ELK Stack),以全面优化我们的日志管理流程。
通过这套ELK Stack系统,我们期望实现以下关键目标:
-
日志持久化保存:利用Elasticsearch的强大存储能力,确保所有日志数据都能被安全、可靠地保存,不再因服务重启而丢失历史记录。
-
高效搜索与分析:Elasticsearch的分布式搜索引擎将提供快速且强大的数据检索和分析能力,让我们能够迅速定位并分析问题,提高故障排查效率。
-
可视化展示:Kibana的图形化界面将直观地展示日志数据,帮助我们更好地理解系统行为,发现潜在问题,并做出及时响应。
几年前,我有幸与公司的技术专家一起搭建过ELK系统,积累了丰富的实践经验。因此,我先在本地搭建并测试这套系统,为测试环境的日志管理带来显著的改进。
通过本地搭建ELK系统,我们可以先验证其效果,并根据实际情况进行调整和优化。一旦验证成功,我们可以考虑将其部署到测试环境中,以提升整个团队的日志管理效率。
Filebeat:
-
作用:轻量级的日志数据收集器,专门用于转发和集中日志数据。
-
特性:
-
监控指定的日志文件或位置,实时收集日志事件。
-
支持多种输入类型,如日志文件、syslog、Redis等。
-
作为服务器上的代理安装,确保日志数据的实时性和完整性。
-
Kafka:
-
作用:作为分布式消息队列,用于实时数据流的传输和处理。
-
特性:
-
分布式、高吞吐、可扩展的消息队列服务。
-
支持大量的实时数据处理和分析,确保数据的可靠性和持久性。
-
可以作为Filebeat的输出端,实时接收Filebeat采集的数据。
-
Logstash:
-
作用:作为数据收集引擎,负责数据的过滤、解析和转换。
-
特性:
-
实时管道功能,支持从多个数据源获取数据,并对其进行转换。
-
过滤器能够解析各个事件,识别已命名的字段以构建结构,并将它们转换成通用格式。
-
Elasticsearch:
-
作用:作为分布式搜索引擎,提供高性能的日志数据存储和检索功能。
-
特性:
-
分布式、可扩展的搜索和分析引擎。
-
提供强大的全文搜索、结构化搜索和分析功能。
-
支持PB级数据的存储和检索,确保数据的快速访问和高效处理。
-
Kibana:
-
作用:作为数据可视化工具,提供日志数据的可视化展示和分析功能
-
特性:
-
提供了强大的搜索和查询功能,支持多种查询方式。
-
提供了丰富的可视化图表和仪表板,帮助用户更好地理解数据。
-
支持与Elasticsearch的无缝集成,实现数据的实时展示和分析。
-
项目配置log4j
所需依赖:注意要排除掉[logback]的依赖
<!-- Log4j 2 核心包 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
<!-- Log4j 2 API -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.2</version>
</dependency>
<!-- Log4j 2 和 SLF4J 绑定 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.2</version>
</dependency>
log4j2.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR" monitorInterval="5">
<Properties>
<Property name="logPath">/Library/javaWork/logs/cloud2024/log</Property> <!-- 日志存储目录 -->
<Property name="fileName">cloud2024</Property> <!-- 日志名称 -->
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<RollingFile name="fileB" fileName="${logPath}/${fileName}.log"
filePattern="${logPath}/${fileName}_%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss:SSS} [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="100MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingFile>
</Appenders>
<Loggers>
<!-- <Root level="info" includeLocation="false">-->
<!-- <AppenderRef ref="Console"/>-->
<!-- </Root>-->
<Logger name="com.syl.cloud_2024" level="info" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="fileB"/>
</Logger>
</Loggers>
</Configuration>
我们先造几条日志数据
查看日志文件
将日志文件上传服务器
到此处日志数据已经就位,接下来我们需要做的就是让filebeat读取日志数据。
Filebeat篇:
下载:curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.10.0-linux-arm64.tar.gz
解压:tat -zxvf filebeat-7.10.0-linux-arm64.tar.gz
修改文件名:mv filebeat-7.10.0-linux-arm64 filebeat-7.10.0
目录:
[root@shenyulong filebeat-7.10.0]# ll
总用量 104924
drwxr-x---. 3 root root 60 5月 29 02:13 data
-rw-r--r--. 1 root root 3703475 11月 10 2020 fields.yml
-rwxr-xr-x. 1 root root 94984074 11月 10 2020 filebeat
-rw-r--r--. 1 root root 134504 11月 10 2020 filebeat.reference.yml
-rwxr--r--. 1 root root 1677 5月 21 12:13 filebeat.sh
-rw-------. 1 root root 9034 5月 21 12:58 filebeat.yml
drwxr-xr-x. 3 root root 15 11月 10 2020 kibana
-rw-r--r--. 1 root root 13675 11月 10 2020 LICENSE.txt
drwx------. 2 root root 148 5月 29 02:13 logs
drwxr-xr-x. 66 root root 4096 11月 10 2020 module
drwxr-xr-x. 2 root root 4096 11月 10 2020 modules.d
-rw-r--r--. 1 root root 8566190 11月 10 2020 NOTICE.txt
-rw-r--r--. 1 root root 814 11月 10 2020 README.md
文件介绍:
|
启动:./filebeat -c /yeYingXuan/app/filebeat/filebeat-7.10.0/filebeat.yml -e
每次启动都要输这么大一串,为了方便,我们写一个启动脚本filebeat.sh
#!/bin/bash
if [[ $# != 1 ]] || [[ $1 != 'start' && $1 != 'stop' && $1 != 'log' && $1 != 'slow' && $1 != 'moniter' && $1 != 'rmlog' ]]
then
echo " 输入参数有误 !!! "
echo " 接收参数为 start | stop | log | moniter "
fi
function start(){
pid=`ps -ef|grep filebeat|grep -v 'grep'|grep -v 'filebeat.sh'|awk -F ' ' '{print $2}'`
if [ ! -z $pid ]
then
echo "已经在运行中 正在停止 ...."
kill $pid
sleep 10s
fi
run
}
function run(){
echo "开始启动 .... ...."
nohup /yeYingXuan/app/filebeat/filebeat-7.10.0/filebeat -c /yeYingXuan/app/filebeat/filebeat-7.10.0/filebeat.yml >/dev/null 2>&1 &
echo "启动成功"
}
function stop(){
pid=`ps -ef|grep filebeat|grep -v 'grep'|grep -v 'filebeat.sh'|awk -F ' ' '{print $2}'`
if [ ! -z $pid ]
then
echo "开始停止进程 ... ..."
kill $pid
else
echo '未找到 filebeat 相关进程 请检查是否运行中'
fi
}
function log(){
tail -f /data/filebeat/filebeat/logs/filebeat
}
function moniter(){
pid=`ps -ef|grep filebeat|grep -v 'grep'|grep -v 'filebeat.sh'|awk -F ' ' '{print $2}'`
if [ -z $pid ]
then
run
else
echo "filebeat 进程已经在运行中。。。。。。"
fi
}
function rmlog(){
cd /data/filebeat/filebeat/logs &&
find . -name 'file*' -mmin +180 -exec rm -rf {} \;
}
case $1 in
start)
start;;
stop)
stop;;
log)
log;;
moniter)
moniter;;
rmlog)
rmlog;;
esac
配置输入源与输出源:
输入源为我们要要读取哪个文件的数据。
输出源可以输出到控制台、logstash、kafka等,但是不能同时配置输出多个位置,这里我们先配置输出到控制台测试读取数据是否成功,然后再改成输出到logstash。(输出到kafka这里就先不演示了,kafka的配置我下面已经列出来了。)
[root@shenyulong filebeat-7.10.0]# vi filebeat.yml
# ============================== Filebeat inputs ===============================
filebeat.inputs:
- type: log
# Change to true to enable this input configuration.
enabled: true
# Paths that should be crawled and fetched. Glob based paths.
paths:
- /yeYingXuan/app/log/*.log #要读取的文件目录,我这里读取了log下的所有.log文件。
输出到文件(方法一)
#output.file:
# path: "/yeYingXuan/app/filebeat"
# filename: "filebeat_test.log"
#输出到文件(方法二)
#output:
# file:
# path: "/yeYingXuan/app/filebeat"
# filename: "filebeat_test.log"
# rotate_every_kb: 1000
# number_of_files: 7
#输出到控制台(方法一)
#output:
# console:
# pretty: true
#输出到控制台(方法二)
#output.console:
# enable: true
# ============================== Filebeat modules ==============================
#默认的
filebeat.config.modules:
# Glob pattern for configuration loading
path: ${path.config}/modules.d/*.yml
# Set to true to enable config reloading
reload.enabled: true
# Period on which files under path should be checked for changes
reload.period: 10s
# ------------------------------ Logstash Output -------------------------------
#输出到logstash
#output.logstash:
# hosts: ["localhost:5044"]
#--------------------------------- kafka Output --------------------------------
#输出到kafka(使用时修改对应的kafka机器和topic(topic在kafka创建))
output.kafka:
hosts: ["*.*.*.*:8422","*.*.*.*:8422","*.*.*.*:8422"]
topic: yeYingXuanLogTest
enabled: true
#username: "yeYingXuan_kafka"
#password: "**********"
因为我们要看控制台有没有读取到数据,所以这里先不用启动脚本后台执行。
[root@shenyulong filebeat-7.10.0]# ./filebeat -c /yeYingXuan/app/filebeat/filebeat-7.10.0/filebeat.yml -e
查看控制台结果:
可以看到已经读取到了日志数据,证明到此为止filebeat已经配置成功,将输出源改成输出到logstarch。
接下来我们来配置logstarch。
logstarch篇:
下载:
curl -L -O https://artifacts.elastic.co/downloads/logstash/logstash-7.10.0-linux-aarch64.tar.gz
解压:
[root@shenyulong logstash]# tar -zxvf logstash-7.10.0-linux-aarch64.tar.gz
修改文件名
[root@shenyulong logstash]# mv logstash-7.10.0-linux-aarch64.tar.gz logstash-7.10.0
目录:
我这里就只列主要的配置目录config了。
config目录
-rw-r--r--. 1 root wheel 2019 11月 10 2020 jvm.options
-rw-r--r--. 1 root wheel 9097 11月 10 2020 log4j2.properties
-rw-r--r--. 1 root root 1908 5月 29 03:58 logstarch.config
-rw-r--r--. 1 root wheel 342 11月 10 2020 logstash-sample.conf
-rw-r--r--. 1 root wheel 11194 11月 10 2020 logstash.yml
-rw-r--r--. 1 root wheel 3693 11月 10 2020 pipelines.yml
-rw-r--r--. 1 root root 1449 5月 29 03:59 rubyLogParse.rb
-rw-r--r--. 1 root wheel 1696 11月 10 2020 startup.options
-rw-r--r--. 1 root root 1054 5月 29 01:42 templete_log.json
主要的几个文件介绍:
logstarch.config:需要自己创建,作用是配置输入源、输出源、过滤器、格式化日期、调用格式化日志配置。
rubyLogParse.rb:需要自己创建,作用为logstash解析处理日志逻辑的配置文件。
templete_log.json:需要自己创建,作用为入es时候的模板文件。
详细配置:
logstarch.config配置:输入员可以配置为kafka和filebeat,我这里没用kafka,配置为filebeat,配置为kafka为输入源的配置我也写了,因为用不上我这先给注释掉。输出源可以配置为输出到控制台、es,这里输出源2个可以共存
input {
beats {
# filebeat的端口是5044,从filebeat里读取数据。
port => 5044
}
#kafka{
# id => "kfk43" #无关键作用,唯一即可
#kafka机器
# bootstrap_servers=>["*.*.*.*:8422,*.*.*.*:8422,*.*.*.*:8422"]
#从哪个topic获取消息
# topics => "yeYingXuanLogTest"
#消费者名(订阅者topics的消费者)
# group_id =>"c_yeYingXuanLogTest"
# consumer_threads => 10 #消费线程数,集群中所有logstash相加最好等于 topic 分区数
#以下3个默认
# auto_offset_reset => "latest"
# decorate_events => false
# codec => json {
# charset => "UTF-8"
# }
#}
}
filter {
#执行解析逻辑
ruby{
id=>"ls43"
#指向rubyLogParse.rb 指向解析日志
path =>"/yeYingXuan/app/logstash/logstash-7.10.0/config/rubyLogParse.rb"
}
#格式化日期
date{
match=>["requestTime","yyyy-MM-dd HH:mm:ss"]
target =>"@timestamp"
locale=>"en"
timezone =>"+00:00"
}
#删除多余字段,减少es的存储空间(不想要哪个字段就在此去除)
#mutate{
# remove_field => ["host","log","ecs","agent","@version","input","message","tags"]
#}
}
output {
# 在这里配置 Logstash 要发送数据的地方,例如 Elasticsearch
stdout { codec => rubydebug } #---》输出到控制台,方便看日志
elasticsearch{
codec => plain{charset => "UTF-8"}
# %{+YYYY-MM-dd} 和%{+YYYY.MM.dd}都行,具体看你要设置成什么样
index => "yeyingxuan-farmlog-%{+YYYY-MM-dd}" #字母大写会报错
#es机器
hosts => [ "http://localhost:9200" ]
manage_template => true #开启logstash自动管理模板功能
template_overwrite => true
#模板名,取的是templete_log.json 文件里的"index_patterns" : ["yeYingXuan*"]
template_name => "yeYingXuan"
#指向模板文件路径
template => "/yeYingXuan/app/logstash/logstash-7.10.0/config/templete_log.json"
#es用户名/密码(es如果没有设置用户名可以不写)
user => "elastic"
password => "*******"
}
}
rubyLogParse.rb配置:
#解析日志(event.set是最终展示的结果,logdata是采集到的日志)
def farmLog(event,logdata)
#根据%分割日志(1.url 2.请求时间 3.用户id 4.农场id 5.请求报文 5.返回报文)
log = logdata.split("%")
#打印log
# print "#{log[1]}" #控制台输出
#获取接口url
urlArray = log[1].split(":")
event.set("url", urlArray[1])
#获取用户id
userIdArray = log[3].split(":")
event.set("userId", userIdArray[1])
#获取农场id
farmIdArray = log[4].split(":")
event.set("farmId", farmIdArray[1])
#获取请求报文
reqMessageArray = log[5].split(":")
event.set("reqMessage", reqMessageArray[1])
#获取返回报文
respMessageArray = log[6].split(":")
event.set("respMessage", respMessageArray[1])
end
def filter(event)
#取出日志信息(就当这里是固定的,取'message'就能取到日志)
logdata=event.get('message')
# print "------打印logdata------ #{logdata}"
#如果日志不是短信日志,缓存监控日志,缓存日志 Access nodechanged
#解析日志级别和请求时间,并保存到event对象中
# logleve=logdata[(logdata.index('[')+1)...logdata.index(']')]
# event.set('logleve',logleve)#截取日志级别
# event.set('requestTime',logdata[0,logdata.index(' - [')])
#我这里只取日志里包含'farmLog'的日志数据。
if(logdata.include?'farmLog')
#执行解析接口逻辑
farmLog event,logdata
end
return [event]
end
templete_log.json配置:配置入es时候的字段类型。
{
"index_patterns": [
"yeYingXuan*"
],
"settings": {
"index": {
"max_result_window": "10000",
"refresh_interval": "10s",
"number_of_shards": "5",
"translog": {
"flush_threshold_size": "500m",
"sync_interval": "5s",
"durability": "async"
},
"number_of_replicas": "1"
}
},
"mappings": {
"properties": {
"host_name": {
"type": "keyword"
},
"requestTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"userId": {
"type": "integer"
},
"farmId": {
"type": "integer"
},
"url": {
"type": "keyword"
},
"reqMessage": {
"type": "keyword"
},
"respMessage": {
"type": "keyword"
}
}
}
}
启动:
[root@shenyulong logstash-7.10.0]# bin/logstash -f config/logstarch.config
因为配置了输出源为控制台,这里我们验证下是否配置成功,可以看到已经成功的解析了我们日志数据。
下面为了节省时间,就直接下载docker镜像了
ES下载:
docker pull docker.elastic.co/elasticsearch/elasticsearch:8.3.2
启动:
docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" -e "ELASTIC_PASSWORD=changeme" docker.elastic.co/elasticsearch/elasticsearch:8.3.2
启动后请求路径:https://*.*.*.*:9200/
用户名:elastic
密码:******
获取Elasticsearch容器的IP地址:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' elasticsearch
检查Elasticsearch的健康状态:
curl -k -u elastic:changeme -X GET "https://localhost:9200/_cluster/health"
HTTPS 改为 HTTP,需要禁用 HTTP 和传输层的 SSL。
进入容器
[root@shenyulong ~]# sudo docker exec -it a39909d75826 /bin/bash
elasticsearch@a39909d75826:~$
elasticsearch@a39909d75826:~$ vi config/elasticsearch.yml
因为开启ssl的话启动Kibana的时候会校验证书,这里为了方便,先将配置ssl禁用,这4处改为false
重启容器
[root@shenyulong ~]# docker restart a39909d75826
安装Kibana:
[root@shenyulong ~]# docker pull docker.elastic.co/kibana/kibana:8.3.2
运行容器:
[root@shenyulong ~]# docker run -d --name kibana -p 5601:5601 -e "ELASTICSEARCH_HOSTS=http://localhost:9200" docker.elastic.co/kibana/kibana:8.3.2
启动成功后访问:http://*.*.*.*:5601
可以看到我们日志数据已经成功的插入到了es中。