Перейти к содержимому
Hogin Hogin
Назад

OBI: zero-code трейсинг приложений через eBPF, наследник Grafana Beyla

7 мин чтения

Обычный путь получить трейс из приложения — добавить SDK, обернуть хендлеры, пересобрать образ и надеяться, что инструментация не сломает то, что она наблюдает. OpenTelemetry eBPF Instrumentation (OBI) обходит весь этот путь: он читает HTTP/gRPC/SQL-трафик прямо в ядре, через eBPF-пробы на syscalls и TLS-библиотеках, и отдаёт готовые RED-метрики и трейсы в OTLP — без единой строчки в коде приложения и без агента внутри контейнера. Это не новый проект с нуля: наработки Grafana Beyla перешли под крыло OpenTelemetry как OBI при участии Splunk, и на KubeCon + CloudNativeCon Europe 2026 проект вышел из беты. Разбираем, что он даёт поверх уже развёрнутого OTel Collector и где заканчивается его зона ответственности.

Содержание

Открыть содержание

Почему это не игнорировать в 2026

CNCF Observability TAG отчитался, что 67% продакшн-кластеров уже гоняют хотя бы один eBPF-инструмент обсервабилити — Cilium/Hubble для сети, Tetragon для security, Pyroscope для профилирования. Трейсинг оставался последним белым пятном: чтобы получить спаны с латентностью конкретного HTTP-запроса, приходилось тащить auto-instrumentation SDK в каждый сервис на каждом языке рантайма отдельно. OBI закрывает именно эту дыру, используя тот же класс механизма, что и остальные eBPF-инструменты в кластере, — только направленный на L7-семантику приложения, а не на сеть или на syscalls.

Beyla был экспериментом Grafana Labs: доказать, что HTTP/gRPC-трейсинг можно получить без SDK, читая буферы прямо на входе/выходе из сокета и на uprobe в TLS-библиотеках (чтобы видеть трафик до шифрования). Когда стало ясно, что подход работает на реальной нагрузке, а не только в демо, наработки объединили с параллельными eBPF-эксперименты Splunk и отдали в OpenTelemetry как отдельный официальный проект — OBI. Это важно: OBI не конкурирует с OpenTelemetry Collector, он — ещё один источник телеметрии, который говорит с коллектором на том же OTLP.

Формально OBI живёт в статусе beta, а не GA: API конфигурации ещё может поменяться между минорными версиями, а список поддерживаемых языковых рантаймов (Go, Java, Node.js, Python, .NET, Rust) закрывает подавляющее большинство продакшн-сервисов, но не все — например, статически слинкованные бинарники на экзотических рантаймах eBPF-пробы иногда не подхватывают автоматически. Для типового HTTP/gRPC/SQL-сервиса на одном из перечисленных языков это не блокер, но перед раскаткой на весь кластер стоит свериться со списком поддержки в конкретной версии.

Проблема: auto-instrumentation SDK — это код, деплой и его же поддержка

SDK-инструментация решает задачу трейсинга, но платит за это тремя вещами сразу: правкой кода (или как минимум оборачивающим враппером), отдельной сборкой под каждый рантайм и агентом, который живёт внутри процесса приложения и может на него повлиять — от лишнего оверхеда до конфликта версий зависимостей. На гетерогенном парке сервисов (Go, Java, Python, Node в одном кластере) это означает четыре разных способа получить один и тот же трейс, и четыре разных места, где инструментация может отстать от версии приложения или сломаться при обновлении.

Два пути получить трейс: ручная SDK-инструментация против OBI

OBI убирает два из трёх пунктов — правки кода и отдельную сборку — потому что цепляется не к процессу приложения, а к ядру. eBPF-пробы ставятся на syscalls (accept, read, write) и на функции TLS-библиотек (OpenSSL, BoringSSL, Go crypto/tls), так что OBI видит и открытый, и зашифрованный HTTP/gRPC-трафик, сопоставляет запрос с ответом и генерирует span с латентностью, статус-кодом и путём — не трогая ни одного байта в бинарнике приложения. Разворачивается это как один DaemonSet на кластер, а не как N агентов на N сервисов.

Что OBI даёт поверх уже настроенного OTel Collector

Если в кластере уже стоит минимальный OpenTelemetry Collector, OBI встраивается в него как ещё один источник — он экспортирует те же OTLP-спаны и метрики, ничего в конфигурации коллектора менять не нужно. Конкретно OBI добавляет:

Зоны ответственности: OBI, Hubble, Tetragon и Pyroscope на одном узле

