콘텐츠로 이동

OpenTelemetry

OpenTelemetry는 애플리케이션과 인프라에서 원격 측정 데이터(trace, metrics, logs)를 수집하기 위한 CNCF 표준입니다.

설치

Node.js/JavaScript

# Core packages
npm install @opentelemetry/api @opentelemetry/sdk-node

# Auto-instrumentation
npm install @opentelemetry/auto

# Exporters
npm install @opentelemetry/exporter-trace-otlp-http
npm install @opentelemetry/exporter-metrics-otlp-http

# Instrumentation for libraries
npm install @opentelemetry/instrumentation-express
npm install @opentelemetry/instrumentation-http
npm install @opentelemetry/instrumentation-pg

Python

# Core packages
pip install opentelemetry-api
pip install opentelemetry-sdk

# Exporters
pip install opentelemetry-exporter-otlp
pip install opentelemetry-exporter-jaeger

# Instrumentation
pip install opentelemetry-instrumentation-flask
pip install opentelemetry-instrumentation-requests
pip install opentelemetry-instrumentation-sqlalchemy

Java

# Maven dependencies
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
    <version>1.28.0</version>
</dependency>

<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-sdk</artifactId>
    <version>1.28.0</version>
</dependency>

# Java agent auto-instrumentation
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.28.0/opentelemetry-javaagent.jar

기본 설정

Node.js 추적

// tracing.js
const { NodeSDK } = require("@opentelemetry/sdk-node");
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node");
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-http");

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: "http://localhost:4318/v1/traces"
  }),
  instrumentations: [getNodeAutoInstrumentations()]
});

sdk.start();

console.log("Tracing initialized");

Python 추적

# tracing.py
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor

# Setup OTLP exporter
otlp_exporter = OTLPSpanExporter(
    endpoint="http://localhost:4318/v1/traces"
)

# Setup tracer provider
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(otlp_exporter)
)

# Auto-instrument libraries
FlaskInstrumentor().instrument()
RequestsInstrumentor().instrument()

tracer = trace.get_tracer(__name__)

Java 추적

# Run with Java agent
java -javaagent:opentelemetry-javaagent.jar \
  -Dotel.exporter.otlp.endpoint=http://localhost:4318 \
  -Dotel.service.name=my-app \
  -jar app.jar

수동 계측

Span 생성

// Node.js
const { trace } = require("@opentelemetry/api");
const tracer = trace.getTracer("my-app");

const span = tracer.startSpan("process-request", {
  attributes: {
    "http.method": "GET",
    "http.url": "/api/users"
  }
});

try {
  // Do work
  span.addEvent("Processing user data");
  span.setStatus({ code: 0 });
} catch (error) {
  span.recordException(error);
  span.setStatus({ code: 2, message: error.message });
} finally {
  span.end();
}
# Python
from opentelemetry import trace

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("process_request") as span:
    span.set_attribute("http.method", "GET")
    span.set_attribute("http.url", "/api/users")

    try:
        # Do work
        span.add_event("Processing user data")
    except Exception as e:
        span.record_exception(e)
        raise

메트릭 수집

JavaScript 메트릭

// metrics.js
const { MeterProvider, PeriodicExportingMetricReader } = require("@opentelemetry/sdk-metrics");
const { OTLPMetricExporter } = require("@opentelemetry/exporter-metrics-otlp-http");

const reader = new PeriodicExportingMetricReader({
  exporter: new OTLPMetricExporter({
    url: "http://localhost:4318/v1/metrics"
  })
});

const meterProvider = new MeterProvider({ reader });
const meter = meterProvider.getMeter("my-app");

// Create counter
const requestCounter = meter.createCounter("http.requests", {
  description: "Total HTTP requests"
});

requestCounter.add(1, {
  "http.method": "GET",
  "http.status_code": 200
});

// Create histogram
const latencyHistogram = meter.createHistogram("http.duration", {
  unit: "ms",
  description: "HTTP request duration"
});

latencyHistogram.record(123, {
  "http.method": "GET"
});

Python 메트릭

# metrics.py
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter

reader = PeriodicExportingMetricReader(
    OTLPMetricExporter(endpoint="http://localhost:4318/v1/metrics")
)
meter_provider = MeterProvider(metric_readers=[reader])
meter = meter_provider.get_meter("my-app")

# Counter
request_counter = meter.create_counter(
    "http.requests",
    description="Total HTTP requests"
)
request_counter.add(1, {"http.method": "GET", "http.status_code": 200})

# Histogram
latency_histogram = meter.create_histogram(
    "http.duration",
    unit="ms",
    description="HTTP request duration"
)
latency_histogram.record(123, {"http.method": "GET"})

내보내기 구성

OTLP 내보내기

// Over HTTP
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-http");

const exporter = new OTLPTraceExporter({
  url: "http://localhost:4318/v1/traces",
  headers: {
    "api-key": "your-api-key"
  },
  concurrencyLimit: 10,
  timeoutMillis: 30000
});

// Over gRPC
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-grpc");

const exporter = new OTLPTraceExporter({
  url: "grpc://localhost:4317"
});

Jaeger 내보내기

// Node.js
const { JaegerExporter } = require("@opentelemetry/exporter-jaeger");

const exporter = new JaegerExporter({
  host: "localhost",
  port: 6831,
  serviceName: "my-app"
});
# Python
from opentelemetry.exporter.jaeger.thrift import JaegerExporter

jaeger_exporter = JaegerExporter(
    agent_host_name="localhost",
    agent_port=6831,
)

trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(jaeger_exporter)
)

Zipkin 내보내기

// Node.js
const { ZipkinExporter } = require("@opentelemetry/exporter-zipkin");

const exporter = new ZipkinExporter({
  url: "http://localhost:9411/api/v2/spans",
  serviceName: "my-app"
});

컨텍스트 전파

W3C Trace Context

// Propagator setup
const { W3CTraceContextPropagator } = require("@opentelemetry/core");
const { CompositePropagator } = require("@opentelemetry/core");

const propagator = new CompositePropagator({
  propagators: [new W3CTraceContextPropagator()]
});

// Extract context from request
const ctx = propagator.extract(context.active(), {
  get: (key) => req.headers[key.toLowerCase()]
});

// Inject context into outgoing request
propagator.inject(ctx, carrier, {
  set: (key, value) => outgoingRequest.headers[key] = value
});

배치 처리

JavaScript

const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-node");

const batchProcessor = new BatchSpanProcessor(otlpExporter, {
  maxQueueSize: 2048,
  maxExportBatchSize: 512,
  scheduledDelayMillis: 5000,
  exportTimeoutMillis: 30000
});

tracerProvider.addSpanProcessor(batchProcessor);

Docker Compose 설정

# docker-compose.yml
version: '3'
services:
  otel-collector:
    image: otel/opentelemetry-collector:latest
    ports:
      - "4317:4317"  # OTLP gRPC
      - "4318:4318"  # OTLP HTTP
      - "9411:9411"  # Zipkin
    volumes:
      - ./otel-config.yaml:/etc/otel-collector-config.yaml
    command: ["--config=/etc/otel-collector-config.yaml"]

  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "6831:6831/udp"
      - "16686:16686"  # Jaeger UI

  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

모범 사례

  • 애플리케이션 시작 초기에 추적 초기화
  • 분산 추적을 위해 서비스 전체에서 컨텍스트 전파 사용
  • 디버깅 및 필터링을 위해 의미있는 span 속성 설정
  • 높은 트래픽 시스템에서 오버헤드를 줄이기 위해 샘플링 구현
  • 더 나은 처리량을 위해 배치 프로세서 구성
  • Span과 함께 구조화된 로깅 사용
  • 내보내기 상태 모니터링 및 실패한 내보내기 확인
  • 추적 지연 시간에 대한 알림 임계값 설정
  • 비즈니스 메트릭을 위해 커스텀 계측 사용
  • 커스텀 span 속성과 그 의미 문서화
  • 계측 패키지를 정기적으로 검토 및 업데이트
  • 프로덕션과 유사한 환경에서 추적 샘플링 테스트

리소스


최종 업데이트: 2025-03-30