Обычный путь получить трейс из приложения — добавить 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 в одном кластере) это означает четыре разных способа получить один и тот же трейс, и четыре разных места, где инструментация может отстать от версии приложения или сломаться при обновлении.
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 добавляет:
- RED-метрики из коробки — request rate, error rate, duration по каждому HTTP/gRPC-эндпоинту, без единой строчки инструментации метрик в приложении.
- Распределённый трейсинг между сервисами — OBI пробрасывает и читает
traceparent, так что спаны из zero-code-сервисов встраиваются в ту же трассу, что и спаны из сервисов с ручной SDK-инструментацией. - Покрытие legacy и сторонних бинарников — сервисы, код которых менять нельзя или дорого (вендорские образы, старые монолиты), получают трейсинг наравне с новым кодом.
- Единую точку обновления — версия инструментации привязана к DaemonSet, а не размазана по N образам приложений; обновить OBI — это
kubectl rollout, а не N пул-реквестов.
Важно не путать OBI с остальными eBPF-инструментами обсервабилити, которые часто уже стоят в том же кластере. Hubble (часть Cilium) смотрит на L3/L4 — кто с кем соединяется, какие сетевые policy сработали, но не видит HTTP-путей и SQL-запросов внутри соединения. Tetragon решает security-задачу: детектирует и блокирует опасные syscalls и файловые операции в рантайме, а не измеряет латентность. Pyroscope профилирует — показывает, где приложение тратит CPU и память на уровне стека вызовов, а не что именно оно ответило клиенту. Все четыре читают ядро через один и тот же eBPF-механизм, но отвечают на разные вопросы, и их имеет смысл держать вместе, а не выбирать один вместо другого.
| Инструмент | Что видит | Уровень | Zero-code |
|---|---|---|---|
| OBI | HTTP/gRPC/SQL-спаны, RED-метрики | L7, приложение | Да |
| Hubble | Сетевые флоу, policy, connectivity | L3/L4, сеть | Да |
| Tetragon | Syscalls, файловые операции, security-события | Ядро, security | Да |
| Pyroscope | CPU/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 этого не обещает: он закрывает сетевой слой наблюдаемости, а не заменяет инструментацию продукта.