DevOps/Docker

[Docker] Docker-Compose를 사용하여 Springboot와 ELK stack 연동

SeungbeomKim 2023. 9. 4. 16:08
반응형

 

ELK에 대해 간단하게 설명드리고 springboot와 elk stack을 연동해서 springboot 애플리케이션의 log를 수집하고, 검색 및 시각화하는 과정에 대해서 보여드리겠습니다.

 

ELK Stack

  • Logstash
    • 다양한 소스(DB)의 로그 또는 트랜잭션 데이터를 수집, 집계, 파싱하여 ES에게 전달
  • ElasticSearch
    • Logstash로부터 받은 데이터를 검색 및 집계하여 필요한 관심 있는 정보 획득
  • Kibana
    • Elasticsearch의 빠른 검색을 통해 데이터를 시각화 및 모니터링

 

우선 elk stack을 연동하기 위해 elk 관련 image를 다운로드 받아야 합니다. (호환성을 위해 elk 버전 일치 시켰습니다)

docker pull elasticsearch:8.3.3
docker pull kibana:8.3.3
docker pull logstash:8.3.3

 

docker-compose.yml

version: "3"

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.3.3 
    container_name: elastic_springboot
    environment:
      - bootstrap.memory_lock=true // 하드디스크의 일정 부분을 가져다쓰는 swap 메모리 사용 X
      - "ES_JAVA_OPS=-Xms512m -Xmx512m" // JVM heap 메모리 설정
      - "discovery.type=single-node" // 단일 노드로 구성 
      - xpack.security.enabled=false // x-pack 보안 비활성화
    ports:
      - "9200:9200" 
    volumes:
      - elastic_data:/usr/share/elasticsearch/data // es의 데이터를 저장하기 위한 volume 설정
    networks:
      - elastic // 하나의 네트워크에서 여러 컨테이너가 실행되도록 networks 지정 
  kibana:
    image: docker.elastic.co/kibana/kibana:8.3.3
    container_name: kibana_springboot
    ports:
      - "5601:5601" 
    environment:
      ELASTICSEARCH_URL: http://elasticsearch:9200 // es와 연결하기 위한 url
      ELASTICSEARCH_HOST: '["http://elasticsearch:9200"]' // host 설정
    depends_on:
      - elasticsearch // es가 실행중일 때만 kibana가 실행되도록 설정
    networks:
      - elastic // 하나의 네트워크에서 여러 컨테이너가 실행되도록 networks 지정
  logstash:
    image: docker.elastic.co/logstash/logstash:8.3.3
    container_name: logstash_springboot
    volumes:
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro
      - ./logstash/pipeline:/usr/share/logstash/pipeline:ro 
    ports:
      - "5000:5000"
    environment:
      LS_JAVA_OPTS: "-Xmx1024m -Xms1024m" 
    networks:
      - elastic // 하나의 네트워크에서 여러 컨테이너가 실행되도록 networks 지정
    depends_on:
      - elasticsearch // es가 실행중일 때만 logstash가 실행되도록 설정
networks:
  elastic:
    driver: bridge  // network: bridge(기본 값) 지정, 명시적으로 보이기 위해 작성 
volumes:
  elastic_data:
    driver: local

 

log를 ES의 기본 데이터 형식인 Json으로 변경하고, elasticsearch에 전송하기 위해 의존성 추가

 

pom.xml 

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

 logstash-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>localhost:5000</destination>
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <mdc />
                <context />
                <logLevel />
                <loggerName />
                <pattern>
                    <pattern>
                        {
                        "app": "test-log"
                        }
                    </pattern>
                </pattern>
                <threadName />
                <message />
                <logstashMarkers />
                <stackTrace />
            </providers>
        </encoder>
    </appender>
    <root level="info">
        <appender-ref ref="logstash" />
    </root>
</configuration>

logstash.conf (logstash의 입출력(수, 송신) 지정)

input {
    tcp {
        port => 5000
        codec => json
    }
}

output {
    elasticsearch {
        hosts => "elasticsearch:9200"
        index => "springboot-%{app}"
    }
}

Controller.java (Client의 요청시, 간단하게 로그를 출력해 주는 API)

@RestController
@Slf4j
public class Controller {

    @GetMapping("/")
    public String HomePage() {
        LocalDateTime localDateTime = LocalDateTime.now();
        log.info("Welcome home Page " + localDateTime);
        return "Welcome SpringBoot";
    }

    @GetMapping("/logs")
    public String LogsPage() {
        LocalDateTime localDateTime = LocalDateTime.now();
        log.info("This Logs page " + localDateTime);
        return "Welcome SpringBoot";
    }

    @GetMapping("/warn")
    public String WarnPage() {
        LocalDateTime localDateTime = LocalDateTime.now();
        log.warn("This warn page " + localDateTime);
        return "Welcome to warn page";
    }


    @GetMapping("/er")
    public String ErrorPage() {
        LocalDateTime localDateTime = LocalDateTime.now();
        log.error("This error page " + localDateTime);
        return "Welcome to error page";
    }
}

 

docker-compose up (elk 컨테이너 생성 및 실행)

spring application (build & execute)

 

프로젝트 build 후, target 폴더 내의 jar 파일 실행

 

elasticsearch ,kibana 연동 확인(localhost:9200, localhost:5601)

 

이제 localhost:8080/logs, /warn, /er로 요청을 보내보고, kibana로 요청에 대한 로그를 확인해 보겠습니다.

 

다음과 같이 logLevel과 Controller에서 작성한 log message를 확인할 수 있게 되었습니다.

반응형