Перейти к содержимому

OpenTelemetry и Grafana stack: production observability

Зачем знать: Observability — это не “просто логи и метрики”. Это способность отвечать на произвольные вопросы о работе системы в production. Middle 2 должен уметь развернуть полноценный observability-стек: traces + metrics + logs + profiling, объединить их через trace_id, настроить sampling, контролировать стоимость и overhead. OpenTelemetry стал de-facto стандартом в 2023-2026 — заменил OpenTracing, OpenCensus, Jaeger Client. Без знания OTel и Grafana stack ты не пройдёшь собеседование на middle 2 в любую современную инженерную команду.

  1. Концепция: три столпа observability и модель OpenTelemetry
  2. Production-практики: SDK, Collector, Grafana stack
  3. Gotchas: cardinality, sampling, overhead
  4. Real cases: Datadog migration, Cloudflare, Uber
  5. Вопросы для собеседования
  6. Practice
  7. Источники

1. Концепция: три столпа observability и модель OpenTelemetry

Заголовок раздела «1. Концепция: три столпа observability и модель OpenTelemetry»

Observability традиционно строится на трёх типах сигналов:

Metrics — числовые ряды, агрегированные за временные окна.

  • Низкая стоимость (сжимаются хорошо).
  • Высокая cardinality убивает (про это ниже).
  • Используются для: алертов, дашбордов, capacity planning, SLO.
  • Примеры: http_requests_total, request_duration_seconds, db_pool_active.

Logs — структурированные события с timestamp.

  • Высокая детализация, дорогие в хранении.
  • Идеальны для debugging конкретного инцидента.
  • Структурированный (JSON) намного лучше plain text.

Traces — распределённые цепочки вызовов через сервисы.

  • Показывают, где время теряется в request lifecycle.
  • Дорогие в полном объёме → нужен sampling.
  • Помогают понять межсервисные dependencies.

Дополнительные сигналы (2024-2026):

  • Continuous profiling (Pyroscope, Parca) — непрерывные CPU/memory профили.
  • eBPF events — низкоуровневые сигналы из ядра.

OpenTelemetry (OTel) — это open-source observability framework от CNCF. Включает:

  • Specification — единая модель данных, семантические конвенции.
  • SDK — реализации для языков (Go, Java, Python, JS и др.).
  • Collector — универсальный агент для приёма/обработки/отправки.
  • Protocol (OTLP) — gRPC/HTTP формат для передачи телеметрии.

Цель OTel: vendor-neutral сбор телеметрии. Меняешь backend (Jaeger → Tempo → Datadog) без переписывания кода приложения.

Trace — корневая сущность, представляющая запрос через систему.

  • trace_id: 128-bit уникальный идентификатор.
  • Состоит из spans.

Span — единица работы (HTTP вызов, DB запрос, обработка сообщения).

  • span_id: 64-bit.
  • parent_span_id: ссылка на родителя.
  • start_time, end_time.
  • attributes: key-value (метаданные).
  • events: timestamped logs внутри span.
  • links: ссылки на другие spans (для async/fan-in).
  • status: OK / ERROR / UNSET.
trace_id: abc123...
└─ span: HTTP GET /api/order (root, span_id=001)
├─ span: SELECT FROM orders (span_id=002, parent=001)
├─ span: HTTP GET /api/user (span_id=003, parent=001)
│ └─ span: SELECT FROM users (span_id=004, parent=003)
└─ span: publish to Kafka (span_id=005, parent=001)

OTel определяет несколько типов instruments:

Synchronous (вызываются в hot path):

  • Counter — монотонно растущее значение (requests, errors).
  • UpDownCounter — может расти/уменьшаться (active_connections).
  • Histogram — распределение значений (latency).

Asynchronous (callback’и, читаются периодически):

  • ObservableCounter — снимок counter’а (CPU time used).
  • ObservableUpDownCounter — снимок (memory used).
  • ObservableGauge — мгновенное значение (current temperature).

