прослеживать путь каждого запроса через микросервисы в Kubernetes
Распределённое трассирование OpenTelemetry в промышленном Kubernetes
14 минут
Один запрос пользователя проходит через десятки сервисов до ответа. Логи и метрики не показывают, на каком шаге цепочки появилась задержка или ошибка. В материале — OpenTelemetry Operator, коллекторы агент и шлюз, автоинструментирование и передача контекста W3C в Tempo.
Почему логи и метрики не отвечают на вопросы о сквозных запросах
В микросервисных средах Kubernetes один вызов API часто проходит через шлюзы, доменные сервисы, кэши и очереди сообщений. Метрики показывают суммарную задержку и долю ошибок, логи фиксируют локальные события. Ни то ни другое не восстанавливает полный путь по шагам и не указывает, где начался таймаут. Без распределённого трассирования дежурные ищут совпадения по времени в разных пространствах имён и угадывают сломанную зависимость. Неполные трейсы появляются, когда один сервис не передаёт контекст, когда выборка на входе отбрасывает спаны посередине цепочки или когда имена спанов с высокой кардинальностью раздувают хранилище. Трассирование работает, когда передача контекста единообразна, инструментирование согласовано между языками, а конвейер сохраняет ошибки и прореживает обычный трафик.
Промышленная архитектура: Operator, коллекторы, propagation и Tempo
OpenTelemetry отделяет инструментирование от экспорта. Приложения отправляют OTLP-спаны и передают заголовки W3C Trace Context traceparent и tracestate в HTTP, метаданных gRPC и атрибутах сообщений. OpenTelemetry Operator управляет custom resource коллектора и подключает агенты автоинструментирования к подам. Агент DaemonSet на каждом узле принимает локальные спаны, применяет memory_limiter и batch и пересылает их на шлюз Deployment. Шлюз выполняет отложенную выборку трейсов, чтобы сохранять ошибочные и медленные цепочки и снижать объём обычного трафика, затем экспортирует данные в Grafana Tempo или любой OTLP-совместимый backend. Выборка на стороне SDK уменьшает объём заранее, отложенная выборка на шлюзе сохраняет полные трейсы с ошибками, которые входная выборка могла отбросить.
Установка OpenTelemetry Operator и коллектора-шлюза
Установите operator из официального Helm-чарта и зафиксируйте тег образа contrib. Опишите шлюз как custom resource OpenTelemetryCollector: memory_limiter первым в конвейере, tail_sampling для ошибок и медленных запросов, обогащение атрибутов и OTLP-экспорт в Tempo. Задавайте шлюзу достаточно памяти под буферы выборки — для умеренного объёма трейсов разумный старт от одного гибибайта лимита памяти.
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
helm upgrade --install otel-operator open-telemetry/opentelemetry-operator \
--namespace observability --create-namespace \
--set manager.collectorImage.repository=otel/opentelemetry-collector-contrib \
--set manager.collectorImage.tag=0.106.1apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: otel-gateway
namespace: observability
spec:
mode: deployment
replicas: 3
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
config: |
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
memory_limiter:
check_interval: 1s
limit_mib: 1536
spike_limit_mib: 384
tail_sampling:
decision_wait: 10s
num_traces: 100000
policies:
- name: errors
type: status_code
status_code: { status_codes: [ERROR] }
- name: slow-requests
type: latency
latency: { threshold_ms: 2000 }
- name: standard
type: probabilistic
probabilistic: { sampling_percentage: 10 }
attributes:
actions:
- key: deployment.environment
action: upsert
value: production
batch:
timeout: 5s
send_batch_size: 8192
exporters:
otlp/tempo:
endpoint: tempo.observability.svc:4317
tls:
insecure: true
sending_queue:
enabled: true
retry_on_failure:
enabled: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, attributes, batch]
exporters: [otlp/tempo]Агент DaemonSet и автоинструментирование
OpenTelemetryCollector в режиме DaemonSet пересылает спаны на DNS-имя сервиса шлюза. Создайте custom resource Instrumentation с parentbased_traceidratio на десять процентов для обычного трафика, propagators tracecontext и baggage и образами автоинструментирования под нужные языки. Аннотируйте Deployment через inject-java, inject-nodejs или inject-python с именем ресурса Instrumentation — одной аннотации inject-sdk недостаточно, она не подключает языковые агенты. Перед массовым rollout проверьте сквозной запрос и убедитесь, что в Tempo появляется многоспановый трейс.
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: otel-agent
namespace: observability
spec:
mode: daemonset
config: |
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
memory_limiter:
check_interval: 500ms
limit_mib: 300
spike_limit_mib: 80
batch:
timeout: 2s
send_batch_size: 4096
exporters:
otlp:
endpoint: otel-gateway-collector.observability.svc:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp]apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: order-service-instrumentation
namespace: default
spec:
exporter:
endpoint: http://otel-agent-collector.observability.svc:4318
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "0.1"
java:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:2.6.0
nodejs:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:0.52.1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: default
spec:
template:
metadata:
annotations:
instrumentation.opentelemetry.io/inject-java: order-service-instrumentation
spec:
containers:
- name: order-service
image: registry.example.com/order-service:v1.2.3Ручная передача контекста, когда автоинструментирования недостаточно
Собственные HTTP-клиенты, устаревшие очереди или обёртки gRPC нужно дополнить передачей W3C Trace Context вручную. Используйте API propagator из OpenTelemetry Go, а не проприетарные injectors. Для gRPC регистрируйте otelgrpc server handler и client handler, чтобы метаданные несли активный span context. Пропущенная передача на одном внутреннем hop разрывает трейс и сводит на нет смысл всего конвейера.
import (
"context"
"net/http"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
"google.golang.org/grpc"
)
func callDownstream(ctx context.Context, url string) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header))
return http.DefaultClient.Do(req)
}
func newGRPCServer() *grpc.Server {
return grpc.NewServer(
grpc.StatsHandler(otelgrpc.NewServerHandler()),
)
}Операционные практики: выборка, именование, стоимость, корреляция и безопасность
Применяйте выборку на стороне SDK для ограничения объёма и отложенную выборку на шлюзе для сохранения ошибок и медленных трейсов. Именуйте спаны по шаблонам маршрутов вроде GET /api/v2/orders/{orderId}, а не по сырым идентификаторам — они создают взрыв кардинальности. Храните данные в Tempo или backend семь–четырнадцать дней, если compliance не требует больше. Добавляйте trace_id и span_id в структурированные JSON-логи, чтобы Grafana или Loki переходили от строки лога к трейсу. Не записывайте персональные данные в атрибуты спанов. Используйте TLS между коллекторами и backend. Следите за otelcol_exporter_send_failed_spans и otelcol_processor_refused_spans на шлюзе. Перед выводом в продакшен проверьте агенты DaemonSet на всех узлах, лимиты ресурсов шлюза, политики отложенной выборки, автоинструментирование критичных сервисов, сквозную передачу W3C и переход от лога к трейсу в панели мониторинга.
Потоки трассировок проходят через те же уровни Collector, что описаны в гайде по единому конвейеру OpenTelemetry Collector.
Задайте допустимые бюджеты задержки и ошибок перед настройкой выборки по практикам SLO, SLI и error budget для платформенных команд.
