분산 시스템의 관측 가능성을 위한 표준 프레임워크
계측(Instrumentation)을 위한 표준 인터페이스를 제공합니다.
API의 구체적인 구현체로 데이터 수집과 처리를 담당합니다.
애플리케이션과 라이브러리에 관측 가능성을 추가하는 코드입니다.
텔레메트리 데이터를 수신, 처리, 내보내는 중앙 서비스입니다.
수집된 데이터를 백엔드 시스템으로 전송하는 컴포넌트입니다.
텔레메트리를 생성하는 엔티티에 대한 메타데이터입니다.
// 필요한 패키지 설치 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'); });
# 필요한 패키지 설치
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)
// 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.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 }
# 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]
Docker와 Docker Compose를 설치하고, OpenTelemetry 데모 환경을 구성합니다.
# OpenTelemetry 데모 클론 git clone https://github.com/open-telemetry/opentelemetry-demo.git cd opentelemetry-demo # Docker Compose로 실행 docker-compose up -d
사용하는 프로그래밍 언어에 맞는 OpenTelemetry SDK를 설치합니다.
자동 계측을 활성화하거나 수동으로 스팬을 생성합니다.
수집된 데이터를 처리하고 백엔드로 전송하도록 Collector를 설정합니다.
Jaeger, Prometheus, Grafana 등에서 수집된 데이터를 확인합니다.
버튼을 클릭하여 OpenTelemetry가 어떻게 데이터를 수집하고 처리하는지 확인해보세요