当前位置:   article > 正文

基于Docker搭建ELK日志搜集处理分析系统_docker elk

docker elk

简介

ELK=elasticsearch+logstash+kibana, 常作为大型分布式系统的日志分析收集处理的解决方案

  • ElasticSearch是一个基于Lucene的搜索服务器. 它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,是一种流行的企业级搜索引擎.
  • logstash是一个用于日志搜集, 分析, 过滤的工具. client端一般部署在用于搜集日志的主机上, server端负责将接收的到日志进行过滤, 然后转发到elasticsearch上.
  • kibana是一个用于汇总分析和搜索日志的工具, 可以为elasticsearch和logstash提供友好的日志分析的web界面.

三者之间的关系如下所示:
在这里插入图片描述


搭建

搭建前提

  • 腾讯云服务器(1核2G都可以) or 其它类型的服务器 or 虚拟机
  • 安装Docker服务 ( 点击查看详细安装步骤 )
  • 在防火墙以及安全组中开放5044, 9200, 5601端口

搭建步骤

服务器/虚拟机配置

  1. 设置能够打开的最大文件数
    vi /etc/security/limits.conf 增加下述内容。

    * soft nofile 65536 
    * hard nofile 65536 
    
    • 1
    • 2
  2. 设置es 启动时的线程池最低容量
    vi /etc/security/limits.d/20-nproc.conf , 修改下述内容(如果被注释掉了请放开, 非常重要! )

    *          soft    nproc     4096 
    root       soft    nproc     unlimited 
    
    • 1
    • 2
  3. 设置一个进程可以拥有的VMA(虚拟内存区域)的数量
    vi /etc/sysctl.conf 新增下述内容

    vm.max_map_count=262144
    
    • 1

    使用命令,让 sysctl 配置生效 sysctl -p

安装配置elk镜像

  1. Docker安装ELK镜像

    docker pull sebp/elk:latest
    
    • 1

    注意: 如何查看docker版本? 只需进入Ddcoker Hub官网, 搜索你需要下载的镜像(图1), 在Tag页找到你需要下载的版本(图2)即可
    图1
    图2
    ps: 这里elk的版本是7.10.0之后的版本, 后面随着镜像的更新lastest会不断变更版本, 因此只需要明确这里下载的是7.10.0后面的一个版本. 后续想要下载该版本只需要 docker pull sebp/elk:对应版本

  2. 运行elk镜像

    docker run -d \
    -e ES_JAVA_OPTS="-Xms256m -Xmx256m" \
    -p 5601:5601 -p 5044:5044 -p 9200:9200 -p 9300:9300 -it \
    --restart=always --name elk 1f1020bb13d6 
    
    注意: 
          1.根据自己的服务器内存大小分配合适的内存,  这里为期分配了256m的内存(1核2G参考配置)
          2. -p用于对需要使用的端口, 在docker容器和服务器端口之间进行映射
          3. --restart=always 可以让docker镜像生成的容器实例开机自启( 只要docker服务启动 )
          4. 1f1020bb13d6 指的是elk的镜像ID, 可以通过 docker images 命令查看镜像ID
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

ps: 关于注意事项1的补充
在分配初始内存(Xms)和最大内存(Xms)时, 建议二者的配置相等. 否则会导致创建容器失败. 并在日志中输出
initial heap size [268435456] not equal to maximum heap size [536870912]; this can cause resize pauses
原因是: 如果初始堆大小小于最大堆大小,那么当Java虚拟机需要调整堆大小以适应更大的内存需求时,可能会发生调整大小的暂停。这意味着Java虚拟机需要停止应用程序的执行,并重新分配更大的堆空间。这个过程可能会导致一段时间内的性能下降或暂停。为了避免调整大小的暂停,通常建议将初始堆大小设置为与最大堆大小相等的值。这样,Java虚拟机在应用程序启动时就会分配足够的堆空间,从而减少了调整大小的需求。

