# OpenTelemetry: signals, OTLP, Collector pipeline _Observability и мониторинг · LinuxLab Knowledge Base_ **TL;DR:** OpenTelemetry - CNCF-стандарт для metrics+traces+logs в одном SDK. OTLP протокол (gRPC или HTTP). Collector принимает, фильтрует, роутит в Prom/Tempo/Loki/Jaeger. Auto-instrumentation без code change. ## Зачем OpenTelemetry До OTel у каждого vendor свой SDK: - **Prometheus client** для metrics - **Jaeger / Zipkin client** для traces - **Datadog APM agent** для всего, но vendor lock - **structured logger** для logs 4 SDK, 4 пайплайна, 4 формата. Поменять backend = переписать всю instrumentation в коде. **OpenTelemetry (CNCF, 2019, объединение OpenCensus + OpenTracing)** даёт: - Один SDK на все три signals (metrics+traces+logs) - Один протокол **OTLP** (OpenTelemetry Line Protocol) - Один **Collector** для трансформации/роутинга - **Vendor-neutral**: код не знает куда летит, в Prom, Datadog или cloud monitoring. Меняй backend через config. В 2025 OTel, де-факто стандарт для новых проектов. Old Prometheus client всё ещё работает (OTel Collector умеет принимать), но новый код пишут на OTel. ## Три signal'а **Traces** ([tracing-basics](/kb/tracing-basics.md)), request flow через сервисы. Span'ы с parent-child связями, context propagation через [[http2-internals|`traceparent` header]]. **Metrics**, counter, gauge, histogram. Семантика та же что в [[metric-types|Prometheus]], но через OTel SDK. **Logs**, structured events с автоматической correlation: trace_id и span_id вшиваются в log record. Все три едут одним SDK через один OTLP-канал. Это уменьшает coupling и даёт coherence. ## Архитектура ``` ┌──────────────┐ OTLP gRPC :4317 ┌─────────────┐ │ App │ ──────────────────────► │ Collector │ │ ┌──────────┐ │ OTLP HTTP :4318 │ │ │ │ OTel SDK │ │ │ ┌─────────┐ │ │ │ ┌──────┐ │ │ │ │receivers│ │ │ │ │tracer│ │ │ │ ├─────────┤ │ │ │ │meter │ │ │ │ │processor│ │ │ │ │logger│ │ │ │ ├─────────┤ │ │ │ └──────┘ │ │ │ │exporters│ │ │ └──────────┘ │ │ └────┬────┘ │ └──────────────┘ └──────┼──────┘ │ ┌─────────┬───────┼────────┬────────┐ ▼ ▼ ▼ ▼ ▼ Prometheus Tempo Loki Jaeger Datadog ``` ## OTLP, протокол **OTLP**, единый wire-format. Два транспорта: | Транспорт | Порт (default) | Когда | |-----------|---------------|-------| | **gRPC** | 4317 | server-to-server, internal, low-latency | | **HTTP/protobuf** | 4318 | через прокси, browser, restrictive net | Payload, Protocol Buffers. Структура: ``` ResourceSpans ├── Resource (service.name, host.name, k8s.pod.name) └── ScopeSpans ├── InstrumentationScope (library name + version) └── Span[] ├── trace_id, span_id, parent_span_id ├── name, start_time_nano, end_time_nano ├── attributes (key-value) ├── events[] ├── links[] └── status (OK / ERROR) ``` Аналогично для metrics (`ResourceMetrics → ScopeMetrics → Metric`) и logs (`ResourceLogs → ScopeLogs → LogRecord`). Преимущества над Prom format: - Binary, компактнее в 3-5× - Streaming через [[grpc-basics|gRPC]] (без http poll) - Один формат для трёх signal'ов ## SDK: auto vs manual instrumentation **Auto-instrumentation**, agent патчит библиотеки runtime, без изменений кода: - **Java**: `-javaagent:opentelemetry-javaagent.jar`, патчит JDBC, Servlet, Kafka client, gRPC, ~120 библиотек - **Python**: `opentelemetry-instrument python app.py`, патчит requests, Flask, Django, psycopg2, redis-py - **Node.js**: `--require @opentelemetry/auto-instrumentations-node` - **Go**: нет рефлексии → нужно добавить вручную (eBPF-based agent в работе) - **.NET**: `OTEL_DOTNET_AUTO_HOME` env Получаешь traces для HTTP/DB/Kafka **без единой строки кода**. Дальше можно дописать manual span'ы для бизнес-логики. **Manual instrumentation**, explicit API: ```python from opentelemetry import trace, metrics tracer = trace.get_tracer(__name__) meter = metrics.get_meter(__name__) request_counter = meter.create_counter("requests") duration_histogram = meter.create_histogram("request_duration_ms") @app.get("/checkout") def checkout(): with tracer.start_as_current_span("checkout") as span: span.set_attribute("user.id", user_id) request_counter.add(1, {"endpoint": "/checkout"}) # ... business logic ``` ## OTel Collector Standalone сервис, **deploy в каждом узле** (DaemonSet) или **gateway per-cluster**. Конфиг, три секции: ```yaml receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 prometheus: config: scrape_configs: - job_name: app static_configs: - targets: [app:8080] processors: batch: # batches before exporting send_batch_size: 8192 timeout: 200ms memory_limiter: # backpressure limit_mib: 512 tail_sampling: # sample by trace condition policies: - name: errors type: status_code status_code: {status_codes: [ERROR]} - name: slow type: latency latency: {threshold_ms: 1000} - name: probabilistic-1pct type: probabilistic probabilistic: {sampling_percentage: 1} exporters: prometheusremotewrite: endpoint: http://victoriametrics:8480/api/v1/write otlp/tempo: endpoint: tempo:4317 tls: insecure: true loki: endpoint: http://loki:3100/loki/api/v1/push service: pipelines: traces: receivers: [otlp] processors: [memory_limiter, tail_sampling, batch] exporters: [otlp/tempo] metrics: receivers: [otlp, prometheus] processors: [memory_limiter, batch] exporters: [prometheusremotewrite] logs: receivers: [otlp] processors: [memory_limiter, batch] exporters: [loki] ``` Pipeline, ациклический граф. Один collector может обслуживать все три signal'а независимо. ## Resource, что описывает источник `Resource`, атрибуты процесса/хоста, общие для всех signal'ов: ``` service.name=checkout service.version=1.4.2 service.instance.id=checkout-7f8b9c k8s.namespace.name=prod k8s.pod.name=checkout-7f8b9c-q2lx9 host.name=node-12.us-east cloud.provider=aws cloud.region=us-east-1 ``` Установить через env: ``` OTEL_SERVICE_NAME=checkout OTEL_RESOURCE_ATTRIBUTES=service.version=1.4.2,deployment.environment=prod ``` Auto-injected в k8s через [[opentelemetry-operator-k8s|OTel Operator]] (sidecar/auto-instrumentation CRD). ## OTel vs Prometheus client | Аспект | Prom client | OTel SDK | |--------|-------------|----------| | Signals | metrics only | metrics+traces+logs | | Transport | HTTP pull (`/metrics`) | OTLP push | | Vendor neutrality | Prom-only | любой backend | | Auto-instrumentation | minimal | полный | | Adoption | широчайшая | растёт быстро | | Wire format | text/OpenMetrics | protobuf | Можно совместить: OTel SDK для traces+logs + Prom client для metrics. Или OTel SDK для всего, а Collector экспортит metrics в Prom format. ## Sampling, head vs tail 100% traces неподъёмны: 10K req/s × 5 spans × 5KB = ~250 MB/s. Sampling нужен. - **Head-based**: решение sample-or-drop **в начале** трассы (на edge), все downstream спаны соблюдают. Просто и предсказуемо. Минус рандомно роняет error traces. - **Tail-based**: собрать **всю** трассу в Collector, потом решить keep/drop по trace properties (status, latency, attributes). Видим все ошибки, но Collector держит все spans в памяти на 5-30s. Tail-based предпочтительно. Tail Sampling Processor в Collector. ## Когда что-то пошло не так - **`OTLP/gRPC connection refused`**, Collector не запущен или другой port. Default 4317. Проверь firewall. - **Trace отсутствует, хотя ошибка**, head sampling 1% уронил. Используй tail sampling с `status_code: ERROR` policy. - **Collector OOM**, `memory_limiter` processor отсутствует или лимит выше RAM. Добавь limit < 80% от container memory. - **Cardinality explosion**, `attributes` в metrics с user-id или request-id. См. [cardinality-explosion](/kb/cardinality-explosion.md). - **Auto-instrumentation сломала app**, обычно Java agent + конфликт байткод-патча. Поднимай OTel agent версию или disable конкретный instrumentation: `OTEL_INSTRUMENTATION__ENABLED=false`. - **Span.attributes теряются**, забыт `batch` processor, или attributes добавлены **после** `end()`. Set до .end(). - **Service.name = "unknown_service"** в Tempo, забыт env `OTEL_SERVICE_NAME`. Resource не сконфигурен. ## OTel vs Datadog/New Relic Vendor APMs (Datadog, New Relic, Splunk) дают всё-в-одном с UI и ML-features. Но vendor lock, заменить = переписать. С OTel пишешь instrumentation один раз, отправляешь в Datadog через native receiver. Через год, переключаешь exporter на Tempo/Loki/Mimir, код не трогаешь. Cost-aware: OTel + self-hosted (Tempo+Loki+VictoriaMetrics) дешевле Datadog в 5-10× при ≥100 GB/day, но требует ops-инвестиций. ## Команды ```bash opentelemetry-instrument --service_name=myapp python app.py ``` Auto-instrumentation для Python - HTTP, DB, Kafka traces без кода ```bash java -javaagent:opentelemetry-javaagent.jar -Dotel.service.name=myapp -jar app.jar ``` Java auto-agent - патчит JDBC, Servlet, gRPC и ~120 библиотек на старте ```bash otelcol --config=/etc/otelcol/config.yaml ``` Запуск Collector. Логи покажут ошибки конфига и pipeline-проблемы ```bash curl -X POST -H 'Content-Type: application/x-protobuf' --data-binary @trace.pb http://collector:4318/v1/traces ``` Manual OTLP HTTP push - для отладки или CI-тестов ```bash OTEL_TRACES_EXPORTER=console python -c 'import myapp' ``` Debug-режим: traces в stdout вместо отправки. Видишь span structure ```bash otelcol validate --config=/etc/otelcol/config.yaml ``` Проверка конфига Collector без запуска - в CI ```bash curl -s localhost:13133/ # health check ``` OTel Collector health endpoint - 200 если pipeline ОК, 503 если backpressure ## См. также - [Distributed tracing: span, context propagation, sampling](/kb/tracing-basics.md) - [Prometheus: scrape, TSDB, PromQL и production-pitfalls](/kb/prometheus-basics.md) - [Типы метрик: counter, gauge, histogram, summary](/kb/metric-types.md) - [gRPC - HTTP/2 + Protobuf RPC framework](/kb/grpc-basics.md) - [HTTP/2 internals - binary framing, HPACK, stream multiplexing](/kb/http2-internals.md)