一、组件介绍
1.1 Filebeat
Filebeat是本地文件的日志数据采集器,可监控日志目录或特定日志文件(tail file),并将它们转发给Elasticsearch或Logstatsh进行索引、kafka等。带有内部模块(auditd,Apache,Nginx,System和MySQL),可通过一个指定命令来简化通用日志格式的收集,解析和可视化。
Filebeat涉及两个组件:查找器prospector和采集器harvester,来读取文件(tail file)并将事件数据发送到指定的输出。
启动Filebeat时,它会启动一个或多个查找器prospector,查看你为日志文件指定的本地路径。对于prospector所在的每个日志文件,prospector启动采集器harvester。每个harvester都会为新内容读取单个日志文件,并将新日志数据发送到libbeat,后者将聚合事件并将聚合数据发送到Filebeat配置的输出。
ELK是Logstash、Elasticsearch、Kibana三大开源框架首字母大写简称。
1.2 Logstash
Logstash是一个数据分析软件,主要目的是分析log日志。如上图所示,Logstash主要分为三个部分,input、filter和output(input:设置数据来源 ; filter:可以对数据进行一定的加工处理过滤 ; output:设置输出目标)
filter是logstash功能强大的原因,filter的插件介绍:
①grok
grok模式的语法如下:
%{SYNTAX:SEMANTIC}
SYNTAX:代表匹配值的类型,例如3.44可以用NUMBER类型所匹配,127.0.0.1可以使用IP类型匹配。 SEMANTIC:代表存储该值的一个变量名称,例如 3.44 可能是一个事件的持续时间,127.0.0.1可能是请求的client地址。所以这两个值可以用 %{NUMBER:duration} %{IP:client} 来匹配,IPORHOST、HTTPDATE等是封装过的正则表达式,直接使用即可,无需自己再写。。
也可以选择将数据类型转换添加到Grok模式。默认情况下,所有语义都保存为字符串。如果想转换成默认的数据类型,可以%{NUMBER:num:int}将num语义从一个字符串转换为一个整数。目前唯一支持的转换是int和float。
# 解析日志生成相关的IP,访问地址,日志级别
grok {
match => {
"message" => "%{SYSLOG5424SD:time} %{IP:hostip} %{URIPATHPARAM:url}\s*%{LOGLEVEL:loglevel}"
}
}
#解析log生成的时间为时间戳
grok{
match => {
"message" => "%{TIMESTAMP_ISO8601:log_create_date}"
}
}
}
②geoip
根据ip地址获取经纬度,国家,城市,地区等信息。
wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz
tar -zxvf GeoLite2-City.tar.gz
cp GeoLite2-City.mmdb /data/logstash/
filter {
geoip {
source => "message"
}
}
output{stdout{codec=>rubydebug}}
#输入:183.60.92.253
#输出:
{
"type" => "std",
"@version" => "1",
"@timestamp" => 2019-03-19T05:39:26.714Z,
"host" => "zzc-203",
"message" => "183.60.92.253",
"geoip" => {
"country_code3" => "CN",
"latitude" => 23.1167,
"region_code" => "44",
"region_name" => "Guangdong",
"location" => {
"lon" => 113.25,
"lat" => 23.1167
},
"city_name" => "Guangzhou",
"country_name" => "China",
"continent_code" => "AS",
"country_code2" => "CN",
"timezone" => "Asia/Shanghai",
"ip" => "183.60.92.253",
"longitude" => 113.25
}
}
filter {
geoip {
source => "http_x_forwarded_for" # 取自nginx中的客户端ip
target => "geoip"
database => "/data/logstash/GeoLite2-City.mmdb"
add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}" ]
}
mutate {
convert => [ "[geoip][coordinates]", "float" ]
}
}
③mutate
filter {
mutate {
#将字段的值转换为其他类型,例如将字符串转换为整数。如果字段值是数组,则将转换所有成员。
convert => {
"fieldname" => "integer"
"booleanfield" => "boolean"
}
}
mutate {
#将现有字段复制到另一个字段。将覆盖现有目标字段。
copy => { "source_field" => "dest_field" }
}
mutate {
#使用分隔符将字段拆分为数组
split => { "fieldname" => "," }
}
}
④date
# 将log_create_date日期解析为指定格式,并将@timestamp字段替换为该日期
date {
match => [ "log_create_date", "yyyy-MM-dd HH:mm:ss" ]
target => "@timestamp"
}
}
⑤useragent
filter {
if [user_ua] != "-" {
useragent {
target => "agent" #agent将过来出的user agent的信息配置到了单独的字段中
source => "user_ua" #这个表示对message里面的哪个字段进行分析
}
}
}
⑥ruby脚本
filter {
#利用ruby代码来动态修改logstash event
ruby {
# Cancel 90% of events
path => "/mnt/elastic/logstash-6.5.1/config/test.rb"
# script_params => { "message" => "%{message}" }
}
json {
source => "json"
remove_field => ["json","message"]
}
}
⑦dissect:分隔符解析
filter {
dissect {
mapping => {
"message" => "%{ts} %{+ts} %{+ts} %{src} %{} %{prog}[%{pid}]: %{msg}"
}
}
}
Dissect过滤器是一种拆分操作。与常规拆分操作(其中一个分隔符应用于整个字符串)不同,此操作将一组分隔符应用于字符串值。Dissect不使用正则表达式,速度非常快。
语法解释
我们看到上面使用了和Grok很类似的%{}语法来表示字段,这显然是基于习惯延续的考虑。不过示例中%{+ts}的加号就不一般了。dissect 除了字段外面的字符串定位功能以外,还通过几个特殊符号来处理字段提取的规则:
%{+key}这个+表示,前面已经捕获到一个key字段了,而这次捕获的内容,自动添补到之前 key 字段内容的后面。
%{+key/2}这个/2表示,在有多次捕获内容都填到 key字段里的时候,拼接字符串的顺序谁前谁后。/2表示排第2位。
%{}是一个空的跳过字段。
%{?string}这个?表示,这块只是一个占位,并不会实际生成捕获字段存到事件里面。
%{?string} %{&string}当同样捕获名称都是string,但是一个?一个&的时候,表示这是一个键值对。
填充符
字段的->后缀例如%{function->},表示忽略它右边的填充,否则右边的多余填充将拆分到下一个字段中。
1.3 Elasticsearch
Elasticsearch是一个开源的分布式、RESTful 风格的搜索和数据分析引擎,它的底层是开源库Apache Lucene。Lucene是当下最先进、高性能、全功能的搜索引擎库。为了解决Lucene使用时的繁复性,Elasticsearch对其进行了封装,内部采用 Lucene 做索引与搜索,提供了一套简单一致的 RESTful API 来帮助我们实现存储和检索。 当然,Elasticsearch 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎。 它可以被下面这样准确地形容:
一个分布式的实时文档存储,每个字段可以被索引与搜索;
一个分布式实时分析搜索引擎;
能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据。
由于Elasticsearch的功能强大和使用简单,维基百科、卫报、Stack Overflow、GitHub等都纷纷采用它来做搜索。现在,Elasticsearch已成为全文搜索领域的主流软件之一。
1.4 Kibana
Kibana 是为 Elasticsearch设计的开源分析和可视化平台。你可以使用 Kibana 来搜索,查看存储在 Elasticsearch 索引中的数据并与之交互。你可以很容易实现高级的数据分析和可视化,以图标的形式展现出来。
实时监控:通过 histogram 面板,配合不同条件的多个 queries 可以对一个事件走很多个维度组合出不同的时间序列走势。时间序列数据是最常见的监控报警了。 问题分析:搜索,通过下钻数据排查问题,通过分析根本原因来解决问题;实时可见性,可以将对系统的检测和警报结合在一起,便于跟踪 SLA 和性能问题;历史分析,可以从中找出趋势和历史模式,行为基线和阈值,生成一致性报告。
总结:
整一套软件可以当作一个MVC模型,logstash是controller层,Elasticsearch是一个model层,kibana是view层。首先将数据传给logstash,它将数据进行过滤和格式化(转成JSON格式),然后传给Elasticsearch进行存储、建搜索的索引,kibana提供前端的页面再进行搜索和图表可视化,它是调用Elasticsearch的接口返回的数据进行可视化。logstash和Elasticsearch是用Java写的,kibana使用node.js框架。
二、总体结构
三、准备工作
3.1 微服务配置
添加依赖
net.logstash.logback
logstash-logback-encoder
4.10
在微服务classpath路径下添加logback日志框架的配置文件logback.xml
${SERVICE_NAME}
INFO
${CONSOLE_LOG_PATTERN}
utf8
logs/logFile.%d{yyyy-MM-dd}.log
30
10MB
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level
%logger{50} - %msg %n
192.168.32.128:5044
UTC
{
"level": "%level",
"service": "${springAppName:-}",
"trace": "%X{X-B3-TraceId:-}",
"span": "%X{X-B3-SpanId:-}",
"exportable": "%X{X-Span-Export:-}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"stack_trace": "%exception{30}",
"message": "%message"
}
logback在.yml文件中的配置(会每日滚动生成带有日期后缀的文件): logging: level: root: info file: logs/${spring.application.name}.log
3.2 Nginx配置
Nginx配置文件规定日志输出格式:
log_format main '$server_name $remote_addr - $remote_user [$time_local] "$request" '
'$status $upstream_status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$ssl_protocol $ssl_cipher $upstream_addr $request_time $upstream_response_time';
如果不经过logstash的处理,可以直接写成json格式:
log_format main '{"server_name":"$server_name",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"time_local":"$time_local",'
'"request":"$request",'
'"status":"$status",'
'"upstream_status":"$upstream_status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for",'
'"ssl_protocol":"$ssl_protocol",'
'"ssl_cipher":"$ssl_cipher",'
'"upstream_addr":"$upstream_addr",'
'"request_time ":"$request_time",'
'"upstream_response_time":"$upstream_response_time"'
'}';
访问日志中一个典型的记录如下:
192.168.1.102 - scq2099yt [18/Mar/2013:23:30:42 +0800] "GET /stats/awstats.pl?config=scq2099yt HTTP/1.1" 200 899 "http://192.168.1.1/pv/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows XXX; Maxthon)"
每个样式的含义如下:
$server_name:虚拟主机名称。
$remote_addr:远程客户端的IP地址。
-:空白,用一个“-”占位符替代,历史原因导致还存在。
$remote_user:远程客户端用户名称,用于记录浏览者进行身份验证时提供的名字,如登录百度的用户名scq2099yt,如果没有登录就是空白。
[$time_local]:访问的时间与时区,比如18/Jul/2012:17:00:01 +0800,时间信息最后的"+0800"表示服务器所处时区位于UTC之后的8小时。
$request:请求的URI和HTTP协议,这是整个PV日志记录中最有用的信息,记录服务器收到一个什么样的请求
$status:记录请求返回的http状态码,比如成功是200。
$upstream_status:upstream状态,比如成功是200.
$upstream_addr:后端服务器的IP地址
在server{}中添加:add_header backendIP $upstream_addr;add_header backendCode $upstream_status;
$body_bytes_sent:发送给客户端的文件主体内容的大小,比如899,可以将日志每条记录中的这个值累加起来以粗略估计服务器吞吐量。
$http_referer:记录从哪个页面链接访问过来的。
$http_user_agent:客户端浏览器信息
$http_x_forwarded_for:客户端的真实ip,通常web服务器放在反向代理的后面,这样就不能获取到客户的IP地址了,通过$remote_add拿到的IP地址是反向代理服务器的iP地址。反向代理服务器在转发请求的http头信息中,可以增加x_forwarded_for信息,用以记录原有客户端的IP地址和原来客户端的请求的服务器地址。
$ssl_protocol:SSL协议版本,比如TLSv1。
$ssl_cipher:交换数据中的算法,比如RC4-SHA。
$upstream_addr:upstream的地址,即真正提供服务的主机地址。
$request_time:整个请求的总时间。
$upstream_response_time:请求过程中,upstream的响应时间。
四、拉取镜像
注意filebeat和elk的版本需要一致
docker pull sebp/elk:7.6.1
docker pull elastic/filebeat:7.6.1
五、配置文件
1)宿主机上创建文件夹
mkdir -p /root/elk4log/conf.d
mkdir -p /root/filebeat/conf.d
2)在文件夹下编写配置文件
vim /root/elk4log/conf.d/02-beats-input.conf
input {
tcp {
mode => "server"
port => 5044
codec => json_lines
type => "microserver"
}
}
input {
beats {
port => 5045
codec => json
type => "nginx"
}
}
filter {
date {
match => ["time_local","dd/MMM/yyyy:HH:mm:ss Z"] # 解析"14/Mar/2017:00:00:02 +0800"
target => "time_local" # 指定覆盖到的字段,缺省为@timestamp
}
}
output{
if [type] == "microserver" {
elasticsearch {
hosts => ["192.168.32.128:9200"] # 127.0.0.7:9200
index => "smartcook-log-%{+YYYY.MM.dd}"
#manage_template => true
#template => ""
#template_name => "apache_elastic_example"
#template_overwrite => true
}
}
if [type] == "nginx" {
elasticsearch {
hosts => ["192.168.32.128:9200"] # 127.0.0.7:9200
index => "nginx-zuul-log-%{+YYYY.MM.dd}"
manage_template => true
#文件中的template的名字需要与index对应,用*匹配日期
template => "/etc/logstash/conf.d/gateway_zuul_template.json"
template_name => "nginx-zuul-log"
template_overwrite => true
}
}
}
vim /root/elk4log/conf.d/gateway_zuul_template.json
{
"order": 0,
"template": "nginx-zuul-log*",
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": "1",
"refresh_interval": "20s"
}
},
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"@version": {
"type": "keyword"
},
"server_name": {
"type":"keyword"
},
"remote_addr": {
"type": "text"
},
"remote_user": {
"type":"keyword"
},
"time_local": {
"type": "date"
},
"request": {
"type": "text"
},
"status": {
"type": "integer"
},
"upstream_status": {
"type": "integer"
},
"body_bytes_sent": {
"type": "long"
},
"http_referer": {
"type": "text"
},
"http_user_agent": {
"type": "text"
},
"http_x_forwarded_for": {
"type": "text"
},
"ssl_protocol": {
"type": "keyword"
},
"ssl_cipher": {
"type": "keyword"
},
"upstream_addr": {
"type": "text"
},
"request_time": {
"type": "date"
},
"upstream_response_time": {
"type": "long"
}
}
}
}
vim /root/filebeat/conf.d/filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/gateway_access.log
#============================= 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
output.logstash:
# The Logstash hosts
hosts: ["192.168.32.128:5045"]
六、启动容器
6.1 启动elk
docker run -d --name elk4log -p 5601:5601 -p 9200:9200 -p 5044:5044 -p 5045:5045 -v /root/elk4log/logstash/conf.d:/etc/logstash/conf.d -v /root/elk4log/elasticsearch/config:/opt/elasticsearch/config -v /root/elk4log/elasticsearch/data:/var/lib/elasticsearch/nodes sebp/elk
注: #5601 - Kibana web 接口 #9200 - Elasticsearch JSON 接口 #5044 - Logstash 微服务日志监听接口 #5045 - Logstash Nginx日志监听接口
6.2 启动filebeat
docker run -d --name=filebeat -v /root/filebeat/conf.d/filebeat.yml:/usr/share/filebeat/filebeat.yml --volumes-from nginx elastic/filebeat:7.6.1
注 : 如果es启动报错,需要先修改宿主机的配置
sudo sh -c "echo 'vm.max_map_count=655360' >> /etc/sysctl.conf"
sysctl -p 使配置生效
6.3 访问UI页面
通过http://192.168.32.128:5601访问Kibana
注:时区问题 logstash和es使用的都是UTC时间,而kibana默认读取浏览器的时区,所以kibana显示的时间会比logstash和es的多8小时。如果需要调整如下:
filter {
date {
match => ["message","UNIX_MS"]
target => "@timestamp"
}
ruby {
code => "event.set('timestamp', event.get('@timestamp').time.localtime + 8*60*60)"
}
ruby {
code => "event.set('@timestamp',event.get('timestamp'))"
}
mutate {
remove_field => ["timestamp"]
}
}
将日志通过logstash导入到redis中的文档参见:https://blog.csdn.net/d597180714/article/details/82382703