티스토리 뷰
[Monitoring] Spring Logback, Kafka, ELK 를 이용한 application log monitoring
5_Clock 2024. 3. 22. 22:47서론
모니터링은 보통 인프라와 어플리케이션 모니터링으로 나뉩니다.
인프라 모니터링은 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
그리고 더욱 간단하게 생각하면 아주 성능좋은 큐라고 생각하면 된다.
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
위 포스팅의 마지막 부분은 로그를 파일로 떨어뜨려 줍니다.
만약 파일을 이용해서 로그를 모니터링하고 싶다면, 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 파일에서 비밀번호를 수정했다면 그 것으로 입력해주면 됩니다.
그럼 아래와 같은 화면을 확인 할 수 있습니다.
그리고 Management>Stack Management>Index Management를 들어가면
ek-log-{날짜}가 아래와 같이 index가 있으면 됩니다.
그리고 위 페이지에서 Kibana>Data Views에서 Create data view를 해줍니다.
여기서 ek-log*처럼 우리가 위에서 logstash에서 설정한 패턴으로 index pattern을 지정해주고
save data view to Kibana를 해주면 모니터링 준비가 완료 되었습니다.
그리고 다시 홈으로 가서 Discover로 들어가면 아래와 같은 화면을 볼 수 있습니다.
로그들이 잘 적재되고 보이는 걸 확인할 수 있습니다.
마지막으로 KQL을 이용해서 내가 원하는 패턴의 로그를 확인하면 됩니다.
결론
Kafka, ELK stack으로 spring 어플리케이션의 모니터링을 구현하면서 항상 느꼈던게 인프라를 구성하면 어느정도까지 엔지니어링을 가져가야 하나가 중요해 보입니다. 위에서 잠깐 나왔지만 Fluentd를 사용할수 있고, 그 외에 제가 모르는 모니터링 설계가 많을 것입니다. 최근에 우아한 형제들에서는 ELK에서 Loki로 변경했다는 글을 봤습니다. 현재 나의 어플리케이션 상황을 먼저 정의하는게 선행되야 합니다. 그리고 어느 오픈소스를 사용해야하는 지 정하고, 그에 맞는 설계를 하는 것이 중요하다고 느꼈습니다.
- Total
- Today
- Yesterday
- spring
- Linux
- Container
- K8S
- KAFKA
- API
- zookeeper
- JPA
- broker
- Front
- OS
- Java
- feign client
- Producer
- backend
- cs
- consumer
- NextJS
- Data Engineering
- frontend
- apache
- spring boot
- centos
- 리액트
- Firebase
- 프론트엔드
- React
- rhel
- apache kafka
- caching
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |