티스토리 뷰

반응형

서론

모니터링은 보통 인프라와 어플리케이션 모니터링으로 나뉩니다.

인프라 모니터링은 cpu, memory 등 서버 성능에 대한 데이터인 메트릭을 제공합니다. 

어플리케이션 모니터링은 API 호출, 어플리케이션 에러 등에 대한 데이터인 로그를 제공합니다. 

어플리케이션 모니터링 시스템을 구축하게 되면 운영에 있어서 많은 이점을 얻을 수 있습니다.

먼저 어플리케이션에서 서비스 중단과 같은 에러를 해결하기 위한 로그를 빠르게 확인할 수 있습니다.

그리고 사용자 행동 패턴, 인기있는 기능, 사용 중인 문제점 등을 분석함으로써

사용자 경험을 개선하는데 유용한 정보를 제공 할 수 있습니다.

 

이 글에서는 어플리케이션 모니터링을 Spring logback에서 경로를 지정해 Kafka로 publish하고,

ELK중 logstash에서 subscribe하는 구조로 모니터링 시스템을 구축하고자 합니다.

 

Kafka와 ELK stack

로그를 ELK stack으로 보내기 전에 Kafka를 중간에 둔 이유는 여러 가지가 있습니다.

우선은 단일 프로세스로 구성되어 있는 서비스라면 굳이 Kafka를 쓸 이유는 없다고 생각합니다.

하지만 여러개로 나뉘어진 MSA 같은 경우에는 Kafka 같이 중앙화되고, 부하를 분산해주는

역할을 하는 Kafka를 뒀습니다.

 

kafka를 잘 모른다면 이 글에서 설명하는 범위가 아니기 때문에 아래 글을 참고하면 도움이 됩니다.

https://dolgogae.tistory.com/category/Data%20Engineering/Kafka

 

'Data Engineering/Kafka' 카테고리의 글 목록

CONTACT: ohsh0731@gmail.com

dolgogae.tistory.com

그리고 더욱 간단하게 생각하면 아주 성능좋은 큐라고 생각하면 된다.

 

ELK stack은 따로 저의 블로그에서 다룬 글이 없어서 다른 블로그를 참고하시면 됩니다.

 

Kafka 설정

base project로 https://github.com/deviantony/docker-elk 를 기본 프로젝트로 사용했습니다.

먼저 docker-compose.yml 파일에서 kafka를 추가해주도록 합니다.

Kafka는 zookeeper 3개 노드와 broker 4개 노드가 기본 설정이지만, 

데모이기 때문에 한개씩만 구성했습니다.

  zookeeper:
    container_name: zookeeper
    image: confluentinc/cp-zookeeper:latest
    ports:
      - "9900:2181"
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    networks:
      - elk

  kafka:
    container_name: kafka
    image: confluentinc/cp-kafka:latest
    depends_on:
      - zookeeper
    ports:
      - "9092:9092"
    environment:
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_CREATE_TOPICS: "ek-log:1:1"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - elk

 

위 설정은 docker-compose.yml에서 services 밑에 두면 됩니다. 

KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092

이 환경변수를 조금 해석하자면, kafka:29092는 docker network 내부로 사용하는 변수이고,

localhost는 외부에서 접근 가능한 리스터를 나타냅니다.

 

따라서 이후 docker에서 띄워지는 설정은 kafka:29092로 해주어야 합니다.


추가적으로 spring을 docker의 같은 네트워크를 쓴다면 kafka:29092를 사용하면 되고,

외부에서 띄운다면, localhost:9092를 해주면 됩니다. 다른 서버에서 띄운다면 ip주소를 적어주면 됩니다.


Spring Logback 설정

spring logback에 대해서 알고싶으면 아래 포스팅을 참고해주시면 됩니다.

https://dolgogae.tistory.com/80

 

[Spring] Logback으로 Spring 애플리케이션 Logging 하기

서론 애플리케이션에서 로깅(logging)은 디버깅, 모니터링, 그리고 성능 최적화를 위한 필수적인 요소입니다. 특히, 대규모 시스템에서는 로깅의 중요성이 더욱 강조됩니다. Spring 애플리케이션에

dolgogae.tistory.com

 

위 포스팅의 마지막 부분은 로그를 파일로 떨어뜨려 줍니다.

만약 파일을 이용해서 로그를 모니터링하고 싶다면, fluentd를 이용해서 할 수 있습니다.

하지만 여기서는 kafka로 publish를 해주는 것이 목적입니다.

위의 포스팅에서 참고해야할 점은 profile에 따라서 logback을 분리해준 것을 참고하면 됩니다.

 

<included>
    <appender name="LOG-KAFKA" class="com.github.danielwegener.logback.kafka.KafkaAppender">
        <encoder class="com.github.danielwegener.logback.kafka.encoding.LayoutKafkaMessageEncoder">
            <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>
                    [ORDER-LOG] [%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] [%logger{40}:%line] - %msg%n
                </pattern>
            </layout>
        </encoder>
        <topic>ek-log</topic>
        <keyingStrategy class="com.github.danielwegener.logback.kafka.keying.RoundRobinKeyingStrategy"/>
        <deliveryStrategy class="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy"/>
        <producerConfig>retries=1</producerConfig>
        <producerConfig>bootstrap.servers=kafka:29092</producerConfig>
        <producerConfig>compression.type=snappy</producerConfig>
        <producerConfig>max.block.ms=1000</producerConfig>
    </appender>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <layout>
            <pattern>
                [CONSOLE] [%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] [%logger{40}:%line] - %msg%n
            </pattern>
        </layout>
    </appender>

    <logger name="elk" level="INFO" additivity="false">
        <appender-ref ref="LOG-KAFKA"/>
        <appender-ref ref="CONSOLE"/>
    </logger>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</included>

 

