Zero-code инструментация с OpenTelemetry eBPF (OBI)

Автоматическая трассировка и метрики приложений без изменения кода с помощью OpenTelemetry eBPF Instrumentation (OBI) и отправка данных в агент ProtoOBP по OTLP.

На этой странице:

Введение

OpenTelemetry eBPF Instrumentation (OBI) — это zero-code инструментация на основе eBPF. OBI автоматически отслеживает сетевые запросы приложения (HTTP/HTTPS, gRPC и др.) на уровне ядра Linux и сам формирует трейсы и метрики в формате OpenTelemetry (OTLP) — без изменения кода приложения, пересборки образов или подключения SDK/агентов внутрь контейнера.

Это удобно, когда:

  • приложение написано на языке, для которого нет (или неудобно подключать) трейсер;
  • нет возможности менять код или образ (legacy, сторонний софт, прокси вроде NGINX/Apache);
  • нужно быстро получить базовую observability по входящим/исходящим HTTP-запросам.

Сформированную телеметрию OBI отправляет по OTLP в агент Proto Observability Platform, который умеет принимать OTLP (см. Подключение OpenTelemetry).

Что вы получите

Несмотря на zero-code инструментацию, Proto Observability Platform полностью выполняет задачи мониторинга производительности сервисов, инструментированных с помощью eBPF (OBI): обзорный дашборд сервиса, карта взаимодействия сервисов, распределённые трейсы, анализ производительности эндпоинтов и сбор логов.

Обзорный дашборд сервиса:

Обзорный дашборд сервиса, инструментированного с помощью OBI

Карта взаимодействия сервисов:

Карта взаимодействия сервисов, инструментированных с помощью OBI

Трейс сервиса:

Трейс сервиса, инструментированного с помощью OBI

Анализ производительности эндпоинтов сервиса:

Анализ производительности эндпоинтов сервиса

Логи сервиса:

Логи сервиса, инструментированного с помощью OBI, в интерфейсе ProtoOBP

Ниже описано, как развернуть и настроить OBI, чтобы получить такой результат.

Как это работает

OBI разворачивается в кластере как DaemonSet (по одному поду на узел). Под OBI запускается привилегированным и с hostPID, чтобы через eBPF подключаться к процессам приложений, работающим на этом же узле. Обнаружение процессов и обогащение телеметрии метаданными Kubernetes выполняется автоматически по правилам discovery.

   ваше приложение (под)                     агент ProtoOBP
   ┌───────────────────┐   eBPF (zero-code)  (DaemonSet, OTLP receiver)
   │  процесс (nginx,   │◄─────────┐          ┌──────────────────────┐
   │  java, go, ...)    │          │          │  OTLP 4317 gRPC /     │
   └───────────────────┘   ┌───────┴───────┐  │       4318 HTTP       │
                           │  OBI DaemonSet │──┴──► OTLP traces+metrics │
                           └────────────────┘     └──────────────────────┘

Требования

  • Кластер Kubernetes и Helm 3.
  • Узлы на Linux с ядром, поддерживающим eBPF (рекомендуется 5.8 и новее).
  • Установленный и работающий агент ProtoOBP с включённым приёмом OTLP (см. Шаг 1).
  • OBI запускается привилегированным DaemonSet с hostPID — это требование eBPF-инструментации (нужны права на подключение к процессам узла).

Шаг 1. Включите приём OTLP на агенте

OBI отправляет данные по OTLP, поэтому на агенте ProtoOBP должен быть включён OTLP receiver на портах 4317 (gRPC) и/или 4318 (HTTP). Подробно это описано на странице Подключение OpenTelemetry → Включение поддержки OpenTelemetry на агенте.

Кратко, в values.yaml Helm-чарта агента:

protoobp:
  otlp:
    receiver:
      protocols:
        grpc:
          endpoint: 0.0.0.0:4317
          enabled: true
        http:
          endpoint: 0.0.0.0:4318
          enabled: true

После изменения обновите релиз агента, например:

helm upgrade protoobp -f values.yaml protoobp/protoobp --namespace protoobp

Шаг 2. Установите OBI через Helm

Для Kubernetes используется официальный Helm-чарт OpenTelemetry:

helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update

helm upgrade --install obi open-telemetry/opentelemetry-ebpf-instrumentation \
  --namespace <ваш-namespace> \
  -f obi-values.yaml

Файл obi-values.yaml описан ниже. OBI должен работать в том же кластере, что и инструментируемые приложения; правила discovery указывают, какие именно рабочие нагрузки инструментировать.

Шаг 3. Настройте values.yaml

Минимальная конфигурация чарта: режим application, привилегированный режим и блок config.data с настройками OBI.

preset: application
privileged: true

config:
  create: true
  data:
    attributes:
      kubernetes:
        enable: true
    discovery:
      instrument:
        - namespace: <ваш-namespace>
          k8s_deployment_name: <имя-deployment>
    otel_metrics_export:
      endpoint: http://protoobp.protoobp.svc.cluster.local:4318
    otel_traces_export:
      endpoint: http://protoobp.protoobp.svc.cluster.local:4318

Что инструментировать (discovery)

