Когда сервис внезапно тормозит, первая мысль — добавить логи и перезапустить его. Но перезапуск убирает проблему из поля зрения, а strace на работающем процессе замедляет его в сотни раз и требует прав на изменение запуска. eBPF решает задачу иначе: программа встраивается в точки ядра на лету, не перезапуская и не перекомпилируя ни ядро, ни приложение. Разберём, что это значит на практике, и пройдём путь от установки пакетов до готовых трассировщиков.
Как это устроено
Программа eBPF пишется на похожем на C языке, компилируется в байт-код и загружается в ядро. Перед запуском верификатор проверяет, что код не выйдет за границы памяти и не зайдёт в бесконечный цикл, — именно это отличает eBPF от произвольной загрузки модулей ядра. Дальше байт-код исполняется в точках трассировки: kprobe и tracepoint срабатывают на функции ядра, uprobe — на функции пользовательских программ. События уходят в кольцевой буфер, откуда их читает утилита в пользовательском пространстве, а частота срабатываний и объём буфера задаются программой.
Подготовка окружения
Для работы нужны актуальное ядро и заголовки именно работающей его версии:
uname -r
sudo apt install bpfcc-tools bpftrace bpftool linux-headers-$(uname -r)
sudo bpftool feature probe kernel | head -20
cat /proc/sys/kernel/unprivileged_bpf_disabled
В Debian и Ubuntu инструменты BCC поставляются с суффиксом -bpfcc, поэтому скрипты запускаются как execsnoop-bpfcc. На других дистрибутивах набор почти тот же: Fedora ставит пакет bcc, Arch — bcc и bpftrace, меняются только имена пакетов, но не синтаксис инструментов. Последняя команда показывает, разрешён ли eBPF обычным пользователям: значение 1 или 2 означает запрещён, и это правильно — доступ к трассировке даёт почти неограниченный контроль над системой, поэтому все примеры ниже запускаются от root.
Готовые трассировщики BCC
Начинать лучше с готовых скриптов: они отвечают на типовые вопросы без написания кода.
- execsnoop-bpfcc — показывает каждый запускаемый процесс с аргументами и родителем; находит cron-задачи и скрипты, о которых вы не помните;
- opensnoop-bpfcc — открываемые файлы в реальном времени; удобно искать, кто трогает конфиг;
- biolatency-bpfcc — гистограмма задержек дисковых операций: видно, что накопитель не справляется;
- tcpconnect-bpfcc — исходящие соединения; помогает найти неожиданные вызовы наружу;
- filetop-bpfcc — файлы, к которым идёт активное чтение и запись.
Практический пример: держим execsnoop открытым во время установки пакета и смотрим, что реально происходит под капотом.
sudo timeout 30 execsnoop-bpfcc
Для подсчёта вызовов функций ядра есть funccount-bpfcc и stackcount-bpfcc: funccount-bpfcc 'vfs_*' показывает, сколько раз в секунду вызываются функции виртуальной файловой системы, что помогает увидеть неожиданно частые обращения к диску.
bpftrace: однострочники
Когда готового скрипта нет, пишется короткая программа вида «событие, условие, действие». Сначала найдите интересующую точку:
sudo bpftrace -l 'kprobe:*vfs*'
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'
sudo bpftrace -e 'kretprobe:vfs_read /retval > 0/ { @bytes = sum(retval); }'
Первая команда перечисляет доступные точки трассировки. Вторая считает количество вызовов openat по именам процессов и по завершении печатает итоговую таблицу. Третая суммирует прочитанные байты — так за минуту видно, какой процесс съедает диск. По завершении bpftrace выводит все накопленные карты и завершается по сочетанию Ctrl+C; флаг -d показывает отладочный вывод, а -v — сообщения верификатора, если программа отвергнута. Ключевое преимущество: ни одно приложение не перезапускалось и не перекомпилировалось, а нагрузка от трассировки остаётся на уровне единиц процентов.
Профилирование процессорного времени
Для вопроса «куда уходит процессор» есть профайлер, собирающий стеки вызовов по сигналам таймера:
sudo profile-bpfcc -f 99 30 > profile.txt
sudo flamegraph.pl profile.txt > profile.svg
Файл profile.svg открывается в браузере: ширина прямоугольника пропорциональна доле времени, а горизонтальные блоки складываются в стек вызовов. Полоса, занимающая почти всю ширину, и есть виновник. Глубина стека читается сверху вниз: верхние прямоугольники — это непосредственный код приложения, нижние — служебные функции ядра, в которых процесс провёл большую часть времени. Для случая, когда сервис ждёт планировщик, а не диск, пригодится runqlat-bpfcc: пик в правом хвосте гистограммы означает, что ядер не хватает и очередь выросла.
Сценарий: разбираем медленный сервис
Соберём всё вместе. Сервис отвечает дольше обычного, перезапуск помогает ненадолго — нужен ответ не «почему медленно», а «где именно».
- Снимаем профиль процессорного времени за тридцать секунд:
profile-bpfcc -f 99 30 > profile.txt. - Открываем SVG и смотрим, в каком блоке стека уходит время: в коде приложения, в чтении файлов или в ожидании планировщика.
- Если подозрение на диск — запускаем biolatency-bpfcc для задержек и opensnoop-bpfcc, чтобы увидеть, какие файлы трогает процесс.
- Если подозрение на сеть — tcpconnect-bpfcc показывает, куда уходят соединения и как часто.
Повторять измерение на исправленной версии легко: те же команды, другие числа. Сравнение двух гистограмм даст объективный ответ, помогло ли изменение. Учтите, что профайлер с частотой 99 герц сам создаёт небольшую нагрузку, поэтому держать его включённым сутками не нужно.
Ограничения и типичные проблемы
- Не те заголовки ядра. Ошибка про отсутствие заголовков означает, что установлен пакет linux-headers не для текущего ядра; сверьте вывод uname -r со списком установленных заголовков.
- Верификатор отклоняет программу. Обычно виноваты неинициализированная переменная или слишком длинный цикл; упрощайте условие и включайте отладочный вывод утилиты.
- Теряются события. При очень высокой частоте кольцевой буфер переполняется, и утилита пишет о потерях; сузьте условие срабатывания или увеличьте размер буфера.
- Контейнеры. Без привилегированного режима и доступа к /sys/kernel/bpf инструменты не запускаются — это ожидаемое ограничение безопасности, а не поломка.
Итог
eBPF превращает диагностику Linux из искусства угадывания в измеримую процедуру: вы встраиваете наблюдение в работающую систему, собираете факты и снимаете нагрузку без перезапуска. Начните с готовых скриптов BCC, переходите к однострочникам bpftrace по мере необходимости и держите в голове границы инструмента: привилегии, требования к ядру и ограниченный буфер событий. Этого достаточно, чтобы закрыть большинство вопросов о поведении проблемного сервиса.