Histogram заслуживает отдельного внимания:

  • Конфигурируемые buckets (default OTel: 0, 5ms, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000).
  • Exponential histograms (новый стандарт) — auto-scaling, точные перцентили.

В Go 1.21+ появился log/slog — official structured logger.

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
logger.Info("order created",
"order_id", orderID,
"user_id", userID,
"amount", amount,
)

OTel предоставляет logs bridgeslog handler, который превращает каждый лог в OTel LogRecord и отправляет в Collector. Logs автоматически коррелируются с активным span через trace_id.

OTel определяет semantic conventions — стандартные имена атрибутов:

  • service.name, service.version, service.namespace — обязательные resource attributes.
  • http.request.method, http.response.status_code, url.path, url.full.
  • db.system, db.statement, db.name.
  • messaging.system, messaging.destination.name, messaging.operation.
  • rpc.system, rpc.service, rpc.method.

Использование semantic conventions критично — иначе дашборды и алерты не работают одинаково между сервисами.

Между сервисами нужно пробросить trace_id и parent_span_id. Это делается через HTTP headers по W3C TraceContext стандарту:

traceparent: 00-<trace_id>-<span_id>-<flags>
tracestate: vendor1=value1,vendor2=value2
baggage: user_id=42,tenant=acme

Baggage — это произвольный key-value контекст, который пробрасывается через все hop’ы. Используется для:

  • Tenant ID, feature flags, A/B test variant.
  • НЕ для секретов — baggage видим в трафике.
ctx = baggage.ContextWithValues(ctx, baggage.String("tenant", "acme"))
// В downstream сервисе:
tenant := baggage.FromContext(ctx).Member("tenant").Value()

Head-based sampling — решение в начале трейса (на корневом сервисе).

  • TraceIDRatioBased(0.1) — 10% всех трейсов.
  • ParentBased(...) — следовать решению родителя (если родитель сэмплирован — мы тоже).
  • Плюсы: дешёво, простой алгоритм.
  • Минусы: можно пропустить редкие важные трейсы (ошибки, slow).

Tail-based sampling — решение в конце трейса, в Collector’е.

  • Видит все spans трейса перед решением.
  • Policies: keep error traces, keep slow traces (latency > 1s), keep N% normal.
  • Плюсы: гарантированно ловит проблемы.
  • Минусы: Collector должен буферизовать ВСЕ spans до конца трейса → память, задержка.

Production стратегия (2026):

  • App: head sampling 100% (всё в Collector).
  • Collector: tail sampling — keep 100% errors/slow, 1-5% normal.

Exemplar — это запись внутри метрики, содержащая trace_id примера. Когда видишь spike в histogram’е latency, можешь кликнуть на exemplar и перейти в trace, который вызвал этот spike.

В Prometheus exemplars поддерживаются с 2.26+ через OpenMetrics format. В Go SDK включаются автоматически, если есть активный span при записи метрики.


package observability
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/propagation"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
func Setup(ctx context.Context, serviceName, version string) (func(context.Context) error, error) {
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName(serviceName),
semconv.ServiceVersion(version),
semconv.DeploymentEnvironment("production"),
),
resource.WithFromEnv(),
resource.WithHost(),
resource.WithProcess(),
resource.WithContainer(),
)
if err != nil {
return nil, err
}
// Traces
traceExporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("otel-collector:4317"),
otlptracegrpc.WithInsecure(),
)
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(traceExporter,
sdktrace.WithMaxQueueSize(2048),
sdktrace.WithMaxExportBatchSize(512),
),
sdktrace.WithResource(res),
sdktrace.WithSampler(sdktrace.ParentBased(
sdktrace.TraceIDRatioBased(1.0), // 100% — Collector сделает tail sampling
)),
)
otel.SetTracerProvider(tp)
// Metrics
metricExporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithEndpoint("otel-collector:4317"),
otlpmetricgrpc.WithInsecure(),
)
if err != nil {
return nil, err
}
mp := sdkmetric.NewMeterProvider(
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExporter,
sdkmetric.WithInterval(15*time.Second),
)),
sdkmetric.WithResource(res),
)
otel.SetMeterProvider(mp)
// Propagators
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))
return func(ctx context.Context) error {
if err := tp.Shutdown(ctx); err != nil {
return err
}
return mp.Shutdown(ctx)
}, nil
}
var tracer = otel.Tracer("order-service")
func (s *Service) CreateOrder(ctx context.Context, req CreateOrderReq) (*Order, error) {
ctx, span := tracer.Start(ctx, "Service.CreateOrder",
trace.WithAttributes(
attribute.String("order.user_id", req.UserID),
attribute.Int("order.items_count", len(req.Items)),
),
)
defer span.End()
order, err := s.repo.Create(ctx, req)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "create failed")
return nil, err
}
span.SetAttributes(attribute.String("order.id", order.ID))
return order, nil
}

