서브타이틀 : Spring Boot 로그를 ELK 스택과 함께 실시간으로 분석하고 시각화하는 인프라 구성기
🧐 왜 이걸 딥다이빙하게 되었을까요?
서비스 운영 중 발생하는 수많은 로그를 단순 저장만 하는 건 이제 너무 아쉽죠. 로그에서 유의미한 정보를 추출하고, 에러 발생 시 신속하게 대응하기 위해선 수집, 저장, 분석, 시각화까지 한 번에 가능한 ELK 스택을 제대로 써보는 게 중요하다고 생각했어요. 특히 Redis와 함께 연동하면 더욱 안정적으로 로그 버퍼링이 가능하다는 점이 끌렸고요!

🔍 ELK 스택 구성 및 이해
- ELK 스택이란?
- Elasticsearch: 강력한 검색 및 분석 엔진
- Logstash: 다양한 소스로부터 데이터 수집 및 가공
- Kibana: 데이터 시각화 및 탐색 툴
- 이렇게 셋을 합쳐 ELK 스택이라고 불러요!
- Grafana vs Kibana
- Grafana는 시계열 메트릭 시각화에 강점, 다양한 소스 연동 가능
- Kibana는 Elasticsearch 데이터에 대한 검색, 시각화에 최적화
- 텍스트 기반 쿼리는 Kibana가 유리, 알림(Alert) 기능은 Grafana가 더 편리해요
🚀 시스템 구성 목표
WAS Log → Redis(Buffer) → Logstash(가공) → Elasticsearch(저장) → Kibana(시각화)
## 참고: beats는 현재 구성에선 제외했어요.

