> Используете ли трейсинг, например Jaeger (Go)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: Wildberries
Стек: Go
> Пример ответа
Короткий ответ
Да, использую трейсинг в production-системах на Go, в основном с Jaeger или OpenTelemetry Collector как агентом. Трейсинг критичен для распределённых систем: он даёт сквозную видимость запроса через сервисы, базы данных и очереди. В Go обычно подключаю OpenTelemetry SDK, экспортирую спаны в Jaeger, и настраиваю sampling (обычно head-based с rate limiting). Это помогает быстро находить узкие места и ошибки в цепочках вызовов.
Подробное объяснение
Трейсинг - это инструмент observability, который фиксирует путь запроса через распределённую систему. В отличие от логов и метрик, трейсинг показывает временную иерархию операций: каждый span - это единица работы с именем, временем начала, длительностью и атрибутами. Спаны связаны в trace через trace ID и parent-child отношения.
В Go стандартный подход - OpenTelemetry SDK (бывший OpenTracing). Основные компоненты:
- TracerProvider - фабрика трасеров, конфигурируется один раз на процесс.
- Tracer - создаёт спаны, обычно через
tracer.Start(ctx, "operation"). - Propagation - передача trace context через HTTP headers (например,
traceparent), gRPC metadata или Kafka headers. - Exporter - отправка спанов в бэкенд (Jaeger, Zipkin, Tempo, Datadog).
Для Jaeger есть два пути: использовать OTLP exporter в OpenTelemetry Collector, который затем форвардит в Jaeger, или напрямую использовать Jaeger exporter. Первый вариант предпочтительнее, потому что Collector позволяет агрегировать, фильтровать и батчить спаны, а также менять бэкенд без изменения кода.
Ключевые практики:
- Sampling: не трейсить каждый запрос. Head-based sampling (решение до выполнения) с rate 5-10% для высоконагруженных систем, или tail-based sampling для редких ошибок.
- Context propagation: всегда передавать
context.Contextчерез все функции, даже если он не используется напрямую - это обязательное условие для корректного трейсинга. - Span attributes: добавлять бизнес-атрибуты (user ID, order ID, error code) - без них трейс бесполезен для диагностики.
- Naming: спаны называть по шаблону
HTTP GET /api/v1/users,DB SELECT users,Kafka consume topic.
На практике
В Go-сервисах я обычно делаю так:
- В
main.goсоздаюTracerProviderс OTLP exporter, настраиваю resource (service.name, version, environment). - Добавляю middleware для HTTP/gRPC, который стартует span на каждый входящий запрос, записывает метод, path, status code, длительность.
- Для исходящих вызовов (HTTP клиент, gRPC, SQL, Kafka) использую instrumentation libraries:
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp,go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc,go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql. - Для ручных операций (например, парсинг файла, вызов внешнего API без готовой instrumentation) создаю дочерние спаны через
tracer.Start(ctx, "operation"). - Настраиваю sampling:
ParentBased(AlwaysSample)для development,TraceIdRatioBased(0.1)для production.
Важно: трейсинг не заменяет логи и метрики. Он отвечает на вопрос "где именно запрос потратил время", но не на "сколько ошибок было в час". Поэтому в системах всегда три кита: logs, metrics, traces.
Пример кода
GOpackage mainimport ("context""log""net/http""time""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc""go.opentelemetry.io/otel/sdk/resource"sdktrace "go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.21.0""go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp")func initTracer() func() {ctx := context.Background()exporter, err := otlptracegrpc.New(ctx,otlptracegrpc.WithEndpoint("jaeger:4317"),otlptracegrpc.WithInsecure(),)if err != nil {log.Fatal(err)}res, _ := resource.New(ctx,resource.WithAttributes(semconv.ServiceName("payment-service"),semconv.ServiceVersion("1.0.0"),),)tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter),sdktrace.WithResource(res),sdktrace.WithSampler(sdktrace.TraceIDRatioBased(0.1)),)otel.SetTracerProvider(tp)return func() { _ = tp.Shutdown(ctx) }}func main() {shutdown := initTracer()defer shutdown()handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {// ручной span для бизнес-операцииtracer := otel.Tracer("payment")ctx, span := tracer.Start(r.Context(), "process.payment")defer span.End()span.SetAttributes(attribute.String("payment.id", "12345"),attribute.Int("amount", 100),)// имитация работыtime.Sleep(50 * time.Millisecond)w.WriteHeader(http.StatusOK)})// middleware для автоматического трейсинга входящих запросовwrapped := otelhttp.NewHandler(handler, "HTTP POST /payment")log.Fatal(http.ListenAndServe(":8080", wrapped))}
Как отвечать на собеседовании
Начни с конкретного примера из практики: "Да, в последнем проекте мы использовали Jaeger через OpenTelemetry Collector". Расскажи, какую проблему решал трейсинг (например, медленные ответы из-за N+1 запросов в БД или потерянные сообщения в Kafka). Покажи понимание propagation - это главный вопрос, который любят задавать. Упомяни sampling и почему нельзя трейсить всё подряд. Если спросят про альтернативы - скажи про Zipkin, Tempo, Datadog, но подчеркни, что OpenTelemetry - стандарт де-факто.
Не углубляйся в детали конфигурации Jaeger UI, лучше расскажи про архитектуру: SDK → Collector → Jaeger. Если спросят про производительность - скажи, что overhead обычно < 1-2% при разумном sampling, и что можно использовать async exporter.
Что проверяет интервьюер
- Понимание разницы между tracing, logging и metrics.
- Знание механизма propagation (trace ID, parent span ID, headers).
- Умение объяснить, как трейсинг помогает в дебаге распределённых систем.
- Практический опыт: как настраивал, какие проблемы решал, как боролся с overhead.
- Знание экосистемы Go: OpenTelemetry SDK, instrumentation libraries, middleware.
- Понимание sampling стратегий и trade-off между полнотой данных и стоимостью.
Типичные ошибки
- Путать трейсинг с логированием: "мы просто пишем request ID в логи" - это не трейсинг.
- Не упоминать propagation - без него трейсинг в распределённой системе бесполезен.
- Говорить, что трейсинг решает все проблемы observability - это не так, нужны метрики и логи.
- Не знать, как передать context в Go:
context.Contextдолжен быть первым аргументом функции. - Предлагать трейсить каждый запрос без sampling - это приведёт к перегрузке хранилища и сети.
- Забывать про resource attributes (service.name) - без них нельзя фильтровать трейсы по сервисам.
> Похожие задачи по backend
Как долго хранятся логи
Используете ли алертинг через Grafana
Как реализовать добавление цифр к результату с помощью append и слайса байт в Go
Какой план действий при перегрузке CPU и срабатывании алерта
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью