sobota, 19 czerwca 2010

Ringmap - wydajne przechwytywanie ruchu sieciowego na FreeBSD

Jak co roku Google uruchomiło program Summer of Code. GSoC to niezwykle wartościowa inicjatywa, katalizująca zmiany w różnych projektach open source, wykonywane rękoma pełnych zapału (i głodnych dolarów) studentów nadzorowanych przez doświadczonych mentorów. Program ten ma sens biznesowy dla Googla - pomijając kwestie wizerunkowe, polepszanie jakości ogólnodostępnego oprogramowania sprzyja tworzeniu przeciwwagi dla Microsoftu.

Jednym z wspieranych projektów jest oczywiście FreeBSD. Na liście wspartych w tym roku projektów możemy znaleźć m.in. Ringmap Capturing Stack for High Performance Packet Capturing. Projekt stara się zaadresować problemy wydajnościowe napotykane podczas przechwytywania (sniffing) ruchu sieciowego z użyciem klasycznego Berkeley Packet Filter, kojarzonego bardziej z narzędziem tcpdump (i innymi korzystającymi z biblioteki pcap).

Aplikacja korzysta z wywołań biblioteki pcap aby instrumentować jądro (BPF) do przechwytywania (w warstwie łącza danych) pakietów spełniających określone kryteria. Kryteria zadajemy wyrażeniem filtrującym, np.:

tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)

Fragmenty pakietów spełniających kryteria kopiowane są następnie z przestrzeni jądra do przestrzeni użytkownika, do aplikacji (np. sniffera wydobywającego hasła).

Ten model przetwarzana słabo skaluje się w świecie powszechnych w serwerach interfejsów sieciowych 1Gbit i coraz śmielej wkraczających 10Gbit. Szczególnie kosztowne jest kopiowanie danych (raz - wewnątrz jądra, drugi - do przestrzeni użytkownika), którego częściowo uniknięto wprowadzając we FreeBSD 8 mechanizm Zero-Copy BPF Buffers.

Ringmap ma na celu wprowadzenie prawdziwego zero-copy ;) poprzez mapowanie pamięci DMA karty sieciowej bezpośrednio do przestrzeni użytkownika. Wymaga to oczywiście szczególnego wsparcia po stronie sterownika karty sieciowej - projekt koncentruje się na 10Gbitowych Intelach (ixgbe).

Istnieją również inne pomysły na usprawnienia i rozszerzenia w podsystemie BPF: multiqueue BPF support and other BPF features. Jedno z rozszerzeń (wspomniane pod koniec prezentacji Zero-Copy BPF Buffers) to Direct-to-disk BPF - mechanizm bezpośredniego zapisu ruchu sieciowego na urządzenie dyskowe (zachodzący w całości w przestrzeni jądra). Z pewnością zafrapuje to niektórych subskrybentów POLIP-a, gdzie od czasu do czasu powraca zagadnienie logowania danych u ISP, jego zakresu i możliwości technicznych realizacji na szybkich łączach. ;) Jednym z ograniczeń jest z pewnością konieczność stosowania specjalizowanego podsystemu dyskowego - wysycone obustronnie łącze 10Gbit to przeszło 2.5GB danych na sekundę.


Poza BPF istnieją również inne, nieco mniej standardowe mechanizmy kopiowania ruchu sieciowego do przestrzeni użytkownika, zarówno we FreeBSD jak i w Linuksie. Ale o tym może... innym razem?


Zobacz także:

sobota, 12 czerwca 2010

rrdcached

Dawno, dawno temu, ciekawscy administratorzy wizualizowali sobie obciążenie różnorakich parametrów (np. wykorzystania łączy) przy pomocy MRTG - bo też i nie było nic lepszego. Z kolei co mniej dociekliwi administratorzy u operatorów telekomunikacyjnych odpalali agenta SNMP na ruterach szkieletowych z domyślnym community więc było co wizualizować. ;)

W starym MRTG dane gromadzone były w zwykłych plikach, pliki graficzne odświeżane były dla wszystkich przypadków - nawet jesli nikt danego wykresu nie oglądał. Nie było to dobre i słabo się skalowało. Aż w końcu Tobi zesłał nam RRDtool - cudowne narzędzie do gromadzenia, agregacji i wizualizacji serii danych. Zostało ono użyte nie tylko w MRTG, ale i powstało wiele lepszych narzędzi zauroczonych potęgą RRDtoola.

