NFsec Logo

Jak znaleźć ducha w linuksowej skorupie? – eBPF

07/09/2017 w Administracja Brak komentarzy.  (artykuł nr 637, ilość słów: 4070)

A

naliza wydajności często jest ograniczona przez brak widoczności różnych zjawisk zachodzących w systemie. Obserwacja tych zjawisk pozwala inżynierom systemów łatwo identyfikować elementy, które zawierają ograniczenia i mogą być szybsze. “Najnowszym” narzędziem do obserwacji systemu operacyjnego Linux jest BPF (ang. Berkeley Packet Filter). Został on opracowany w 1992 roku, aby zapewnić sposób filtrowania pakietów sieciowych oraz uniknięcia ich bezużytecznego kopiowania z przestrzeni jądra do użytkownika. Początkowo składał się z prostego kodu bajtowego, który był wstrzykiwany z przestrzeni użytkownika do jądra. Tam był sprawdzany przez weryfikator – w celu uniknięcia krachu jądra lub problemów z bezpieczeństwem – i dołączany do gniazda, a następnie uruchamiany na każdym odebranym pakiecie.

Kilka lat później (1997 r.) mechanizm ten został przeniesiony do systemu Linux (v2.1.75) i używany przez niewielką liczbę aplikacji, jak np. tcpdump. Prostota języka oraz dodanie kompilatora Just-In-Time (JIT) przez Erica Dumazeta (2011 r.) w jądrze dla BPF przyczyniły się do doskonałych osiągów tego narzędzia. W 2013 r. Alexei Starovoitov przedstawił propozycję przekształconego i wyposażonego w nowe funkcje BPF. Nowa wersja została zarejestrowana oficjalnie w jądrze v3.15 jako eBPF (rozszerzony BPF – ang. extended BPF), podczas gdy poprzednia wersja stała się cBPF (klasycznym BPF – ang. classic BPF). Od tego czasu przepisano JIT oraz dodano nowe punkty zaczepienia w jądrze (Tracepoint / Kprobe / USDT (ang. Userland Statically Defined Tracepoints)). Dzięki nowym zaczepom programy eBPF mogą być projektowane dla różnych zastosowań, których działanie możemy podzielić na dwa obszary. Pierwszym z obszarów jest sfera śledzenia jądra przez monitorowanie i mierzenie opóźnień zdarzeń systemowych. Drugi obszar natomiast pozostaje w dziedzinie programowania sieciowego, gdzie programy eBPF mogą być podłączane do socketów (v3.19), czy interfejsów wejściowych i wyjściowych tc (ang. traffic controlv4.1) wykonując wiele zadań związanych z przetwarzaniem pakietów. Mogą zostać także wykorzystane w służbie: programowalnych sieci komputerowych, łagodzenia ataków DDoS (wczesne zrzucanie pakietów), poprawy wydajności sieci (eXpress Data Path), systemów wykrywania intruzów (IDS), bezpieczeństwa kontenerów i wielu innych.

Ingo Molnár opisał eBPF jako:

One of the more interesting features in this cycle is the ability to attach eBPF programs (user-defined, sandboxed bytecode executed by the kernel) to kprobes. This allows user-defined instrumentation on a live kernel image that can never crash, hang or interfere with the kernel negatively.

Jak już wspomniałem wcześniej kod BPF jest kompilowany do kodu bajtowego i wysyłany do jądra, gdzie weryfikator decyduje o jego przyjęciu bądź odrzuceniu. Jeśli zostanie zaakceptowany może zostać podpięty do różnych źródeł zdarzeń:

