코드 저장소.

Spring 서비스의 운영 모니터링 환경 구축기1 Prometheus, Grafana, Loki, Kafka/Redis Exporter 본문

포폴/일정관리앱

Spring 서비스의 운영 모니터링 환경 구축기1 Prometheus, Grafana, Loki, Kafka/Redis Exporter

slown 2025. 6. 17. 08:24

목차

1.도입: 왜 도입을 했는가?

2.아키텍처 개요

3.Prometheus로 메트릭 수집

4.Loki + Promtail로그 수집

5. Grafana로 통합 대시보드 구성

6.느낀점

 

1.도입: 왜 도입을 했는가?

일정관리 프로젝트 개발 초기에는 Spring Boot의 MDC(Mapped Diagnostic Context)를 활용해서 requestId, username 등의 식별자를 로그에 포함시키고, 콘솔(logback)을 통해 디버깅을 진행했습니다.

MDC.put("requestId", UUID.randomUUID().toString());
MDC.put("username", authentication.getName());
<!-- logback-spring.xml -->
<pattern>
  {
    "timestamp": "%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ}",
    "level": "%level",
    "logger": "%logger",
    "message": "%message",
    "requestId": "%X{requestId}",
    "email": "%X{email}"
  }
</pattern>

 

이 방식은 로컬 개발 환경에서는 효과적이었습니다. IDE 콘솔에서 실시간으로 로그를 확인을 할 수 있었고, requestId 기반으로 로직의 흐름을 추적할 수 있었기 때문입니다. 

 

하지만 이 방식은 배포 이후, 아래와 같은 문제에 직면을 하게 되었습니다. 

 

  • 로그 접근 불편
    • 서버에서는 콘솔이 아닌 로그 파일(/var/log/...)로 남기 때문에, 일일이 ssh에 접속을 해서 tail,grep를  해야된다는 점
  • 실시간 추적 불가
    • 여러 요청이 동시에 처리가 되면 어떤 요청이 어떤 로그인지가 혼동이 되기 시작함
  • 분산 로그 수집 어려움
    • Kafka,Redis 등의 로그는 백엔드 로그와 분리되어 있어서 전체 흐름 파악이 어려움
  • 시각화가 없음
    • 배포를 하게 되면 서버의 메모리 및 JVM Memory, API 응답속도, GC 동작 등을 숫자나 그래프로 볼 수가 없음.

이러한 이유로 인해서 실시간 모니터링 시스템을 도입을  하기로 했습니다. 

운영 환경에서는 로그를 단순히 보는 것만으로는 부족하다는 판단하에, 다음 요소들을 고려하여 관측성(observability) 시스템을 도입하게 됐습니다. 운영 환경에서의 로그 수집은 결국 파일 I/O 병목과 병렬 요청 처리 문제와 밀접하게 연결됩니다. 여러 요청이 동시에 로그를 남기면 스레드 간 순서를 보장하기 어려운데, Spring의 MDC는 ThreadLocal 기반 컨텍스트 전파를 이용해 각 요청을 구분하도록 도와준다. 이 기능을 기반으로 requestId를 로그에 포함시켜 요청 단위 추적이 가능하도록 설계했다.

2.아키텍처 개요

이번에 구축을 할 모니터링 아키텍처는 아래의 사진과 같습니다.

 

위의 아키텍처를 설명을 하면 다음과 같습니다. 

  • 로그 기반 모니터링 시스템
    • Spring Boot, Redis, Kafka 로그를 각각 Promtail이 수집
    • Loki에 전송된 로그를 Grafana에서 시각화
    • MDC 기반 JSON 로그 포맷으로 requestId, email 등 필터링 가능
  • 메트릭 기반 모니터링 시스템
    • Spring Boot는 /actuator/prometheus 메트릭을 노출
    • Redis/Kafka는 Exporter를 통해 상태 정보 전달
    • Prometheus가 메트릭을 주기적으로 수집하고 Grafana에서 시각화

3.Prometheus로 메트릭 수집

Prometheus는 시계열 DB 기반 매트릭 수집 시스템이다. 이번 프로젝트에서는 다음과 같이 지표들을 수집을 했습니다.

 

수집대상

  • Spring Boot : JVM Memory, Heap 사용량, GC시간, HTTP 요청수/속도 등
  • Redis: Key수, 메모리 사용량, 연결 수, latency 등
  • Kafka: Borker 상태, Topic lag,파티션 수, consumer offset등

구성 방식

  • docker-compose.dev.yml 파일 내에 Promethus 서비스 정의
  • 각 서비스에 exporter를 설치를 하고 targets 설정을 통해서 Prometheus가 주기적으로 메트릭을 수집
  • 메트릭은 기본적으로 15초 간격(scrape_interval)으로 수집
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'spring-boot'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['api.schedulemanagement.site:8082']

  - job_name: 'kafka'
    static_configs:
      - targets: ['api.schedulemanagement.site:9308']

  - job_name: 'redis'
    static_configs:
      - targets: ['api.schedulemanagement.site:9121']

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['api.schedulemanagement.site:9100']

 