在这里插入图片描述

  1. 查看elk容器启动日志
    如果没有错误则可以测试elasticsearch和kibana能否访问

    # 查看日志
    docker logs -f -t elk
    
    # 查看elasticsearch
    http://服务器ip:9200 
    
    # 查看kibana
    http://服务器ip:5601
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    查看es
    查看kibana

  2. 修改logstash配置

    # 进入容器
    docker exec -it elk /bin/bash
    
    # 编辑配置文件
    vim /etc/logstash/conf.d/02-beats-input.conf 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    配置文件修改内容
    指定5044作为server端的端口, 输出到本地的elasticsearch上, 添加日志的index(方便在kibana上面找到相应日志)

    input {
        tcp {
            port => 5044
            codec => json_lines
        }
    }
    output{
        elasticsearch {
        hosts => ["localhost:9200"]
        index => "rizhi-log-%{+YYYY.MM.dd}"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    退出容器内部, 并重启容器

    # 退出容器内部
     ctrl + p + q (ctrl按住的状态下先按p再按q)
    
    # 重启容器
     docker restart elk
    
    • 1
    • 2
    • 3
    • 4
    • 5

    再次测试elasticsearch和kibana能否访问

项目整合

  1. 在任意一个Springboot项目中添加logstash插件

    <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>6.1</version>
     </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
  2. 在resource目录下新建一个 lockback.xml 的文件, 用于规定日志格式以及logstash的server端配置
    logback.xml
    具体配置

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <contextName>ProviderLog</contextName>
        <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
        <!--本工程中没使用文件输出日志,只用了console和logstash,此处配置无用-->
        <property name="LOG_HOME" value="home" />
        <!--可以手动指定log名字-->
        <property name="appName" value="wdnmdService" />
        <!--也可以使用工程的名字-->
        <springProperty scope="context" name="springAppName" source="spring.application.name"/>
        <springProperty scope="context" name="serverPort" source="server.port"/>
        <!-- logstash远程日志配置-->
        <appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
            <destination>logstash所在服务器ip:5044</destination>
    
            <!--        默认是JSON格式,所以logstash中应该配置codec为json_lines-->
            <!--        LoggingEventCompositeJsonEncoder是LogstashEncoder的父类,可以使用pattern自定义json的关键字
            -->
            <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder" >
                <!--        <encoder class="net.logstash.logback.encoder.LogstashEncoder" >-->
                <providers>
                    <!--可以配合LogstashEncoder使用-->
                               <!-- <timestamp/>-->
                                <!--<version/>-->
                                <!--<message/>-->
                                <!--<loggerName/>-->
                                <!--<threadName/>-->
                                <logLevel/>
                                <callerData/>
                    <timestamp>
                        <timeZone>UTC</timeZone>
                    </timestamp>
                    <pattern>
                        <pattern>
                            {
                            "app": "${springAppName}_${serverPort}",
                            "level": "%level",
                            "thread": "%thread",
                            "class": "%logger{40}",
                            "message": "%message"
                            }
                        </pattern>
                    </pattern>
    
                </providers>
            </encoder>
        </appender>
        <!-- 控制台输出 -->
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            </encoder>
        </appender>
        <!-- 按照每天生成日志文件 -->
        <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--日志文件输出的文件名-->
                <FileNamePattern>${LOG_HOME}/system.log.%d{yyyy-MM-dd}.log</FileNamePattern>
                <!--日志文件保留天数-->
                <MaxHistory>30</MaxHistory>
            </rollingPolicy>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            </encoder>
            <!--日志文件最大的大小-->
            <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <MaxFileSize>10MB</MaxFileSize>
            </triggeringPolicy>
        </appender>
    
        <!-- 日志输出级别 -->
        <root level="INFO">
            <appender-ref ref="stash" />
            <appender-ref ref="STDOUT" />
            <!--        <appender-ref ref="FILE" />-->
        </root>
    </configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
  3. 在任意springboot项目的启动类编写测试用接口, 并启动项目, 访问接口 http://localhost:8080/test/测试elk用户

    @SpringBootApplication
    @RestController
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        Logger logger = LoggerFactory.getLogger("DemoApplication");
        
        @RequestMapping("/test/{name}")
        public String test1(@PathVariable String name){
            logger.info("用户名称注册成功66666, 用户名为: " +name);
            return name+"登录成功";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    访问测试接口
    ps: 在启动项目时, 可能出现的问题, 这些问题可能出现的非常频繁, 需要仔细留意哦~~~

    // 1. 超时问题 ==>重启后未启动docker服务, 或是elk因为异常而终止 ==> 重新启动即可
    09:22:46,036 |-WARN in net.logstash.logback.appender.LogstashTcpSocketAppender[stash] - Log destination 192.168.40.21:5044: connection failed. java.net.SocketTimeoutException: connect timed out
    	at java.net.SocketTimeoutException: connect timed out
    
    // 2.连接拒绝问题 ( 前提: 检查自己防火墙或者云服务的安全组是否开放对应端口, 如5044 )
    //      1). 虚拟机配置 ==> 检查虚拟机配置
    //      2). elk刚启动后的瞬间导致内存占用过大  ==> 等待1-2min重新连接一下   
    //      3). 系统其它进程内存空间占用过大 ==> 重启 or 关闭无用的进程
    09:30:24,927 |-WARN in net.logstash.logback.appender.LogstashTcpSocketAppender[stash] - Log destination 192.168.40.21:5044: connection failed. java.net.ConnectException: Connection refused: connect
    	at java.net.ConnectException: Connection refused: connect
    	at 	at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
    	at 	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
    	at 	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    	at 	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    	at 	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    	at 	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    	at 	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    	at 	at java.net.Socket.connect(Socket.java:606)
    	at 	at net.logstash.logback.appender.AbstractLogstashTcpSocketAppender$TcpSendingEventHandler.openSocket(AbstractLogstashTcpSocketAppender.java:586)
    	at 	at net.logstash.logback.appender.AbstractLogstashTcpSocketAppender$TcpSendingEventHandler.onStart(AbstractLogstashTcpSocketAppender.java:508)
    	at 	at net.logstash.logback.appender.AsyncDisruptorAppender$EventClearingEventHandler.onStart(AsyncDisruptorAppender.java:351)
    	at 	at net.logstash.logback.encoder.com.lmax.disruptor.BatchEventProcessor.notifyStart(BatchEventProcessor.java:224)
    	at 	at net.logstash.logback.encoder.com.lmax.disruptor.BatchEventProcessor.run(BatchEventProcessor.java:120)
    	at 	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    	at 	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    	at 	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    	at 	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    	at 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at 	at java.lang.Thread.run(Thread.java:748)
    09:30:24,927 |-WARN in net.logstash.logback.appender.LogstashTcpSocketAppender[stash] - Log destination 192.168.40.21:5044: Waiting 28979ms before attempting reconnection.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

kibana使用

查看日志

  1. 登录kibana, 点击Stack Management页面, 创建索引( 用于接收指定格式日志名称的日志 )
    kiban中的management页面

  2. 输入rizhi-log-*去匹配( 会匹配所有以rizhi-log- 开头的日志 ),然后点击下一步,选择@timestamp
    在这里插入图片描述
    在这里插入图片描述

  3. 如果出现You'll need to index some data into Elasticsearch before you can create an index pattern或者输入后下一步为灰或者出现No results match your search criteria,这是因为里面没有数据导致的,在命令行输入一个假的数据即可

    curl -H "Content-Type: application/json" -XPOST 'http://es所在主机ip:9200/rizhi-log-2021-01-09/test-log' -d '{"code":200,"message":"测试"}'
    
    • 1
  4. 在kibana的web界面中的discover下查看生成的日志
    在这里插入图片描述

使用技巧

  1. 使用关键字查询
    日志中出现的所有关键字都能够通过这个搜索到 => 可以用来搜索含有关键字的日志
    在这里插入图片描述
  2. 使用属性去查询
    在Discover页面的左边的Available fields中, 如果想要过滤耨个属性, 则点击属性右侧的 “+”, 可以结合步骤一进行更细致的查询
    在这里插入图片描述
    属性过滤后的页面( 可以使用多个属性进行过滤展示 )
    在这里插入图片描述
  3. 通过使用日期对格式进行过滤
    可以自定义也可以使用系统定义好的时间段, 方便我们按时间进行查看
    在这里插入图片描述
    后续如果有新东西get到了, 会继续补充哦, 觉得不错就点个赞吧~~~

docker elk安装ik中文分词器

  1. 在github上下载和es版本匹配的ik中文分词器
    https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.10.0
    在这里插入图片描述
    上传到服务器后解压(), 然后通过docker cp将其从本地复制到docker es上
    # 创建目录
    mkdir elasticsearch-analysis-ik-7.10.0
    # 复制插件
    cp elasticsearch-analysis-ik-7.10.0.zip elasticsearch-analysis-ik-7.10.0
    cd elasticsearch-analysis-ik-7.10.0/
    #解压
    unzip elasticsearch-analysis-ik-7.10.0.zip
    # 复制到docker elasticsearch上,elk为容器的名称
    docker cp elasticsearch-analysis-ik-7.10.0  elk:/opt/elasticsearch/plugins
    # 重启elk容器
    docker restart elk
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  2. 测试中文分词器
    访问kibana的dev tools工具栏 http://kibanaip:kibana端口/app/dev_tools#/console, 输入下面内容测试
    GET _analyze
    {
    "text" : "中华人民共和国国歌",
    "analyzer": "ik_max_word"
    }
    
    GET _analyze
    {
    "text" : "中华人民共和国国歌",
    "analyzer": "ik_smart"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    如果对中文出现了分词则说明安装成功了
    在这里插入图片描述
    在这里插入图片描述

常见问题

elk内存占用过大问题

elk在使用一段时间后, 无法在kibana中访问日志数据, 并且在重启elk后, 在日志中输出的es日志中出现以下问题: flood stage disk watermark [95%] exceeded on all indices on this node will marked read-only, 通df -h查看占用超过95%(清理前忘了截图, 下图为清理过后的截图), 因此需要清理docker中无用的容器和镜像

在这里插入图片描述

  1. 清理未使用的镜像和容器来释放磁盘空间。可以使用以下命令来删除未运行的容器:
    docker container prune -f 这将删除所有处于停止状态的容器。

  2. 使用以下命令来删除未被任何容器引用的镜像:docker image prune -a -f

  3. 清理 overlay2 目录
    如果磁盘空间仍然不够,我们可以清理 overlay2 目录。首先,我们需要找到 Docker 的数据根目录。可以通过 docker info 命令来查找 Docker Root Dir 的值: docker info
    在输出结果中,找到 Docker Root Dir 的值,例如 /var/lib/docker。

    # 1. 进入 overlay2 目录:
    cd /var/lib/docker/overlay2
    # 2. 我们可以查看每个 overlay2 目录所占用的空间
    du -sh *
    # 3. 使用以下命令删除不需要的 overlay2 目录
    rm -rf <overlay2_directory>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

如何让elk 信息错误信息以及相关堆栈信息

有读者反映在使用elk时, 按照本文配置, 在打印错误信息时, 只显示第一行数据.
经过我尝试, 确实如此. 在查询资料之后发现, 是因为需要在 log,error中需要额外的增加一个参数

        try {
            int i = 10 / 0;
        } catch (Exception e) {
            log.error("发生异常3:{}",e.getMessage());   //发生异常3:/ by zero, 无堆栈信息
            log.error("发生异常3+1:{}", e.getMessage(), e);  //发生异常3+1:/ by zero,有堆栈信息
            log.error("发生异常3+2:{}",e, e);   //发生异常3+2:{}, 有堆栈信息
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

但在初次定位问题时, 发现仍未成功, 通过定位发现logback-spring.xml 中对日志的格式化影响了stack_trace的输出

  <pattern>
      {
      "app": "${appName}",
      "level": "%level",
      "thread": "%thread",
      "class": "%logger{40}",
      "message": "%message",
      "stackTrace":"%stackTrace"
      }
  </pattern>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

因此, 我们需要修改logback-spring.xml 中的配置. 然后使用上面代码中第二种异常方式的抛出.
步骤如下

  1. 修改logback-spring.xml

        <!--手动指定log名字-->
        <property name="appName" value="日志名称(可采用项目-环境方式命名)"/>
        <!-- logstash远程日志配置-->
        <appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
            <destination>logstash所在服务器ip:端口(5044)</destination>
            <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder" >
                <customFields>{"app":"${appName}"}</customFields>
            </encoder>
        </appender>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  2. 代码中的异常使用如下方式记录

    // 在原来打印异常的基础上多追加一个异常堆栈的信息
    log.error("发生异常3+1:{}", e.getMessage(), e);
    
    • 1
    • 2
  3. 进入kibana, 然后打开指定记录, 查看具体信息, stack_trace 显示的即为项目中错误信息的堆栈信息

在这里插入图片描述


参考博客

https://zhuanlan.zhihu.com/p/107346014?from_voters_page=true

https://www.cnblogs.com/xiao987334176/p/13565790.html

https://blog.csdn.net/abc8125/article/details/106858862

https://blog.csdn.net/github_38924695/article/details/105348442

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/992872
推荐阅读
相关标签
  

闽ICP备14008679号