🫵 설치 및 설정 방법
1. docker-elk (v8.7.0) 설치
git clone https://github.com/deviantony/docker-elk.git
2. docker-compose.yml 설정
vi docker-compose.yml
services:
setup:
container_name: setup
build:
context: setup/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
init: true
volumes:
- ./setup/entrypoint.sh:/entrypoint.sh:ro,Z
- ./setup/lib.sh:/lib.sh:ro,Z
- ./setup/roles:/roles:ro,Z
- setup:/state:Z
environment:
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
METRICBEAT_INTERNAL_PASSWORD: ${METRICBEAT_INTERNAL_PASSWORD:-}
FILEBEAT_INTERNAL_PASSWORD: ${FILEBEAT_INTERNAL_PASSWORD:-}
HEARTBEAT_INTERNAL_PASSWORD: ${HEARTBEAT_INTERNAL_PASSWORD:-}
MONITORING_INTERNAL_PASSWORD: ${MONITORING_INTERNAL_PASSWORD:-}
BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-}
depends_on:
- elasticsearch
elasticsearch:
container_name: elasticsearch
build:
context: elasticsearch/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
volumes:
- ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro,Z
- elasticsearch:/usr/share/elasticsearch/data:Z
ports:
- 9200:9200
- 9300:9300
environment:
node.name: elasticsearch
ES_JAVA_OPTS: -Xms512m -Xmx512m
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
discovery.type: single-node
restart: unless-stopped
logstash:
container_name: logstash
build:
context: logstash/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
volumes:
- ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro,Z
- ./logstash/pipeline:/usr/share/logstash/pipeline:ro,Z
ports:
- 5044:5044
- 20000:20000/tcp
- 20000:20000/udp
- 9600:9600
environment:
LS_JAVA_OPTS: -Xms256m -Xmx256m
LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
depends_on:
- elasticsearch
restart: unless-stopped
kibana:
container_name: kibana
build:
context: kibana/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
volumes:
- ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml:ro,Z
ports:
- 5601:5601
environment:
KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
depends_on:
- elasticsearch
restart: unless-stopped
# 해당 docker 네트워크를 docker-elk-redis_elk 로 외부와 공유한다.
networks:
docker-elk-redis_elk:
external: true
volumes:
setup:
elasticsearch:
3. .env 파일에서 비밀번호 및 버전 관리
docker-compose.yml 파일안에 ${} 이런식으로 변수 설정이 되어있는데,
이건 폴더의 .env 파일을 열어보면 값을 확인하실 수 있습니다. 아래와 같은 구조로 작성되어있습니다.
(버전 및 패스워드를 관리하는 환경 파일)
vi .env
ELASTIC_VERSION=8.7.0
## Passwords for stack users
#
# User 'elastic' (built-in)
#
# Superuser role, full access to cluster management and data indices.
# https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html
ELASTIC_PASSWORD='changeme'
# User 'logstash_internal' (custom)
#
# The user Logstash uses to connect and send data to Elasticsearch.
# https://www.elastic.co/guide/en/logstash/current/ls-security.html
LOGSTASH_INTERNAL_PASSWORD='changeme'
# User 'kibana_system' (built-in)
#
# The user Kibana uses to connect and communicate with Elasticsearch.
# https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html
KIBANA_SYSTEM_PASSWORD='changeme'
# Users 'metricbeat_internal', 'filebeat_internal' and 'heartbeat_internal' (custom)
#
# The users Beats use to connect and send data to Elasticsearch.
# https://www.elastic.co/guide/en/beats/metricbeat/current/feature-roles.html
METRICBEAT_INTERNAL_PASSWORD=''
FILEBEAT_INTERNAL_PASSWORD=''
HEARTBEAT_INTERNAL_PASSWORD=''
# User 'monitoring_internal' (custom)
#
# The user Metricbeat uses to collect monitoring data from stack components.
# https://www.elastic.co/guide/en/elasticsearch/reference/current/how-monitoring-works.html
MONITORING_INTERNAL_PASSWORD=''
# User 'beats_system' (built-in)
#
# The user the Beats use when storing monitoring information in Elasticsearch.
# https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html
BEATS_SYSTEM_PASSWORD=''
4. elasticsearch.yml 설정 변경
이제 저희는 basic 버전을 사용할 것이기 때문에 설정파일을 수정해야합니다.
vi docker-elk/elasticsearch/config/elasticsearch.yml 파일을 열어 수정합시다.
(default : trial로 되어 있을꺼에요!)
## 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
#
#security
xpack.license.self_generated.type: basic
xpack.security.enabled: true
xpack.monitoring.collection.enabled: true
xpack.security.authc.api_key.enabled: true #apiKey 사용설정.
5. logstash.conf 수정 (Slack 알림 설정)
Redis를 이용하여 Log를 쌓을 것이기 때문에 logstash의 pipeline도 수정해줘야합니다.
vi docker-elk/logstash/pipeline/logstash.conf 파일을 아래처럼 수정합시다.
Slack 알림 설정 : ERROR 로그 발생 시 Slack 채널로 자동 전송되도록 설정
vi docker-elk/logstash/pipeline/logstash.conf
input {
redis {
host => "THINKANDTHING" # 192.168.0.123
port => 6379
codec => "json"
data_type => "list"
key => "logstash"
}
}
filter {
# timestamp 필드를 ISO8601 형식으로 변환 및 타임존 설정
date {
match => [ "timestamp", "ISO8601" ]
timezone => "Asia/Seoul"
}
mutate {
rename => { "host" => "[host][name]" }
}
# 시간 계산을 위한 ruby 필터 추가
ruby {
code => "
timestamp = event.get('@timestamp')
event.set('from_time', (timestamp - 300).strftime('%Y-%m-%dT%H:%M:%S.%LZ')) # 5분(300초) 전
event.set('to_time', (timestamp + 300).strftime('%Y-%m-%dT%H:%M:%S.%LZ')) # 5분(300초) 후
"
}
if [throwable] {
ruby {
code => '
throwable = event.get("throwable")
if throwable.is_a?(String)
lines = throwable.split("\n") # 줄바꿈 기준으로 분리
short_trace = lines[0..4].join("\n") # 처음 5줄만 가져옴
event.set("short_trace", short_trace)
else
event.set("short_trace", "No stack trace available.") # 기본 메시지 설정
end
'
}
} else {
mutate {
add_field => { "short_trace" => "No stack trace available." } # 기본 메시지 설정
}
}
}
output {
stdout { codec => rubydebug }
elasticsearch {
hosts => "elasticsearch:9200"
index => "%{[type]}-%{+YYYY.MM.dd}"
user => "logstash_internal"
password => "${LOGSTASH_INTERNAL_PASSWORD}"
ecs_compatibility => "disabled"
manage_template => false
#document_id => "%{[@metadata][_id]}"
action => "index"
retry_on_conflict => 5
}
# Slack 알림: log.level이 ERROR인 경우
if [level] == "ERROR" {
slack {
url => "https://hooks.slack.com/services/THINKANDTHING~~~~~"
channel => "THINKANDTHING-alert"
username => "ELK-Logstash-Bot"
icon_emoji => ":rotating_light:"
format => ""
attachments => [
{
"color" => "danger"
"fields" => [
{"title" => "service" "value" => "%{source}" "short" => true},
{"title" => "url" "value" => "<http://192.168.0.211:5601/app/discover#/?_g=(time:(from:'%{from_time}',to:'%{to_time}'))&_a=(index:'%{[type]}-%{+YYYY.MM.dd}',query:(language:kuery,query:'level:ERROR%20AND%20logger:\"%{[logger]}\"'))|*Kibana URL*>" "short" => true},
{"title" => "thread" "value" => "%{thread}" "short" => true},
{"title" => "level" "value" => "%{level}" "short" => true},
{"title" => "logger" "value" => "%{logger}"},
{"title" => "message" "value" => "%{message}" },
{"title" => "Stack Trace" "value" => "%{short_trace}"} # 처음 5줄만 포함
]
}
]
}
}
}
6. kibana 설정
기본 설정 유지 (별도 수정 없음)
🔚 마무리 멘트
ELK 스택은 단순 로그 저장을 넘어, 서비스 품질과 운영 효율을 높이는 핵심 인프라 구성 요소예요. 특히 Redis를 이용한 버퍼링과 Slack 연동으로 실시간 대응까지 갖춘다면 더 이상 로그는 단순 참고자료가 아니라, 강력한 운영 무기가 된답니다! 🙌
🙇♀️ 다음 글 (많관부)
이번 글에서는 설정하는 방법에 대해서 작성했는데요, 다음 글에서는 ELK 설정하고 대시보드 확인하는 방법에 대해 작성해볼게요 !
다음글 : https://thinkandthing.tistory.com/122
[ELK] ELK 스택 구축으로 로그를 수집하고 시각화하기(2/3) (feat. Redis)
서브타이틀 : 로그를 모으고 보는 것까지! Kibana UI 확인과 Spring Boot 로그 설정법 🙇♂️ 이전 글 : https://thinkandthing.tistory.com/121 🧐 왜 이걸 딥다이빙하게 되었을까요?앞선 글에서 ELK 스택을 구
thinkandthing.tistory.com
'SpringBoot' 카테고리의 다른 글
| [ELK] ELK 스택 구축으로 로그를 수집하고 시각화하기(3/3) (feat. Redis) (5) | 2025.06.11 |
|---|---|
| [ELK] ELK 스택 구축으로 로그를 수집하고 시각화하기(2/3) (feat. Redis) (4) | 2025.06.11 |
| [ELK] Kibana Dev Tools 명령어 모음 & 해설 🧰 (4) | 2025.06.10 |
| [SpringBoot] Springboot tar 파일 생성 (0) | 2023.03.09 |
| [SpringBoot] 스프링DB 마이그레이션 (0) | 2023.03.07 |
댓글