Готовые библиотеки покрывают 80% инструментации:

  • otelhttp — net/http (server + client).
  • otelgrpc — gRPC interceptors.
  • otelsql — database/sql wrapper.
  • otelpgx — pgx native instrumentation.
  • otelmongo — MongoDB.
  • otelredis — go-redis hook.
  • otelkafka — Kafka producer/consumer.
// HTTP server
handler := otelhttp.NewHandler(myHandler, "api")
// HTTP client
client := http.Client{
Transport: otelhttp.NewTransport(http.DefaultTransport),
}
// gRPC server
grpc.NewServer(grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()))
// SQL
db, _ := otelsql.Open("postgres", dsn, otelsql.WithAttributes(semconv.DBSystemPostgreSQL))
import (
"log/slog"
"go.opentelemetry.io/contrib/bridges/otelslog"
)
logger := otelslog.NewLogger("order-service")
slog.SetDefault(logger)
// Теперь все slog.Info/Error попадают в OTel
slog.InfoContext(ctx, "order created", "id", order.ID)
// trace_id и span_id автоматически прикрепляются

Collector — это сердце production setup’а. Архитектура:

[App SDK] --OTLP--> [Collector Agent (daemonset)] --OTLP--> [Collector Gateway (deployment)] --> [Backends]

Pipeline: receivers → processors → exporters.

receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
prometheus:
config:
scrape_configs:
- job_name: 'apps'
kubernetes_sd_configs:
- role: pod
processors:
memory_limiter:
check_interval: 1s
limit_mib: 1500
spike_limit_mib: 300
batch:
timeout: 5s
send_batch_size: 1024
resource:
attributes:
- key: cluster
value: prod-eu-1
action: insert
tail_sampling:
decision_wait: 30s
num_traces: 100000
policies:
- name: errors
type: status_code
status_code: { status_codes: [ERROR] }
- name: slow
type: latency
latency: { threshold_ms: 1000 }
- name: random
type: probabilistic
probabilistic: { sampling_percentage: 5 }
exporters:
otlp/tempo:
endpoint: tempo:4317
tls: { insecure: true }
prometheusremotewrite:
endpoint: http://mimir:9009/api/v1/push
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]

Ключевые processors:

  • memory_limiter — должен быть ПЕРВЫМ, чтобы не уронить Collector.
  • batch — последний или предпоследний, объединяет данные перед отправкой.
  • tail_sampling — должен быть до batch для traces.
  • attributes/resource — нормализация атрибутов.
  • filter — отбрасывание ненужных метрик/spans (cardinality control).
  • transform — мощный язык OTTL для трансформаций.

Tempo — backend для traces.

  • Object storage (S3, GCS, Azure Blob) — дешёвое хранение.
  • Не индексирует по атрибутам (только по trace_id, time range).
  • TraceQL — query language для поиска по trace атрибутам (с 2023).
  • Hot/warm tiers.

Loki — backend для logs.

  • Индексирует только метки (labels), не текст.
  • Логи хранятся в object storage.
  • LogQL — язык запросов (похож на PromQL).
  • Дешевле Elasticsearch в 5-10 раз.

