OpenTelemetry 완벽 가이드

분산 시스템의 관측 가능성을 위한 표준 프레임워크

OpenTelemetry 핵심 구성요소

📡

API

계측(Instrumentation)을 위한 표준 인터페이스를 제공합니다.

  • 언어별 API 제공
  • Tracer, Meter, Logger 인터페이스
  • 벤더 중립적 설계
  • Context Propagation
🔧

SDK

API의 구체적인 구현체로 데이터 수집과 처리를 담당합니다.

  • 샘플링 전략 설정
  • 배치 처리 및 버퍼링
  • 리소스 감지
  • 내보내기(Export) 구성
🎯

Instrumentation

애플리케이션과 라이브러리에 관측 가능성을 추가하는 코드입니다.

  • 자동 계측 라이브러리
  • 수동 계측 도구
  • 프레임워크별 통합
  • 플러그인 시스템
📊

Collector

텔레메트리 데이터를 수신, 처리, 내보내는 중앙 서비스입니다.

  • 다양한 수신자(Receiver)
  • 데이터 처리 파이프라인
  • 다중 내보내기(Exporter)
  • 성능 최적화
🚀

Exporters

수집된 데이터를 백엔드 시스템으로 전송하는 컴포넌트입니다.

  • Jaeger Exporter
  • Prometheus Exporter
  • OTLP Exporter
  • 커스텀 Exporter
🏷️

Resources

텔레메트리를 생성하는 엔티티에 대한 메타데이터입니다.

  • 서비스 이름 및 버전
  • 호스트 정보
  • 클라우드 프로바이더 정보
  • 컨테이너/쿠버네티스 정보

실제 사용 예제

Node.js에서 OpenTelemetry 설정하기

// 필요한 패키지 설치
npm install @opentelemetry/api
npm install @opentelemetry/sdk-node
npm install @opentelemetry/auto-instrumentations-node
npm install @opentelemetry/exporter-trace-otlp-http
// tracing.js - OpenTelemetry 초기화
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');

// Jaeger로 내보내기 설정
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');

const jaegerExporter = new JaegerExporter({
  endpoint: 'http://localhost:14268/api/traces',
});

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'my-service',
    [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
  }),
  traceExporter: jaegerExporter,
  instrumentations: [getNodeAutoInstrumentations()]
});

// SDK 초기화
sdk.start()
  .then(() => console.log('Tracing initialized'))
  .catch((error) => console.log('Error initializing tracing', error));

// Graceful shutdown
process.on('SIGTERM', () => {
  sdk.shutdown()
    .then(() => console.log('Tracing terminated'))
    .catch((error) => console.log('Error terminating tracing', error))
    .finally(() => process.exit(0));
});
// app.js - Express 애플리케이션 예제
require('./tracing'); // OpenTelemetry 초기화

const express = require('express');
const { trace } = require('@opentelemetry/api');

const app = express();
const tracer = trace.getTracer('my-service-tracer');

