전체 비유: 국제 의료 기록 표준#

OpenTelemetry를 **국제 의료 기록 표준(HL7/FHIR)**에 비유하면 이해하기 쉽습니다:

의료 표준 비유OpenTelemetry역할
병원마다 다른 차트 양식벤더별 SDK호환 안 되는 형식들
국제 의료 기록 표준OpenTelemetry 표준통일된 데이터 형식
표준 진료 기록 양식Semantic Conventions공통 속성 이름 규약
병원 내 기록 시스템OTel SDK데이터 수집
기록 전송 센터OTel Collector수집/변환/전송
다양한 EMR 시스템다양한 백엔드Jaeger, Prometheus 등
병원 이전해도 기록 호환벤더 중립성백엔드 변경 용이
자동 기록 시스템Auto-instrumentation코드 변경 없이 계측

이처럼 국제 표준이 있으면 어느 병원에서도 환자 기록을 읽을 수 있듯, OpenTelemetry는 어떤 백엔드로도 데이터를 보낼 수 있습니다.


대상 독자: 관측성 시스템을 표준화하려는 개발자, SRE 선수 지식: 관측성 3요소, 분산 추적 소요 시간: 약 25-30분 이 문서를 읽으면: OpenTelemetry를 이해하고 프로젝트에 적용할 수 있습니다

TL;DR#

핵심 요약:

  • OpenTelemetry (OTel): Metrics, Logs, Traces를 위한 벤더 중립 표준
  • 구성 요소: SDK (계측) + Collector (수집/변환/전송)
  • 장점: 벤더 종속성 없음, 한 번 계측으로 여러 백엔드 지원
  • CNCF 프로젝트: Kubernetes 다음으로 활발한 프로젝트

OpenTelemetry란?#

OpenTelemetry는 관측성 데이터를 생성, 수집, 전송하기 위한 표준 프레임워크입니다.

이전 상황 (OTel 이전)#

graph LR
    subgraph "벤더별 SDK"
        DD["Datadog SDK"]
        NR["New Relic SDK"]
        JA["Jaeger SDK"]
        PR["Prometheus SDK"]
    end

    APP["Application"]
    APP --> DD
    APP --> NR
    APP --> JA
    APP --> PR

문제점:

  • 벤더마다 다른 SDK
  • 벤더 변경 시 코드 수정 필요
  • 여러 SDK로 오버헤드 증가

OTel 도입 후#

graph LR
    APP["Application"] --> OTEL["OpenTelemetry SDK"]
    OTEL --> COLLECTOR["OTel Collector"]
    COLLECTOR --> DD["Datadog"]
    COLLECTOR --> NR["New Relic"]
    COLLECTOR --> JA["Jaeger"]
    COLLECTOR --> PR["Prometheus"]

장점:

  • 한 번 계측, 여러 백엔드
  • 벤더 변경 시 Collector 설정만 수정
  • 표준화된 의미론(Semantic Conventions)

구성 요소#

1. API & SDK#

애플리케이션 코드에서 관측성 데이터를 생성합니다.

// Tracer 생성
Tracer tracer = openTelemetry.getTracer("order-service");

// Span 생성
Span span = tracer.spanBuilder("processOrder").startSpan();
try (Scope scope = span.makeCurrent()) {
    span.setAttribute("order.id", orderId);
    // 비즈니스 로직
} finally {
    span.end();
}

2. Collector#

데이터를 수신, 처리, 전송하는 에이전트입니다.

graph LR
    subgraph "OTel Collector"
        R["Receivers<br>(수신)"]
        P["Processors<br>(처리)"]
        E["Exporters<br>(전송)"]
    end

    APP["Applications"] --> R
    R --> P --> E
    E --> B1["Jaeger"]
    E --> B2["Prometheus"]
    E --> B3["Loki"]

3. Instrumentation#

자동/수동 계측 라이브러리입니다.

유형설명예시
자동 계측코드 변경 없이 적용Java Agent, Python auto-instrumentation
수동 계측코드에서 직접 SDK 호출커스텀 Span 생성

Collector 설정#

기본 구조#

# otel-collector.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    timeout: 1s
    send_batch_size: 1024
  memory_limiter:
    limit_mib: 512

exporters:
  otlp:
    endpoint: "tempo:4317"
    tls:
      insecure: true
  prometheus:
    endpoint: "0.0.0.0:8889"
  loki:
    endpoint: "http://loki:3100/loki/api/v1/push"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, memory_limiter]
      exporters: [otlp]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheus]
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [loki]

Receivers (수신기)#

Receiver용도
otlpOpenTelemetry Protocol (권장)
jaegerJaeger 형식
zipkinZipkin 형식
prometheusPrometheus scrape

Processors (처리기)#

