Инструменты eBPF на практике: BCC и bpftrace

Рейтинг: 70.2% · 15 голосов
Подробный курс по диагностике и производительности Linux для админов и DevOps: с чего начать, методология USE, логи (journalctl, dmesg), процессы, CPU, память и OOM, диск и IO (iostat), сеть (ss, tcpdump), системные вызовы и strace, ltrace, perf, флеймграфы, ftrace, eBPF и bpftrace, lsof, мониторинг. Разбор каждого инструмента с чтением вывода.
Ответить
Аватара пользователя
Dmitry_SRE
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Инструменты eBPF на практике: BCC и bpftrace

Сообщение Dmitry_SRE »

Оглавление курса (47)
  1. С чего начать диагностику Linux: методология вместо паники
  2. Первые 60 секунд: экспресс-диагностика нагруженного сервера
  3. Методологии диагностики: USE, RED и здравый смысл
  4. Логи systemd через journalctl: где искать причину
  5. Где лежат логи Linux: /var/log, dmesg и rsyslog
  6. Источники правды: load average, /proc и /sys
  7. Процессы Linux: ps, pstree и состояния процессов
  8. Интерактивный мониторинг: top и htop
  9. Метрики процессов во времени: pidstat
  10. Приоритеты и ограничения: nice, ionice, cgroups
  11. Сигналы и зависшие процессы: kill, и что делать с D-state
  12. Загрузка CPU: user, system, iowait, steal и контекст-свитчи
  13. Диагностика CPU по ядрам: vmstat и mpstat
  14. Профилирование CPU: perf top и поиск пожирателя
  15. Частоты, троттлинг и NUMA: turbostat и numastat
  16. Память Linux: RSS, VSZ, page cache и миф о нехватке памяти
  17. Сколько памяти занято: free, /proc/meminfo и vmstat
  18. Поиск утечек и пожирателей памяти: smem, pmap, smaps
  19. Swap и подкачка: swapon, swappiness, когда своп - это боль
  20. OOM killer: кто и за что убил процесс
  21. Память ядра и slab: slabtop и куда уходит RAM
  22. Подсистема ввода-вывода: путь запроса от приложения до диска
  23. Диагностика диска: iostat и чтение await, %util
  24. Кто грузит диск: iotop и атрибуция io процессам
  25. Файловые системы: df, du, иноды и куда делось место
  26. Глубокая диагностика блочного слоя: blktrace и biolatency
  27. Кеш страниц и грязные данные: dirty pages, fsync, drop_caches
  28. Сетевой стек Linux: путь пакета и где возникают задержки
  29. Сокеты и соединения: ss и netstat
  30. Захват трафика: tcpdump для диагностики сети
  31. Задержки и потери в сети: ping, mtr, диагностика латентности
  32. Сеть как источник проблем: nftables, conntrack, дропы
  33. Что такое системный вызов и зачем его трассировать
  34. strace: трассировка системных вызовов на практике
  35. strace в бою: почему программа висит, падает или тормозит
  36. ltrace: трассировка вызовов библиотек
  37. Накладные расходы трассировки и безопасные альтернативы
  38. perf: универсальный профайлер Linux
  39. Флеймграфы: визуализация профиля производительности
  40. Трассировка ядра: ftrace и trace-cmd
  41. eBPF: революция в наблюдаемости Linux
  42. Инструменты eBPF на практике: BCC и bpftrace (вы здесь)
  43. lsof: открытые файлы, дескрипторы, кто держит файл и порт
  44. Разбор кейса: приложение тормозит - пошаговая диагностика
  45. Непрерывный мониторинг: sar, node_exporter, Grafana, Zabbix
  46. Диагностика в контейнерах: cgroups, лимиты, Docker и Kubernetes
  47. Карта инструментов и сквозной разбор инцидента производительности
Зачем тебе готовые eBPF инструменты

Представь типичную боль. На сервере раз в минуту скачет нагрузка, top показывает чистый CPU, а что-то всё равно жрёт ресурсы. Или приложение тормозит на старте, а ты не понимаешь, какие файлы оно дёргает. Классический ответ - strace. Но strace тяжёлый: он перехватывает каждый системный вызов через ptrace и на каждом дважды перекидывает процесс из user space в ядро и обратно. На горячем коде это легко раздувает latency в 10-100 раз. На нагруженном сервисе запустить strace - почти всегда плохая идея, а strace -f на форк-бомбе вообще может его придушить.