Блок discovery.instrument — это список правил, какие рабочие нагрузки подключать. Правила можно задавать по имени деплоймента и namespace:

    discovery:
      instrument:
        - namespace: payments
          k8s_deployment_name: api-gateway
        - namespace: payments
          k8s_deployment_name: checkout

При attributes.kubernetes.enable: true OBI автоматически добавляет к телеметрии метаданные Kubernetes (k8s.namespace.name, k8s.pod.name, k8s.deployment.name, k8s.node.name и др.). Чтобы это работало, ServiceAccount OBI должен иметь права на чтение pods, services, nodes и replicasets — официальный чарт создаёт нужные ClusterRole/ClusterRoleBinding автоматически.

Куда отправлять данные (OTLP endpoint)

OBI определяет протокол OTLP по порту эндпоинта: порт, оканчивающийся на 4317, трактуется как gRPC, на 4318 — как HTTP/protobuf.

При стандартной установке агента (Helm-релиз protoobp в namespace protoobp) в кластере присутствуют:

  • Service protoobp в namespace protoobp (FQDN protoobp.protoobp.svc.cluster.local) с OTLP-портами 4317 (gRPC) и 4318 (HTTP);
  • DaemonSet protoobp (поды вида protoobp-<hash>), работающий в режиме hostNetwork, поэтому OTLP-порты доступны и на IP узла.

Есть два рабочих варианта адреса агента:

Агент ProtoOBP, как правило, развёрнут как DaemonSet в режиме hostNetwork, поэтому доступен на IP-адресе узла. Официальный чарт OBI уже прокидывает в под переменную HOST_IP (из status.hostIP), поэтому достаточно задать стандартную переменную OpenTelemetry OTEL_EXPORTER_OTLP_ENDPOINT:

env:
  OTEL_EXPORTER_OTLP_ENDPOINT: "http://$(HOST_IP):4318"   # HTTP; для gRPC используйте :4317

В этом случае поля otel_metrics_export.endpoint / otel_traces_export.endpoint в config.data можно не указывать. Этот вариант соответствует рекомендации из Подключение OpenTelemetry для Kubernetes.

У агента есть ClusterIP Service protoobp в namespace protoobp, поэтому можно указать его DNS-имя прямо в конфиге OBI:

config:
  data:
    otel_metrics_export:
      endpoint: http://protoobp.protoobp.svc.cluster.local:4318
    otel_traces_export:
      endpoint: http://protoobp.protoobp.svc.cluster.local:4318

Группировка маршрутов (routes)

Чтобы идентификаторы в путях (ID пользователя, заказа и т. п.) не «раздували» имена спанов и значения меток метрик, задайте низкокардинальные шаблоны маршрутов:

config:
  data:
    routes:
      patterns:
        - /users/:user_id/home
        - /api/orders/:order_id
        - /api/products/:product_id/reviews
      unmatched: path

Параметры в шаблоне обозначаются через :имя. Запросы, не подошедшие ни под один шаблон, обрабатываются согласно unmatched (например, path — оставить путь как есть).

Шаг 4. Добавьте теги и привязку к инфраструктуре

Чтобы в интерфейсе Proto Observability Platform у спанов отображались ссылки на инфраструктурные сущности (кластер, узел, namespace, под) и удобная группировка, к телеметрии нужно добавить соответствующие теги. Для этого используются:

  • поле config.data.attributes.kubernetes.cluster_name — для имени кластера;
  • стандартная переменная OTEL_RESOURCE_ATTRIBUTES — для статических тегов (окружение, группа сервисов и т. д.).

Перечисленные в OTEL_RESOURCE_ATTRIBUTES атрибуты добавляются ко всей телеметрии данного пода OBI и становятся тегами на агенте.

Имя кластера

Без явного имени кластера OBI пишет предупреждение can't fetch Kubernetes Cluster Name, поле k8s.cluster.name остаётся пустым, и ссылка на кластер в интерфейсе не строится. Задайте имя кластера:

config:
  data:
    attributes:
      kubernetes:
        enable: true
        cluster_name: <имя-кластера>

Дополнительно продублируйте имя кластера тегом kube_cluster_name (см. ниже) — именно по этому тегу строится ссылка на кластер.

Окружение, группа сервисов и произвольные теги

Статические теги задаются через OTEL_RESOURCE_ATTRIBUTES в блоке env:

env:
  OTEL_RESOURCE_ATTRIBUTES: "deployment.environment=<env>,kube_cluster_name=<имя-кластера>,service_group=<группа>"

Где:

  • deployment.environment → тег env (логическое окружение: production, demo и т. д.);
  • kube_cluster_name → ссылка на кластер в интерфейсе;
  • service_group → группировка сервисов;
  • можно добавить любые свои пары ключ=значение через запятую.

Тег узла (node)

Так как OBI — это DaemonSet с hostPID, каждый под OBI инструментирует процессы только своего узла. Значит имя узла одинаково для всех его спанов и его можно взять из downward API. Официальный чарт поддерживает блок envValueFrom:

envValueFrom:
  NODE_NAME:
    fieldRef:
      fieldPath: spec.nodeName