Specjalizowany backend plikowy jest bardzo wydajny w obsłudze, ale również napotyka problemy w skalowaniu. Wynikają one z faktu, że aktualizowanie dużej liczby serii danych przekłada się na dużą liczbę operacji na plikach:
  • otwarcie pliku
  • odczyt nagłówka
  • aktualizację kilku fragmentów pliku
  • zamknięcie pliku
Szczególną uwagę należy poświęcić odczytowi. Podsystem dyskowy jest zwykle tak skonstruowany, że stara się odczytywać dane z pewnym wyprzedzeniem. W większości przypadków taka strategia znakomicie się sprawdza, ale nie w przypadku plików RRD. Otwieranie grupy plików do stosunkowo małej liczby zapisów powoduje jednocześnie w tle potężną liczbę odczytów i wciąganie tych plików do pamięci podręcznej. Wystarczy popatrzeć na systemowe statystyki I/O. Pół biedy, jeśli się one w tej pamięci zmieszczą. Ale jeśli masz więcej niż kilkadziesiąt GB plików RRD, raczej się na typowych serwerach nie zmieszczą. ;)

Na niektórych systemach można ten problem rozwiązać - od wersji 1.3 RRDtool korzysta z funkcji posix_fadvise(), którą można poinformować system, że otwierając plik wcale nie chcemy wczytywać go z wyprzedzeniem (read ahead).

Można też wyłączyć czytanie z wyprzedzeniem na dyskowym urządzeniu blokowym, na którym leży system plików z RRD-kami. Pod Linuksem robi to komenda blockdev --setra 0 /dev/sda (zamiast sda wstaw swój dysk). Ale jeśli na tym samym urządzeniu dyskowym mamy innego rodzaju zasoby niż pliki RRD, to ustawienie może nas zaboleć. ;)

Innym ograniczeniem może być liczba jednoczesnych operacji aktualizacji i wynikający z niej wycisk dla głowic dysków. ;) Problem znika na dyskach SSD - pisałem poprzednio o tego typu instalacji w GG.

Począwszy od wersji 1.4 RRDtool dostarcza rrdcached - mechanizm asynchronicznej aktualizacji plików. Od tej pory możliwe jest natychmiastowe wykonywanie operacji update - zapis trafia do gniazda (lokalnego lub zdalnego) demona rrdcached, który wykonuje faktyczne zapisy większymi paczkami, zgodnie z przyjętą strategią, w zdefiniowanej liczbie wątków. Różnica w wydajności jest dramatyczna. Dane przed zapisem składowane są w pamięci lub w pliku kroniki.

Niestety, jest pewne ograniczenie - rrdcached nie wspiera aktualizacji z użyciem szablonu (template). Niektóre aplikacje oparte o RRD, jak np. Cacti, korzystają z szablonów.

Materiały dla dociekliwych:

piątek, 11 czerwca 2010

OSEC Barcamp - czerwiec 2010

Kilka dni temu odwiedziłem klub studencki "Dziekanat 161" na SGGW, gdzie odbywał się Barcamp - kameralna impreza o smaku open source, w cieniu której promuje się jej organizator czyli OSEC. ;)

Gwiazdą wieczoru był Michał Gruchała z GG Network S.A., opowiadający o zastosowaniach open source w zarządzaniu infrastrukturą dla znanego komunikatora i innych projektów tej spółki.

Jeśli mam być szczery, rozwiązania wypracowane przez Allegro lub naszą-klasę, prezentowane na różnych konferencjach np. PLNOG, wydają się ciekawsze.

Audytorium było wyraźnie zaskoczone:
  • brakiem redundancji geograficznej - tylko jedno datacenter (i tylko 300 fizycznych serwerów)
  • polityką aktualizacji systemów - zgodne z zasadą "działa, to nie ruszaj" (delikatna sugestia, że na serwerach backendowych pomija się nawet poprawki bezpieczeństwa), a jak nie działa - to zaoraj od nowa
  • brakiem uzasadnienia dla strategicznych wyborów oprogramowania np. wybór Xen-a jako rozwiązania do wirtualizacji podyktowany faktem, że "był w Debianie"
  • wyborem leciwego już Nagiosa jako rozwiąznia fault management
Moją uwagę zwrócił także rozmiar opartej o collectd instalacji performance management - aż 300 tys. plików RRD. Obciążenie podsystemu dyskowego było tak wielkie, że wykorzystano w tym miejscu dyski SSD. Postaram się w jednym z kolejnych wpisów poświęcić nieco uwagi dużym instalacjom RRDtoola.

W drugiej części spotkania Łukasz Podkalicki prezentował swój projekt fstorage - będące w początkowym stadium rozwoju rozproszone repozytorium plików i danych strumieniowych np. do zastosowań webowych, mające zaadresować słabości innych rozwiązań (autor często odnosił się do MogileFS).