Prometheus는 pull 방식을 사용합니다. 이는 각 노드의 상태를 주기적으로 요청해 데이터를 수집하는 구조입니다. push 방식보다 관리가 단순하고, 장애 시 데이터 일관성을 보장하기 쉽습니다. Prometheus는 내부적으로 LSM(Log-Structured Merge) Tree 구조를 활용해 시계열 데이터를 효율적으로 압축하고 인덱싱합니다. 덕분에 메모리 사용량을 최소화하면서도 시간순으로 빠르게 조회할 수 있다.

4.Loki + Promtail로그 수집

로그 수집은 Grafana의 Loki와 Promtail을 사용했습니다. Promtail은 각 컨테이너의 로그 디렉토리를 tail하여 Loki로 전송을 하는 역할이고, Loki는 수신된 로그를 Grafana를 통해서 시각화 가능하게 해줍니다.

 

구성 방식

  • 로그 파일은 컨테이너에서 /var/log/schedule-backend/app.log 로 출력이 되도록 logback을 설정
  • Promtail은 해당 경로의 로그를 감시(tailing)하여 Loki로 전송
  • JSON 포맷 로그를 사용해 필터링 및 검색 용이
# log-back예시
<pattern>
    <pattern>
        {
        	"level": "%level",
            "thread": "%thread",
            "logger": "%logger",
            "message": "%message",
            "requestId": "%X{requestId}"
        }
    </pattern>
</pattern>

 

위의 포맷의 장점은 아래와 같습니다. 

  • requestId 기준으로 백엔드 로그를 추적 가능( MDC 활용)
  • Kafka,Redis,Spring 로그를 한 화면에서 통합 조회 가능
  • 에러, WARN 로그를 수준별로 필터링하거나 특정 사용자 기반 검색 가능 

5. Grafana로 통합 대시보드 구성

Grafana는 Loki와 Prometheus 데이터를 시각화해주는 대시보드 툴입니다. 이번 프로젝트에서는 JVM Heap, CPU 사용률, Kafka Broker 상태, Redis 메모리 사용량, API 응답속도 등을 한 화면에서 모니터링할 수 있도록 구성했습니다. Grafana는 단순한 시각화 도구가 아니라 데이터 질의 엔진(Query Engine) 으로 동작합니다. Prometheus의 시계열 데이터는 PromQL, Loki의 로그 데이터는 LogQL로 질의합니다. 즉, 단순히 데이터를 보여주는 게 아니라, 필터링과 집계를 통해 의미 있는 상태 정보를 도출한다는 점이 핵심입니다.

6.느낀점

이번 구축을 통해 단순히 로그를 보는 단계를 넘어서, 서비스의 동작 상태를 실시간으로 읽고 분석할 수 있는 환경을 만들 수 있었습니다.

  • 운영 장애 대응 속도 향상
    예전에는 오류 원인 파악에 grep, tail 명령어를 반복했지만, 이제는 Grafana 대시보드에서 즉시 파악이 가능하다.
  • 서비스 병목 구간 파악
    Prometheus 기반 메트릭 덕분에 GC 시간, 요청 응답속도가 튀는 구간을 시각적으로 확인할 수 있었다.
  • 이벤트 흐름 추적
    Kafka → Consumer → DLQ 재처리까지 전체 이벤트 흐름을 한눈에 파악할 수 있었다.

이번 경험을 통해 모니터링은 단순히 “데이터를 보는 일”이 아니라, 시스템 내부의 동작 원리를 이해하는 과정이라는 걸 느꼈다.
운영체제의 파일 I/O, 네트워크 통신, DB 인덱싱, 스레드 컨텍스트 등 다양한 기술들이 하나의 관측 체계로 엮여 있다는 점이 인상 깊었다. 이제는 단순히 코드를 작성하는 개발자가 아니라, 서비스를 운영하고 개선할 수 있는 개발자로 한 단계 성장했다고 생각한다.

 

다음 글에서는 Prometheus, Grafana, Loki를 모두 포함한 모니터링 스택은 생각보다 많은 리소스를 요구했고, 단일 2GB 서버에서 Spring Boot, Kafka, Redis, Nginx, Prometheus, Loki, Grafana가 동시에 돌아가면서 메모리 사용량이 1.5GB를 넘어서자 결국 서버가 다운되기 시작했다.이 문제를 해결하기 위해 모니터링 스택 분리, 로그 자동 순환(logrotate), JVM 튜닝 등을 적용하며 “한정된 리소스 환경에서 어떻게 안정적으로 서비스를 유지할 수 있었는가”를 다루려 한다.