Po odczycie z źródła program BPF ma dwie drogi, aby przekazać pobrane dane z powrotem do przestrzeni użytkownika: albo szczegółowo per zdarzenie albo przez mapy. Te drugie mogą implementować tablice, tablice asocjacyjne lub histogramy, które są odpowiednimi obiektami do przekazywania podsumowujących statystyk (obrazek #1 – wewnętrzny mechanizm eBPF – źródło).

BCC:

BPF Compiler Collection jest zestawem narzędzi do tworzenia wydajnych programów służących do śledzenia i manipulowania zdarzeniami jądra systemu. Posiada także interfejsy w języku Python i LUA do ich developmentu. Zestaw sam w sobie zawiera już dość sporą kolekcję narzędzi wraz z przykładami ich zastosowania. W celu ich wykorzystania wymagane jest jądro w wersji 4.1 lub wyżej (obrazek #2 – wsparcie eBPF w zależności od wersji jądra – źródło). Przykład instalacji BCC oprzemy o Ubuntu 16.04 ze względu na możliwość instalacji jądra w wersji 4.10:

echo "deb [trusted=yes] https://repo.iovisor.org/apt/xenial xenial-nightly main" | \
tee /etc/apt/sources.list.d/iovisor.list
apt-get update
apt-get install linux-image-4.10.0-32-generic linux-headers-4.10.0-32-generic 
apt-get install bcc-tools libbcc-examples
shutdown -r now

Po restarcie systemu narzędzia bcc znajdziemy w katalogu /usr/share/bcc/tools. Jeśli do dyspozycji mamy starsze wersje jądra (pomiędzy 4.1, a 4.8), a interesujące nas narzędzie nie do końca działa zgodnie z naszymi oczekiwaniami to powinniśmy zainteresować się wersjami dostępnymi w katalogu /usr/share/bcc/tools/old, które zawierają starsze z różnymi obejściami. Poniżej znajduje się demonstracja działania kilku programów bcc – zarówno tych z serii śledzenia aktywności systemowej, jak i aktywności sieciowej:

1) bashreadline – wyświetla wszystkie polecenia wydane w powłoce bash poprzez śledzenie funkcji readline() za pomocą uprobes.

root@darkstar:/usr/share/bcc/tools# ./bashreadline
TIME      PID    COMMAND
22:30:11  1833   ps x
22:30:21  1833   uname -a
22:36:38  1833   uptime

2) execsnoop – śledzi nowe procesy poprzez wywołanie exec() wyświetlając proces rodzica i inne szczegóły. Poniżej znajduje się lista procesów odpalanych przy okazji polecenia: sudo su –:

root@darkstar:/usr/share/bcc/tools# ./execsnoop
PCOMM            PID    PPID   RET ARGS
sudo             2463   2450     0 /usr/bin/sudo su -
su               2464   2463     0 /bin/su -
bash             2465   2464     0 /bin/bash
groups           2467   2466     0 /usr/bin/groups
ls               2469   2468     0 /bin/ls /etc/bash_completion.d
lesspipe         2471   2470     0 /usr/bin/lesspipe
basename         2472   2471     0 /usr/bin/basename /usr/bin/lesspipe
dirname          2474   2473     0 /usr/bin/dirname /usr/bin/lesspipe
dircolors        2476   2475     0 /usr/bin/dircolors -b
mesg             2477   2465     0 /usr/bin/mesg n

3) opensnoop – program bardzo podobny do powyższego z tą różnicą, że śledzi wywołanie open(). Poniżej znajduje się lista otwieranych plików przy okazji polecenia: ping:

root@darkstar:/usr/share/bcc/tools# ./opensnoop
PID    COMM               FD ERR PATH
2716   ping                3   0 /etc/ld.so.cache
2716   ping                3   0 /lib/x86_64-linux-gnu/libcap.so.2
2716   ping                3   0 /lib/x86_64-linux-gnu/libc.so.6
2716   ping                4   0 /etc/resolv.conf
2716   ping                4   0 /etc/resolv.conf
2716   ping                4   0 /etc/nsswitch.conf
2716   ping                4   0 /etc/ld.so.cache
2716   ping                4   0 /lib/x86_64-linux-gnu/libnss_files.so.2
2716   ping                4   0 /etc/host.conf
2716   ping                4   0 /etc/hosts
2716   ping                4   0 /etc/ld.so.cache
2716   ping                4   0 /lib/x86_64-linux-gnu/libnss_mdns4_minimal.so.2
2716   ping                4   0 /etc/ld.so.cache
2716   ping                4   0 /lib/x86_64-linux-gnu/libnss_dns.so.2
2716   ping                4   0 /lib/x86_64-linux-gnu/libresolv.so.2
2716   ping                4   0 /etc/hosts

4) ext4slower – śledzi operacje systemu plików ext4 (istnieją wersje dla: brtfs, xfs oraz zfs), które zajmują więcej niż X milisekund. Poniżej znajduje się lista operacji, które zajęła więcej niż 10 milisekund poleceniu: apt-get remove snapd:

root@darkstar:/usr/share/bcc/tools# ./ext4slower 10
Tracing ext4 operations slower than 10 ms
TIME     COMM           PID    T BYTES   OFF_KB   LAT(ms) FILENAME
21:53:48 mandb          8234   S 0       0          12.14 8234
21:53:48 dpkg           8523   S 0       0          10.82 status-new
21:53:48 apt-get        8019   R 47095092 0          13.47 srcpkgcache.bin
21:53:48 apt-get        8019   W 47095191 0          20.12 pkgcache.bin.1xKxwV

5) cachestat – co sekundę pokazuje statystyki odnośnie trafień i chybień w page cache. Poniżej znajdują się statystyki pokazujące ciągłe odpytywanie o zawartość katalogu /etc/ poleceniem: ls -al /etc:

