Powrót do strony głównej

eBPF NetFlow monitorowanie ruchu bare metal K8s

Artykuł opisuje implementację systemu zbierania statystyk NetFlow z eBPF/XDP dla bare metal, wirtualizacji i Kubernetes. Rozważane są wybór map BPF (PERCPU_HASH), shardowanie, próbkowanie z timerami. Przedstawione benchmarki i kod.

eBPF + NetFlow: telemetria od serwerów do Kubernetes
Advertisement 728x90

eBPF i NetFlow do monitorowania ruchu: od bare metal po Kubernetes

System oparty na eBPF/XDP oraz NetFlow zapewnia szczegółową telemetrię ruchu sieciowego na wszystkich poziomach – od fizycznych serwerów po klastry Kubernetes. Rozwiązuje ograniczenia tradycyjnych rozwiązań sFlow/NetFlow, takie jak wysokie obciążenie sprzętu, próbkowanie oraz brak możliwości dostosowywania pól. Ruch jest przetwarzany przez jednolity interfejs w trybie router-on-a-stick, co umożliwia XDP przechwytywanie pakietów przychodzących bez modyfikacji jądra.

eBPF uruchamia programy w jądrze systemu operacyjnego bezpiecznie i z minimalnym opóźnieniem, idealnie nadający się do monitoringu w czasie rzeczywistym. Statystyki są zbierane według 5-tuple (adresy IP, porty, protokół), bajtów i pakietów, a następnie eksportowane w formacie NetFlow do kolektorów.

Wybór typu map BPF dla wysokiej wydajności

Mapy BPF przechowują statystyki jako tabele klucz-wartość: klucz to 5-tuple, wartość to liczniki. Testy wykazały problemy z BPF_MAP_TYPE_HASH:

Google AdInline article slot
  • Konflikty blokad przy jednoczesnym dostępie wielowątkowym z różnych rdzeni.
  • Warunki wyścigu prowadzą do nieprecyzyjnych liczników.

Operacje atomowe (__sync_fetch_and_add) eliminują błędy, ale obniżają wydajność ze względu na synchronizację.

Przejście na BPF_MAP_TYPE_PERCPU_HASH tworzy osobną mapę na każdy rdzeń, eliminując blokady. Sumowanie odbywa się w przestrzeni użytkownika. Benchmark na 100 rdzeniach przy różnym obciążeniu potwierdził przewagę map PERCPU.

Przy przepełnieniu (600 tys. 5-tuple na mapę o pojemności 60 tys.) BPF_MAP_TYPE_LRU_PERCPU_HASH usuwa najstarsze wpisy, ale z kosztami dodatkowymi. Standardowe mapy PERCPU_HASH są lepsze dla szybkiego, jednorazowego odczytu.

Google AdInline article slot
// Określamy, do której mapy zapisywać
#define SHARD_COUNT 25 // liczba shardów
map_hash_idx = ctx->rx_queue_index % SHARD_COUNT + shard_offset;

Shardowanie map i eksport wielowątkowy

Jedna mapa o 10 milionach rekordów była ograniczona jednowątkowym eksportorem. Shardowanie na 25 map (według rx_queue_index poprzez RSS) umożliwiło równoległe odczytywanie, przyspieszając przetwarzanie trzykrotnie.

Zalety shardowania:

  • Skalowalność: dodawanie map bez restartu.
  • Paralelizm: czujnik może czytać wiele map jednocześnie.
  • Efektywność cache: małe mapy mieszczą się w L1/L2 cache.

Czujnik napisany w Go (biblioteki Cilium) używa BPF_MAP_LOOKUP_AND_DELETE_BATCH do odczytu i czyszczenia map co minutę. Minimalizuje to straty danych przy przepełnieniu.

Google AdInline article slot

Próbka i dynamiczna adaptacja obciążenia

Aby osiągnąć równowagę między przesyłaniem a zbieraniem statystyk, zaimplementowano próbkowanie. Dodatkowa stats_map (BPF_MAP_TYPE_HASH, max_entries=128) liczy pakiety po shardach:

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 128);  // Maksymalna liczba shardów
    __type(key, __u32);        // Numer shardu
    __type(value, __u64);      // Liczniki pakietów/bajtów
    __uint(pinning, LIBBPF_PIN_BY_NAME);
} stats_map SEC(".maps");

Timer eBPF okresowo oblicza prędkość (pakiety/s) i aktywuje próbkowanie, gdy próg zostanie przekroczony w shardzie. Timery wykonują zadania w tle, poza kontekstem pakietów: agregację, reset liczników.

To zapewnia stabilność podczas szczytów (DDoS), zachowując telemetrię sesji.

Dostosowanie do wirtualizacji i Kubernetes

Schemat skaluje się z bare metal na wirtualizację i K8s. W klastrach eBPF/XDP wdraża się jako DaemonSet, przechwytując ruch pods poprzez CNI. Shardowanie dostosowuje się do node affinity, a PERCPU_HASH do vCPU.

Eksport NetFlow agreguje metadane (namespace, IP poda), zwiększając widoczność mikroserwisów.

Co ważne:

  • PERCPU_HASH eliminuje konflikty blokad, zwiększając przepustowość o 2–3x w porównaniu do HASH.
  • Shardowanie przyspiesza eksport trzykrotnie dzięki paralelizmowi.
  • Timer eBPF + próbkowanie balansują obciążenie bez utraty przesyłania.
  • Wersja LRU do dynamicznych scenariuszy z przepełnieniem.
  • Czujnik w Go z bibliotekami Cilium jest optymalny dla intensywnego parsowania.

— Editorial Team

Advertisement 728x90

Czytaj dalej