И тут на сцену выходит eBPF. Если коротко: eBPF - это способ запускать маленькие безопасные программы прямо внутри ядра Linux, не патча его и не перезагружаясь. Перед загрузкой каждую такую программу проверяет верификатор ядра (никаких бесконечных циклов, никакого выхода за границы памяти), поэтому уронить ядро ей нельзя. Ядро само считает события (запуски процессов, открытия файлов, дисковые операции) и сразу агрегирует их - тебе наверх отдаётся уже готовая сводка или гистограмма, а не поток сырых строк. Накладные расходы крошечные, поэтому eBPF можно крутить на живом проде. Это и есть прод-безопасная замена strace.

Хорошая новость: тебе не надо писать eBPF-программы руками. Уже есть наборы готовых инструментов, которые решают типовые задачи буквально в одну команду - BCC tools и bpftrace. Их и разбираем. По состоянию на 2026 год это базовый инструментарий любого Linux perf/SRE-инженера, и оба набора давно в штатных репозиториях дистрибутивов.

Изображение

BCC tools: диагностика в одну команду

BCC (BPF Compiler Collection) - это пакет с десятками готовых утилит. В Debian/Ubuntu он ставится так:

Код: Выделить всё

sudo apt install bpfcc-tools linux-headers-$(uname -r)
В RHEL/Fedora/RED OS пакет называется bcc-tools, в Astra Linux - bpfcc-tools из репозитория. После установки утилиты лежат в /usr/share/bcc/tools/. Важная деталь: в Debian/Ubuntu у бинарников добавлен суффикс, то есть команда называется execsnoop-bpfcc, а в Fedora/RED OS - просто execsnoop. Я буду писать без суффикса, ты подставь свой вариант. Все они требуют root (или связки CAP_BPF + CAP_PERFMON, об этом ниже), поэтому sudo.

Что важно знать в 2026. Классические BCC-утилиты написаны на Python и компилируют eBPF-программу под твоё ядро прямо в момент запуска через LLVM/Clang - отсюда требование linux-headers и заметное потребление памяти (десятки-сотни МБ на запуск). Сейчас сообщество переходит на libbpf-tools: это те же execsnoop/opensnoop/biolatency, но переписанные на C с механизмом CO-RE (Compile Once - Run Everywhere) поверх BTF. Они компилируются один раз в маленький статический бинарник, не тянут на прод ни Python, ни Clang, ни заголовки ядра, стартуют мгновенно и едят килобайты вместо мегабайт. В Debian/Ubuntu это пакет libbpf-tools, в свежих RHEL/Fedora многие из них уже идут именно в таком виде. Требование одно: ядро должно быть собрано с BTF (CONFIG_DEBUG_INFO_BTF=y, по умолчанию во всех актуальных ядрах). Если видишь оба варианта - на проде предпочитай libbpf-tools. Поведение и колонки у них совпадают с тем, что разберём ниже.

Пройдёмся по самым полезным.

execsnoop - показывает каждый новый запуск процесса (перехватывает execve/execveat). Это ровно то, что не ловит top: короткоживущие процессы, которые родились и умерли между его опросами. Идеально для "кто-то форкает что-то из крона" или "что за процесс мелькает в нагрузке".

Код: Выделить всё

$ sudo execsnoop
PCOMM            PID    PPID   RET ARGS
sh               18923  1      0   /bin/sh -c /usr/bin/backup.sh
backup.sh        18924  18923  0   /usr/bin/backup.sh
gzip             18925  18924  0   /bin/gzip -9
Как читать. PCOMM - имя запущенной команды. PID - её номер процесса, PPID - номер родителя (кто её запустил). RET - код возврата вызова execve: 0 значит запуск удался, отрицательное число (например -2) - что-то пошло не так, файл не нашёлся или нет прав. ARGS - командная строка с аргументами (длинные строки обрезаются, полный аргумент-вектор можно вытащить флагом -a в libbpf-версии). Цепочку родителей читаешь по PPID: sh с PID 18923 запустил backup.sh (PID 18924, PPID 18923), а тот - gzip. Так за пару секунд распутывается, кто кого породил. Полезные флаги: -t добавляет временную метку, -U/-u фильтрует по пользователю, -n ловит только процессы, чьё имя совпадает с шаблоном.