root@darkstar:/usr/share/bcc/tools# ./cachestat
    HITS   MISSES  DIRTIES  READ_HIT% WRITE_HIT%   BUFFERS_MB  CACHED_MB
    2585        4        4      99.7%       0.1%           84        441
    3244       12       10      99.3%       0.1%           84        441
    3228        0        0     100.0%       0.0%           84        441
    6391        0        0     100.0%       0.0%           84        441
    1083        0        0     100.0%       0.0%           84        441

6) biosnoop – śledzi operacje wejścia/wyjścia urządzeń blokowych pokazując informacje o procesie, dysku oraz opóźnieniach.

root@darkstar:/usr/share/bcc/tools# ./biosnoop
TIME(s)        COMM           PID    DISK    T  SECTOR    BYTES   LAT(ms)
14.995055000   updatedb       8603   sda     R  4327128   4096       0.34
14.999967000   updatedb       8603   sda     W  9017344   688128     3.43
17.441281000   jbd2/sda1-8    332    sda     W  4642296   40960      0.46
17.443919000   jbd2/sda1-8    332    sda     W  4642376   4096       0.07
42.060127000   bash           8584   sda     R  1371008   16384      1.16
42.061292000   rmdir          8584   sda     R  1371040   24576      0.43
46.657396000   mc             8586   sda     W  627880    4096       0.11

7) biotop – śledzi operacje wejścia/wyjścia z danego czasu i sporządza listę programów wraz z podsumowaniem ich aktywności:

root@darkstar:/usr/share/bcc/tools# ./biotop 60 1
Tracing... Output every 60 secs. Hit Ctrl-C to end
21:03:08 loadavg: 0.15 0.06 0.02 1/186 4759

PID    COMM             D MAJ MIN DISK       I/O  Kbytes  AVGms
1235   kworker/u2:2     W 8   0   sda        193   46948   4.02
4698   updatedb         R 8   0   sda       7033   30604   0.30
333    jbd2/sda1-8      W 8   0   sda        756   14848   0.10
4698   updatedb         W 8   0   sda          5    2552   4.74
4282   dpkg             W 8   0   sda        161    2416   0.17
3785   dpkg             W 8   0   sda         73    1516   0.11
3825   mandb            W 8   0   sda         20     688   0.25
4670   dpkg             W 8   0   sda          1     548   0.83
4698   bash             R 8   0   sda          1      16   1.33
3769   apt-get          W 8   0   sda          1      12   0.14
4494   insserv          W 8   0   sda          3      12   0.16
4731   mc               W 8   0   sda          1      12   0.12

8) gethostlatency – pokazuje opóźnienie dla wywołań funkcji getaddrinfo/gethostbyname w całym systemie:

root@darkstar:/usr/share/bcc/tools# ./gethostlatency
TIME      PID    COMM                  LATms HOST
22:41:38  8642   curl                  43.37 nfsec.pl
22:41:52  8643   hostname               0.13 darkstar
22:42:01  8644   ping                  16.77 wp.pl
22:43:15  8645   ssh                   75.02 darkstar.example.com

9) tcplife – śledzi połączenia TCP dla zdalnych i lokalnych portów oraz konkretnych procesów PID:

root@darkstar:/usr/share/bcc/tools# ./tcplife -tD 80
TIME(s)   PID   COMM       LADDR           LPORT RADDR           RPORT TX_KB RX_KB MS
0.000000  9000  links      10.0.2.15       52608 213.180.141.140 80        0     0 5136.69
11.983495 9000  links      10.0.2.15       48868 213.180.141.188 80        0     9 12505.75
11.983542 9000  links      10.0.2.15       34960 213.180.141.148 80        0     2 12530.80
11.984304 9000  links      10.0.2.15       41138 216.58.209.68   80        0     0 12531.60
11.984767 9000  links      10.0.2.15       52988 213.180.141.132 80        0     0 12507.60
11.985196 9000  links      10.0.2.15       47092 213.180.141.178 80        0     0 12530.88
11.985682 9000  links      10.0.2.15       35604 213.180.141.156 80        0    68 12580.80
11.986178 9000  links      10.0.2.15       59688 213.180.141.150 80        0     0 12531.68
11.986532 9000  links      10.0.2.15       57260 213.180.141.128 80        0     0 12533.65
31.357647 9018  http       10.0.2.15       40066 91.189.91.26    80        0  1011 1027.56

10) tcpaccept – śledzi aktywne połączenia TCP dzięki monitorowaniu connect() (o tcpretrans możemy przeczytać tutaj):

root@darkstar:/usr/share/bcc/tools# ./tcpconnect -t
TIME(s)  PID    COMM         IP SADDR            DADDR            DPORT
0.000    2518   telnet       4  127.0.0.1        127.0.0.1        22
19.902   2520   links        4  192.168.111.2    192.168.111.2    80
49.121   2539   wget         4  10.0.2.15        37.187.104.217   80
49.222   2539   wget         4  10.0.2.15        37.187.104.217   443

