ELK是三个开源软件的缩写,分别为:Elasticsearch 、 Logstash以及Kibana , 它们都是开源软件。不过现在还新增了一个Beats,它是一个轻量级的日志收集处理工具(Agent),Beats占用资源少,适合于在各个服务器上搜集日志后传输给Logstash,官方也推荐此工具,目前由于原本的ELK Stack成员中加入了 Beats 工具所以已改名为Elastic Stack.
Elastic Stack包含:
我的需求是,需要采集两种日志,一种是正常的系统日志,这个日志异常排查做服务的,还有一种日志是自定义日志结构,这个日志用来统计分析.
在这里我们使用docker来部署ElK,我们使用docker hub 上的镜像来进行搭建.
我们这里使用docker hub 上的sebp/elk镜像,网站为https://hub.docker.com/r/sebp/elk.下面我针对该镜像开始介绍搭建.我们以670为例.
在你的系统中运行 docker pull sebp/elk:670
docker run -p 5601:5601 -p 9200:9200 -p 5044:5044 -p 9300:9300 -p 9201:9201 -v /home/ubuntu/iri/chenzhe/logstash/config:/etc/logstash/conf.d/ -v /home/ubuntu/iri/chenzhe/elasticsearch/elasticsearch.yml:/etc/elasticsearch/elasticsearch.yml -it --name elk --restart=always sebp/elk:670
上面了命令我们做了端口映射,和文件映射,
做目录映射的目的是为了方便我们操作容器内部的配置文件,防止因为直接修改容器内的配置文件,出错后容器跑不起来,我们还改不了配置文件,只能重新搭建.
容器创建后会默认启动全部程序,你可能遇到一些错误
当你完成上面的步骤之后到这里你的ELK环境已经搭建完成,下面我们针对我遇到的实际需求来讲解我的实现过程.
可以看到我们将日志文件全部输出到了/opt/logs/tdf-cloud/zipkin/zipkin.log下
CONSOLE_LOG_PATTERN 是日志采集的正则表达式符合规则的日志才进行采集
这个日志配置你可以在Spirng Cloud Sleuth 官网中看到
https://cloud.spring.io/spring-cloud-static/spring-cloud-sleuth/2.2.0.M1/
这个配置文件你必须在你每个项目下都配置一个,你可以根据不同的项目将日志输出到不同的目录下.
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<property resource="bootstrap.yml" />
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
<property name="LOG_FILE" value="/opt/logs/tdf-cloud/zipkin/zipkin.log"/>
<property name="CONSOLE_LOG_PATTERN"
value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUGlevel>
filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>utf8charset>
encoder>
appender>
<appender name="flatfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gzfileNamePattern>
<maxHistory>7maxHistory>
rollingPolicy>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>utf8charset>
encoder>
appender>
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="flatfile"/>
root>
configuration>
docker run
-p 8770:8770
-e “SPRING_PROFILES_ACTIVE=test”
–name tdf-cloud-zipkin
–restart=always
-e “eureka.instance.hostname=192.168.70.40”
-e “spring.cloud.client.hostname=192.168.70.40”
-v /opt/logs/tdf-cloud:/opt/logs/tdf-cloud \
我们这里动态传入了,注册中心地址和服务器地址. 同时做了目录映射.做目录映射的目的是将容器内的日志,映射到服务器中.
在日志输出到文件后,我进行了查看,发现乱码,里面有大量的ESC字样,查找问题发现,是因为SpringBoot的彩色日志导致的.通过spring.output.ansi.enabled=NEVER 可以解决这个问题.
因为我们需要通过FileBeat采集日志,后将日志输出到LogStash.再有LogStash存到ES中
https://www.elastic.co/guide/en/beats/filebeat/6.7/index.html
这里我们使用docker-compose进行部署
version: '3'
services:
filebeat:
image: docker.elastic.co/beats/filebeat:6.7.0
restart: always
user: root
volumes:
- /opt/logs/tdf-cloud:/opt/logs/tdf-cloud
- /home/ubuntu/iri/chenzhe/filebeat/config/filebeat.yml
:/usr/share/filebeat/filebeat.yml
我们这里将容器下的/opt/logs/tdf-cloud和Ubuntu上的/opt/logs/tdf-cloud做了目录映射.注意在Ubuntu下的opt/logs/tdf-cloud因为已经和部署服务的容器做了目录映射,里面已经有日志文件了.我再将Ubuntu的目录和filebeat容器做目录映射,所有的日志文件都会到filebeat容器中.
filebeat采集自己容器下的目录,/opt/logs/tdf-cloud/因为做了目录映射这时候已经全是日志了.
filebeat.inputs:
- type: log
enabled: true
paths:
- /opt/logs/tdf-cloud/*/*/*.log
output.logstash:
hosts: ["192.168.70.40:5044"]
fileBeat已经采集到了两种结构不同的日志,全部输出到了LogStash中我们需要在LogStash中区分两种日志,并存储到ES不同的索引中.
https://www.elastic.co/guide/en/logstash/6.7/index.html
2019-07-26 01:44:24.602 INFO [tdf-cloud-gateway,085bc5cef3a9ec0a,085bc5cef3a9ec0a,true] 1 — [or-http-epoll-9] c.c.t.gateway.filter.LocalLimiterFilter : 10.0.52.92 admin tdf-service-sys 2019-07-26T01:44:24.557Z 2019-07-26T01:44:24.602Z true
"%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}]\s+%{DATA:pid}\s±–\s+[%{DATA:thread}]\s+%{DATA:class}\s+:\s+%{IP:ip}\s+%{USERNAME:usernmae}\s+%{NOTSPACE:appname}\s+%{TIMESTAMP_ISO8601:requesttime}\s+%{TIMESTAMP_ISO8601:reponsetime}\s+%{NOTSPACE:success}
2019-07-26 01:46:55.618 INFO [tdf-cloud-gateway,1bb08e068c422a72,1bb08e068c422a72,false] 1 — [r-http-epoll-66] c.c.t.gateway.filter.LocalLimiterFilter : /assets/js/sba-core.ad0df74c.js:------------: 13ms
%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}]\s+%{DATA:pid}\s±–\s+[%{DATA:thread}]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}
https://github.com/elastic/logstash/blob/v1.4.2/patterns/grok-patterns
可以用Kibana里的Dev Tools 里面有Grok Debugger. 建议在里面调试好,调试正则没有太好的方法,就把那里面的正则表达式全都试一下,看那个能解析出来就可以了.
input {
beats {
port => 5044
type => syslog
}
}
filter {
grok {
match => [
"message","%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{IP:ip}\s+%{USERNAME:usernmae}\s+%{NOTSPACE:appname}\s+%{TIMESTAMP_ISO8601:requesttime}\s+%{TIMESTAMP_ISO8601:reponsetime}\s+%{NOTSPACE:success}",
"message", "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}"
]
}
}
output {
if[type]=="sleuth"{
elasticsearch {
hosts => ["localhost"]
manage_template => false
index => "sleuth-%{+YYYY.MM.dd}"
document_type => "sleuth"
}
}
if [type]=="syslog" and [success]=="true" or [success]=="false" {
elasticsearch {
hosts => ["localhost"]
index => "gatewaylog-%{+YYYY.MM.dd}"
document_type => "gatewaylog"
template => "/etc/logstash/gatewaylog.json"
template_name => "gatewaylog"
template_overwrite => true
}
}
if [type]=="syslog" and ![requesttime]{
elasticsearch {
hosts => ["localhost"]
manage_template => false
index => "syslog-%{+YYYY.MM.dd}"
document_type => "syslog"
}
}
}
可以看到我给了它一个类型同时,我创建了一个过滤器,使用grok 配置了日志格式的正则表达;符合规则的日志会被解析成JSON,最后存到ES中
在第一个if中我根据类型判断将数据存到那个index中,第二个和第三个if是因为他们的类型都是syslog但是里面的字段是不一样的,这个时候我根据字段进行了判断,应该把数据放到那个index里面,template是自己定义的模板,因为有的字段在ES中不建立索引,所以我自己建立了一个模板,这样ES就会将我的字段建立索引,完了我就可以使用Kibana进行字段过滤.
{
"template":"gatewaylog-*",
"order":1,
"settings":{
"number_of_shards":5,
"number_of_replicas":0
},
"mappings":{
"_default_":{
"_all":{
"enabled":true,
"omit_norms":true
},
"dynamic_templates":[
{
"message_field":{
"match":"message",
"match_mapping_type":"string",
"mapping":{
"type":"string",
"index":"analyzed",
"search_analyzer":"ik_max_word",
"analyzer":"ik_max_word",
"omit_norms":true,
"fielddata":{
"format":"disabled"
}
}
}
},
{
"string_fields":{
"match":"*",
"match_mapping_type":"string",
"mapping":{
"type":"string",
"index":"not_analyzed",
"doc_values":true
}
}
}
],
"properties":{
"@timestamp":{
"type":"date"
},
"@version":{
"type":"string",
"index":"not_analyzed"
}
},
"dynamic_date_formats":[
"yyyy-MM-dd HH:mm:ss.SSS"
]
}
}
}
https://www.elastic.co/guide/en/elasticsearch/reference/6.7/index.html
因为ES部署在docker容器中,在我使用java 操作ES的时候,始终获取不到连接,所以我在elasticsearch.yml中配置了network.publish_host: 192.168.70.40 192.168.70.40 是Ubuntu的地址,这样你的服务器就相当于一个代理,就可以通过服务器连接到ES.
ES默认只支持localhost访问,其他机器想访问需要配置network.host: 0.0.0.0 我这里配置的是允许所有访问.
ES Head插件不能连接,出现跨域问题 http.cors.enabled: true http.cors.allow-origin: “*” 配置这两项就可以了,
上面都完成后,数据已经到ES中了,接下来可以通过Kibana进行查看了.
https://www.elastic.co/guide/en/kibana/6.7/index.html
服务器ip:5601 ,访问之后你会看到一个default的工作空间.我们先进去
点击左下角的Default,选择Manage Space .再点击Creeat Space ,为你的空间起个名字,创建后,再点击左下角的Default你会看到你刚刚创建的空间,完了你会看到Try our sample data 这个是Kibana提供的demo ,我们选择用自己的,
进入空间后我们我们选择Discover这个时候,我们可以看到Create index pattern这时候你要填写的是索引名,这个索引名它是支持正则的比如说我现在有gatewaylog-2019.07.11 gatewaylog-2019.07.19 我现在填写gatewaylog-* 是都可以查询出来了
我们在点击Discover,我们这时可以看到数据,那怎么可以看到你想看的数据.举个例子,我现在的数据是系统日志数据,我现在想看Error级别的日志,这是后点击 Add a filter 我们日志级别的字段是 severity .我们输入字段 选择is value 输入 ERROR .就能查出所有的错误日志