안녕하세요. Micrometer, Prometheus, Grafana를 활용해 Spring Boot 애플리케이션의 부하 테스트 지표를 수집하고 시각화한 경험을 정리해보았습니다.
Redis 만료 이벤트 처리와 같은 비동기 처리 로직의 성능을 검증하면서, 각 구성 요소가 어떻게 연동되었는지, 어떤 지표를 수집했는지, 그리고 이를 어떻게 Grafana 대시보드에서 시각화했는지를 상세히 소개드리겠습니다.
적용배경
최근 실무에서 Redis의 TTL 기능을 활용하여, 데이터가 만료되었을 때 자동으로 EntryExpiredListener에 유입되어 처리되는 로직을 적용하게 되었습니다.
이 로직은 좌석 자동 해제와 같은 중요한 작업을 처리하기 때문에, 만료 데이터가 대량으로 발생하는 상황에서 시스템에 어떤 부하가 걸리는지 확인하는 것이 핵심 과제였습니다.
처음에는 많은 분들이 사용하는 JMeter, nGrinder와 같은 도구를 고려했지만, 이들은 주로 HTTP 통신 기반의 응답 속도 측정에 초점이 맞춰져 있어, 내부 비즈니스 로직의 처리 성능을 직접적으로 측정하기에는 한계가 있었습니다.
그래서 고민 끝에, Spring Boot에서 지원하는 Micrometer를 통해 커스텀 메트릭을 수집하고, 이를 Prometheus로 스크래핑한 뒤, Grafana에서 시각화하는 구조를 도입해보았습니다.
덕분에 EntryExpiredListener 로직의 수행 시간, 호출 횟수, 예외 발생률 등 다양한 지표를 실시간으로 수치화하고 모니터링할 수 있었고, 실제 부하 상황에서의 병목 구간도 파악할 수 있었습니다.
이 글에서는 해당 구성 방식과 설정 방법, 그리고 실제 적용 과정에서 얻은 인사이트를 공유드리겠습니다.
Micrometer, Prometheus, Grafana 구성도
[Spring Boot App]
|
| (지표 수집, Timer / Counter)
v
[Micrometer]
|
| (메트릭 export)
v
[Prometheus]
|
| (데이터 쿼리)
v
[Grafana Dashboard]
- Micrometer: Spring Boot 애플리케이션 내에서 Timer, Counter, Gauge 등의 메트릭을 생성합니다.
- Prometheus: 일정 주기(예: 5초)에 한 번씩 애플리케이션의 /actuator/prometheus 엔드포인트를 호출하여 메트릭을 수집합니다.
- Grafana: Prometheus에 쿼리를 날려 실시간 지표를 시각화합니다.
Metric 수집 항목 및 코드 적용 방법
저는 Redisson EntryExpiredListener에서 처리되는 만료 이벤트 처리 시간과 처리 횟수를 모니터링 했습니다. 이를 위해 Micrometer의 Timer 기능을 사용했습니다.
의존성 추가 (pom.xml/build.gradle)
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Micrometer Prometheus Registry -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
- build.gradle
dependencies {
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
application.yml 설정
management:
endpoints:
web:
exposure:
include: health, info, metrics, prometheus
metrics:
export:
prometheus:
enabled: true
prometheus:
metrics:
export:
enabled: true
listener 내 지표 수집 코드 예시
@Component
@Slf4j
@RequiredArgsConstructor
public class RedissonExpireListener {
private final RedissonClient redissonClient;
private final BookingStatusExpireService bookingStatusExpireService;
private final MeterRegistry meterRegistry;
/**
* bookingStatusExpireManager의 RMapCache에 등록된 key가 만료되면 처리하도록 함.
*/
@EventListener(ApplicationReadyEvent.class)
public void register() {
RMapCache<String, String> mapCache = redissonClient.getMapCache(TwayRedisCacheConst.RedissonCacheConst.BOOKING_STATUS_EXPIRE.getCacheName());
mapCache.addListener((EntryExpiredListener<String, String>) event -> {
Timer.Sample sample = Timer.start(meterRegistry); // 측정 시작
sample.stop(meterRegistry.timer("event_duration")); // 측정 종료
});
}
}
prometheus, grafana 세팅을 위한 docker compose 생성
version: '2.4'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
container_name: grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
ports:
- "3000:3000"
volumes:
- grafana-storage:/var/lib/grafana
spring-app:
image: spring-app:latest
container_name: spring-app
build:
context: ./
ports:
- "8080:8080"
depends_on:
- prometheus
volumes:
grafana-storage:
prometheus.yml 설정 추가
global:
scrape_interval: 5s
scrape_configs:
- job_name: 'spring-actuator'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['host.docker.internal:8080'] # Windows: Spring Boot가 실행 중인 호스트
- Mac/Linux의 경우 host.docker.internal이 동작하지 않을 수 있으므로 docker0 브리지 IP를 사용하거나 네트워크 설정이 필요합니다.
docker compose 실행
docker compose -f docker-compose.yml up --build -d
이제 http://localhost:9090 (Prometheus), http://localhost:3000 (Grafana)에 접속해서 데이터를 확인할 수 있습니다.
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000
- 기본 계정: admin / admin
이제 실시간으로 event_duration 메트릭을 확인하고, 부하 상황에서 처리 시간이 어떻게 변화하는지를 시각화할 수 있습니다.
Grafana 대시보드 구성
기존에도 잘만들어진 대시보드를 이용해서 많이쓰시지만 저는 해당 기능만을 모니터링하기 위해 커스터마이징하여 그라파나 대시보드를 구성하였습니다.
새 패널 생성
1. Grafana 대시보드 접속 후 `+ Create → Dashboard → Add new panel` 클릭
2. 데이터 소스는 `Prometheus` 선택
3. 시각화 타입은 기본값인 `Time series` 유지
4. 아래의 쿼리 입력란에 PromQL을 작성 (작성 시 코드 탭 클릭 후 작성)
- 만료 이벤트 처리 시간 평균
rate(event_duration_seconds_sum[1m])
/
rate(event_duration_seconds_count[1m])
- 처리 횟수
rate(booking_status_expire_listener_duration_count[1m])
패널 생성 후 대시보드에서 위와 같은 그래프를 확인할 수 있습니다.
프로메테우스도 결과 값을 보여주기 위해 쿼리 같은 문법을 사용하는데 이 경우 공식 사이트에서 확인이 가능합니다.
https://prometheus.io/docs/prometheus/latest/querying/operators/
Operators | Prometheus
Prometheus project documentation for Operators
prometheus.io
'Spring' 카테고리의 다른 글
[SpringBoot] 스프링 부트 비동기 처리(@Async) 이해와 적용 방법 (0) | 2025.07.28 |
---|---|
SpringBoot VersionUp 전환 대응기 - thymeleaf편 (0) | 2025.02.14 |
spring.profiles.active vs spring.config.activate.on-profile? (0) | 2024.10.08 |
쿠폰발급 서비스 동시성 처리하기3 - (3/3) (feat.Redis) (0) | 2024.02.13 |
쿠폰 발급 서비스 동시성 처리하기 2 - (2/3) (feat. Database Lock) (1) | 2024.01.30 |