Mimir — distributed Prometheus (форк Cortex).

  • Горизонтально масштабируемый long-term storage.
  • Tenant isolation.
  • Совместимый с Prometheus API.

Pyroscope — continuous profiling.

  • pprof formats (CPU, heap, goroutine, mutex).
  • В Go SDK: pyroscope-go.
  • Flame graphs с поиском по времени и labels.

Grafana — UI dashboards.

  • Data sources: Prometheus, Tempo, Loki, Pyroscope, ClickHouse, MySQL, etc.
  • Correlations: jump from metric exemplar → trace → log.

Alloy — universal telemetry collector от Grafana Labs (2024+). Конкурирует с OTel Collector.

  • Поддерживает OTel pipeline.
  • Lightweight (Go).
  • Декларативный config (HCL-подобный).
  • Используется на edge / k8s nodes.

VictoriaMetrics — альтернатива Prometheus, написана в Москве (изначально):

  • В 5-10 раз меньше disk usage.
  • Быстрее в чтении (PromQL совместимый).
  • Cluster version для HA.
  • MetricsQL — расширения PromQL.
  • VictoriaLogs (2023+) — конкурент Loki.

В компаниях типа Авито, Тинькофф, ВК — VictoriaMetrics стандарт.

Uptrace, SigNoz — open-source observability платформы на ClickHouse.

  • Один backend для traces + logs + metrics.
  • Дешевле фрагментированного стека.
  • ClickHouse отлично сжимает observability data.
  • SQL-based queries.

В России ClickHouse-based observability особенно популярен — снимает зависимость от GitHub-disabled tooling.

СценарийStrategyRationale
Dev / staging100% head-basedВидеть всё
Низкий трафик (< 100 RPS)100% head-basedCost тривиален
Средний (100-10k RPS)Head 100% + tail в CollectorГарантия ошибок
Высокий (> 10k RPS)Head 10-50%, tail приоритезируетCost optimization
Очень высокий (> 100k RPS)Adaptive sampling (rate-limiting)Hard cost ceiling

Стоимость observability часто превышает стоимость самой инфраструктуры:

  • Datadog: $15-23/host + $1.27/M spans (быстро растёт).
  • New Relic: похоже.
  • Self-hosted Grafana stack: дешевле в 5-10 раз, но нужны люди.

Стратегии экономии:

  1. Aggressive tail sampling.
  2. Drop low-value metrics (тегированные по user_id — катастрофа).
  3. Short retention для debug data (logs — 7 дней, traces — 3 дня).
  4. Long retention только для metrics (1 год для capacity planning).
  5. Cold storage для archive.

OTel overhead обычно < 5% CPU при нормальной инструментации:

  • BatchSpanProcessor (не SimpleSpanProcessor!).
  • Async export.
  • Reasonable batch size (512-1024).

Что НЕ делать:

  • SimpleSpanProcessor в production (blocking export).
  • Слишком много attributes (cardinality + сериализация).
  • Trace на каждый log call (раздувает).

Проблема: добавил user_id как label → 1M активных users × 100 метрик = 100M time series → Prometheus OOM.

Правило: cardinality для одной метрики не должна превышать 10k-100k.

Что НЕ должно быть в labels:

  • user_id, request_id, trace_id, session_id.
  • URL paths с динамическими ID (/users/123/orders/456).

Что МОЖНО:

  • HTTP method, endpoint pattern (/users/:id/orders/:id), status code class (2xx/4xx/5xx).
  • Service version, environment, region.

SimpleSpanProcessor экспортирует span синхронно при End() → блокирует request thread.

Всегда используй BatchSpanProcessor в production.

// ПЛОХО: контекст пропадает в горутине
go func() {
process(item) // нет ctx
}()
// ХОРОШО: пробрасываем ctx
go func(ctx context.Context) {
process(ctx, item)
}(ctx)

Для async задач из Kafka/queue: восстанавливай trace context из message headers через propagation.Extract.

Если используешь http.Client{} без otelhttp.NewTransport, trace_id не пропагируется → traces “ломаются” между сервисами.