app.get('/api/users', async (req, res) => {
  // 커스텀 span 생성
  const span = tracer.startSpan('get-users');
  
  try {
    // 속성 추가
    span.setAttributes({
      'http.method': req.method,
      'http.url': req.url,
      'user.id': req.query.userId
    });
    
    // 비즈니스 로직
    const users = await getUsersFromDatabase();
    
    span.setStatus({ code: SpanStatusCode.OK });
    res.json(users);
  } catch (error) {
    span.recordException(error);
    span.setStatus({ code: SpanStatusCode.ERROR });
    res.status(500).json({ error: error.message });
  } finally {
    span.end();
  }
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Python에서 OpenTelemetry 설정하기

# 필요한 패키지 설치
pip install opentelemetry-api
pip install opentelemetry-sdk
pip install opentelemetry-instrumentation
pip install opentelemetry-exporter-jaeger
# tracing.py - OpenTelemetry 초기화
from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# 리소스 설정
resource = Resource(attributes={
    SERVICE_NAME: "my-python-service"
})

# TracerProvider 설정
provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
    JaegerExporter(
        agent_host_name="localhost",
        agent_port=6831,
    )
)
provider.add_span_processor(processor)

# 전역 TracerProvider 설정
trace.set_tracer_provider(provider)

# Tracer 가져오기
tracer = trace.get_tracer(__name__)
# app.py - Flask 애플리케이션 예제
from flask import Flask, jsonify
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from tracing import tracer

app = Flask(__name__)

# Flask 자동 계측
FlaskInstrumentor().instrument_app(app)

@app.route('/api/users')
def get_users():
    with tracer.start_as_current_span("get-users") as span:
        # 속성 추가
        span.set_attribute("user.count", 100)
        
        try:
            # 데이터베이스 작업 시뮬레이션
            with tracer.start_as_current_span("database-query"):
                users = fetch_users_from_db()
            
            return jsonify(users)
        except Exception as e:
            span.record_exception(e)
            return jsonify({"error": str(e)}), 500

if __name__ == '__main__':
    app.run(debug=True)

Java에서 OpenTelemetry 설정하기

// build.gradle 의존성 추가
dependencies {
    implementation 'io.opentelemetry:opentelemetry-api:1.32.0'
    implementation 'io.opentelemetry:opentelemetry-sdk:1.32.0'
    implementation 'io.opentelemetry:opentelemetry-exporter-jaeger:1.32.0'
    implementation 'io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter:1.32.0-alpha'
}
// OpenTelemetryConfig.java
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenTelemetryConfig {
    
    @Bean
    public OpenTelemetry openTelemetry() {
        Resource resource = Resource.getDefault()
            .merge(Resource.create(Attributes.of(
                ResourceAttributes.SERVICE_NAME, "my-java-service",
                ResourceAttributes.SERVICE_VERSION, "1.0.0"
            )));
        
        JaegerGrpcSpanExporter jaegerExporter = JaegerGrpcSpanExporter.builder()
            .setEndpoint("http://localhost:14250")
            .build();
        
        SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
            .addSpanProcessor(BatchSpanProcessor.builder(jaegerExporter).build())
            .setResource(resource)
            .build();
        
        return OpenTelemetrySdk.builder()
            .setTracerProvider(sdkTracerProvider)
            .buildAndRegisterGlobal();
    }
    
    @Bean
    public Tracer tracer(OpenTelemetry openTelemetry) {
        return openTelemetry.getTracer("my-service-tracer");
    }
}

Go에서 OpenTelemetry 설정하기

// go.mod 의존성
require (
    go.opentelemetry.io/otel v1.19.0
    go.opentelemetry.io/otel/exporters/jaeger v1.19.0
    go.opentelemetry.io/otel/sdk v1.19.0
    go.opentelemetry.io/otel/trace v1.19.0
)
// tracer.go - OpenTelemetry 초기화
package main

import (
    "context"
    "log"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/jaeger"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)

func initTracer() (func(context.Context) error, error) {
    // Jaeger exporter 생성
    exp, err := jaeger.New(jaeger.WithCollectorEndpoint(
        jaeger.WithEndpoint("http://localhost:14268/api/traces"),
    ))
    if err != nil {
        return nil, err
    }
    
    // TracerProvider 생성
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exp),
        sdktrace.WithResource(resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceName("my-go-service"),
            semconv.ServiceVersion("1.0.0"),
            attribute.String("environment", "production"),
        )),
    )
    
    // 전역 TracerProvider 설정
    otel.SetTracerProvider(tp)
    
    return tp.Shutdown, nil
}

OpenTelemetry Collector 설정

Collector 데이터 플로우

Receivers
Processors
Exporters
# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
  
  # Prometheus 메트릭 수집
  prometheus:
    config:
      scrape_configs:
        - job_name: 'otel-collector'
          scrape_interval: 10s
          static_configs:
            - targets: ['0.0.0.0:8888']

processors:
  # 배치 처리로 성능 최적화
  batch:
    timeout: 1s
    send_batch_size: 1024
  
  # 리소스 속성 추가
  resource:
    attributes:
      - key: environment
        value: production
        action: insert
      - key: team
        value: backend
        action: insert
  
  # 메모리 제한
  memory_limiter:
    check_interval: 1s
    limit_mib: 512

exporters:
  # Jaeger로 추적 데이터 내보내기
  jaeger:
    endpoint: jaeger-collector:14250
    tls:
      insecure: true
  
  # Prometheus로 메트릭 내보내기
  prometheus:
    endpoint: "0.0.0.0:8889"
  
  # 로그를 Elasticsearch로 내보내기
  elasticsearch:
    endpoints: [http://elasticsearch:9200]
    logs_index: otel-logs

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

OpenTelemetry 에코시스템

🔍 추적(Tracing) 백엔드

분산 추적 데이터를 저장하고 분석하는 시스템

📊 메트릭(Metrics) 백엔드

시계열 메트릭 데이터를 저장하고 시각화하는 시스템

📝 로그(Logs) 백엔드

로그 데이터를 수집, 저장, 검색하는 시스템

🛠️ 계측 라이브러리

인기 프레임워크와 라이브러리를 위한 자동 계측

🌐 클라우드 서비스

관리형 OpenTelemetry 서비스

🔧 개발 도구

개발과 디버깅을 위한 도구

빠른 시작 가이드

환경 준비

Docker와 Docker Compose를 설치하고, OpenTelemetry 데모 환경을 구성합니다.

# OpenTelemetry 데모 클론
git clone https://github.com/open-telemetry/opentelemetry-demo.git
cd opentelemetry-demo

# Docker Compose로 실행
docker-compose up -d

SDK 설치

사용하는 프로그래밍 언어에 맞는 OpenTelemetry SDK를 설치합니다.

계측 추가

자동 계측을 활성화하거나 수동으로 스팬을 생성합니다.

Collector 구성

수집된 데이터를 처리하고 백엔드로 전송하도록 Collector를 설정합니다.

모니터링 시작

Jaeger, Prometheus, Grafana 등에서 수집된 데이터를 확인합니다.

인터랙티브 데모: OpenTelemetry 작동 방식

버튼을 클릭하여 OpenTelemetry가 어떻게 데이터를 수집하고 처리하는지 확인해보세요

데모를 시작하려면 버튼을 클릭하세요...