eBPF для наблюдаемости: профилирование Linux без overhead

Что такое eBPF и почему он меняет наблюдаемость

eBPF (extended Berkeley Packet Filter) — технология ядра Linux, позволяющая запускать безопасный код в пространстве ядра без модификации ядра или загрузки модулей. Программы eBPF перехватывают системные вызовы, сетевые пакеты, дисковые операции, планировщик и другие события — при этом overhead составляет наносекунды на событие.

Для системного администратора eBPF открывает возможности, недоступные классическим инструментам:

  • strace замедляет приложение в 50-100 раз, eBPF-трассировка — менее 1%
  • tcpdump показывает только пакеты, eBPF связывает пакет с процессом и сокетом
  • perf требует специальных сборок, eBPF работает на любых бинарниках

В этом руководстве разберём практические инструменты на базе eBPF: BCC (BPF Compiler Collection) — набор готовых утилит, и bpftrace — скриптовый язык для создания собственных трассировщиков.

Установка BCC и bpftrace

Требования: ядро Linux 4.9+ (рекомендуется 5.8+), установленные заголовки ядра:

# Ubuntu 22.04
sudo apt install -y bpfcc-tools bpftrace linux-headers-$(uname -r)

# RHEL 9 / AlmaLinux 9
sudo dnf install -y bcc-tools bpftrace kernel-devel-$(uname -r)

# Проверяем
bpftrace --version
# Список доступных BCC-утилит
ls /usr/share/bcc/tools/

BCC содержит более 100 готовых утилит. Каждая решает конкретную задачу: execsnoop — отслеживание запуска процессов, opensnoop — открытие файлов, tcplife — TCP-соединения, biolatency — задержки дисковых операций.

Проверка поддержки eBPF

Убедитесь, что ядро поддерживает необходимые функции:

# Проверка конфигурации ядра
zcat /proc/config.gz 2>/dev/null | grep -i bpf || \
    grep -i bpf /boot/config-$(uname -r)

# Ожидаемые значения:
# CONFIG_BPF=y
# CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_JIT=y
# CONFIG_HAVE_EBPF_JIT=y

# Проверка через bpftool
sudo bpftool feature probe kernel

Профилирование CPU: кто потребляет процессор

Утилита profile из BCC семплирует стеки вызовов с заданной частотой и показывает, какие функции (ядра или приложения) потребляют CPU:

# Профилирование всех CPU в течение 10 секунд
sudo /usr/share/bcc/tools/profile -df 10 > profile.out

# Результат — flame graph data, визуализируем:
git clone https://github.com/brendangregg/FlameGraph
./FlameGraph/flamegraph.pl profile.out > profile.svg

Для быстрого анализа без flame graph:

# Топ функций ядра по потреблению CPU
sudo /usr/share/bcc/tools/profile -K 5

# Топ пользовательских функций
sudo /usr/share/bcc/tools/profile -U 5

# Профилирование конкретного процесса
sudo /usr/share/bcc/tools/profile -p $(pidof nginx) 10

Альтернатива через bpftrace:

# Семплирование стеков каждые 99 Гц в течение 5 секунд
sudo bpftrace -e 'profile:hz:99 { @[ustack] = count(); } interval:s:5 { exit(); }'

Трассировка дисковых операций

eBPF позволяет видеть каждую дисковую операцию с привязкой к процессу:

# Задержки блочных операций (гистограмма)
sudo /usr/share/bcc/tools/biolatency
# Вывод:
# usecs      : count    distribution
#   0 -> 1   : 15      |***                 |
#   2 -> 3   : 42      |*********           |
#   4 -> 7   : 183     |********************|
#   8 -> 15  : 95      |**********          |

# Каждая операция с деталями
sudo /usr/share/bcc/tools/biosnoop
# TIME(s)  COMM         PID    DISK    T  SECTOR    BYTES   LAT(ms)
# 0.000    postgres     1234   sda     R  123456    4096    0.35
# 0.001    nginx        5678   sda     R  789012    8192    0.12

# Топ процессов по дисковой нагрузке
sudo /usr/share/bcc/tools/biotop

Для диагностики медленных запросов PostgreSQL посмотрите, какие файлы читаются:

# Трассировка файловых операций PostgreSQL
sudo /usr/share/bcc/tools/fileslower -p $(pidof postgres) 1
# Показывает файловые операции дольше 1 мс

Анализ write amplification

bpftrace позволяет измерить, сколько байт приложение пишет через write() и сколько реально попадает на диск:

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_write {
    @app_writes = sum(args->count);
}
tracepoint:block:block_rq_issue /args->rwbs == "W"/ {
    @disk_writes = sum(args->bytes);
}
interval:s:10 {
    printf("App writes: %d MB, Disk writes: %d MB\n",
        @app_writes / 1048576, @disk_writes / 1048576);
    clear(@app_writes); clear(@disk_writes);
}'

Трассировка сети: TCP, DNS, HTTP

eBPF показывает сетевую активность с контекстом процессов:

# Все новые TCP-соединения
sudo /usr/share/bcc/tools/tcpconnect
# PID    COMM         IP SADDR          DADDR          DPORT
# 1234   curl         4  10.0.1.5       93.184.216.34  443
# 5678   postgres     4  10.0.1.5       10.0.1.20      5432

# TCP-соединения с временем жизни и переданными данными
sudo /usr/share/bcc/tools/tcplife
# PID   COMM       LADDR          LPORT RADDR          RPORT TX_KB RX_KB MS
# 1234  nginx      10.0.1.5       443   203.0.113.1    54321 15    2     350

# Ретрансмиты TCP (признак потерь в сети)
sudo /usr/share/bcc/tools/tcpretrans