Если у тебя несколько Collector replicas, все spans одного trace должны попадать в одну реплику для tail sampling. Решение: loadbalancing exporter перед tail_sampling Collector’ом.

exporters:
loadbalancing:
routing_key: traceID
resolver:
dns:
hostname: tail-sampling-collector
protocol:
otlp: { tls: { insecure: true } }

OTel SDK имеет лимиты:

  • 128 attributes per span.
  • 128 events per span.
  • 128 links per span.

При превышении — отбрасывается. Если bulk-обработка → не пиши event на каждую запись.

Если в коде есть legacy log.Printf(...), OTel slog handler их не поймает. Нужно глобально переопределить:

slog.SetDefault(logger)
log.SetOutput(slog.NewLogLogger(logger.Handler(), slog.LevelInfo).Writer())

Exemplars увеличивают размер метрик ~20%. На очень высоких QPS (>50k) это заметно. Альтернатива — sample exemplars (записывать только 1 из N).

Tail sampling буферизует все spans трейса (default 30 сек). Если трафик 50k spans/sec и avg trace 50 spans, нужно ~75M spans в памяти. Конфигурируй num_traces и decision_wait аккуратно.

service.name — это логическое имя приложения (order-service), а не hostname. Hostname в host.name. Иначе дашборды разваливаются при autoscaling.

PII (email, phone, password) НЕ должны попадать в traces/logs. OTel Collector умеет redact’ить через transform processor:

processors:
transform:
trace_statements:
- context: span
statements:
- delete_key(attributes, "user.email")
- replace_pattern(attributes["http.url"], "token=[^&]+", "token=REDACTED")

Если sample 1% — то и метрики error_rate из traces неточные. Используй raw metrics (Counter в SDK) для SLO, traces только для root cause.


Компания X росла, Datadog bill достиг $500k/month. Миграция:

  1. Сначала replace logs: Fluent Bit → Loki. Экономия $200k.
  2. Затем metrics: Prometheus Operator → Mimir. Экономия $150k.
  3. Traces: OTel SDK + Tempo. Экономия $100k.
  4. Профайлинг: pprof + Pyroscope. Экономия $30k.

Итог: $500k → $50k self-hosted (infra). Команда 2 человека support.

Cloudflare обрабатывает миллионы RPS, и их observability stack:

  • ClickHouse как backend для traces/logs/metrics.
  • Sampling: 0.01% обычных, 100% errors.
  • Custom OTel Collector с heavy filtering.
  • Hot path с минимальной instrumentation.

Uber открыто публикуют свои observability практики:

  • M3 — собственный TSDB (open-source).
  • Jaeger (создан в Uber).
  • automaxprocs (для CPU limits в k8s).

Их stack обрабатывает > 100M метрик/sec.

Авито использует:

  • VictoriaMetrics для metrics.
  • ClickHouse для logs.
  • Tempo для traces.
  • OTel SDK во всех Go-сервисах.

Их CTO рассказывал на конференциях про переход с Prometheus → VictoriaMetrics из-за storage costs.

  • ClickHouse-based observability.
  • Свой fork Pyroscope.
  • В Go-сервисах OTel SDK.

В реальных Go-сервисах (Авито, Cloudflare, корпоративные тесты) typical OTel overhead:

  • CPU: +3-5%.
  • Memory: +50-100 MB на типичный pod.
  • Latency: +0.2-1ms на span (в основном sampling decision + serialization).

При SimpleSpanProcessor можно увидеть +50ms на запрос — это ловушка.


Q1: Что такое три столпа observability и в чём их различия? A: Metrics — агрегированные числа (счётчики, histograms), дёшевы, для алертов/SLO. Logs — детальные события, дороги. Traces — цепочки межсервисных вызовов, помогают понять request lifecycle. Плюс continuous profiling и eBPF events в современных стеках.

Q2: Что такое OpenTelemetry и зачем он нужен? A: CNCF-стандарт для observability. Включает спецификацию, SDK для языков, Collector и OTLP протокол. Vendor-neutral — можно менять backend без переписывания кода.