i wiele innych (obrazek #3 – inne skrypty eBPF – źródło). Jednak moim faworytem jest ttysnoop, który potrafi przechwytywać informacje wyjściowe z danego terminala (czyli administrator może w czasie rzeczywistym zobaczyć, co się dzieje na dowolnej konsoli użytkownika):

root@darkstar:/usr/share/bcc/tools# ./ttysnoop -h
usage: ttysnoop [-h] [-C] device

Snoop output from a pts or tty device, eg, a shell

positional arguments:
  device         path to a tty device (eg, /dev/tty0) or pts number

optional arguments:
  -h, --help     show this help message and exit
  -C, --noclear  don't clear the screen

examples:
    ./ttysnoop /dev/pts/2    # snoop output from /dev/pts/2
    ./ttysnoop 2             # snoop output from /dev/pts/2 (shortcut)
    ./ttysnoop /dev/console  # snoop output from the system console
    ./ttysnoop /dev/tty0     # snoop output from /dev/tty0

Co w przypadku, gdy chcemy prześledzić dowolny proces? Możemy wykonać to za pomocą trace. Dla przykładu podepnijmy się do procesu apache2 i sprawdźmy, jakie pliki otwiera funkcja do_sys_open():

root@darkstar:/usr/share/bcc/tools# pidof apache2
1052 1051 1048
root@darkstar:/usr/share/bcc/tools# ./trace -p 1052 'do_sys_open "%s", arg2'
PID    TID    COMM         FUNC             -
1052   1069   apache2      do_sys_open      /var/www/html/index.html
1052   1069   apache2      do_sys_open      /etc/localtime

Możemy również wyświetlić listę tracepointów i sond USDT danego procesu:

root@darkstar:/usr/share/bcc/tools# ./tplist -p 1052
/lib/x86_64-linux-gnu/libpthread-2.23.so libpthread:pthread_start
/lib/x86_64-linux-gnu/libpthread-2.23.so libpthread:pthread_create
/lib/x86_64-linux-gnu/libpthread-2.23.so libpthread:pthread_join
/lib/x86_64-linux-gnu/libpthread-2.23.so libpthread:pthread_join_ret

Dla lepszego ich wsparcia powinniśmy skompilować wybrany język lub serwer z odpowiednimi parametrami np.:

- MySQL / PostgreSQL : --enable-dtrace
- JVM                : -XX:+ExtendedDTraceProbes (przy uruchomieniu)
- NodeJS             : --with-dtrace
- Python             : --with-dtrace
- Ruby               : --enable-dtrace

Jeśli jesteśmy zainteresowani rozwijaniem własnych narzędzi ebpf z pomocą bcc powinniśmy zapoznać się z bcc Reference Guide oraz bcc Python Developer Tutorial.

Podsumowanie:

“eBPF to niesamowita technologia jądra Linuksa napędzająca niestandardowe narzędzia do analizy, które mogą być uruchamiane na produkcji, aby znaleźć wygrane na wydajności, których nie są w stanie wykryć inne narzędzia. Dzięki niej, jak nigdy dotąd możemy wyciągnąć miliony metryk z jądra i aplikacji oraz badać uruchomione programy. To supermoc.” – jak powiedział Brendan Gregg – ten sam, który stworzył DTraceToolkit – zresztą bardzo wiele skryptów z tego rozwiązania zostało właśnie przeniesione do BCC. Kamienie milowe nowych funkcjonalności eBPF w kolejnych wersjach jąder (3.18, 3.19, 4.1, 4.4, 4.6, 4.7, 4.8, 4.9, 4.10, 4.11) czynią ten projekt jednym z ważniejszych, jeśli chodzi o analizę systemu. Stawia to Linuksa jako system operacyjny i jądro w świetle doskonałej platformy do analizy aplikacji (oprócz konkretnych narzędzi dostarczonych z aplikacjami). Niektóre rzeczy są łatwiejsze do debugowania z poziomu jądra np. jak performuje system plików i urządzenia pamięci masowej; czy planista blokuje aplikację oraz gdzie jest wąskie gardło na sieci. Dzięki eBPF możemy zbadać obszary systemu operacyjnego, na które aplikacja jest totalnie ślepa.

Więcej informacji: New (and Exciting!) Developments in Linux Tracing, Trace Aggregation and Collection with eBPF, TraceCompass, Dynamic Tracing Tools for Linux, Dive into BPF, Notes on BPF & eBPF

Kategorie K a t e g o r i e : Administracja

Tagi T a g i : , , , , , , , , , ,

Komentowanie tego wpisu jest zablokowane.