opensnoop - кто какие файлы открывает (перехватывает open/openat). Незаменимо, когда надо понять, где приложение ищет конфиг или почему ломится не туда.

Код: Выделить всё

$ sudo opensnoop
PID    COMM       FD ERR PATH
1456   nginx      12   0 /etc/nginx/nginx.conf
1456   nginx      -1   2 /etc/nginx/sites/secret.conf
2201   php-fpm     8   0 /var/www/app/.env
FD - файловый дескриптор, который вернул open(). ERR - код ошибки: 0 - всё ок, ненулевое - ошибка. Здесь главный сигнал во второй строке: FD равен -1, а ERR равен 2 (это ENOENT, "нет такого файла"). То есть nginx пытался открыть secret.conf и не нашёл его. PATH - путь к файлу. Хочешь только промахи - фильтруй флагом -x (показывать лишь неуспешные open). Частые коды в колонке ERR: 2 - ENOENT (нет файла), 13 - EACCES (нет прав), 21 - EISDIR (это каталог). Связка opensnoop -x плюс grep по ERR быстро ловит классику "приложение молча игнорирует конфиг, потому что лезет не туда".

biolatency - задержки дисковых операций (block I/O) в виде гистограммы. Когда жалуются "диск тормозит", это первый инструмент. Запусти с интервалом и числом выводов - тут раз в 10 секунд, один вывод:

Код: Выделить всё

$ sudo biolatency 10 1
     usecs        : count     distribution
       128 -> 255 : 12       |***                                     |
       256 -> 511 : 145      |****************************************|
       512 -> 1023: 60       |****************                        |
      1024 -> 2047: 4        |*                                       |
Читается так. Первые две колонки - диапазон задержки одной операции (тут в микросекундах, usecs; для медленных дисков шкала будет в msecs - миллисекундах). count - сколько операций попало в этот диапазон. distribution - тот же count, нарисованный звёздочками, чтобы глазами увидеть форму. Что искать: если основная масса в районе сотен микросекунд - это нормальный NVMe/SSD. Если у тебя есть "хвост" в диапазоне 1024-2047 и выше, да ещё 8192+ usecs (8 мс) - это те самые редкие тормоза, которые портят latency сервису. Гистограмма по основанию 2 показывает и норму, и выбросы в одном экране. Полезные флаги: -m выводит в миллисекундах, -D разбивает по дискам, -Q учитывает время в очереди до диска (показывает перегрузку очереди, а не самого устройства), -F разбивает по флагам (чтение/запись отдельно). Рядом стоит держать biosnoop (каждая операция отдельной строкой с PID-инициатором) и biotop (top по дисковому I/O в разрезе процессов).

Ещё несколько утилит, которые стоит знать на 2026:
  • tcpconnect - исходящие TCP-соединения (кто куда коннектится), tcpconnlat - задержка установки соединения, tcplife - завершившиеся соединения с длительностью и переданными байтами (удобно ловить долгие или болтливые сессии), tcpretrans - повторные передачи (первый признак потерь в сети).
  • runqlat - гистограмма задержки планировщика (сколько поток ждал в очереди готовых, прежде чем получил CPU). Растёт - значит ядер не хватает или есть троттлинг. Рядом runqlen (длина очереди по CPU).
  • profile - семплит стеки по всем CPU на основе perf-событий, из его вывода строят флеймграф (тема отдельного урока).
  • cachestat - попадания/промахи page cache, cachetop - то же по процессам.
  • funccount, argdist, trace - универсальные конструкторы, когда готовой утилиты нет, но не хочется писать целый bpftrace-скрипт.
bpftrace: awk для ядра и его однострочники

BCC хорош, когда есть готовая утилита под твою задачу. А если нет? Тогда берёшь bpftrace - это маленький язык, на котором свой инструмент пишется одной строкой. По духу он как awk, только событиями для него служат не строки файла, а события ядра. Ставится пакетом bpftrace. На 2026 актуальная ветка - 0.23/0.24; нужно ядро хотя бы 4.9, а практически всё интересное живёт на 5.x и новее. Свежие версии умеют булевы литералы (true/false), range-based циклы for, встроенный ncpus, syscall_name() для перевода номера вызова в имя - но для разбора ниже хватит и базы.

