新手小白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会自动生成输出日志吗 docker-compose 日志_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会自动生成输出日志吗 docker-compose 日志_spring boot_02


然后将二进制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

docker会自动生成输出日志吗 docker-compose 日志_spring boot_03

关闭防火墙

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

docker会自动生成输出日志吗 docker-compose 日志_elk_04

3、访问http://localhost:5601 (ip:192.168.9.128)

docker会自动生成输出日志吗 docker-compose 日志_elasticsearch_05


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

docker会自动生成输出日志吗 docker-compose 日志_elasticsearch_06


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

docker会自动生成输出日志吗 docker-compose 日志_elk_07

# 重启elasticsearch容器即可
docker restart [elasticsearch容器ID]
# 查看IK是否安装成功
docker exec -it [elasticsearch容器ID] /bin/bash
cd plugins/
ls
  1. 安装htpasswd工具
yum  -y install  httpd-tools
  1. 设置用户名和密码,并把用户名、密码保存到指定文件中:
htpasswd -c /data/nginx/htpasswd  cfw  #交互输入密码
  1. 修改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; #生成的密码存放路径
        }
}

docker会自动生成输出日志吗 docker-compose 日志_elk_08

  1. 访问需要输入登录账号、密码

    (补充)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

docker会自动生成输出日志吗 docker-compose 日志_elk_09

SpringBoot 2.3.7 集成Logstash

  1. 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

docker会自动生成输出日志吗 docker-compose 日志_elk_10

  1. 在SpringBoot项目pom.xml文件中添加logstash-logback-encoder依赖
<!--集成logstash-->
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>5.2</version>
</dependency>
  1. 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>
  1. 启动springboot应用
  2. 使用 kibana 查看日志信息

访问http://localhost:5601,创建索引 boutique-logstash-*
索引名称可以在 /data/elk/logstash/logstash-springboot.conf文件 output配置中修改

docker会自动生成输出日志吗 docker-compose 日志_docker_11

docker会自动生成输出日志吗 docker-compose 日志_elk_12

docker会自动生成输出日志吗 docker-compose 日志_docker_13

docker会自动生成输出日志吗 docker-compose 日志_docker_14

  1. 查看springboot服务输出到kibana的日志

docker会自动生成输出日志吗 docker-compose 日志_spring boot_15

注意生产环境不要使用上面的方式,而是使用 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端口映射