Audytorium zwrociło uwagę, że autor skupiając się na deweloperce wyraźnie zaniedbał otoczenie projektu warunkujące o być albo nie być - stronę, dokumentację instalacyjną, forum użytkowników, narzędzie śledzenia błędów.


Całość okraszona była browarami i ciepłą strawą, a od czasu do czasu prelekcje przerywał barman z głośnym shakerem i zbłąkane studentki uciekające na widok sali geeków. ;)

poniedziałek, 24 maja 2010

rwące potoki

Każdy elementarz UNIX-a opisuje mechanizm potoków. Przypomnijmy, że potokiem łączymy 2 strumienie: standardowe wyjście stdout jednego programu ze standardowym wejściem stdin drugiego. Standardowe wyjście błędów stderr domyślnie nie trafia do potoku, co można zaobserwować w niektórych programach robiąc program --help | more i klnąc, że 3 strony pomocy przeleciały nam przez ekran niepowstrzymane. ;)

Istnieje kilka narzędzi, które można wykorzystać do zmierzenia i wizualizacji wolumenu danych płynących potokiem, a nawet do ograniczenia natężenia:
Są one często dostępne w standardowym repozytorium pakietów oprogramowania.

Przy ich pomocy można "zobaczyć", jak szybko przetwarzane są dane np. podczas obliczania sumy kontrolnej pliku. Można też zobaczyć na bieżąco, jak dane na wejściu kompresują się po przejściu przez archiwizer. Można wreszcie spowolnić przesyłanie danych, aby zmniejszyć wykorzystanie zasobów systemu (np. sieci, jeśli w potoku uczestniczy program sieciowy) lub... po prostu wyświetlać animację w ASCII. ;)

Przykłady? Dla testu wyślę do potoku dane pobrane dd z generatora liczb pseudolosowych urandom (o nieco niższej jakości, ale za to nieblokującego po wyczerpaniu entropii). Następnie spróbuję ograniczyć prędkość, z jaką pobieram pseudolosowe dane.


$ dd if=/dev/urandom bs=1M count=10 | pipebench > /dev/null
10+0 records in  9.37 MB    6.25 MB/second (Sun May 23 13:24:40 2010)
10+0 records out
10485760 bytes (10 MB) copied, 1,52052 s, 6,9 MB/s
Summary:
Piped   10.00 MB in 00h00m01.52s:    6.55 MB/second



$ dd if=/dev/urandom bs=512k count=1 | cpipe -vr -vt -vw -ngr -s 100 > /dev/null
in:  96.327ms at    1.3MB/s (   1.3MB/s avg)  128.0kB
out:   0.002ms at   61.0GB/s (  61.0GB/s avg)  128.0kB
thru: 1256.614ms at  101.9kB/s ( 101.9kB/s avg)  128.0kB
in:   0.266ms at  469.9MB/s (   2.6MB/s avg)  256.0kB
out:   0.002ms at   61.0GB/s (  61.0GB/s avg)  256.0kB
thru: 1257.576ms at  101.8kB/s ( 101.8kB/s avg)  256.0kB
in:   0.220ms at  568.2MB/s (   3.9MB/s avg)  384.0kB
out:   0.003ms at   40.7GB/s (  52.3GB/s avg)  384.0kB
thru: 1260.403ms at  101.6kB/s ( 101.7kB/s avg)  384.0kB
1+0 records in
1+0 records out
524288 bytes (524 kB) copied, 3,77087 s, 139 kB/s
in:   0.985ms at  126.9MB/s (   5.1MB/s avg)  512.0kB
out:   0.003ms at   40.7GB/s (  48.8GB/s avg)  512.0kB
thru: 1262.864ms at  101.4kB/s ( 101.6kB/s avg)  512.0kB
in:   0.005ms at       0B/s (   5.1MB/s avg)  512.0kB   (bsize=0)
out:   0.001ms at       0B/s (  44.4GB/s avg)  512.0kB
thru:   0.055ms at       0B/s ( 101.6kB/s avg)  512.0kB



$ dd if=/dev/urandom bs=1M count=1 | pv -L 100K > /dev/null
90kB 0:00:03 [ 101kB/s] [ <=> ]


Potoki mogą też być nazwane - widnieją wówczas w systemie plików jako plik typu FIFO i możemy do nich zapisywać dane jak do każdego innego pliku. Różnica jest taka, że zapis będzie blokujący tzn. będzie czekał, aż "z drugiej strony" do potoku nie podłączy się konsument.