Важно не путать OBI с остальными eBPF-инструментами обсервабилити, которые часто уже стоят в том же кластере. Hubble (часть Cilium) смотрит на L3/L4 — кто с кем соединяется, какие сетевые policy сработали, но не видит HTTP-путей и SQL-запросов внутри соединения. Tetragon решает security-задачу: детектирует и блокирует опасные syscalls и файловые операции в рантайме, а не измеряет латентность. Pyroscope профилирует — показывает, где приложение тратит CPU и память на уровне стека вызовов, а не что именно оно ответило клиенту. Все четыре читают ядро через один и тот же eBPF-механизм, но отвечают на разные вопросы, и их имеет смысл держать вместе, а не выбирать один вместо другого.

ИнструментЧто видитУровеньZero-code
OBIHTTP/gRPC/SQL-спаны, RED-метрикиL7, приложениеДа
HubbleСетевые флоу, policy, connectivityL3/L4, сетьДа
TetragonSyscalls, файловые операции, security-событияЯдро, securityДа
PyroscopeCPU/memory flame graphsРантайм-профиль процессаЧастично (нужен агент)
Ручной SDKКастомные бизнес-спаны, произвольная семантикаL7, бизнес-логикаНет

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

OBI ставится как DaemonSet с privileged: true (нужны CAP_BPF и доступ к /sys/kernel/debug) и минимальным конфигом, который просто указывает, куда слать OTLP — на тот же коллектор, что уже принимает трейсы от остальных сервисов:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: obi
  namespace: observability
spec:
  selector:
    matchLabels:
      app: obi
  template:
    metadata:
      labels:
        app: obi
    spec:
      hostPID: true
      containers:
        - name: obi
          image: otel/obi:latest
          securityContext:
            privileged: true
          env:
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: "http://otel-collector.observability:4317"
            - name: OTEL_EBPF_KUBE_METADATA_ENABLE
              value: "true"
            - name: OTEL_EBPF_TRACE_PRINTER
              value: "text"
          volumeMounts:
            - name: sys-kernel-debug
              mountPath: /sys/kernel/debug
      volumes:
        - name: sys-kernel-debug
          hostPath:
            path: /sys/kernel/debug

OTEL_EBPF_KUBE_METADATA_ENABLE подтягивает namespace, pod и service из Kubernetes API, чтобы спаны сразу были подписаны так же, как спаны от сервисов с ручной инструментацией — без этого пришлось бы сопоставлять их вручную по IP. Дальше ничего дополнительно настраивать не нужно: OBI сам находит процессы, слушающие TCP-порты в тех же network namespace, ставит на них пробы и начинает отдавать спаны в коллектор.

По умолчанию OBI пытается обнаружить и заинструментировать все процессы на узле, что на плотном кластере означает лишний оверхед на сервисы, трейсинг которых никому не нужен (служебные sidecar-ы, init-контейнеры). Это стоит сузить через OTEL_EBPF_DISCOVERY_SERVICES — селектор по namespace и label, который ограничивает набор процессов, на которые ставятся пробы, тем же кругом сервисов, что уже пишут метрики в Prometheus. Разница в overhead ощутима: eBPF-пробы дешевле, чем SDK-инструментация внутри процесса, но не бесплатны, и профилировать namespace вроде kube-system обычно нет смысла.

Как проверить, что это работает

Убедиться, что OBI действительно генерирует спаны, проще всего через логи с text-принтером трейсов и через сравнение с ручной инструментацией на одном и том же сервисе:

kubectl logs -n observability -l app=obi --tail=50 | grep "HTTP"

Если в выводе видны строки вида GET /api/orders 200 12ms — пробы отработали и спаны ушли в коллектор. Дальше стоит открыть тот же сервис в Grafana/Tempo и свериться: если сервис уже был инструментирован SDK, трейсы от OBI должны появиться рядом с трейсами от SDK с той же латентностью на HTTP-уровне — это подтверждает, что оба источника видят одну и ту же реальность, просто с разной глубиной детализации.

Где заканчивается zero-code

OBI видит границу сети — вход и выход из процесса, HTTP-путь, SQL-запрос, статус-код. Он не видит то, что происходит внутри бизнес-логики между этими границами: кастомные атрибуты span (ID пользователя, тип тарифа, номер заказа), явные под-спаны вокруг конкретной функции, счётчики бизнес-событий, которые не соответствуют сетевому вызову. Как только нужна семантика, которую не вывести из самого факта запроса-ответа, — это работа для ручного SDK поверх той же OTel-инфраструктуры, а не для OBI.

OBI закрывает 80% типового HTTP/SQL-трейсинга бесплатно и без деплоя SDK в каждый сервис — это честная сделка для гетерогенного парка, legacy-кода и сервисов, которые менять дорого. Оставшиеся 20% бизнес-семантики всё ещё требуют ручных спанов, и OBI этого не обещает: он закрывает сетевой слой наблюдаемости, а не заменяет инструментацию продукта.


Поделиться:

Следующая статья
OpenCost: считаем реальную стоимость namespace, а не всего кластера