В сегодняшнем мире, где всё тесно связано и ориентировано на данные, производительность сети имеет решающее значение для обеспечения эффективного взаимодействия и оптимального пользовательского опыта. XDP и eBPF — это мощные технологияи, которые помогают обрабатывать пакеты с высокой скоростью и оптимизировать работу сети. В этом пошаговом руководстве мы рассмотрим процесс создания XDP eBPF программы с использованием языков C и Golang.
XDP позволяет перехватывать пакеты на уровне драйвера сетевого интерфейса, а eBPF предоставляет гибкую и эффективную среду для выполнения пользовательской логики обработки пакетов. В совокупности эти технологии обеспечивают беспрецедентный уровень контроля и производительности в сетевых приложениях. Наш проект, названный «dilih» (drop it like it’s hot), демонстрирует создание простого инструмента хаос-инжиниринга, который произвольно отбрасывает пакеты на указанном сетевом интерфейсе, что может быть полезно разработчикам для понимания поведения их приложений при проблемах с сетью. С помощью этого руководства вы получите базовое представление о XDP, eBPF и их практическом применении для работы с сетью.
Примечание: весь исходный код данной статьи доступен на github.
Обзор проекта
Цель проекта — создать программу XDP на основе eBPF, написанную на C и Golang. Программа, получившая название «dilih», представляет собой простой инструмент хаос-инжиниринга, который случайным образом сбрасывает около 50% пакетов на заданном сетевом интерфейсе. Этот проект демонстрирует мощь и гибкость XDP и eBPF в управлении обработкой пакетов на высокой скорости, что делает его отличной отправной точкой для изучения этих технологий.
XDP eBPF программа, реализованная на C, подключается к сетевому стеку ядра Linux на раннем этапе, чтобы перехватывать пакеты и решать их дальнейшую судьбу. Используя простой механизм случайного отбора, программа избирательно сбрасывает пакеты, создавая управляемый хаос в сетевом трафике. Кроме того, программа применяет механизм событий perf eBPF для сбора статистики и измерения времени обработки как сброшенных, так и пропущенных пакетов.
Сопутствующее приложение на Golang взаимодействует с XDP eBPF программой, предоставляя удобный интерфейс для мониторинга поведения сброса пакетов и визуализации статистики производительности. Оно использует eBPF-карты для извлечения и агрегирования собранных данных из пространства ядра, что позволяет пользователям получить представление о влиянии сброшенных пакетов и эффективности обработки пакетов.
Настройка среды разработки
Чтобы приступить к созданию XDP eBPF программы на C и Golang, необходимо подготовить среду разработки. Следуйте этим шагам, чтобы установить все необходимые инструменты и зависимости:
-
Установка инструментов разработки
Для начала убедитесь, что на вашей системе установлены необходимые инструменты разработки, такие как clang, llvm и bpftool. Вы можете установить эти инструменты с помощью менеджера пакетов, доступного в вашей дистрибуции Linux. Однако я рекомендую выделить время на сборку этих инструментов из исходных кодов, так как это даст вам больший контроль над флагами и функциями, встроенными в инструменты.
Если вам интересно, как именно настроена моя среда LLVM/Clang, посмотрите следующие ansible-задачи:
-
https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/llvm.yaml
-
https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/debugtools.yaml
-
https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/docker.yaml
-
Установка Golang
Далее вам нужно установить Golang — язык программирования, на котором написано сопровождающее приложение. Посетите официальный сайт Golang по адресу https://golang.org и следуйте инструкциям по установке для вашей операционной системы. После установки убедитесь, что команда go доступна из командной строки, добавив соответствующий каталог с бинарными файлами в переменную PATH вашей системы.
Если вам интересно, как именно настроена моя среда для Golang, вы можете посмотреть следующую ansible-задачу.
-
Установка зависимостей проекта
Зависимости Go
Перейдите в корневую директорию проекта и установите необходимые зависимости для Golang, выполнив следующую команду:
go mod download
Эта команда скачает и установит необходимые пакеты Golang, указанные в файле go.mod проекта.
libbpf
В нашем коде на C мы будем использовать библиотеку libbpf. В репозитории dilih она добавлена как подмодуль Git, но вы можете управлять ею в другом месте, если пожелаете. При сборке нашей программы на C мы включим libbpf с помощью флага -I../libbpf/src
.
-
Настройка IDE (необязательно)
Независимо от того, какой редактор вы предпочитаете, стоит потратить время на его настройку для работы с C и Golang. Это особенно важно для автозаполнения, линтинга, обнаружения символов и других функций, которые сделают разработку удобнее.
Если вам интересно, как именно настроена моя среда, посмотрите этот репозиторий, где я устанавливаю neovim, настраиваю LSP и конфигурирую всё необходимое для разработки.
Написание программы XDP eBPF на C
Программа XDP реализована с использованием фреймворка eBPF на языке C и библиотеки libbpf. Это позволяет нам перехватывать пакеты на раннем этапе в сетевом стеке ядра Linux и выполнять пользовательскую логику обработки пакетов. В этом разделе мы пошагово рассмотрим процесс написания программы XDP eBPF на языке C.
-
Логика программы
Перед тем как перейти к коду, давайте разберёмся с логикой нашей XDP программы. Цель состоит в том, чтобы случайным образом сбрасывать около 50% пакетов на заданном сетевом интерфейсе. Мы будем использовать механизм рандомизации для определения, сбросить или пропустить каждый пакет («является ли случайное число чётным?»). Программа также будет собирать статистику и измерять время обработки сброшенных и пропущенных пакетов, используя механизм событий perf в eBPF. Наша программа BPF выполняется в пространстве ядра, но для передачи данных в пространство пользователя мы будем использовать BPF-карты, чтобы передавать данные в наше приложение на Go.
-
Создание исходного файла программы
Начните с создания нового файла с именем dilih_kern.c
в каталоге ./bpf/
вашего проекта. Этот файл будет содержать логику XDP eBPF программы. Откройте файл в своем любимом текстовом редакторе.
-
Определение необходимых заголовков и структур
Для начала подключите необходимые заголовочные файлы и определите нужные структуры для нашей XDP программы. Нам потребуются bpf.h
и bpf_helpers.h
, которые содержат полезные структуры и вспомогательные функции.
#include <linux/bpf.h> #include <bpf_helpers.h>
-
Определение структур данных и карт
Далее определите необходимые структуры данных и карты, которые будет использовать наша программа XDP. Мы создадим структуру для представления данных perf-события и карту BPF_MAP_TYPE_PERF_EVENT_ARRAY
для хранения этих событий. Определите следующие структуры и карты:
struct perf_trace_event { __u64 timestamp; __u32 processing_time_ns; __u8 type; }; #define TYPE_ENTER 1 #define TYPE_DROP 2 #define TYPE_PASS 3 struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(key_size, sizeof(int)); __uint(value_size, sizeof(struct perf_trace_event)); __uint(max_entries, 1024); } output_map SEC(".maps");
Карта output_map
будет использоваться для хранения perf-событий, которые генерирует наша программа XDP. Определения TYPE_*
помогут сделать код более читаемым в дальнейшем.
-
Реализация функции программы XDP
Теперь приступим к реализации функции самой программы XDP. Начнём с объявления функции XDP с соответствующей сигнатурой:
SEC("xdp") int xdp_dilih(struct xdp_md *ctx) { // Добавьте логику программы здесь ... подробно описано на следующем этапе }
Функция xdp_dilih
будет точкой входа для нашей XDP eBPF программы и вызываться для каждого входящего пакета.
-
Обработка perf-событий и сбор данных
Внутри функции xdp_dilih
мы можем обрабатывать perf-события для сбора данных и измерения времени обработки. Мы уже определили output_map
для хранения этих событий. Используйте вспомогательную функцию bpf_perf_event_output
, чтобы записывать perf-события в карту.
struct perf_trace_event e = {}; // Событие perf для входа в программу xdp e.timestamp = bpf_ktime_get_ns(); e.type = TYPE_ENTER; e.processing_time_ns = 0; bpf_perf_event_output(ctx, &output_map, BPF_F_CURRENT_CPU, &e, sizeof(e)); // Логика отбрасывания пакетов if (bpf_get_prandom_u32() % 2 == 0) { // Событие perf для отбрасывания пакета e.type = TYPE_DROP; __u64 ts = bpf_ktime_get_ns(); e.processing_time_ns = ts - e.timestamp; e.timestamp = ts; bpf_perf_event_output(ctx, &output_map, BPF_F_CURRENT_CPU, &e, sizeof(e)); return XDP_DROP; } // Событие perf для пропуска пакета e.type = TYPE_PASS; __u64 ts = bpf_ktime_get_ns(); e.processing_time_ns = ts - e.timestamp; e.timestamp = ts; bpf_perf_event_output(ctx, &output_map, BPF_F_CURRENT_CPU, &e, sizeof(e)); return XDP_PASS;
В этом участке кода мы обрабатываем perf-события для сбора данных и измерения времени обработки сброшенных и пропущенных пакетов. Мы сначала создаём perf-событие при входе в программу XDP (тип 1). Затем используем механизм рандомизации, чтобы решить, сбросить или пропустить пакет. Если пакет сбрасывается, создаём perf-событие с типом 2 и возвращаем XDP_DROP
. Если пакет пропускается, создаём perf-событие с типом 3 и возвращаем XDP_PASS
.
Функция bpf_ktime_get_ns()
используется для измерения временной метки (в наносекундах с момента загрузки системы, без учёта времени приостановки) и измерения времени обработки пакета. Функция bpf_get_prandom_u32()
генерирует случайное значение, которое помогает решить, сбросить или пропустить пакет — то самое «является ли случайное число чётным».
Дополнительно мы используем bpf_printk()
для вывода отладочных сообщений, которые можно просматривать в буфере трассировки ядра.
Таким образом, реализация XDP eBPF программы на C завершена. Эта программа будет избирательно сбрасывать пакеты на основе механизма рандомизации и генерировать perf-события для сбора данных и измерения времени обработки.
Компиляция и загрузка XDP eBPF программы
После написания программы на языке C следующий шаг — её компиляция и загрузка в ядро. В этом разделе мы разберём данный процесс.
Компиляция программы XDP
Для компиляции программы XDP мы будем использовать компилятор LLVM Clang с соответствующими флагами. Откройте терминал и перейдите в каталог bpf
, где находится файл dilih_kern.c
. Затем выполните следующую команду:
clang -S \ -g \ -target bpf \ -I../libbpf/src\ -Wall \ -Werror \ -O2 -emit-llvm -c -o dilih_kern.ll dilih_kern.c
Разберём флаги:
-
-S
: Создаёт файл с промежуточным представлением (IR) вместо объектного кода. Этот шаг используется для генерации кода LLVM IR. -
-g
: Включает информацию о типах BTF (BPF Type Format). -
-target bpf
: Указывает архитектуру цели как «bpf» (Berkeley Packet Filter), что означает, что код компилируется для выполнения на eBPF. -
-I../libbpf/src
: Добавляет путь../libbpf/src
к путям поиска include-файлов, позволяя компилятору найти необходимые заголовочные файлы (вспомогательные файлы bpf) из библиотекиlibbpf
. -
-Wall
: Включает все предупреждения компилятора. -
-Werror
: Обрабатывает все предупреждения как ошибки, из-за чего процесс компиляции прерывается при возникновении любых предупреждений. -
-O2
: Применяет второй уровень оптимизации, что повышает производительность без увеличения размера кода. Этот уровень оптимизации обязателен для некоторых случаев использования BPF. -
-emit-llvm
: Указывает компилятору выводить код LLVM IR. -
-c
: Компилирует входной исходный файл без линковки, создавая объектный файл. -
-o dilih_kern.ll
: Указывает имя выходного файла для сгенерированного кода LLVM IR какdilih_kern.ll
.
Теперь используем команду llc
, чтобы дополнительно обработать LLVM IR код и сгенерировать итоговый объектный файл:
llc -march=bpf -filetype=obj -O2 -o dilih_kern.o dilih_kern.ll
Разберём флаги:
-
-march=bpf
: Указывает архитектуру цели как «bpf» для стадии генерации кода. -
-filetype=obj
: Указывает требуемый тип выходного файла как объектный файл. -
-O2
: Применяет второй уровень оптимизации к сгенерированному коду на стадии генерации. -
-o dilih_kern.o
: Указывает имя выходного файла для сгенерированного объектного кода какdilih_kern.o
.
Эта команда компилирует файл dilih_kern.c
в объектный файл BPF с именем dilih_kern.o
. Флаг -target bpf
задаёт архитектуру цели как BPF, а флаг -O2
включает оптимизацию.
Загрузка программы XDP
Чтобы загрузить программу XDP в ядро, используем командную утилиту bpftool. Убедитесь, что утилита bpftool установлена на вашей системе. Если она ещё не установлена, то сделать это можно через менеджер пакетов вашего дистрибутива.
В терминале выполните следующую команду для загрузки программы XDP:
sudo bpftool prog load dilih_kern.o /sys/fs/bpf/dilih
Эта команда загружает объектный файл dilih_kern.o
и закрепляет его в директории /sys/fs/bpf/dilih
. При необходимости скорректируйте путь в зависимости от конфигурации вашей системы. Утилита bpftool выполнит процесс загрузки и проверит корректность программы.
Подключение программы XDP
После загрузки программы XDP нужно подключить её к сетевому интерфейсу для перехвата пакетов. Для этого выполните следующую команду:
sudo bpftool net attach xdp pinned /sys/fs/bpf/dilih dev <interface> # you can get <interface> by running `ip link'
Замените <interface_name>
на имя сетевого интерфейса, к которому хотите подключить программу XDP, например, eth0
. Эта команда подключает программу XDP к указанному интерфейсу, позволяя ей перехватывать входящие пакеты.
Makefile
Для удобства давайте добавим описанные выше команды в файл Makefile в директорию ./bpf/Makefile
. В этой статье мы не будем углубляться в принцип работы Makefile, но кратко опишем функциональность после примера кода:
TARGET = dilih BPF_TARGET = ${TARGET:=_kern} BPF_C = ${BPF_TARGET:=.c} BPF_OBJ = ${BPF_C:.c=.o} BPF_PINNED_PATH := /sys/fs/bpf/$(TARGET) XDP_NAME := dilih DEV := ens160 xdp: $(BPF_OBJ) -bpftool net detach xdpgeneric dev $(DEV) rm -f $(BPF_PINNED_PATH) bpftool prog load $(BPF_OBJ) $(BPF_PINNED_PATH) bpftool net attach xdpgeneric pinned $(BPF_PINNED_PATH) dev $(DEV) $(BPF_OBJ): %.o: %.c clang -S \ -g \ -target bpf \ -I../libbpf/src\ -Wall \ -Werror \ -O2 -emit-llvm -c -o ${@:.o=.ll} $< llc -march=bpf -filetype=obj -O2 -o $@ ${@:.o=.ll} clean: -bpftool net detach xdpgeneric dev $(DEV) sudo rm -f $(BPF_PINNED_PATH) rm -f $(BPF_OBJ) rm -f ${BPF_OBJ:.o=.ll}
С этим Makefile и установленным make
можно запустить команду DEV=eth0 make
, чтобы скомпилировать и загрузить eBPF-программу, а DEV=eth0 make clean
— чтобы удалить файлы и выгрузить eBPF-программу.
Поздравляем! Вы успешно скомпилировали и загрузили XDP eBPF программу в ядро и подключили её к сетевому интерфейсу. Программа теперь готова перехватывать и обрабатывать пакеты в соответствии с вашей логикой.
Учтите, что процесс компиляции и загрузки может различаться в зависимости от конфигурации системы и конкретных требований. При необходимости корректируйте команды и обращайтесь к документации используемых инструментов и утилит.
Написание приложения на Golang
В этом разделе мы создадим приложение на Golang, которое будет взаимодействовать с программой XDP eBPF и собирать метрики. Приложение будет считывать perf-события из загруженной программы XDP и отображать статистику на основе собранных данных.
Написание кода для приложения на Golang
Создайте новый файл с именем main.go
и откройте его в текстовом редакторе. Этот файл будет содержать код нашего приложения. Скопируйте и вставьте следующий код в main.go
:
package main import ( "encoding/binary" "fmt" "net" "os" "os/signal" "syscall" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/perf" ) const ( TYPE_ENTER = 1 TYPE_DROP = 2 TYPE_PASS = 3 ) type event struct { TimeSinceBoot uint64 ProcessingTime uint32 Type uint8 } const ringBufferSize = 128 // размер кольцевого буфера, используемого для расчета среднего времени обработки type ringBuffer struct { data [ringBufferSize]uint32 start int pointer int filled bool } func (rb *ringBuffer) add(val uint32) { if rb.pointer < ringBufferSize { rb.pointer++ } else { rb.filled = true rb.pointer= 1 } rb.data[rb.pointer-1] = val } func (rb *ringBuffer) avg() float32 { if rb.pointer == 0 { return 0 } sum := uint32(0) for _, val := range rb.data { sum += uint32(val) } if rb.filled { return float32(sum) / float32(ringBufferSize) } return float32(sum) / float32(rb.pointer) } func main() { spec, err := ebpf.LoadCollectionSpec("bpf/dilih_kern.o") if err != nil { panic(err) } coll, err := ebpf.NewCollection(spec) if err != nil { panic(fmt.Sprintf("Failed to create new collection: %v\n", err)) } defer coll.Close() prog := coll.Programs["xdp_dilih"] if prog == nil { panic("No program named 'xdp_dilih' found in collection") } iface := os.Getenv("INTERFACE") if iface == "" { panic("No interface specified. Please set the INTERFACE environment variable to the name of the interface to be use") } iface_idx, err := net.InterfaceByName(iface) if err != nil { panic(fmt.Sprintf("Failed to get interface %s: %v\n", iface, err)) } opts := link.XDPOptions{ Program: prog, Interface: iface_idx.Index, // Flags — одно из значений XDPAttachFlags (необязательно). } lnk, err := link.AttachXDP(opts) if err != nil { panic(err) } defer lnk.Close() fmt.Println("Successfully loaded and attached BPF program.") // обработка perf-событий outputMap, ok := coll.Maps["output_map"] if !ok { panic("No map named 'output_map' found in collection") } perfEvent, err := perf.NewReader(outputMap, 4096) if err != nil { panic(fmt.Sprintf("Failed to create perf event reader: %v\n", err)) } defer perfEvent.Close() buckets := map[uint8]uint32{ TYPE_ENTER: 0, // вход в программу BPF TYPE_DROP: 0, // программа BPF отбрасывает пакет TYPE_PASS: 0, // программа BPF пропускает пакет } processingTimePassed := &ringBuffer{} processingTimeDropped := &ringBuffer{} go func() { // переменная event типа event for { record, err := perfEvent.Read() if err != nil { fmt.Println(err) continue } var e event if len(record.RawSample) < 12 { fmt.Println("Invalid sample size") continue } // время с момента запуска системы в первых 8 байтах e.TimeSinceBoot = binary.LittleEndian.Uint64(record.RawSample[:8]) // время обработки в следующих 4 байтах e.ProcessingTime = binary.LittleEndian.Uint32(record.RawSample[8:12]) // тип в последнем байте e.Type = uint8(record.RawSample[12]) buckets[e.Type]++ if e.Type == TYPE_ENTER { continue } if e.Type == TYPE_DROP { processingTimeDropped.add(e.ProcessingTime) } else if e.Type == TYPE_PASS { processingTimePassed.add(e.ProcessingTime) } fmt.Print("\033[H\033[2J") fmt.Printf("total: %d. passed: %d. dropped: %d. passed processing time avg (ns): %f. dropped processing time avg (ns): %f\n", buckets[TYPE_ENTER], buckets[TYPE_PASS], buckets[TYPE_DROP], processingTimePassed.avg(), processingTimeDropped.avg()) } }() c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) <-c }
Вам может понадобиться выполнить go mod init && go mod tidy
, если вы еще этого не сделали.
Код настраивает необходимые компоненты для приложения на Golang: загружает программу BPF, подключает её к указанному сетевому интерфейсу и инициализирует reader для обработки событий perf. Однако код для чтения и обработки событий perf ещё не реализован.
На этом текущий раздел завершается. Содержимое можно изменять и настраивать в соответствии со своими требованиями.
Сборка и запуск проекта
Теперь, когда мы реализовали XDP eBPF программу на C и приложение на Golang, давайте соберём и запустим проект.
Сборка XDP eBPF программы
Этот шаг можно пропустить, если вы уже скомпилировали dilih_kern.o
, выполняя предыдущие инструкции.
Перед сборкой XDP eBPF программы убедитесь, что у вас установлены необходимые инструменты сборки и зависимости. Для получения информации о конкретных требованиях можно обратиться к README проекта или документации.
Для сборки программы перейдите в каталог bpf и выполните следующую команду:
make
Эта команда скомпилирует код на C и создаст объектный файл dilih_kern.o
.
Сборка приложения на Golang
Чтобы собрать приложение, убедитесь, что вы находитесь в корневой директории проекта. Затем выполните команду:
CGO_ENABLED=0 go build
Эта команда скомпилирует код на Golang и создаст исполняемый бинарный файл. Обратите внимание, что для этого приложения CGO не требуется. Хотя его можно оставить включённым, я предпочитаю использовать CGO_ENABLED=0
, так как это позволяет создать статически скомпилированный бинарный файл, который легко загружать в контейнеры.
Запуск проекта на Go
sudo ./dilih
Вы увидете сводку об обработанных пакетах на указанном интерфейсе:
Запускайте приложение с повышенными привилегиями (через sudo), чтобы получить доступ к необходимым ресурсам.
Приложение на Golang начнёт собирать данные из программы XDP и отображать статистику на основе полученных событий perf.
Чтобы очистить проект и удалить программу XDP с сетевого интерфейса, выполните следующую команду:
sudo make clean
Эта команда отключит программу XDP от сетевого интерфейса и удалит все связанные артефакты.
Готово! Вы успешно собрали и запустили проект. Попробуйте подключить разные сетевые интерфейсы и наблюдайте статистику сброса пакетов, отображаемую приложением.
Вы можете также исследовать дополнительные функции и модификации, чтобы улучшить проект.
Тестирование и проверка XDP eBPF программы
Тестирование и проверка функциональности программы — важный шаг для обеспечения её корректности и эффективности. В этом разделе мы рассмотрим некоторые методы её проверки.
Настройка тестовой среды
Для создания подходящей тестовой среды мы будем использовать виртуальные сетевые интерфейсы (veth-устройства) для имитации сетевого трафика и наблюдения за поведением программы XDP.
Установите пакет iproute2, если он ещё не установлен на вашей системе. Этот пакет предоставляет необходимые инструменты для управления сетевыми интерфейсами.
Создайте пару veth-устройств, используя следующие команды:
sudo ip link add veth0 type veth peer name veth1
Эта команда создаст два виртуальных сетевых интерфейса (veth0 и veth1), соединеных друг с другом.
Настройте интерфейсы и назначьте им IP-адреса:
sudo ip link set veth0 up sudo ip link set veth1 up sudo ip addr add 10.0.0.1/24 dev veth0 sudo ip addr add 10.0.0.2/24 dev veth1
Эти команды активируют интерфейсы и назначат им IP-адреса (10.0.0.1 и 10.0.0.2).
С настроенными устройствами veth мы можем приступить к тестированию и проверке функциональности XDP eBPF программы.
Проверка сброса пакетов
Одной из основных функций программы XDP является сброс определённого процента пакетов. Это можно проверить, отправляя пакеты между устройствами veth и наблюдая за процентом сброса.
Откройте два окна терминала и перейдите в каталог проекта в обоих окнах.
В первом терминале выполните следующую команду для прослушивания ICMP-запросов эхо (ping):
sudo tcpdump -i veth1 icmp
Во втором терминале отправьте ICMP-запросы эхо (ping) с veth0 на veth1, используя следующую команду:
sudo ip netns exec veth0 ping 10.0.0.2
Наблюдайте за выводом в первом терминале. Вы должны увидеть захваченные ICMPАнализируйте захваченные пакеты, чтобы проверить процент сброса. Если программа XDP работает корректно, около 50% ICMP-запросов эхо должно быть сброшено, что приведёт к уменьшению количества захваченных пакетов.
Тесты на проверку сброса пакетов позволяют убедиться, что программа XDP работает как ожидается и сбрасывает пакеты в соответствии с указанным процентом.
Анализ производительности
Помимо функциональной проверки, важно проанализировать влияние XDP eBPF программы на производительность. Этот анализ помогает оценить эффективность и накладные расходы, вводимые программой.
-
Используйте приложение на Golang для сбора метрик производительности и статистики из программы XDP. Обратитесь к разделу «Сборка и запуск проекта» для инструкций по запуску приложения на Golang.
-
Мониторьте и наблюдайте за средним временем обработки как для пропущенных, так и для сброшенных пакетов. Приложение на Golang отображает среднее время обработки в наносекундах (нс) для каждого типа пакетов.
Стабильно низкое среднее время обработки указывает на то, что программа XDP работает эффективно и вызывает минимальные накладные расходы на обработку.
Высокое среднее время обработки может указывать на то, что программа XDP вводит значительные накладные расходы на обработку, что может потребовать оптимизации или дополнительного исследования.
-
Собирайте данные и анализируйте метрики производительности в течение продолжительного периода сетевого трафика, чтобы выявить любые закономерности или тенденции. Обратите внимание на аномалии и отклонения во времени обработки, которые могут указывать на потенциальные узкие места или неэффективности.
-
Экспериментируйте с разными процентами сброса пакетов и наблюдайте их влияние на среднее время обработки. Изменяя процент сброса, вы сможете оценить баланс между потерей пакетов и эффективностью обработки.
-
Проведение анализа производительности позволяет получить представление о влиянии XDP eBPF программы на производительность сети и принять обоснованные решения относительно её оптимизации и настройки.
Интеграционное и системное тестирование
Для обеспечения корректной интеграции XDP eBPF программы в общую систему важно провести интеграционное и системное тестирование. Это включает тестирование взаимодействия программы XDP с сетевым стеком и другими компонентами системы.
Создайте тестовый сценарий, максимально приближенный к продакшен-среде, в которой будет работать программа XDP. Учитывайте такие факторы, как структура сетевого трафика, системная нагрузка и наличие других сетевых компонентов.
Сгенерируйте реалистичный сетевой трафик с помощью таких инструментов, как генераторы пакетов, симуляторы трафика или, если возможно, используйте реальный трафик из производственной среды.
Отслеживайте поведение системы, включая обработку пакетов, метрики производительности и использование системных ресурсов. Убедитесь, что программа XDP функционирует как ожидается и не вызывает негативных последствий для системы.
Проверьте крайние случаи и граничные условия, чтобы оценить устойчивость и надёжность программы XDP. Это включает сценарии с высоким объёмом сетевого трафика, необычными структурами пакетов и непредвиденными сетевыми событиями.
Проводя интеграционное и системное тестирование, вы сможете убедиться, что XDP eBPF программа интегрируется в общую систему без сбоев и работает надёжно в различных условиях.
Заключение
Приводим список полезных ресурсов для дальнейшего изучения XDP, eBPF и сетевого программирования:
-
Cilium: проект с открытым исходным кодом, предоставляющий сетевые и защитные возможности на основе eBPF. Их документация и исходный код дают глубокое представление о применении eBPF.
-
Iovisor: проект с открытым исходным кодом, который разрабатывает инструменты, библиотеки и инфраструктуру для трассировки, мониторинга и сетевой работы на основе eBPF. На сайте проекта можно найти обучающие материалы, документацию и примеры кода.
-
BCC (BPF Compiler Collection): набор мощных инструментов командной строки и библиотек, использующих eBPF для различных задач трассировки и анализа производительности. Репозиторий на GitHub предоставляет документацию и примеры для углублённого изучения eBPF.
-
eBPF.io: сайт сообщества, посвящённый ресурсам, руководствам и новостям о eBPF. Здесь вы найдёте статьи, примеры и подборку инструментов и библиотек, связанных с eBPF.
-
Документация ядра Linux — включает обширный раздел по eBPF и XDP, охватывающий различные аспекты, включая справочные API, примеры использования и детали реализации. Доступ к документации: www.kernel.org/doc/html/latest/bpf
Полный исходный код статьи можно найти на github.
Больше про языки программирования эксперты OTUS рассказывают в рамках практических онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке. Приходите также на бесплатные открытые уроки:
-
28 ноября: «Язык Cи и ООП: пошаговая разработка видеоплеера» — узнаем, как применить принципы ООП в языке С для создания сложных программ. Разберем практический пример разработки видеоплеера с использованием объектно-ориентированного подхода. Записаться
-
2 декабря: «Взаимодействие с базой данных и миграции на Go» — научимся создавать таблицы и настраивать структуру БД, разрабатывать БД для веб-сервера на Go, понимать процесс миграции БД и работать с запросами на уровне ОРМ и чистого SQL. Записаться
ссылка на оригинал статьи https://habr.com/ru/articles/860104/
Добавить комментарий