Programming

Spring Boot + ELK + Redis

AlwaysPr 2018. 7. 19. 19:25

분산서비스는 여러개의 서비스가 존재한다. 그래서 서비스 수마다 로그가 분산되어서 각 서버에 저장이 된다.

이는 운영시 특정 로그를 볼 때 불편하기 마련이다.

그래서 우리는 분산된 로그를 ELK의 도움이 받아 로그를 모아보려한다.

Service

먼저 3개의 서버가 존재하고, 각 서버에 Spring Applcation, logstash(shipper)가 설치되어 있다.

각 Spring Application은 log파일을 생성하고, logstash는 생성된 log파일을 읽은 뒤 특정한 곳으로 보내는 역할을 한다. 여기서는 Redis로 보내게 된다.

Queue

지금처럼 서비스의 개수가 적거나 Log수가 적으면 굳이 중간에 Redis같은 Queue 역할을 둘 필요 없이 ElasticSearch로 전송하면 되지만, 항상 만약을 대비해야되는 개발자 입장으로서 Queue까지 구현을 해보자.


만약 Queue가 없고, 수십개의 서비스가 돌고 있는 채로 직접 ElasticSearch에 모든 서비스의 log 데이터를 주면 어떤일이 일어날까?
그러면 ElasticSearch에 엄청난 부하가 걸려 ElasticSearch가 마비가 될 것이다. 이를 우리는 Queue를 통하여 순차적으로 처리함으로써 부하를 줄일 수 있다.
물론 Queue도 부하가 많으면 문제가 생길 수 있지만, Redis는 이를 간단하게 Scale out을 통하여 해결 할 수 있다.

ELK

각 서버에 존재하는 logstash(shipper) 말고 Redis와 ElasticSearch 사이의 logstash(indexer)가 존재하는데 이 녀석의 역할은 무엇일까?


처음 logstash(shipper)가 Redis로 log 데이터를 삽입하게 되면, Redis는 이를 잘 보관하고 있는다. 그런데 이를 Redis가 보관만 하고 있으면 의미가 없다. 이 보관된 데이터를 ElasticSearch에 저장해야지 우리는 ElasticSearch를 활용할 수 있다. Redis에서 ElasticSearch로 데이터를 이동시켜주는 녀석이 두번째 logstash(indexer)이다.


그리고 Kibana는 ElasticSearch에 있는 데이터를 시각적으로 확인할 수 있게 해준다.


그럼 이제 구축을 해보자!
(방화벽이나 Permission 같이 사소한 것으로 실수를 많이할 수 있으니 주의하자)


3개의 서버가 필요하다.
위 사진에서는 3개의 서비스가 존재했지만, 우린 2개의 Service와 편의를 위해 나머지 한 서버에 Redis와 ELK를 다 구축하겠다. 서버환경은 Ubuntu 18.04이다.

Redis + ELK 환경 구축

Java 설치

ELK는 Java에 의존적이다. 미리 설치를 해놓자.

apt-get update
apt-get install software-properties-common
add-apt-repository -y ppa:webupd8team/java
apt-get -y install oracle-java8-installer

Redis 설치 및 실행

apt-get install redis
redis-server --daemonize yes

Redis 설치 확인 및 설정

redis-cli
127.0.0.1:6379> ping  //설치 확인
PONG
127.0.0.1:6379> config set protected-mode no  // 외부IP에서 접속해야될 때
OK

ElasticSearch 설치

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add -
echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-6.x.list
apt-get update
apt-get install elasticsearch

Kibana 설치

apt-get install kibana

Kibana 셋팅

apt-get install vim
vi /etc/kibana/kibana.yml

server.host: "0.0.0.0" 추가 (모든 Host에게 개방)

Logstash(Indexer) 설치

apt-get install logstash

Logstash(Indexer) 셋팅

vi /etc/logstash/conf.d/elkr.conf

위의 명령어로 새로운 conf 파일을 만든 후 아래 코드를 붙여넣는다.

input {
        redis {
                host => "127.0.0.1"
                port => 6379
                codec => "json"
                data_type => "list"
                key => "logstash"
        }
}
output {
        elasticsearch {}
}

ELK 모두 실행

service elasticsearch start
service logstash start
service kibana start

키바나의 경우 IP:5601 을 통하여 확인할 수 있다.
ex)localhost:5601

Service 환경 구축

Spring Application 생성

  1. Initializer를 통하여 Spring Boot 프로젝트를 생성한다.
  2. pom.xml에 아래의 디펜던시를 추가한다.

<dependency> 
    <groupId>net.logstash.logback</groupId> 
    <artifactId>logstash-logback-encoder</artifactId> 
    <version>4.11</version> 
</dependency>
  1. src/main/resources 아래에 logback-spring.xml 파일을 만든다. 그리고 아래의 코드를 붙여넣는다.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />

    <property name="FILE_LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger{36}:%L] ### %msg%n"/>
    <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">
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
        </encoder>
    </appender>

    <appender name="STASH" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>logback/spring.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>logback/spring.%d{yyyy-MM-dd}.log</fileNamePattern>
                <maxHistory>7</maxHistory>
            </rollingPolicy> 
            <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
    </appender>


    <root level="INFO">
        <appender-ref ref="console"/>
        <appender-ref ref="STASH" />
    </root>

</configuration>
    • 첫번째 appender는 console에 나타내기 위함이다.
    • 두번째 appender는 encoder를 통하여 데이터 형식을 JSON으로 변환하고 파일을 저장한다.
  1. Application에서 log를 작성한다.
    3초에 한번씩 Hello World를 로깅한다.
    @Slf4j를 위해서는 lombok의 디펜던시와 설정이 필요하다. 각자알아서...

package com.adwitt.logtest;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@SpringBootApplication
public class LogTestApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(LogTestApplication.class, args);
    }

    @Override
    public void run(String... strings) throws Exception {
        int i = 0;
        while (true) {
            Thread.sleep(3000);
            if (1 != 1)
                break;

            log.info("Hello World ::: {}", ++i);
        }
    }
}
  1. 이 Spring Boot를 .jar로 만든다.
  2. 새로운 서버에 mkdir /spring 를 통하여 디렉토리 생성 후 FTP를 이용하여 jar를 안에 넣는다.

Java 설치

apt-get update
apt-get install software-properties-common
add-apt-repository -y ppa:webupd8team/java
apt-get -y install oracle-java8-installer

Logstash 설치

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add -
echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-6.x.list
apt-get update
apt-get logstash

Logstash 셋팅

apt-get install vim
vi /etc/logstash/conf.d/spring.conf

input {
    file {
        path => "/spring/logback/*.log"
        codec => "json"
        type => "logback"
    }
}

output {
    redis {
        host => "12.65.87.11" (아까 구축 해놓은 서버의 IP 입력)
        port => 6379
        data_type => "list"
        key => "logstash"
    }
}

service logstash start

하나의 서비스가 만들어졌다. 똑같이 반복해서 한개 더 만들자.

Jar 파일 실행

두개의 서버에서 아까 생성해 놓은 jar 파일을 실행시킨 뒤 데이터가 정상적으로 잘 들어가는지 Kibana를 통하여 확인하자.

cd /mkdir
java -jar filename.jar (filename에 파일명을 작성)

여담

logstash(shipper)를 각 서비스에 두지 않는 방법도 있다. 자세한건 여기를 참고하자.

최소의 설정만으로 구축을 하였다. 세부적인 설정 옵션은 해당 프로덕트 레퍼런스를 참고하도록 하자.

참고