新手小白docker-compose搭建ELK, 解决Spring Boot分布式日志问题
- ELK平台简介
- ELK工作原理
- docker-ce,ELK环境
- 安装docker-ce工具
- 安装docker-compose工具
- 关闭防火墙
- max_map_count问题以及解决
- 编写docker-compose.yml安装、运行ELK及汉化Kibana,IK安装
- SpringBoot 2.3.7 集成Logstash
- 注意生产环境不要使用上面的方式,而是使用 fileBeat+logstash
- 注意:添加5044的docker端口映射
ELK平台简介
在搜索ELK资料的时候,发现这篇文章比较好,于是摘抄一小段:
以下内容来自:http://baidu.blog.51cto.com/71938/1676798
日志主要包括系统日志、应用程序日志和安全日志。系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因。经常分析日志可以了解服务器的负荷,性能安全性,从而及时采取措施纠正错误。
通常,日志被分散的储存不同的设备上。如果你管理数十上百台服务器,你还在使用依次登录每台机器的传统方法查阅日志。这样是不是感觉很繁琐和效率低下。当务之急我们使用集中化的日志管理,例如:开源的syslog,将所有服务器上的日志收集汇总。
集中化管理日志后,日志的统计和检索又成为一件比较麻烦的事情,一般我们使用grep、awk和wc等Linux命令能实现检索和统计,但是对于要求更高的查询、排序和统计等要求和庞大的机器数量依然使用这样的方法难免有点力不从心。
开源实时日志分析ELK平台能够完美的解决我们上述的问题,ELK由ElasticSearch、Logstash和Kiabana三个开源工具组成。官方网站:https://www.elastic.co/products
- Elasticsearch是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。
- Logstash是一个完全开源的工具,他可以对你的日志进行收集、过滤,并将其存储供以后使用(如,搜索)。
- Kibana 也是一个开源和免费的工具,它Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助您汇总、分析和搜索重要数据日志。
ELK工作原理
----------------------------摘抄内容结束-------------------------------
docker-ce,ELK环境
系统环境:Centos7.6 内核3.10.0以上
内存:4G以上
JDK: jdk1.8以上
升级CentOS内核
cat /proc/version #查看内核
yum update #升级内核
安装docker-ce工具
卸载旧版本
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
安装依赖包
yum install -y yum-utils device-mapper-persistent-data lvm2
添加Docker软件包源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
但是鉴于国内网络问题,建议使用国内阿里的源
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
执行安装,启动和设置docker服务开机自启动:
yum install -y docker-ce #安装docker
systemctl start docker #启动docker
systemctl enable docker #开机启动docker
systemctl disable docker #关闭开机自启动
配置docker加速器
vi /etc/docker/daemon.json
将下面代码加入文件,或者配置自己的阿里云镜像加速器 https://cr.console.aliyun.com/
中科大加速器地址,无需注册:https://docker.mirrors.ustc.edu.cn
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}
{
"log-driver":"json-file",
"log-opts": {"max-size":"500m", "max-file":"3"}
}
重启docker即可, 执行 docker version 有信息说明安装成功
systemctl daemon-reload #配置生效
systemctl restart docker #重启docker
docker容器日志清理 (选择性配置,磁盘大任性可忽略)
- max-size=500m 表示单个容器日志大小上限是500M
- max-file=3 表示单个容器有三个日志,分别是id+.json、id+1.json、id+2.json。
安装docker-compose工具
下载离线包 https://github.com/docker/compose/releases
然后将二进制docker-compose-Linux-x86_64文件上传到/root目录。
使用mv命令重命名:
mv docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
再授予可执行权限:
chmod +x /usr/local/bin/docker-compose
创建软链接(相当于windows的快捷方式):
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
查看docker-compose版本:
docker-compose --version
关闭防火墙
1.关闭防火墙
systemctl stop firewalld.service #执行关闭命令
systemctl status firewalld.service #查看防火墙状态
systemctl disable firewalld.service #执行开机禁用防火墙自启命令
2.开启防火墙
systemctl start firewalld.service #启动防火墙
systemctl enable firewalld.service #防火墙随系统开启启动
max_map_count问题以及解决
1.vi /etc/sysctl.conf
添加如下代码
vm.max_map_count=262144
2.sysctl -p #这个命令要加上,否则无效
编写docker-compose.yml安装、运行ELK及汉化Kibana,IK安装
mkdir -p /data/elk/{elasticsearch/data,logstash} #新建目录
1、编写docker-compose.yml
vi /data/elk/docker-compose.yml
插入以下文本
version: '3'
services:
elasticsearch:
image: elasticsearch:7.7.0 #镜像
container_name: elk_elasticsearch #定义容器名称
restart: always #开机启动,失败也会一直重启
environment:
- "cluster.name=elasticsearch-spring" #设置集群名称为elasticsearch
- "discovery.type=single-node" #以单一节点模式启动
- "ES_JAVA_OPTS=-Xms512m -Xmx1024m" #设置使用jvm内存大小
volumes:
- /data/elk/elasticsearch/plugins:/usr/share/elasticsearch/plugins #插件文件挂载
- /data/elk/elasticsearch/data:/usr/share/elasticsearch/data #数据文件挂载
ports:
- 9200:9200
kibana:
image: kibana:7.7.0
container_name: elk_kibana
restart: always
depends_on:
- elasticsearch #kibana在elasticsearch启动之后再启动
environment:
- ELASTICSEARCH_URL=http://elasticsearch:9200 #设置访问elasticsearch的地址
ports:
- 5601:5601
logstash:
image: logstash:7.7.0
container_name: elk_logstash
restart: always
volumes:
- /data/elk/logstash/logstash-springboot.conf:/usr/share/logstash/pipeline/logstash.conf #挂载logstash的配置文件
depends_on:
- elasticsearch #kibana在elasticsearch启动之后再启动
links:
- elasticsearch:es #可以用es这个域名访问elasticsearch服务
ports:
- 4560:4560
2、新建文件;安装、运行ELK
#授权目录
cd /data/elk
chmod 777 elasticsearch/data
#新建 vi /data/elk/logstash/logstash-springboot.conf 文件,新增以下内容
input {
tcp {
mode => "server"
host => "0.0.0.0"
port => 4560
codec => json_lines
}
}
output {
elasticsearch {
hosts => "es:9200"
index => "boutique-logstash-%{+YYYY.MM.dd}"
}
}
#安装,运行ELK
cd /data/elk
docker-compose up -d
docker ps
3、访问http://localhost:5601 (ip:192.168.9.128)
4、汉化kibana
docker exec -it elk_kibana /bin/bash # 进入kibanar容器
cd config # 进入容器的配置文件目录
vi kibana.yml # 编辑kibana.yml文本 末尾加入i18n.locale: zh-CN
docker restart elk_kibana # 重启kibana
5、访问汉化后的kibana http://localhost:5601
6、Kibana设置登录密码(开发环境 6 步骤可省略)
kibana默认没有访问的权限控制,如果需要设置访问的账号密码,可以使用nginx配置代理来发布kibana。
在 nginx 下,提供了 ngx_http_auth_basic_module 模块实现让用户只有输入正确的用户名密码才允许访问web内容。默认情况下,nginx 已经安装了该模块。所以整体的一个过程就是先用第三方工具设置用户名、密码(其中密码已经加过密),然后保存到文件中,接着在 nginx 配置文件中根据之前事先保存的文件开启访问验证。
7、安装IK分词器
上面我们安装的是ElasticSearch7.7.0,下载对应的IK分词包 ,elasticsearch挂载的路径是/data/elk/elasticsearch/plugins
下载网址:https://github.com/medcl/elasticsearch-analysis-pinyin/releases
1.将下载的ik包解压拷贝到挂载的路径‘/data/elk/elasticsearch/plugins’,并且重命名为ik
# 重启elasticsearch容器即可
docker restart [elasticsearch容器ID]
# 查看IK是否安装成功
docker exec -it [elasticsearch容器ID] /bin/bash
cd plugins/
ls
- 安装htpasswd工具
yum -y install httpd-tools
- 设置用户名和密码,并把用户名、密码保存到指定文件中:
htpasswd -c /data/nginx/htpasswd cfw #交互输入密码
- 修改nginx配置文件,并且重启nginx服务
user root; #修改配置文件用户为root
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://192.168.1.105:5601;
auth_basic "Please input password"; #这里是验证时的提示信息
auth_basic_user_file /data/nginx/htpasswd; #生成的密码存放路径
}
}
- 访问需要输入登录账号、密码
(补充)htpasswd 其他参数
命令格式:
htpasswd [-cmdpsD] [passwdfile] [username]
htpasswd -b[cmdpsD] [passwdfile] [username] [password]
htpasswd -n[mdps] username
htpasswd -nb[mdps] username password
参数说明:
-c 创建一个加密文件
-n 不更新加密文件,只将htpasswd命令加密后的用户名密码显示在屏幕上
-m 默认htpassswd命令采用MD5算法对密码进行加密
-d htpassswd命令采用CRYPT算法对密码进行加密
-p htpassswd命令不对密码进行进行加密,即明文密码
-s htpassswd命令采用SHA算法对密码进行加密
-b htpassswd命令行中一并输入用户名和密码而不是根据提示输入密码
-D 删除指定的用户
新增用户role02
htpasswd -b [passwdfile] [username] [passwd]
htpasswd -b /data/nginx/htpasswd role02 123456
SpringBoot 2.3.7 集成Logstash
- logstash中安装json_lines插件并重启logstash
docker exec -it elk_logstash /bin/bash -c "cd /bin && logstash-plugin install logstash-codec-json_lines"
docker restart elk_logstash
- 在SpringBoot项目pom.xml文件中添加logstash-logback-encoder依赖
<!--集成logstash-->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>5.2</version>
</dependency>
- logback-spring.xml文件添加配置,让logback的日志输出到logstash
<?xml version="1.0" encoding="UTF-8"?>
<!--该日志将日志级别不同的log信息保存到不同的文件中 -->
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<springProperty scope="context" name="springAppName"
source="spring.application.name"/>
<!-- 日志在工程中的输出位置 -->
<property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}"/>
<!-- 控制台的日志输出样式 -->
<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} %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>INFO</level>
</filter>
<!-- 日志输出编码 -->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 为logstash输出的JSON格式的Appender -->
<appender name="logstash"
class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!--Logstash服务地址-->
<destination>127.0.0.1:4560</destination>
<!-- 日志输出编码 -->
<encoder
class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"severity": "%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}",
"rest": "%message"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="logstash"/>
</root>
</configuration>
- 启动springboot应用
- 使用 kibana 查看日志信息
访问http://localhost:5601,创建索引 boutique-logstash-*
索引名称可以在 /data/elk/logstash/logstash-springboot.conf文件 output配置中修改
- 查看springboot服务输出到kibana的日志
注意生产环境不要使用上面的方式,而是使用 fileBeat+logstash
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 参考文档:https://github.com/apache/skywalking/blob/v8.7.0/docs/en/setup/service-agent/java-agent/Application-toolkit-logback-1.x.md-->
<configuration debug="false" scan="true" scanPeriod="5 seconds">
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!--微服务名称-->
<springProperty scope="context" name="appName" source="spring.application.name" defaultValue="none"/>
<!--打印格式-->
<property name="log_pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [${appName}] [%tid] [%thread] [%logger{36}:%M:%L] -%msg%n"/>
<!-- 日志在磁盘的输出位置 -->
<property name="log.path" value="/data/logs/lean-home"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern>${log_pattern}</Pattern>
</layout>
</encoder>
</appender>
<!--level为 INFO 日志,时间滚动输出 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/${appName}/info.log</file>
<!--日志文档输出格式-->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern>${log_pattern}</Pattern>
<!--Grok正则格式-->
<!--(?m)^%{TIMESTAMP_ISO8601:createTime}%{SPACE}\[%{LOGLEVEL:level}\]%{SPACE}\[%{GREEDYDATA:appName}\]%{SPACE}\[TID:%{GREEDYDATA:traceId}\]%{SPACE}\[%{DATA:thread}\]%{SPACE}\[%{JAVACLASS:class}:(?<method>[?a-zA-Z_]+):%{GREEDYDATA:line}\]%{SPACE}-%{GREEDYDATA:msg}-->
</layout>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/${appName}/info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 每个文件最多100MB,保存3天的历史记录,但最多10GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档记录info级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
</filter>
</appender>
<!-- 日志通过skywalking logstash上传到ELK 默认不启动,推荐使用FileBeat -->
<!-- add converter for %tid
<conversionRule conversionWord="tid" converterClass="org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackPatternConverter"/>
<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<param name="Encoding" value="UTF-8"/>
<destination>10.16.103.11:4560</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
<provider class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.logstash.TraceIdJsonProvider"/>
<customFields>{"appName":"${appName}"}</customFields>
</encoder>
</appender>-->
<!-- 日志输出级别,开通输出端-->
<root level="INFO">
<!--<appender-ref ref="logstash"/>-->
<appender-ref ref="STDOUT"/>
<appender-ref ref="INFO_FILE"/>
</root>
</configuration>
filebeat.yml
filebeat.inputs:
- type: log
enabled: true
tags: ["lean-water-applog"]
paths:
- /home/rs/logs/debug.log
tags: ["lean-home-applog"]
fields:
log_type: lean-home-error
multiline.pattern: '^\d{4}\-\d{2}\-\d{2}\s\d{2}:\d{2}:\d{2}'
multiline.negate: true
multiline.match: after
# 采集同一台服务器上的多个服务,使用 - 进行分隔,如mn项目的error类型的日志
- type: log
enabled: true
paths:
- /home/mn/logs/error.log
tags: ["lean-home-applog"]
# 自定义字段
fields:
log_type: lean-home-error
# 解决错误多行问题
multiline.pattern: '^\d{4}\-\d{2}\-\d{2}\s\d{2}:\d{2}:\d{2}'
multiline.negate: true
multiline.match: after
output.logstash: # 配置重点
hosts: ["logstash1.linuxtechi.local:5044", "logstash2.linuxtechi.local:5044"] # 填写多个logstash地址
loadbalance: true # 开启负载平衡技术
# ================================= Processors =================================
processors:
- add_host_metadata:
when.not.contains.tags: forwarded
- add_cloud_metadata: ~
- add_docker_metadata: ~
- add_kubernetes_metadata: ~
# 在这里可以设置要去除的字段,以免日志臃肿
- drop_fields:
# when: 可以设置去除的条件
# condition
fields: ["host","input","agent","ecs"]
ignore_missing: false
/data/elk/logstash/logstash-springboot.conf
input {
beats {
port => 5044
}
}
filter {
grok {
#过滤格式,从日志文件中提取特定的数据到ES的某个字段
match => { "message" => "(?m)^%{TIMESTAMP_ISO8601:createTime}%{SPACE}\[%{LOGLEVEL:level}\]%{SPACE}\[%{GREEDYDATA:appName}\]%{SPACE}\[TID:%{GREEDYDATA:traceId}\]%{SPACE}\[%{DATA:thread}\]%{SPACE}\[%{JAVACLASS:class}\:%{GREEDYDATA:method}\:%{GREEDYDATA:line}\]%{SPACE}-%{GREEDYDATA:content}" }
}
}
output {
if "lean-home-applog" in [tags] {
elasticsearch {
hosts => [ "192.168.16.21:9200","192.168.16.22:9200","192.168.16.23:9200" ]
index => "lean-home-logstash-%{+YYYY.MM.dd}"
}
}
if "lean-water-applog" in [tags] {
elasticsearch {
hosts => [ "192.168.16.21:9200","192.168.16.22:9200","192.168.16.23:9200" ]
index => "lean-water-logstash-%{+YYYY.MM.dd}"
}
}
}
注意:添加5044的docker端口映射