Q3: Чем отличаются head-based и tail-based sampling? A: Head — решение в начале трейса на корневом сервисе (быстро, дёшево, но можно пропустить редкие ошибки). Tail — решение в Collector’е, видя все spans (гарантия ловить ошибки, но дороже по памяти и задержке).

Q4: Что такое exemplars и зачем нужны? A: Это записи в метрике, содержащие trace_id примера. Позволяют из дашборда (например, spike в histogram latency) “прыгнуть” в конкретный trace, вызвавший этот spike.

Q5: Что такое semantic conventions в OTel? A: Стандартные имена атрибутов: service.name, http.request.method, db.system. Использование критично для cross-service дашбордов и алертов.

Q6: Как работает W3C TraceContext? A: HTTP headers traceparent (содержит trace_id, parent span_id, flags) и tracestate (vendor-specific). Стандарт propagation для interoperability между tracing системами.

Q7: Зачем нужен OTel Collector если SDK может отправлять напрямую в backend? A: Collector предоставляет: централизованную обработку (sampling, фильтрация, обогащение), независимость от backend (поменял exporter — не трогая SDK), retry и буферизацию, агрегацию, и снижение нагрузки на бэкенд.

Q8: Расскажи про pipeline в Collector’е. A: Три этапа: receivers (принимают telemetry) → processors (обрабатывают: batch, memory_limiter, tail_sampling) → exporters (отправляют в backend). Поддерживает множество источников и целей.

Q9: Что произойдёт, если в метрику добавить label с trace_id? A: Cardinality explosion. Каждый уникальный trace_id создаёт новый time series. На миллионе trace’ов получим миллион series → Prometheus OOM. Категорически нельзя.

Q10: SimpleSpanProcessor vs BatchSpanProcessor? A: Simple отправляет span синхронно при End() — блокирует request, в production категорически нельзя. Batch буферизует и шлёт пачками асинхронно — стандарт для prod.

Q11: Что такое Tempo и чем отличается от Jaeger? A: Tempo — backend для traces от Grafana, использует object storage (S3) для удешевления. Изначально не имел индекса по атрибутам (только trace_id), сейчас TraceQL позволяет поиск. Jaeger использует Cassandra/Elasticsearch — дороже, но богаче в поиске.

Q12: Чем Loki отличается от Elasticsearch? A: Loki индексирует только метки (labels), не текст логов. Логи хранятся в object storage. В 5-10 раз дешевле ES при сопоставимой ценности. Минус — нельзя делать full-text поиск по миллиардам строк, но обычно фильтруют по labels и потом grep’ают в окне.

Q13: Что такое Mimir? A: Distributed Prometheus, форк Cortex. Горизонтально масштабируемое long-term storage с tenant isolation. Совместим с Prometheus API.

Q14: Что такое Pyroscope? A: Continuous profiling. Постоянно собирает pprof профили (CPU, heap, goroutine, mutex) и хранит их с labels и временем. Можно делать flame graphs за любой промежуток и сравнивать.

Q15: Как работает tail sampling в Collector? A: Collector буферизует все spans трейса. После decision_wait (30 сек по умолчанию) применяет policies: keep errors, keep slow (latency > X), keep random N%. Требует sticky routing если несколько replicas.

Q16: Что делать с context propagation в Kafka consumer? A: Producer сохраняет trace context в message headers (otelkafka делает автоматически). Consumer извлекает через propagation.Extract и стартует child span. Так trace продолжается через async границу.

Q17: Что такое Alloy? A: Grafana Alloy — universal telemetry collector, замена Grafana Agent. Поддерживает OTel pipeline + Prometheus scrape + Loki + другие. Лёгкий, декларативный config.

Q18: Как контролировать стоимость observability? A: Aggressive tail sampling (только errors и slow), drop low-value metrics, short retention для logs/traces (3-7 дней), long retention только для metrics, фильтрация в Collector до отправки в backend, self-hosted Grafana stack vs vendor.