# DNS-запросы с задержками
sudo bpftrace -e '
tracepoint:net:net_dev_xmit /args->len > 0/ {
    @bytes[comm] = sum(args->len);
}
interval:s:5 {
    print(@bytes);
    clear(@bytes);
}'

Трассировка HTTP-запросов через uprobe

eBPF может трассировать функции пользовательских приложений. Пример для OpenSSL (все HTTPS-соединения):

# Перехват SSL_write — видим данные до шифрования
sudo /usr/share/bcc/tools/sslsniff
# FUNC         TIME(s)  COMM       PID    LEN
# WRITE/SEND   0.000    curl       1234   87
# READ/RECV    0.001    curl       1234   1024

Для трассировки конкретного приложения (например, размеры запросов к Redis):

sudo bpftrace -e '
uprobe:/usr/bin/redis-server:processCommand {
    printf("%s: %s\n", comm, str(arg0));
}'

bpftrace: написание собственных трассировщиков

bpftrace — awk-подобный язык для создания eBPF-программ на лету. Синтаксис прост: probe /filter/ { action }.

# Гистограмма задержек системного вызова read()
sudo bpftrace -e '
tracepoint:syscalls:sys_enter_read { @start[tid] = nsecs; }
tracepoint:syscalls:sys_exit_read /@start[tid]/ {
    @latency = hist(nsecs - @start[tid]);
    delete(@start[tid]);
}'
# Частота системных вызовов по процессам
sudo bpftrace -e '
tracepoint:raw_syscalls:sys_enter {
    @[comm] = count();
}
interval:s:5 { print(@); clear(@); }'
# Мониторинг создания процессов
sudo bpftrace -e '
tracepoint:sched:sched_process_exec {
    printf("%-8d %-16s %s\n", pid, comm, str(args->filename));
}'

Доступные типы проб:

  • tracepoint:category:name — статические точки трассировки ядра
  • kprobe:function — динамическая трассировка функций ядра
  • uprobe:/path:function — трассировка функций приложений
  • profile:hz:99 — семплирование с частотой
  • interval:s:N — периодическое выполнение

Практические сценарии диагностики

Типичные задачи, где eBPF незаменим:

Кто удаляет файлы?

Часто нужно найти процесс, удаляющий логи или конфиги:

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_unlinkat {
    printf("%-8d %-16s %s\n", pid, comm, str(args->pathname));
}'

Утечка файловых дескрипторов

Подсчёт открытий и закрытий файлов по процессам:

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_openat { @open[comm] = count(); }
tracepoint:syscalls:sys_enter_close { @close[comm] = count(); }
interval:s:10 {
    printf("\n--- FD balance ---\n");
    print(@open); print(@close);
    clear(@open); clear(@close);
}'

Медленные DNS-резолвы

Трассировка вызовов getaddrinfo с замером времени:

sudo bpftrace -e '
uprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo {
    @start[tid] = nsecs;
    @name[tid] = str(arg0);
}
uretprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo /@start[tid]/ {
    $dur = (nsecs - @start[tid]) / 1000000;
    if ($dur > 10) {
        printf("SLOW DNS: %s %dms (pid=%d %s)\n",
            @name[tid], $dur, pid, comm);
    }
    delete(@start[tid]); delete(@name[tid]);
}'

Безопасность и ограничения eBPF

eBPF-программы проходят верификацию в ядре: проверяются на зацикливание, выход за границы массивов и другие небезопасные операции. Тем не менее, для запуска требуются привилегии root (или CAP_BPF + CAP_PERFMON в ядре 5.8+).

Ограничения:

  • Размер стека eBPF-программы — 512 байт
  • Максимальное число инструкций — 1 миллион (с ядра 5.2)
  • Некоторые функции ядра не трассируются (inline-функции)
  • На ядрах ниже 5.8 возможности значительно ограничены

Для продакшена рекомендуется: не оставляйте tracing-программы работать постоянно (даже с минимальным overhead — это overhead), используйте их для диагностики, а для постоянного мониторинга — экспортёры метрик (например, ebpf_exporter для Prometheus).

Часто задаваемые вопросы

Overhead eBPF минимален — наносекунды на событие. При трассировке высокочастотных событий (сетевые пакеты на 10 Гбит/с) overhead может достигать 1-3%, но для типичных задач (трассировка системных вызовов, профилирование) — менее 0.1%. Это на порядки меньше, чем strace или SystemTap.

Базовая поддержка eBPF — с ядра 4.9. Для полноценной работы bpftrace и BCC рекомендуется 5.4+. Ядро 5.8+ добавляет CO-RE (Compile Once, Run Everywhere) — программы работают на разных версиях ядра без перекомпиляции.

Да, это одно из главных применений. Инструменты Cilium (сеть), Falco (безопасность), Pixie (наблюдаемость) построены на eBPF. Они работают на уровне ноды и видят все контейнеры без агентов внутри подов.

perf и ftrace — отличные инструменты, но eBPF позволяет программируемую обработку: фильтрацию, агрегацию и вычисления прямо в ядре. Вместо передачи миллионов событий в userspace (как perf) eBPF передаёт только агрегированные результаты. BCC и bpftrace — это удобные обёртки над eBPF для повседневных задач.

Нужна помощь с настройкой?

Специалисты АйТи Фреш помогут с внедрением и настройкой — 15+ лет опыта, обслуживание от 15 000 ₽/мес

📞 Связаться с нами
#ebpf linux#bpftrace#bcc tools#профилирование linux#наблюдаемость linux#ebpf трассировка#linux performance#ebpf мониторинг
Комментарии 0

Оставить комментарий

загрузка...