前言

近期团队内有同学为了搭建基于ELK的日志聚合系统而投入大量精力,个人觉得只是做了思路的建议以及验证和梳理工作,顺便水一篇博客。

ELK简介

es+logstask+kibana+filebeat。

基本架构

ELK如下图所示,相对于传统的ELK架构,这个图吧filebeat也加入进来,因为随着近年来微服务的发展和普及轻量级日志采集的需求在迅速提升,filebeat的应用也是越来越广,特别是sidecar的兴起。

kafak 输出的微服务日志 能排序吗 微服务日志收集架构_ruby

整套系统搭建完成后,es,logstash都运行在后台,只有kibana可以看到,如下图所示。

kafak 输出的微服务日志 能排序吗 微服务日志收集架构_elasticsearch_02

这样你就可以在一个界面上查看全部服务的日志啦,可以用program来区分不同的服务,查询关键词是 program.keyword:XXXX。

获取日志

获取物理节点日志

物理机的日志比较简单,如下图所示。

kafak 输出的微服务日志 能排序吗 微服务日志收集架构_elasticsearch_03

为了与下面容器方式区分,这里直接以代码的方式说明集成方式

下面以常见的springboot程序为例,介绍一下接入方式。

pom.xml中增加logstash的依赖

<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>6.3</version>
</dependency>

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
    <property name="log.path" value="logs/logback.log"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>info</level>
        </filter>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
            </pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>

    <appender name="file"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <fileNamePattern>${log.path}.%i.zip</fileNamePattern>
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>10MB</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
            <charset>GBK</charset>
        </encoder>
    </appender>

    <!-- 为logstash输出的JSON格式的Appender -->
    <appender name="LOGSTASH"
              class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <!--配置logStash 服务地址 -->
        <destination>10.0.4.131:4565</destination>
        <!-- 日志输出编码 -->
        <encoder charset="UTF-8"
                 class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <pattern>
                    <pattern>
                        {
                        "LogLevel": "%level",
                        "ServiceName": "${springAppName:-}",
                        "Pid": "${PID:-}",
                        "Thread": "%thread",
                        "Class": "%logger{40}",
                        "Date": "%d",
                        "ExceptionInfo": "%ex{full}",
                        "message": "%message"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="console"/>
        <appender-ref ref="file"/>
        <appender-ref ref="LOGSTASH"/>
    </root>

</configuration>

对了别忘了往application.properties中写 logback的配置

logging.config=logback.xml

logstash的配置

tcp_logstash.conf

input {

  tcp {
    port => 4565
        type => "jgdq_sim"
  }
}

output {
        if [type] == "jgdq_sim" {
        elasticsearch {
            hosts => ["10.0.4.131:9200"]
            action=>"index"
            #ES索引名称(自己定义的)
            index => "jgdq_sim-%{+YYYY.MM.dd}"
        }
    }

    stdout{
       codec => rubydebug { }
    }
}
input {
   syslog {
    port => "4560"
  }
}

filter{
    mutate{
        remove_field => ["timestamp"]
    }
}


output {
    elasticsearch {
       hosts => ["10.0.4.131:9200"]
       action=>"index"
       #ES索引名称(自己定义的)
       index => "jgdq-%{+YYYY.MM.dd}"
    }

    stdout{
       codec => rubydebug { }
    }
}

获取容器中的日志

由于容器的隔离性,获取容器中的方式往往比较复杂,但本质上和物理机上一样,无外乎是文件或是网络。如下图所示:

kafak 输出的微服务日志 能排序吗 微服务日志收集架构_ruby_04

关键的docker-compose.yml文件

version: '3.7'
services:
  demo:
    build: ./demo
    image: demo:1.0.0
    restart: always
    logging:
      driver: syslog
      options:
        syslog-address: ${SYSLOG_ADDRESS}
        tag: demo
    env_file:
      - .env

需要提供.env文件

SYSLOG_ADDRESS=tcp://10.0.4.131:514

${SYSLOG_ADDRESS}这个是写在.env文件中的

logstash的配置

syslog_logstash.conf

input {
   syslog {
    port => "4560"
  }
}

filter{
    mutate{
        remove_field => ["timestamp"]
    }
}


output {
    elasticsearch {
       hosts => ["10.0.4.131:9200"]
       action=>"index"
       #ES索引名称(自己定义的)
       index => "jgdq-%{+YYYY.MM.dd}"
    }

    stdout{
       codec => rubydebug { }
    }
}

filebeat的用法

filebeat直接对接es的配置

filebeat.inputs:


- type: log

  # Change to true to enable this input configuration.
  enabled: true

  # Paths that should be crawled and fetched. Glob based paths.
  paths:
    - D:\Data\*.txt
  #- c:\programdata\elasticsearch\logs\*
  ignore_older: 1h
  scan_frequency: 1s
  tail_files: true
  backoff: 1s
  max_backoff: 2s
  backoff_factor: 2
 
  
# ========================== Filebeat global options ===========================
filebeat.registry.flush: 0s

# ================================== General ===================================

queue:

  mem:

    events: 128

    flush.min_events: 64

    flush.timeout: 1s

filebeat.config.modules:
  # Glob pattern for configuration loading
  path: ${path.config}/modules.d/*.yml

  # Set to true to enable config reloading
  reload.enabled: false

setup.template.settings:
  index.number_of_shards: 1


setup.kibana:


output.elasticsearch:
  # Array of hosts to connect to.
  hosts: ["10.0.4.131:9200"]
  indices:
    - index: "TODAY-DEMO"



processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
  - add_cloud_metadata: ~
  - add_docker_metadata: ~
  - add_kubernetes_metadata: ~

filebeat与logstash对接

filebeat配置

# Filebeat 收集 /var/logs/ 目录下所有以 service_log. 开头的数据文件

filebeat.inputs:
- type: log
  paths:
    - /var/logs/service_log.*

# 将数据发送至地址为 10.42.32.70:5044 或
output.logstash: 
  hosts: ["10.0.4.131:5044"]

logstash配置

# 使用 beats 作为输入
input {
    beats {
        port => "5044"
    }
}
output {
    elasticsearch {
       hosts => ["10.0.4.131:9200"]
       action=>"index"
       #ES索引名称(自己定义的)
       index => "jgdq-%{+YYYY.MM.dd}"
    }

    stdout{
       codec => rubydebug { }
    }
}

结论

在实际使用中,logstash本身也是支持分布式部署的,但由于logstash基于JVM太过重型,基本每人这么干。

推荐实践方式:

  • 如果要避免侵入式的可以考虑采用filebeat+文件+es的模式或是应用转syslog+logstash+es的模式
  • 不介意侵入的话可以直接通过网络流推入logstash中

照看好你的微服务系统还是很困难的,希望日志聚合系统可以帮你点忙。