$ mkfifo pipe1 pipe2
$ ls -las pipe*
0 prw-r--r-- 1 blog blog 0 maj 24 22:33 pipe1
0 prw-r--r-- 1 blog blog 0 maj 24 22:33 pipe2



$ echo "zapis do potoku w tle" > pipe1 &
[1] 2834
$ cat pipe1
zapis do potoku w tle
[1]+  Done                    echo "zapis do potoku w tle" > pipe1

$ cat pipe1 &
[1] 2836
$ echo "zapis do potoku czytanego w tle" > pipe1
zapis do potoku czytanego w tle
[1]+  Done                    cat pipe1


Tak uzbrojeni możemy sięgnąć po narzędzie do rozszczepiania potoku, czyli tee. tee przepuszcza swoje wejście na wyjście, ale robi kopię danych i wysyła do wskazanego pliku.


$ echo DANE TESTOWE | tee kopia-strumienia
DANE TESTOWE
$ cat kopia-strumienia
DANE TESTOWE


Przy pomocy tee można np. kompresować plik i jednocześnie liczyć sobie sumę kontrolną pliku wynikowego "w trakcie".

W szczególności plik podany tee może być potokiem nazwanym, czyli strumień trafi do 2 potoków jednocześnie.


$ echo DANE TESTOWE | tee pipe1 | sed s/DANE/POTOCZYSCIE/ &
[1] 2932
$ cat pipe1
POTOCZYSCIE TESTOWE
DANE TESTOWE
[1]+  Done                    echo DANE TESTOWE | tee pipe1 | sed s/DANE/POTOCZYSCIE/


Świetnie, a czy da się np. zamienić miejscami strumień stdout i stderr? Albo czy da się otworzyć więcej niż te dwa standardowe strumienie? Oczywiście. :)

Dla przypomnienia: proces ma listę otwartych deskryptorów. Standardowo mamy otwarte:
  • deskryptor 0, czyli stdin
  • deskryptor 1, czyli stdout
  • deskryptor 2, czyli stderr
Możemy otworzyć kolejne. W przypadku powłoki bash, robimy to np. następująco (do czytania):


$ echo DANE > myfile
$ cat <&3
DANE


No to zamieńmy miejscami stdout i stderr:


$ echo DANE | grep -v DANE
$ echo DANE 3>&1 1>&2 2>&3 | grep -v DANE
DANE


A jeśli naotwieramy więcej deskryptorów, to czy da się jakoś operować więcej niż na parze z nich? Z pomocą przychodzi narzędzie multitee.


$ echo DANE1>plik1
$ echo DANE2>plik2
$ echo DANE3>plik3
$ exec 3wyjscie1
$ exec 7>wyjscie2
$ exec 8>>wyjscie3
$ echo WEJSCIOWE | multitee 0:6,7,8 3:8 4:8 5:6
$ cat wyjscie1
DANE3
WEJSCIOWE
$ cat wyjscie2
WEJSCIOWE
$ cat wyjscie3
DANE2
DANE1
WEJSCIOWE



Na koniec pozostaje pozamykać deskryptory:


$ exec 3>&-
itd.

środa, 19 maja 2010

Wydano OpenBSD 4.7

Wydano dziś wersję 4.7 systemu OpenBSD.

Miło jest patrzeć, jak ten niszowy system - zwany żartobliwie małym, kolczastym zjadaczem sprzętowych ruterów ;) - z wersji na wersję zyskuje kolejne interesujące funkcjonalności w zakresie obsługi sieci. Warto także zwrócić uwagę na dołączone OpenSSH 5.5.

Wielkim atutem OpenBSD jest to, że przychodzi jako nieduży, silnie zintegrowany, dobrze udokumentowany, spójny pakiet oprogramowania, pozwalający postawić zaawansowany firewall, ruter i koncentrator VPN-ów IPsec dosłownie w kilka minut po instalacji. I to wszystko z redundancją na wielu poziomach: agregacją portów Ethernet, redundancją adresu IP (CARP), synchronizacją stanów firewalla i kanałów IPsec. Sam firewall jest również bardzo przyjemny w konfiguracji, składnia pf pozwala na zwięzłe wyrażanie złożonych polityk oraz klasyfikowanie pakietów także po danych pobranych z tablicy rutingu, np. numerach systemów autonomicznych BGP - albowiem obsługa m.in. tego protokołu rutingu jest również zaimplementowana.

Być może w jednym z kolejnych wpisów poświęcę OpenBSD nieco więcej uwagi.