env:
  # NODE_NAME подставляется из envValueFrom (объявлен раньше env), Kubernetes
  # раскрывает $(NODE_NAME) во время запуска контейнера.
  OTEL_RESOURCE_ATTRIBUTES: "deployment.environment=<env>,kube_cluster_name=<имя-кластера>,service_group=<группа>,node=$(NODE_NAME)"

Тег node даёт ссылку на узел в интерфейсе.

Теги пода и namespace

Теги пода и namespace (pod_name, kube_namespace) задавать вручную не нужно — они различаются для каждого инструментируемого пода, и OBI проставляет их автоматически из метаданных Kubernetes (k8s.pod.name, k8s.namespace.name) при attributes.kubernetes.enable: true.

Привязка логов сервиса (service / env / version)

OBI формирует трейсы и метрики, но не собирает логи приложения — их собирает сам агент ProtoOBP (при включённом сборе логов контейнеров). По умолчанию агент тегирует такие логи именем образа (например, nginx, httpd), поэтому на вкладке «Логи» сервиса (запрос вида service:"<имя-сервиса>") они не находятся.

Чтобы логи привязались к тому же сервису, что и трейсы OBI, добавьте метки Unified Service Tagging в шаблон пода инструментируемого приложения (не пода OBI):

spec:
  template:
    metadata:
      labels:
        # = имя, под которым сервис виден в Proto Observability Platform
        # (по умолчанию OBI использует имя deployment).
        tags.proto.group/service: <имя-сервиса>
        tags.proto.group/env: <env>            # то же окружение, что и в OBI
        tags.proto.group/version: <version>

После этого логи контейнера тегируются service / env / version и отображаются на вкладке «Логи» соответствующего сервиса. Значение tags.proto.group/service должно точно совпадать с именем сервиса в APM, а tags.proto.group/env — с окружением, заданным для OBI (deployment.environment), чтобы трейсы, метрики и логи сходились на одном сервисе.

Полный пример values.yaml

Ниже — собранный воедино пример для официального чарта open-telemetry/opentelemetry-ebpf-instrumentation. Замените значения в угловых скобках на свои.

preset: application
privileged: true

config:
  create: true
  data:
    attributes:
      kubernetes:
        enable: true
        cluster_name: <имя-кластера>
    discovery:
      instrument:
        - namespace: <ваш-namespace>
          k8s_deployment_name: <имя-deployment>
    routes:
      patterns:
        - /users/:user_id/home
        - /api/orders/:order_id
      unmatched: path
    # Если используете отправку по host IP (ниже, через OTEL_EXPORTER_OTLP_ENDPOINT),
    # эти два поля можно не указывать. Альтернатива — слать на Service агента
    # (релиз protoobp в namespace protoobp):
    # otel_metrics_export:
    #   endpoint: http://protoobp.protoobp.svc.cluster.local:4318
    # otel_traces_export:
    #   endpoint: http://protoobp.protoobp.svc.cluster.local:4318

# Имя узла из downward API — для тега node.
envValueFrom:
  NODE_NAME:
    fieldRef:
      fieldPath: spec.nodeName

env:
  # Отправка в агент по host IP узла (HOST_IP чарт подставляет сам).
  OTEL_EXPORTER_OTLP_ENDPOINT: "http://$(HOST_IP):4318"
  # Прочие настройки OBI.
  OTEL_EBPF_DISCOVERY_POLL_INTERVAL: "5s"
  OTEL_EBPF_METRICS_INTERVAL: "30s"
  OTEL_EBPF_NAME_RESOLVER_SOURCES: "k8s,dns"
  # Теги для группировки и привязки к инфраструктуре.
  OTEL_RESOURCE_ATTRIBUTES: "deployment.environment=<env>,kube_cluster_name=<имя-кластера>,service_group=<группа>,node=$(NODE_NAME)"

Установка/обновление:

helm upgrade --install obi open-telemetry/opentelemetry-ebpf-instrumentation \
  --namespace <ваш-namespace> \
  -f obi-values.yaml

Шаг 5. Проверка

  1. Под OBI запущен и привязался к процессам:

    kubectl -n <ваш-namespace> rollout status ds/obi-opentelemetry-ebpf-instrumentation
    kubectl -n <ваш-namespace> logs ds/obi-opentelemetry-ebpf-instrumentation | grep -i "instrumenting process"
    

    В логах появятся строки msg="instrumenting process" для обнаруженных процессов. Предупреждение про bpffs (если есть) не критично — трейсы и метрики работают; оно лишь отключает дополнительные функции (например, обогащение логов).

  2. Нет ошибок экспорта OTLP:

    kubectl -n <ваш-namespace> logs ds/obi-opentelemetry-ebpf-instrumentation | grep -iE "export|otlp" | grep -i error
    

    Пустой вывод — ошибок нет.

  3. В интерфейсе Proto Observability Platform появятся сервисы с трейсами и HTTP-метриками, сгруппированными по маршруту и http.response.status_code, а у спанов — теги и ссылки на инфраструктуру (кластер, узел, namespace, под). Примеры того, как это выглядит, приведены в начале страницы, в разделе «Что вы получите».

Ссылки