ELK

박용민·2024년 6월 1일
0

Orview

Spring 공부하면서 터미널에서 보고 싶지 않아도 항상 보여주고 있다. 에러 공부하면서 로그의 중요성을 크게 느끼지 못하였는데 컨테이너 서비스를 하면서 오토 스케일링 할때 내부 시스템을 문제가 생겼는데 어느 지점에서 문제가 생겼는지 확인할 수 있는 방법이 없었다. 그때 AWS Cloud Watch를 통해 어느 시점에 오류가 생겼는지 로그를 보면서 처리를 할 수 있었고 덕분에 해결할 수 있었다. 이 계기를 통해 로그의 중요성을 알게 되었고 로그 처리 및 데이터 수집의 필요성을 느끼게 되었다. 특히 향후 검색과 관련된 로직을 처리할일이 있기에 ELK 스택을 사용하여 로깅과 검색을 위한 공부를 위해 써본다.

로깅 장점

먼저 로깅을 써야하는 이유는 알것같다.

  • 로그는 시스템이나 애플리케이션이 예상치 못한 동작을 할 때 그 원인을 추적하는 데 중요한 역할
  • 자원 사용 패턴을 파악하여 효율적인 자원 관리
  • 로그를 통해 사용자 행동 패턴을 분석하고, 사용자 경험을 개선
  • 트렌드 분석, 사용 패턴 분석 등을 통해 데이터 기반의 의사 결정

ELK

Elasticsearch

  • 대용량 데이터를 실시간으로 검색하고 분석
  • JSON 형식으로 데이터를 저장하며, 다양한 검색 쿼리를 지원
  • 확장성이 뛰어나며, 클러스터링을 통해 데이터 분산 저장

Logstash

  • 다양한 소스로부터 데이터를 수집합니다(파일, 데이터베이스, 메시지 브로커 등)
  • 데이터를 필터링, 변환, 구조화하여 Elasticsearch로 전송
  • 플러그인 아키텍처를 통해 입력, 필터, 출력 단계를 유연하게 설정

Kibana

  • Elasticsearch에 저장된 데이터를 시각화
  • 대시보드, 차트, 그래프 등을 통해 데이터를 직관적
  • 실시간으로 데이터 변화를 모니터링하고, 검색 및 필터

ELK 스택의 작동 방식

  1. 데이터 수집: Logstash가 다양한 소스에서 로그 데이터를 수집
  2. 데이터 저장 및 검색: Logstash가 구조화된 데이터를 Elasticsearch에 저장합니다. Elasticsearch는 이 데이터를 인덱싱하고, 효율적인 검색 및 분석 기능을 제공
  3. 데이터 시각화: Kibana가 Elasticsearch에 저장된 데이터를 시각화

docker-compose

# docker-compose.yml
version: "3"

services:
    elasticsearch:
      image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1
      container_name: elasticsearch
      environment:
        - discovery.type=single-node
      ports:
          - "9200:9200"
      volumes:
          - elasticsearch_data:/usr/share/elasticsearch/data
      networks:
          - elastic

    kibana:
      image: docker.elastic.co/kibana/kibana:7.12.1
      container_name: kibana
      ports:
          - "5601:5601"
      environment:
          ELASTICSEARCH_URL: http://elasticsearch:9200
          ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]'
      depends_on:
          - elasticsearch
      networks:
          - elastic

    logstash:
      image: docker.elastic.co/logstash/logstash:7.12.1
      container_name: logstash
      volumes:
        - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro
        - ./logstash/pipeline:/usr/share/logstash/pipeline:ro
      ports:
        - "5044:5044"
        - "5000:5000/tcp"
        - "5000:5000/udp"
        - "9600:9600"
      networks:
        - elastic
      depends_on:
        - elasticsearch

networks:
  elastic:
    driver: bridge

volumes:
    elasticsearch_data:
        driver: local
  • Elasticsearch 버전은 7.12.1을 사용
  • discovery.type=single-node : 단일 노드 모드로 실행되도록 설정합니다.
  • Elasticsearch URL, Elasticsearch 호스트 지정

logstash.yml

# logstash/config/logstash.yml
http.host: "0.0.0.0"
path.config: /usr/share/logstash/pipeline
xpack.monitoring.elasticsearch.hosts: [ "localhost:9200" ]
  • Logstash 인스턴스의 동작을 조정하는 여러 중요한 설정
  • http.host : "0.0.0.0" => Logstash가 바인딩될 호스트를 지정, 다른 컴퓨터 또는 네트워크에서 접속 요청을 수락
  • path.config: /usr/share/logstash/pipeline => Logstash 파이프라인 구성 파일이 위치한 디렉토리를 지정
  • path.config: /usr/share/logstash/pipeline => Logstash의 성능 및 상태 모니터링 데이터를 지정된 Elasticsearch 클러스터로 전송

logstash.conf

# logstash/pipline/logstash.conf
input {
  tcp{
	port => 5000
	codec => json
  }
}
 
output {
  elasticsearch {
    hosts => "elasticsearch:9200"
	index => "springboot-%{app}"
  }
}
  • Logstash 파이프라인을 정의하는 설정 파일
  • input => Logstash가 데이터를 수집하는 방법을 정의
  • tcp => TCP 프로토콜을 통해 데이터를 수집하도록 설정
  • port => 5000: TCP 포트 5000에서 데이터를 수신
  • codec => json: 수신된 데이터를 JSON 형식으로 해석
  • output => Logstash가 수집한 데이터를 어디로 보낼지 정의
  • elasticsearch => 데이터를 Elasticsearch로 전송하도록 설정
  • hosts => "elasticsearch:9200": Elasticsearch 클러스터의 호스트와 포트를 지정, Docker Compose 설정에서 정의한 Elasticsearch 컨테이너와 일치
  • index => "springboot-%{app}": 데이터를 저장할 Elasticsearch 인덱스를 지정

이후 다음과 같이 elk container가 실행이 된다.

logback-spring.xml

# resources/logback-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>
  • Spring Boot 애플리케이션에서 Logback을 구성하는 데 사용
  • Spring Boot의 기본 Logback 설정을 포함
  • Logstash로 로그를 전송하기 위해 LogstashTcpSocketAppender 클래스를 사용
  • Logstash가 실행 중인 호스트와 포트를 지정 여기서는 localhost:5000로 설정하여 로컬에서 실행되는 Logstash로 로그를 전송
  • 로그 메시지를 JSON 형식으로 인코딩하기 위해 LoggingEventCompositeJsonEncoder 클래스를 사용
  • 정의한 LOGSTASH appender를 루트 로거에 추가합니다. 이를 통해 모든 로그가 Logstash로 전송

적용후 스프링 실행시 Logback이 클래스를 인스턴스화하는 데 실패로 다음과 같은 오류 발생

해당 라이브러리 추가

implementation 'net.logstash.logback:logstash-logback-encoder:7.2'

Elasticsearch 화면

Kibana 화면

spring logging 테스트

@RestController
@Slf4j
public class HomeController {

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

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

    @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";
    }
}


🎉Done

참고자료
https://github.com/lemoncode21/docker-loging-elk

0개의 댓글