OpenTelemetry — distributed tracing dla aplikacji na VPS
Opublikowano: 10 kwietnia 2026 · Kategoria: VPS
Gdy aplikacja webowa jest wolna, monitoring CPU i RAM rzadko wyjaśni dlaczego konkretne żądanie trwało 3 sekundy zamiast 200ms. Distributed tracing rozwiązuje ten problem: śledzi żądanie przez wszystkie mikroserwisy, bazy danych i zewnętrzne API, pokazując dokładnie gdzie czas jest tracony. OpenTelemetry to otwarty standard który pozwala instrumentować aplikację raz i wysyłać dane do dowolnego backendu — bez vendor lock-in. Ten artykuł pokazuje setup OTel z Node.js, Python, OpenTelemetry Collector i Jaeger jako backend.
Koncepcja: traces, spans i context propagation
Distributed tracing opiera się na trzech podstawowych konceptach:
- Trace — pełna ścieżka jednego żądania przez system. Identyfikowany przez unikalny TraceID (128-bit hex). Może obejmować dziesiątki serwisów i setki operacji.
- Span — pojedyncza operacja w ramach trace. Ma: nazwę, SpanID, czas start i
end, atrybuty (np.
http.method=GET,db.statement=SELECT...), status (OK/ERROR) i eventy (timestamped log entries wewnątrz spana). - Context propagation — mechanizm przekazywania TraceID i SpanID między serwisami
przez nagłówki HTTP (
traceparentw standardzie W3C TraceContext,X-B3-TraceIdw Zipkin B3). Dzięki temu serwisy A, B, C piszą spany do tego samego trace.
Uruchomienie Jaeger (backend do przechowywania traces)
# Jaeger all-in-one (do testow i dev, in-memory storage) docker run -d \ --name jaeger \ -p 16686:16686 \ -p 4317:4317 \ -p 4318:4318 \ jaegertracing/all-in-one:latest # Porty: # 16686 - Jaeger UI (przegladarka) # 4317 - OTLP gRPC (odbiera traces z OTel Collector / SDK) # 4318 - OTLP HTTP # Sprawdz czy dziala curl http://localhost:16686/api/services
Instrumentacja Node.js — automatic i manual
OpenTelemetry dla Node.js oferuje automatic instrumentation — biblioteka automatycznie dodaje spany dla popularnych frameworków (Express, Fastify, HTTP, MySQL, PostgreSQL, Redis) bez zmian w kodzie aplikacji.
# Instalacja pakietow OTel dla Node.js
npm install @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-http
# tracing.js - plik inicjalizacji (ladowany przed aplikacja)
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const exporter = new OTLPTraceExporter({
url: 'http://localhost:4318/v1/traces',
});
const sdk = new NodeSDK({
traceExporter: exporter,
instrumentations: [getNodeAutoInstrumentations()],
serviceName: 'my-express-app',
});
sdk.start();
// Uruchomienie z tracing.js zaladowanym przed aplikacja:
// node --require ./tracing.js app.js
// Lub w package.json scripts:
// "start": "node --require ./tracing.js app.js" Instrumentacja Python — FastAPI i automatyczne spany
# Instalacja
pip install opentelemetry-sdk \
opentelemetry-exporter-otlp \
opentelemetry-instrumentation-fastapi \
opentelemetry-instrumentation-sqlalchemy \
opentelemetry-instrumentation-httpx
# main.py - inicjalizacja OTel
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.fastapi import FastAPIInstrumentor
# Konfiguracja provider i exporter
provider = TracerProvider()
exporter = OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
app = FastAPI()
FastAPIInstrumentor.instrument_app(app) # Auto-instrument FastAPI
# Reczne spany (manual instrumentation)
tracer = trace.get_tracer(__name__)
@app.get("/items/{item_id}")
async def get_item(item_id: int):
with tracer.start_as_current_span("get_item_from_db") as span:
span.set_attribute("item.id", item_id)
# ... kod
return {"id": item_id} OpenTelemetry Collector — centralne przetwarzanie danych
OTel Collector to middleware między aplikacjami a backendami. Aplikacje wysyłają traces do Collectora, który je przetwarza (filtruje, próbkuje, enriches) i przekazuje do jednego lub wielu backendów. Zmiana backendu wymaga tylko zmiany konfiguracji Collectora, nie kodu aplikacji.
# otel-collector-config.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
# Probkowanie: zachowaj 10% traces
probabilistic_sampler:
sampling_percentage: 10
exporters:
otlp:
endpoint: jaeger:4317
tls:
insecure: true
# Rownoczesny eksport do Grafana Tempo
otlp/tempo:
endpoint: tempo:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, probabilistic_sampler]
exporters: [otlp, otlp/tempo]
---
# docker-compose.yml fragment
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
volumes:
- ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
ports:
- "4317:4317"
- "4318:4318" Porównanie backendów dla traces
| Backend | Typ | Storage | Koszt | Integracja Grafana |
|---|---|---|---|---|
| Jaeger | Self-hosted OSS | In-memory / Elasticsearch / Cassandra | Darmowy (self-hosted) | Plugin Jaeger datasource |
| Grafana Tempo | Self-hosted OSS | Lokalne bloki / S3 / GCS | Darmowy (self-hosted) | Natywna integracja |
| Zipkin | Self-hosted OSS | In-memory / MySQL / Elasticsearch | Darmowy (self-hosted) | Plugin Zipkin datasource |
| Datadog APM | SaaS | Chmura Datadog | Od 31 USD/host/msc | Osobna platforma |
| Grafana Cloud | SaaS (managed Tempo) | Chmura Grafana | Darmowy (50 GB traces/msc) | Natywna (Grafana Cloud) |