Синтаксис простой: проба /фильтр/ { действие }.
  • Проба - что ловим. Например tracepoint:syscalls:sys_enter_openat - вход в системный вызов открытия файла. Сокращённо t:. Бывают пробы kprobe (любая функция ядра), uprobe (функция в пользовательской программе), tracepoint (стабильные точки ядра, лучший выбор), profile/interval (по таймеру).
  • Фильтр (необязательный) - условие в /слешах/, действие сработает только если оно истинно. Например /comm == "nginx"/.
  • Действие - что делать. Чаще всего складывать в @ - это карта (map), которая копит данные прямо в ядре, не выгружая каждое событие в user space.
В действиях доступны встроенные переменные: comm (имя процесса), pid, tid, uid, args (поля пробы), retval (что вернул вызов), nsecs (время в наносекундах), kstack/ustack (стек ядра/приложения), probe (имя текущей пробы). Главные функции-агрегаторы: count() - просто счётчик, hist() - гистограмма по основанию 2, lhist() - линейная гистограмма с заданным шагом.

Несколько bpftrace примеров, которые реально пригодятся.

Посчитать, какие процессы делают больше всего системных вызовов (готовый аналог - утилита syscount):

Код: Выделить всё

sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
Запустил, подождал, нажал Ctrl-C - и bpftrace печатает таблицу: имя процесса и сколько вызовов он сделал. Самый болтливый окажется внизу (карты сортируются по возрастанию значения).

Свой execsnoop в одну строку - кто запускает процессы:

Код: Выделить всё

sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%-16s -> %s\n", comm, str(args->filename)); }'
Здесь comm - кто запускает, str(args->filename) - что именно запускают (str превращает указатель ядра в строку). printf работает как в C.

Гистограмма размеров чтений - сколько байт за один read() утаскивают процессы:

Код: Выделить всё

sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret > 0/ { @bytes = hist(args->ret); }'
Фильтр /args->ret > 0/ отсекает ошибки и EOF (где ret <= 0). На выходе тот же формат гистограммы, что у biolatency: диапазоны и звёздочки. Так видно, читает приложение мелкими кусочками (плохо, лишние syscall-ы) или крупными.

Измерить задержку конкретного syscall - вход и выход связываем через временную метку в карте:

Код: Выделить всё

sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @t[tid] = nsecs; }
tracepoint:syscalls:sys_exit_openat /@t[tid]/ { @ns = hist(nsecs - @t[tid]); delete(@t[tid]); }'
Это шаблон, по которому строится половина измерений латентности: запомнил время на входе по ключу tid, на выходе вычел и удалил ключ, чтобы карта не текла.

Когда bpftrace и BCC бьют strace и perf? Когда нужна агрегация в ядре (миллион событий схлопывается в одну гистограмму, а не сыплется построчно), низкий overhead (можно на проде) и широкий охват (видно сразу все процессы, а не один прицепленный). strace по-прежнему хорош для глубокого ручного разбора одного процесса с расшифровкой структур аргументов в тестовой среде, а perf - для точного профилирования по событиям PMU. Но как массовый прод-инструмент наблюдаемости eBPF их обходит.

Грабли и заблуждения
  • "Команда не найдена". В Debian/Ubuntu имена с суффиксом -bpfcc (execsnoop-bpfcc), в Fedora/RED OS - без. Смотри список в /usr/share/bcc/tools/. Если есть libbpf-tools - там имена без суффикса и отдельный каталог.
  • Запуск без sudo. eBPF-трейсинг грузит программу в ядро: нужен root или связка CAP_BPF + CAP_PERFMON (обе появились в ядре 5.8). Без прав получишь ошибку загрузки, а не результат. В контейнере под Kubernetes для bcc/bpftrace часто требуется privileged или явная выдача этих capability плюс доступ к /sys/kernel/debug.
  • Нет заголовков ядра (только для Python-BCC). Классический BCC компилирует программу под твоё ядро на лету, поэтому нужен пакет linux-headers под текущий uname -r. Забыл - получишь длинную ошибку компиляции. У libbpf-tools этой проблемы нет вообще: им нужен только BTF в ядре.
  • Ждать вывод сразу. biolatency, runqlat, syscount, любые карты в bpftrace копят данные и печатают итог по Ctrl-C или по таймеру. Пустой экран первые секунды - это норма, а не зависание. execsnoop/opensnoop, наоборот, печатают построчно в реальном времени.
  • Старое ядро. На ядрах до 4.x eBPF-трейсинг практически не работает. По-настоящему он живёт с 4.9+, а большинство современных утилит и CO-RE - с 5.x. На 2026 это уже не проблема для актуальных дистрибутивов, но всплывает на legacy-серверах.
  • "eBPF видит всё в зашифрованном трафике/в TLS". Нет. tcplife и подобные видят метаданные соединений и объёмы, а не расшифрованный payload. Для прикладного содержимого нужны uprobe на функции библиотек.
  • Путаница probe-фильтр. Фильтр в /слешах/ ставится между пробой и блоком { }, а не внутри блока. Условие внутри блока пишется через if, но для отбора событий правильнее фильтр - он отсекает раньше и дешевле.