Q19: Что такое VictoriaMetrics и почему популярен в РФ? A: Альтернатива Prometheus, написана в Москве. В 5-10 раз меньше disk usage, быстрее в чтении, PromQL-совместимый, cluster version для HA. В РФ ещё и независимость от санкций важна.

Q20: Какой overhead у OTel в production? A: При правильной настройке (BatchSpanProcessor, разумные attributes) — 3-5% CPU, 50-100MB памяти, +0.2-1ms latency. SimpleSpanProcessor и неразумная инструментация могут добавить десятки мс.

Q21: Что такое Baggage и чем отличается от обычных attributes? A: Baggage — key-value контекст, пробрасываемый между всеми сервисами автоматически через HTTP headers. Используется для cross-cutting concerns (tenant_id, feature flag). Attributes — это локальные данные span, не пропагируются.

Q22: Как объединить trace, log и metric в одном дашборде? A: 1) Использовать единый trace_id в логах (slog с OTel handler автоматически добавляет). 2) Exemplars в метриках (trace_id внутри метрики). 3) Grafana correlations: клик на exemplar → trace, клик на span → logs за то же окно.

Q23: Как работает span links? A: Link — это ссылка на другой span (не parent). Используется для async/batch обработки: один consumer обрабатывает batch из 100 сообщений из разных trace’ов, создаёт один span с 100 links на родительские.

Q24: Что такое ClickHouse-based observability (Uptrace, SigNoz)? A: Один backend (ClickHouse) для traces + logs + metrics. SQL для запросов, отличное сжатие, дешевле фрагментированного стека. В РФ популярно из-за независимости от санкций.

Q25: Если ты увидел spike в P99 latency, какой workflow найти причину? A: 1) Открыть метрику P99 в Grafana. 2) Найти exemplar в spike — кликнуть → перейти в trace. 3) Посмотреть, какой span доминирует. 4) Открыть логи этого span за окно — найти exception. 5) Если нужно — Pyroscope flame graph за окно — увидеть, что CPU делал.


  1. Запусти локальный stack (docker-compose): OTel Collector, Tempo, Loki, Prometheus, Grafana. Инструментируй простое Go API. Сделай 1000 запросов, открой в Grafana traces и метрики.

  2. Tail sampling lab: настрой Collector с tail_sampling, который оставляет 100% errors, 100% slow > 500ms, 5% random. Запусти load test (vegeta, k6). Проверь, что в Tempo все ошибки сохранились.

  3. High-cardinality drill: создай метрику с user_id как label на 100k уникальных users. Поломай Prometheus. Затем перепиши на histogram без user_id, добавь exemplar.

  4. Slog → OTel bridge: интегрируй otelslog handler. Сделай запрос, увидь, что лог автоматически содержит trace_id и можно прыгнуть в trace из Loki.

  5. Multi-window burn rate: реализуй Prometheus rule для burn rate alert (14x за 1h И 6x за 6h). Запусти chaos test — pod inject errors, проверь, что алерт сработал.

  6. Profile flame graph: запусти Pyroscope, сделай нагрузку с CPU bottleneck (sleep tight loop), увидь flame graph. Сравни два периода.

  7. Custom OTTL transform: напиши Collector processor, который redact’ит email в URL query parameter.

  8. VictoriaMetrics swap: возьми существующий Prometheus setup, переключи на VictoriaMetrics, сравни диск и query speed.


  1. OpenTelemetry Documentation — официальный стандарт.
  2. OpenTelemetry Go SDK — исходники и примеры.
  3. Grafana Tempo docs — backend для traces.
  4. Grafana Loki docs — backend для logs.
  5. Prometheus docs — metrics стандарт.
  6. “Distributed Tracing in Practice” — Austin Parker et al., O’Reilly.
  7. “Observability Engineering” — Charity Majors.
  8. W3C Trace Context spec.
  9. VictoriaMetrics docs — РФ-альтернатива.
  10. Uptrace docs — ClickHouse-based platform.