개요

마이크로서비스 환경에서 관찰성(Observability)은 시스템의 내부 상태를 이해하고 문제를 신속히 진단하는 데 필수적입니다. Spring Boot 3.x는 Micrometer를 기반으로 메트릭, 추적, 로그를 통합 관리하며, Prometheus와 Grafana를 통해 시각화할 수 있습니다. 이 문서에서는 Spring Observability 스택의 완전한 구축 방법을 다룹니다.

의존성 설정

Observability를 위한 핵심 라이브러리를 추가합니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
    <groupId>io.zipkin.reporter2</groupId>
    <artifactId>zipkin-reporter-brave</artifactId>
</dependency>

Actuator와 Prometheus 설정

메트릭 엔드포인트를 노출하고 Prometheus가 수집할 수 있도록 구성합니다.

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always
  metrics:
    tags:
      application: my-app
      environment: production
  tracing:
    sampling:
      probability: 1.0  # 개발 환경: 100% 샘플링
  zipkin:
    tracing:
      endpoint: http://localhost:9411/api/v2/spans

커스텀 메트릭 수집

비즈니스 메트릭을 직접 정의하여 수집할 수 있습니다.

@Service
@RequiredArgsConstructor
public class OrderService {

    private final MeterRegistry meterRegistry;
    private final Counter orderCounter;
    private final Timer orderProcessingTimer;

    @PostConstruct
    public void init() {
        this.orderCounter = Counter.builder("orders.created")
            .tag("type", "online")
            .description("Total orders created")
            .register(meterRegistry);

        this.orderProcessingTimer = Timer.builder("orders.processing.time")
            .description("Order processing duration")
            .register(meterRegistry);
    }

    public Order createOrder(OrderRequest request) {
        return orderProcessingTimer.recordCallable(() -> {
            Order order = processOrder(request);
            orderCounter.increment();

            // 주문 금액별 메트릭
            meterRegistry.gauge("orders.amount",
                Tags.of("currency", "USD"),
                order.getAmount());

            return order;
        });
    }
}

분산 추적 (Distributed Tracing)

Micrometer Tracing으로 마이크로서비스 간 요청 흐름을 추적합니다.

@RestController
@RequiredArgsConstructor
public class ApiController {

    private final ObservationRegistry observationRegistry;
    private final RestClient restClient;

    @GetMapping("/api/users/{id}")
    public UserDto getUser(@PathVariable Long id) {
        // 자동으로 Span이 생성됨
        return Observation.createNotStarted("get-user", observationRegistry)
            .lowCardinalityKeyValue("userId", String.valueOf(id))
            .observe(() -> {
                // 하위 서비스 호출도 자동 추적
                UserDto user = restClient.get()
                    .uri("/users/{id}", id)
                    .retrieve()
                    .body(UserDto.class);

                // 추가 Span 생성
                Observation.createNotStarted("enrich-user", observationRegistry)
                    .observe(() -> enrichUserData(user));

                return user;
            });
    }
}

Prometheus 설정

Prometheus가 Spring Boot 메트릭을 수집하도록 설정합니다.

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'spring-boot-app'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080']
        labels:
          application: 'my-app'

Grafana 대시보드

Grafana에서 Prometheus 데이터소스를 연결하고 JVM, HTTP 요청, 커스텀 메트릭을 시각화합니다.

{
  "dashboard": {
    "title": "Spring Boot Observability",
    "panels": [
      {
        "title": "JVM Memory",
        "targets": [
          {
            "expr": "jvm_memory_used_bytes{application=\"my-app\"}"
          }
        ]
      },
      {
        "title": "HTTP Request Rate",
        "targets": [
          {
            "expr": "rate(http_server_requests_seconds_count{application=\"my-app\"}[5m])"
          }
        ]
      },
      {
        "title": "Order Processing Time (p95)",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, orders_processing_time_seconds_bucket)"
          }
        ]
      }
    ]
  }
}

활용 팁

  • 메트릭 카디널리티 주의: 고유값이 많은 태그(예: userId)는 메모리 소비가 크므로 low cardinality 데이터만 태그로 사용해야 합니다.
  • 샘플링 조정: 프로덕션 환경에서는 tracing.sampling.probability를 0.1(10%)로 낮춰 오버헤드를 줄입니다.
  • 알람 설정: Prometheus Alertmanager로 응답 시간 초과, 에러율 임계값 등의 알람을 Slack/Email로 전송할 수 있습니다.
  • JVM 메트릭: Actuator는 기본적으로 힙 메모리, GC, 쓰레드 수 등 JVM 메트릭을 자동 수집합니다.
  • 로그 통합: Loki를 추가하면 Grafana에서 메트릭과 로그를 함께 조회할 수 있습니다.

마무리

Spring Observability 스택은 Actuator, Micrometer, Prometheus, Grafana의 조합으로 완성됩니다. 메트릭과 추적을 통해 시스템의 병목 지점을 찾고, 장애 발생 시 빠르게 원인을 파악할 수 있습니다. 특히 Micrometer Tracing은 마이크로서비스 환경에서 여러 서비스를 거치는 요청의 전체 흐름을 시각화하여 디버깅을 혁신적으로 개선합니다. 프로덕션 환경에서는 반드시 샘플링 비율을 조정하고, 메트릭 카디널리티를 관리하여 성능 저하를 방지해야 합니다.