Processor용도
batch배치 전송으로 효율화
memory_limiter메모리 제한
filter불필요한 데이터 제거
attributes속성 추가/수정/삭제
tail_sampling조건부 샘플링

Exporters (전송기)#

Exporter대상
otlpOTLP 지원 백엔드 (Tempo, Jaeger)
prometheusPrometheus
lokiGrafana Loki
datadogDatadog

Spring Boot 통합#

의존성#

// build.gradle.kts
dependencies {
    // Tracing
    implementation("io.micrometer:micrometer-tracing-bridge-otel")
    implementation("io.opentelemetry:opentelemetry-exporter-otlp")

    // Metrics
    implementation("io.micrometer:micrometer-registry-otlp")

    // 자동 계측 (옵션)
    runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter")
}

application.yml#

spring:
  application:
    name: order-service

management:
  otlp:
    tracing:
      endpoint: http://otel-collector:4318/v1/traces
    metrics:
      export:
        endpoint: http://otel-collector:4318/v1/metrics
        step: 30s
  tracing:
    sampling:
      probability: 0.1  # 10% 샘플링

logging:
  pattern:
    level: "%5p [${spring.application.name},%X{traceId:-},%X{spanId:-}]"

수동 계측 예시#

@Service
@RequiredArgsConstructor
public class OrderService {
    private final Tracer tracer;
    private final MeterRegistry meterRegistry;

    public Order createOrder(OrderRequest request) {
        // Span 생성
        Span span = tracer.nextSpan()
            .name("createOrder")
            .tag("order.type", request.getType())
            .start();

        try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
            // 메트릭 기록
            Timer.Sample sample = Timer.start(meterRegistry);

            Order order = processOrder(request);

            sample.stop(Timer.builder("order.creation.time")
                .tag("type", request.getType())
                .register(meterRegistry));

            span.event("Order created successfully");
            return order;

        } catch (Exception e) {
            span.error(e);
            throw e;
        } finally {
            span.end();
        }
    }
}

Java Agent (자동 계측)#

코드 변경 없이 자동으로 계측합니다.

실행#

java -javaagent:opentelemetry-javaagent.jar \
     -Dotel.service.name=order-service \
     -Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
     -Dotel.traces.sampler=parentbased_traceidratio \
     -Dotel.traces.sampler.arg=0.1 \
     -jar app.jar

Docker 설정#

FROM eclipse-temurin:17-jre

ADD https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar /app/opentelemetry-javaagent.jar

ENV JAVA_TOOL_OPTIONS="-javaagent:/app/opentelemetry-javaagent.jar"
ENV OTEL_SERVICE_NAME="order-service"
ENV OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4317"

COPY app.jar /app/app.jar
CMD ["java", "-jar", "/app/app.jar"]

자동 계측 범위#

카테고리라이브러리
HTTPSpring MVC, JAX-RS, Servlet
데이터베이스JDBC, Hibernate, MyBatis
메시징Kafka, RabbitMQ
캐시Redis, Memcached
HTTP 클라이언트RestTemplate, WebClient, OkHttp

Semantic Conventions#

표준화된 속성 이름으로 일관성을 보장합니다.

HTTP#

속성설명
http.methodGET, POST 등
http.status_code200, 500 등
http.url요청 URL
http.route/users/{id}

데이터베이스#

속성설명
db.systempostgresql, mysql
db.name데이터베이스명
db.operationSELECT, INSERT
db.statementSQL 쿼리

메시징#

속성설명
messaging.systemkafka, rabbitmq
messaging.destination토픽/큐 이름
messaging.operationsend, receive

도입 전략#

단계별 접근#

graph LR
    S1["1단계<br>Collector 배포"] --> S2["2단계<br>자동 계측"]
    S2 --> S3["3단계<br>수동 계측 보강"]
    S3 --> S4["4단계<br>대시보드/알림"]
  1. Collector 배포: 데이터 수집 인프라 구축
  2. 자동 계측: Java Agent로 빠르게 시작
  3. 수동 계측 보강: 비즈니스 로직 커스텀 Span
  4. 대시보드/알림: Grafana 연동

마이그레이션 경로#

현재 상황마이그레이션
Jaeger SDKOTel SDK + Jaeger Exporter
Prometheus 직접 노출OTel + Prometheus Exporter
벤더 SDKOTel SDK + 벤더 Exporter

핵심 정리#

구성요소역할
SDK애플리케이션 계측
Collector수집/변환/전송
OTLP표준 프로토콜
Auto-instrumentation코드 변경 없는 계측

도입 장점:

  • 벤더 종속성 제거
  • 표준화된 의미론
  • 유연한 백엔드 선택
  • 활발한 커뮤니티

다음 단계#

추천 순서문서배우는 것
1대시보드 설계시각화
2풀스택 예제통합 실습