콘텐츠로 이동

Spring Boot Auto-Configuration, Instrumentation and Sampling

스프링 부트에서 추적 파이프라인은 의존성이 만드는 자동 구성 부분과, 그 위에서 개발자가 조정하는 발신 측 설정 부분으로 나뉜다.

추적 신호를 OTLP로 내보내는 최소 조합은 네 의존성으로 구성되며, 각 의존성이 추가되는 순간 조건부로 빈이 등록된다.

의존성역할자동 등록 빈
spring-boot-starter-actuatorObservation·Metrics 인프라 토대ObservationRegistry, MeterRegistry
micrometer-tracingTracer 추상화와 TracingObservationHandler 계약(Tracer 인터페이스 정의)
micrometer-tracing-bridge-otelMicrometer Tracer를 OTel SDK에 연결Tracer(OtelTracer), SdkTracerProvider
opentelemetry-exporter-otlp완료 Span을 OTLP로 직렬화·전송SpanExporter(OtlpHttpSpanExporter)

빈 등록은 클래스패스 존재와 프로퍼티를 보는 조건부 자동 구성(@ConditionalOnClass·@ConditionalOnEnabledTracing)으로 동작한다.

자동 구성된 빈의 동작은 management.tracing·management.otlp 프로퍼티로 조정할 수 있다.

management:
tracing:
sampling:
probability: 0.1 # 기본값, 전체 트레이스의 10%만 추적
propagation:
type: w3c # w3c(기본) | b3 | b3_multi
otlp:
tracing:
endpoint: http://localhost:4318/v1/traces # OTel Collector OTLP/HTTP 수신부
  • sampling.probability: 미설정 시 0.1 (나머지 90%는 샘플링에서 제외되어 기록·전송되지 않음)
  • propagation.type: 송신·수신 헤더 규격을 동시에 지정하며, 송신과 수신을 다르게 두려면 produce·consume로 분리
  • otlp.tracing.endpoint: 이 값이 없으면 SpanExporter 빈이 등록되지 않아 추적이 외부로 나가지 않음

bridge가 있으면 스프링 부트는 주요 I/O 경계에 자동으로 Observation을 생성하는 필터·인터셉터를 끼워 넣는다.

대상계측 컴포넌트hook 지점
인바운드 HTTPServerHttpObservationFilter서블릿 필터 체인 진입부에서 요청당 Observation 생성·헤더 extract
아웃바운드 HTTPRestTemplate·WebClient요청 인터셉터에서 클라이언트 Observation 생성·헤더 inject
JDBCdatasource-micrometer ProxyDataSource·Statement를 래핑해 쿼리 Span 생성
KafkaKafkaTemplate·@KafkaListenerrecord header에 inject·extract
ReactorContext PropagationReactor Context와 Observation 컨텍스트를 상호 브리지
  • 공통 원리: 모든 자동 계측은 등록된 ObservationRegistry를 통과시켜 Observation을 만들고, TracingObservationHandler가 그 이벤트로 Span을 생성
  • 계측되지 않는 직접 호출(예: 수동 HttpURLConnection)은 hook 바깥이라 inject가 누락

자동 계측이 닿지 않는 애플리케이션 내부 로직은 @Observed로 직접 Observation을 만들어 Span으로 남긴다.

  • 동작: 메서드에 @Observed(name = "...")를 붙이면 ObservedAspect(AOP)가 호출을 감싸 Observation 생성
  • 활성화 조건: ObservedAspect는 자동 구성되지 않으므로 빈으로 직접 등록하고 spring-boot-starter-aop 의존성 필요
  • 사용 지점: 결제 검증·재고 차감처럼 비즈니스 의미 단위에 Span을 남겨, I/O 경계 위주의 자동 계측만으로는 안 보이는 구간을 추적

초당 수만 건의 트래픽을 모두 추적해 백엔드로 보내면 관측 시스템 자체가 큰 오버헤드를 유발해 본 서비스 성능을 떨어뜨린다.

  • 샘플링 적용: 전체 트래픽 중 일부만 선별해 추적 데이터를 남겨 부하 방지
  • 결정 시점에 따라 Head Sampling과 Tail Sampling으로 나뉨
구분Head SamplingTail Sampling
결정 시점트레이스 시작 시점트레이스 완료 후
결정 주체애플리케이션(SDK Sampler)Collector(전체 Span 수집 후)
장점즉시 결정, 버퍼링 불필요에러·지연 트레이스만 선별 보존 가능
한계에러·지연 발생 여부를 모른 채 결정전체 Span 버퍼링 필요, 인프라 비용 증가
  • 발신 측(스프링 애플리케이션)이 담당하는 것은 Head Sampling이며, Tail Sampling은 Collector가 담당
  • Head Sampling만 쓰면 희귀한 에러 트레이스가 추첨에서 탈락해 유실될 수 있어, 에러 보존이 중요하면 Collector의 Tail Sampling과 병행

micrometer-tracing-bridge-otel의 기본 Sampler는 ParentBased(TraceIdRatioBased(probability))로 구성된다.

graph TD
A[Span 생성 요청] --> B{부모 Span 존재?}
B -->|있음| C[부모의 sampled 결정 상속]
B -->|없음 - root| D{probability 확률 추첨}
D -->|당첨| E[sampled = true]
D -->|미당첨| F[sampled = false]
  • root span에만 probability가 적용되고, 자식 span은 부모의 결정을 그대로 따름
  • 이 상속 구조가 없으면 같은 트레이스인데 서비스마다 샘플링 결정이 달라져 호출 체인이 반쪽만 기록되는 문제가 발생
  • 결과적으로 하나의 트레이스는 전부 기록되거나 전부 누락되어 호출 트리의 일관성이 유지됨

샘플링 비율은 장애 분석 정밀도와 저장·네트워크 비용 사이의 선택이다.

샘플링 비율가시성비용적합한 상황
100% (전수 추적)모든 요청 추적, 최고 정밀도스토리지/네트워크 비용 최대개발·스테이징 환경, 장애 원인 분석이 시급한 상황
10~50%대부분의 패턴을 포착 가능적절한 비용일반적인 프로덕션 환경
1~5%전체적인 추세만 파악 가능비용 최소화초고트래픽 환경, 비용 최적화가 우선인 경우
  • 적응형 샘플링: 트래픽 추이에 따라 비율을 동적으로 조절해 최소한의 가시성을 항상 확보하는 방식으로, 고정 비율의 트레이드오프를 완화

마지막 업데이트:

Spring