위에서 설정을 보면 LOG-KAFKA와 같이 logback 설정을 해주고

터미널에도 로그가 찍히게 CONSOLE도 추가해줬습니다.

 

ELK stack 설정

마지막으로 ELK stack 설정을 해주면 됩니다.


Elasticsearch는 Nosql DB이자 빠르게 검색을 도와주는 검색엔진.

Logstash는 데이터를 뽑아 저장해주는 역할.

Kibana는 시각화, 모니터링 대시보드.


 

 

Elasticsearch 설정

./elasticsearch/config/elasticsearch.yml 에서 elasticsearch의 설정을 해줄 수 있습니다.

---
## Default Elasticsearch configuration from Elasticsearch base image.
## https://github.com/elastic/elasticsearch/blob/main/distribution/docker/src/docker/config/elasticsearch.yml
#
cluster.name: docker-cluster
network.host: 0.0.0.0

## X-Pack settings
## see https://www.elastic.co/guide/en/elasticsearch/reference/current/security-settings.html
#
xpack.license.self_generated.type: basic
xpack.security.enabled: false
xpack.monitoring.collection.enabled: false

 

elasticsearch는 위와 같이 간단하게 설정이 가능합니다.

만약 실제 서비스에 들어갈 것이라면 security 설정에 튜닝이 필요합니다. 

Logstash 설정

./logstash/pipline/logstash.conf 에서 logstash의 설정을 해줄 수 있습니다.

input {
  kafka {
    bootstrap_servers => "kafka:29092"
    topics => ["ek-log"]
    consumer_threads => 1
    decorate_events => true
  }
} 
      
## Add your filters / logstash plugins configuration here 
output {
  elasticsearch {
    hosts => "elasticsearch:9200"
    user => "elastic"
    password => "${LOGSTASH_INTERNAL_PASSWORD}"
    index => "ek-log-%{+YYYY.MM.dd}"
  }
}

 

input은 logstash가 데이터를 가져오는 부분입니다.

이 프로젝트에서는 logback에서 kafka로 보내고, kafka에서 logstash가 데이터를 가져오는 로직입니다.

그리고 topics은 kafka에서 사용할 토픽 이름을 적어주면 되고,

consumer_threads는 subscribe의 컨슈머 그룹을 몇개로 할지 정하는 것입니다.

 

output은 데이터를 저장할 elasticsearch의 설정을 적어주면 됩니다.

네트워크가 적혀진 bootstrap_servers, hosts는

도커 내부 네트워크를 사용하기 때문에 위 설정처럼 kafka, elasticsearch와 같이 적어줘야 합니다.

 

마지막으로 index에서는 elasticsearch의 index는 table 개념이라고 생각하면 되는데

날짜별로 분리해서 저장되도록 설정해준 것입니다.

 

마지막으로 Kibana는 별도의 설정을 해줄 필요가 없습니다.

그리고 예전 버전의 Kibana에서는 Alert를 위해 다른 서드파티를 설치해줘야하지만

현재 최신 버전의 Kibana에서는 별도의 설치가 필요 없다.

 

실행

실행은 docker-compose.yml 파일이 있는 경로에서 다음 명령어를 입력해주면 된다.

$ docker compose up setup
$ docker compose up -d

 

모니터링 확인

Kibana는 5601포트로 띄워져 브라우져에서 localhost:5601로 접속해보면 됩니다.

id/pw를 입력하는 것이 뜨면, elastic과 changeme를 입력해주면 됩니다.

그리고 이전에 curl등의 명령어를 통해서 spring에서 로그를 몇개 생성해줘야 합니다.

만약 .env 파일에서 비밀번호를 수정했다면 그 것으로 입력해주면 됩니다.

그럼 아래와 같은 화면을 확인 할 수 있습니다.

kibana 화면

그리고 Management>Stack Management>Index Management를 들어가면

ek-log-{날짜}가 아래와 같이 index가 있으면 됩니다.

index management

 

그리고 위 페이지에서 Kibana>Data Views에서 Create data view를 해줍니다.

 

Data View

 

 

여기서 ek-log*처럼 우리가 위에서 logstash에서 설정한 패턴으로 index pattern을 지정해주고

save data view to Kibana를 해주면 모니터링 준비가 완료 되었습니다.

그리고 다시 홈으로 가서 Discover로 들어가면 아래와 같은 화면을 볼 수 있습니다.

Discover

 

로그들이 잘 적재되고 보이는 걸 확인할 수 있습니다.

마지막으로 KQL을 이용해서 내가 원하는 패턴의 로그를 확인하면 됩니다.

 

결론

 Kafka, ELK stack으로 spring 어플리케이션의 모니터링을 구현하면서 항상 느꼈던게 인프라를 구성하면 어느정도까지 엔지니어링을 가져가야 하나가 중요해 보입니다. 위에서 잠깐 나왔지만 Fluentd를 사용할수 있고, 그 외에 제가 모르는 모니터링 설계가 많을 것입니다. 최근에 우아한 형제들에서는 ELK에서 Loki로 변경했다는 글을 봤습니다. 현재 나의 어플리케이션 상황을 먼저 정의하는게 선행되야 합니다. 그리고 어느 오픈소스를 사용해야하는 지 정하고, 그에 맞는 설계를 하는 것이 중요하다고 느꼈습니다.

728x90
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
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
글 보관함
250x250