지금까지 모니터링의 이론과 프로메테우스의 동작 방식을 배웠습니다. 이제 가장 중요한 "그래서 내 스프링 부트 애플리케이션은 어떻게 모니터링하는데?" 라는 질문에 답할 차례입니다. 이 장에서는 Spring Boot Actuator와 Micrometer를 사용하여 실제 애플리케이션의 내부 동작을 메트릭으로 노출하는 방법을 실습합니다.
스프링 부트 환경에서 메트릭을 수집할 때, 이 두 가지 라이브러리는 환상의 콤비로 동작합니다.
Micrometer와 Prometheus의 관계는 로깅 라이브러리인 SLF4J와 Logback의 관계와 정확히 같습니다.
Logger.info())를 제공합니다. 개발자는 어떤 로깅 구현체가 사용될지 신경 쓰지 않고 SLF4J API로만 코딩합니다.이러한 Facade 패턴 덕분에, 우리는 나중에 모니터링 시스템을 Prometheus에서 Datadog으로 바꾸더라도 애플리케이션 코드를 단 한 줄도 수정할 필요가 없습니다. 의존성(dependency)만 교체해주면 되기 때문입니다.
먼저, build.gradle 파일에 Actuator와 프로메테우스용 Micrometer Registry 의존성을 추가합니다.
// build.gradle.kts
dependencies {
// ... 다른 의존성들
implementation("org.springframework.boot:spring-boot-starter-web")
// 1. Actuator 추가
implementation("org.springframework.boot:spring-boot-starter-actuator")
// 2. Prometheus 연동을 위한 Micrometer Registry 추가
implementation("io.micrometer:micrometer-registry-prometheus")
}
다음으로, application.yml 파일에서 Actuator 엔드포인트를 활성화하고 프로메테우스가 메트릭을 수집해갈 수 있도록 /actuator/prometheus 엔드포인트를 노출시킵니다.
# application.yml
management:
endpoints:
web:
exposure:
# "health"와 "prometheus" 엔드포인트를 웹으로 노출
include: health,prometheus
# (선택) 메트릭에 공통 태그(레이블) 추가하기
metrics:
tags:
application: my-awesome-app
이제 애플리케이션을 실행하고 http://localhost:8080/actuator/prometheus에 접속하면, JVM, Tomcat, 시스템 정보 등 Actuator가 기본으로 제공하는 수많은 메트릭이 텍스트 형태로 출력되는 것을 볼 수 있습니다.
Actuator와 Micrometer를 추가하는 것만으로도 우리는 이미 강력한 무기를 손에 넣었습니다. 별도의 코드 없이 아래와 같은 핵심 지표들을 바로 모니터링할 수 있습니다.
jvm_memory_used_bytes: JVM 힙 메모리 사용량system_cpu_usage: 시스템 전체 CPU 사용률tomcat_sessions_active_current: 현재 활성화된 Tomcat 세션 수http_server_requests_seconds_*: HTTP 요청 처리 시간 (Histogram)
특히 http_server_requests_seconds는 모든 API 엔드포인트의 응답 시간을 자동으로 측정해주는 매우 유용한 메트릭입니다. 이 메트릭 하나만으로도 각 API의 RPS, Latency(p99, p95), 에러율 등을 모두 계산할 수 있습니다.
# 예시: URI 별 초당 평균 요청 수 (RPS)
sum by (uri) (rate(http_server_requests_seconds_count[1m]))
# 예시: URI 별 95 percentile 응답 시간
histogram_quantile(0.95, sum by (le, uri) (rate(http_server_requests_seconds_bucket[1m])))
기본 메트릭도 훌륭하지만, 진짜 중요한 것은 우리 서비스의 비즈니스 로직과 관련된 메트릭입니다. "오늘 신규 가입자 수", "주문 실패 건수", "특정 외부 API 호출 시간" 등이 여기에 해당합니다. Micrometer는 두 가지 편리한 방법을 제공합니다.
특정 메소드의 실행 시간을 측정하고 싶을 때 가장 간단한 방법입니다. 메소드 위에 @Timed 애노테이션만 붙여주면 됩니다.
import io.micrometer.core.annotation.Timed;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Timed(
value = "my.api.latency", // 메트릭 이름
description = "My API의 응답 시간을 측정합니다.",
percentiles = {0.5, 0.95, 0.99} // p50, p95, p99 백분위수 계산
)
@GetMapping("/api/my")
public String myApi() throws InterruptedException {
// 임의의 지연 시간
Thread.sleep((long) (Math.random() * 1000));
return "OK";
}
}
@Timed를 사용하려면 애플리케이션에 AOP 의존성이 필요하며, @EnableAspectJAutoProxy 설정이 필요할 수 있습니다. Spring Boot는 보통 자동으로 구성해줍니다. 이제 /api/my를 호출하면 my_api_latency_seconds_* 라는 이름의 Histogram 메트릭이 자동으로 수집됩니다.
더 복잡한 비즈니스 로직을 측정하거나, Counter, Gauge 등 다른 타입의 메트릭을 사용하고 싶을 때는 MeterRegistry를 직접 주입받아 사용합니다.
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final Counter orderSuccessCounter;
private final Counter orderFailedCounter;
private final MeterRegistry meterRegistry;
// 생성자에서 MeterRegistry를 주입받고, 사용할 메트릭을 미리 빌드
public OrderService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.orderSuccessCounter = Counter.builder("order.requests")
.tag("status", "success")
.description("성공한 주문 요청 수")
.register(meterRegistry);
this.orderFailedCounter = meterRegistry.counter("order.requests", "status", "failed");
}
public void order(String item) {
// 특정 외부 API 호출 시간을 측정 (Timer)
meterRegistry.timer("external.api.latency", "api.name", "payment").record(() -> {
try {
// ... 주문 로직 및 외부 결제 API 호출 ...
if (Math.random() > 0.1) { // 90% 확률로 성공
System.out.println(item + " 주문 성공");
orderSuccessCounter.increment();
} else {
throw new RuntimeException("주문 실패!");
}
} catch (Exception e) {
System.out.println(item + " 주문 실패");
orderFailedCounter.increment();
throw e;
}
});
}
}
위 예제에서는 MeterRegistry를 사용하여 주문 성공/실패 횟수를 세는 Counter와 외부 API 호출 시간을 재는 Timer(@Timed의 코드 기반 버전)를 만들었습니다. 이렇게 하면 "성공/실패별 주문 요청 수", "결제 API의 응답 시간 분포" 등 매우 구체적이고 유용한 비즈니스 메트릭을 수집할 수 있습니다.
이제 우리 애플리케이션은 /actuator/prometheus 엔드포인트를 통해 메트릭을 열심히 노출하고 있습니다. 마지막으로 프로메테우스가 이 메트릭을 주기적으로 수집하도록 설정해주면 됩니다.
# prometheus.yml
scrape_configs:
- job_name: 'spring-boot-app'
metrics_path: '/actuator/prometheus' # 메트릭 엔드포인트 경로
scrape_interval: 15s # 15초마다 수집
static_configs:
- targets: ['localhost:8080'] # 우리 애플리케이션의 주소
이제 프로메테우스를 실행하면, 15초마다 우리 스프링 부트 애플리케이션의 모든 메트릭을 수집하여 저장하기 시작합니다. 그라파나에서 이 데이터를 쿼리하여 멋진 대시보드를 만드는 일만 남았습니다!
Q: "담당하고 있는 Spring Boot 애플리케이션의 성능을 어떻게 모니터링하고 있나요? 어떤 도구와 지표를 사용하나요?"
A: "저는 Spring Boot Actuator와 Micrometer를 사용하여 애플리케이션 메트릭을 수집하고, 이를 Prometheus 서버에 저장하여 Grafana로 시각화하고 있습니다. 주로 구글의 4대 골든 시그널을 기준으로 모니터링합니다. 첫째, Micrometer가 기본 제공하는 `http_server_requests_seconds` 메트릭을 사용하여 API별 Latency(p99)와 RPS, 에러율을 확인합니다. 둘째, `jvm_memory_used_bytes`와 `system_cpu_usage` 같은 JVM 및 시스템 메트릭으로 Saturation을 파악합니다. 또한, 비즈니스적으로 중요한 로직, 예를 들어 '신규 유저 가입'이나 '상품 주문' 같은 이벤트는 `MeterRegistry`를 사용하여 커스텀 Counter 메트릭을 직접 만들어 추적하고 있습니다."
Q: "Micrometer란 무엇이며, 왜 사용하는 것이 좋은가요?"
A: "Micrometer는 로깅 세계의 SLF4J와 같은 역할을 하는 메트릭 수집용 Facade 라이브러리입니다. 개발자는 Micrometer가 제공하는 표준 API(`Counter`, `Timer` 등)를 사용하여 메트릭을 기록하기만 하면, Micrometer가 실제 연동된 모니터링 시스템(Prometheus, Datadog 등)에 맞는 포맷으로 데이터를 변환하여 보내줍니다. 이렇게 하면 특정 모니터링 시스템에 코드가 종속되는 것을 막을 수 있습니다. 예를 들어, 나중에 Prometheus에서 Datadog으로 모니터링 시스템을 교체하더라도, 비즈니스 로직이 담긴 메트릭 코드는 전혀 수정할 필요 없이 의존성만 교체하면 되기 때문에 유지보수성과 유연성이 크게 향상됩니다."
Q: "특정 서비스 메소드의 실행 시간을 측정하고 싶을 때, 어떤 방법들이 있을까요?"
A: "두 가지 방법이 있습니다. 가장 간단한 방법은 해당 메소드에 Micrometer의 `@Timed` 애노테이션을 붙이는 것입니다. 메트릭 이름과 태그, 측정할 백분위수 등을 애노테이션 속성으로 지정하면 AOP를 통해 자동으로 실행 시간을 측정하는 Histogram 메트릭이 생성됩니다. 더 복잡한 로직이 필요하거나 애노테이션을 사용하기 어려운 환경이라면, `MeterRegistry`를 직접 주입받아 `Timer` 객체를 생성하고, 람다식이나 `record()` 메소드를 사용하여 코드 블록의 실행 시간을 명시적으로 측정할 수도 있습니다. 일반적으로 간단한 메소드 시간 측정은 `@Timed`를, 비즈니스 로직과 깊게 연관된 측정은 `MeterRegistry`를 사용하는 것이 효율적입니다."