Мини-лаба (10 минут, делай руками)

Открой два терминала.
  • В первом запусти sudo execsnoop. Во втором выполни ls, потом sleep 1, потом date - и смотри, как они появляются в execsnoop с правильными PID и PPID. Найди свой шелл в колонке PPID.
  • Запусти sudo opensnoop -x и в другом терминале сделай cat /no/such/file - найди свою строку с ERR равным 2 (ENOENT).
  • Запусти sudo biolatency 5 1 и параллельно создай нагрузку: dd if=/dev/zero of=/tmp/test bs=1M count=200 oflag=direct - посмотри, как наполнилась гистограмма (после убери rm /tmp/test).
  • Финал: sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }', подожди 10 секунд, Ctrl-C - найди самый болтливый процесс (он внизу таблицы).
Контрольные вопросы
  • Почему execsnoop ловит процессы, которые не видит top?
  • В выводе opensnoop строка с FD равным -1 и ERR равным 2 - что это значит и куда смотреть дальше?
  • Из чего состоит однострочник bpftrace (три части) и зачем нужен символ @?
  • Почему biolatency и bpftrace безопаснее запускать на проде, чем strace? Назови хотя бы две причины.
  • Чем libbpf-tools лучше классических Python-BCC на проде и какое единственное требование к ядру у них есть?
Что запомнить

eBPF считает события внутри ядра и отдаёт готовую сводку - это дёшево и безопасно для прода, в отличие от strace на ptrace. BCC tools (пакет bpfcc-tools, каталог /usr/share/bcc/tools) - готовые утилиты на каждый день: execsnoop ловит короткоживущие процессы, opensnoop показывает обращения к файлам и их ошибки, biolatency и runqlat рисуют гистограммы задержек диска и планировщика, tcpconnect/tcplife/tcpretrans следят за сетью. В 2026 на прод тяни libbpf-tools (CO-RE поверх BTF) - они не требуют заголовков ядра и почти ничего не весят. Когда готового инструмента нет - bpftrace пишет его одной строкой по схеме проба-фильтр-действие, агрегируя данные через @ и count()/hist() прямо в ядре. И главное правило: не вываливай команду ради команды - читай колонки, ищи аномалии (ненулевой ERR, длинный хвост в гистограмме, болтливый процесс) и по ним решай, куда копать дальше.
👍3 ❤️2 🔥3 😄 🤔1
Аватара пользователя
meridian1
Сообщения: 1
Зарегистрирован: 14 май 2026, 22:34

Re: Инструменты eBPF на практике: BCC и bpftrace

Сообщение meridian1 »

Спасибо, наконец дошло зачем нужен biolatency. Запустил на бою во время тормозов - и реально увидел хвост в районе 8192 usecs, диск оказался виноват. Раньше бы полез strace и огрёб.
👍 ❤️ 🔥1 😄 🤔2
Аватара пользователя
rsanders
Сообщения: 1
Зарегистрирован: 31 май 2026, 00:40

Re: Инструменты eBPF на практике: BCC и bpftrace

Сообщение rsanders »

О, не знал про libbpf-tools. У нас в проде как раз бесило что Python-bcc тянет headers и жрёт память. Перешёл на libbpf-версию execsnoop - стартует мгновенно и headers ставить не надо, ядро с BTF и так есть.
👍2 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
eBPF: революция в наблюдаемости Linux
Следующая глава →
lsof: открытые файлы, дескрипторы, кто держит файл и порт

Все главы курса «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Поделиться темой: ✈ Telegram VK
Похожие запросы: ebpf и bpftrace с чего начать в linux

Вернуться в «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость