Analiza TSDB w Prometheusie 2

Analiza TSDB w Prometheusie 2

Baza danych szeregów czasowych (TSDB) w programie Prometheus 2 to doskonały przykład rozwiązania inżynieryjnego, które oferuje znaczne ulepszenia w porównaniu z pamięcią masową w wersji 2 w programie Prometheus 1 pod względem szybkości gromadzenia danych, wykonywania zapytań i efektywności wykorzystania zasobów. Wdrażaliśmy Prometheus 2 w Percona Monitoring and Management (PMM) i miałem okazję zrozumieć działanie Prometheus 2 TSDB. W tym artykule omówię wyniki tych obserwacji.

Średnie obciążenie pracą Prometeusza

Dla osób przyzwyczajonych do baz danych ogólnego przeznaczenia, typowe obciążenie Prometheusem jest dość interesujące. Tempo akumulacji danych jest zwykle stabilne: zazwyczaj monitorowane usługi wysyłają w przybliżeniu tę samą liczbę metryk, a infrastruktura zmienia się stosunkowo powoli.
Prośby o informacje mogą pochodzić z różnych źródeł. Niektóre z nich, jak np. alerty, również dążą do wartości stabilnej i przewidywalnej. Inne, takie jak żądania użytkowników, mogą powodować impulsy, chociaż nie ma to miejsca w przypadku większości obciążeń.

Test obciążenia

Podczas testów skupiłem się na możliwości gromadzenia danych. Wdrożyłem Prometheus 2.3.2 skompilowany z Go 1.10.1 (w ramach PMM 1.14) w usłudze Linode za pomocą tego skryptu: StackScript. Aby uzyskać najbardziej realistyczne generowanie obciążenia, użyj tego StackScript Uruchomiłem kilka węzłów MySQL przy rzeczywistym obciążeniu (test Sysbench TPC-C), z których każdy emulował 10 węzłów Linux/MySQL.
Wszystkie poniższe testy przeprowadzono na serwerze Linode z ośmioma rdzeniami wirtualnymi i 32 GB pamięci, przeprowadzając 20 symulacji obciążenia monitorujących dwieście instancji MySQL. Lub, w terminologii Prometeusza, 800 celów, 440 zeskrobań na sekundę, 380 tysięcy rekordów na sekundę i 1,7 miliona aktywnych szeregów czasowych.

Konstrukcja

Typowe podejście do tradycyjnych baz danych, w tym tej używanej przez Prometheusa 1.x, polega na tym, że limit pamięci. Jeśli to nie wystarczy do obsługi obciążenia, wystąpią duże opóźnienia, a niektóre żądania nie powiodą się. Użycie pamięci w Prometheusie 2 można konfigurować za pomocą klawisza storage.tsdb.min-block-duration, który określa, jak długo nagrania będą przechowywane w pamięci przed zapisaniem na dysk (wartość domyślna to 2 godziny). Ilość wymaganej pamięci będzie zależeć od liczby szeregów czasowych, etykiet i fragmentów dodanych do przychodzącego strumienia netto. Jeśli chodzi o miejsce na dysku, Prometheus zamierza wykorzystać 3 bajty na rekord (próbkę). Z drugiej strony wymagania dotyczące pamięci są znacznie wyższe.

Chociaż możliwe jest skonfigurowanie rozmiaru bloku, nie zaleca się konfigurowania go ręcznie, dlatego konieczne jest zapewnienie Prometheusowi takiej ilości pamięci, jaka jest wymagana dla danego obciążenia.
Jeśli nie ma wystarczającej ilości pamięci do obsługi przychodzącego strumienia metryk, Prometheus wypadnie z pamięci lub dotrze do niej zabójca OOM.
Dodanie wymiany w celu opóźnienia awarii, gdy Prometheusowi zabraknie pamięci, tak naprawdę nie pomaga, ponieważ użycie tej funkcji powoduje gwałtowne zużycie pamięci. Myślę, że ma to coś wspólnego z Go, jego modułem zbierającym elementy bezużyteczne i sposobem, w jaki radzi sobie z zamianą.
Innym interesującym podejściem jest skonfigurowanie bloku głównego tak, aby był opróżniany na dysk w określonym czasie, zamiast liczyć go od początku procesu.

Analiza TSDB w Prometheusie 2

Jak widać na wykresie, opróżnianie dysku następuje co dwie godziny. Jeśli zmienisz parametr min-block-duration na jedną godzinę, resety będą następować co godzinę, zaczynając od pół godziny.
Jeśli chcesz użyć tego i innych wykresów w swojej instalacji Prometheusa, możesz użyć tego panel. Został zaprojektowany dla PMM, ale po niewielkich modyfikacjach pasuje do każdej instalacji Prometheus.
Mamy aktywny blok zwany blokiem głównym, który jest przechowywany w pamięci; bloki ze starszymi danymi są dostępne poprzez mmap(). Eliminuje to potrzebę osobnego konfigurowania pamięci podręcznej, ale oznacza również, że musisz pozostawić wystarczającą ilość miejsca na pamięć podręczną systemu operacyjnego, jeśli chcesz wysyłać zapytania o dane starsze niż to, co może pomieścić blok główny.
Oznacza to również, że zużycie pamięci wirtualnej Prometheus będzie wyglądało na dość wysokie, czym nie należy się martwić.

Analiza TSDB w Prometheusie 2

Kolejnym interesującym punktem projektowym jest użycie WAL (dziennik zapisu z wyprzedzeniem). Jak widać z dokumentacji magazynu, Prometheus używa WAL, aby uniknąć awarii. Konkretne mechanizmy gwarantujące trwałość danych nie są niestety dobrze udokumentowane. Prometheus w wersji 2.3.2 opróżnia WAL na dysk co 10 sekund i użytkownik nie może konfigurować tej opcji.

Zagęszczenia

Baza danych Prometheus TSDB jest zaprojektowana na wzór magazynu LSM (Log Structured Merge): blok główny jest okresowo przesyłany na dysk, podczas gdy mechanizm kompaktowania łączy wiele bloków razem, aby uniknąć skanowania zbyt wielu bloków podczas zapytań. Tutaj możesz zobaczyć liczbę bloków, które zaobserwowałem w systemie testowym po dniu obciążenia.

Analiza TSDB w Prometheusie 2

Jeśli chcesz dowiedzieć się więcej o sklepie, możesz zajrzeć do pliku meta.json, w którym znajdują się informacje o dostępnych blokach i ich powstaniu.

{
       "ulid": "01CPZDPD1D9R019JS87TPV5MPE",
       "minTime": 1536472800000,
       "maxTime": 1536494400000,
       "stats": {
               "numSamples": 8292128378,
               "numSeries": 1673622,
               "numChunks": 69528220
       },
       "compaction": {
               "level": 2,
               "sources": [
                       "01CPYRY9MS465Y5ETM3SXFBV7X",
                       "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                       "01CPZ6NR4Q3PDP3E57HEH760XS"
               ],
               "parents": [
                       {
                               "ulid": "01CPYRY9MS465Y5ETM3SXFBV7X",
                               "minTime": 1536472800000,
                               "maxTime": 1536480000000
                       },
                       {
                               "ulid": "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                               "minTime": 1536480000000,
                               "maxTime": 1536487200000
                       },
                       {
                               "ulid": "01CPZ6NR4Q3PDP3E57HEH760XS",
                               "minTime": 1536487200000,
                               "maxTime": 1536494400000
                       }
               ]
       },
       "version": 1
}

Zagęszczenia w Prometeuszu są powiązane z czasem wypłukiwania bloku głowicy na dysk. Na tym etapie można przeprowadzić kilka takich operacji.

Analiza TSDB w Prometheusie 2

Wygląda na to, że zagęszczenie nie jest w żaden sposób ograniczone i może powodować duże skoki we/wy dysku podczas wykonywania.

Analiza TSDB w Prometheusie 2

Skoki obciążenia procesora

Analiza TSDB w Prometheusie 2

Ma to oczywiście raczej negatywny wpływ na szybkość systemu, a także stanowi poważne wyzwanie dla pamięci masowej LSM: jak przeprowadzić kompresję, aby obsłużyć wysoką liczbę żądań bez powodowania zbyt dużego obciążenia?
Ciekawie wygląda także wykorzystanie pamięci w procesie zagęszczania.

Analiza TSDB w Prometheusie 2

Widzimy, jak po zagęszczeniu większość pamięci zmienia stan z buforowanej na wolną: oznacza to, że potencjalnie cenne informacje zostały z niej usunięte. Ciekawe, czy zostało tu użyte fadvice() lub inna technika minimalizacji, czy też dzieje się tak dlatego, że pamięć podręczna została uwolniona z bloków zniszczonych podczas zagęszczania?

Odzyskiwanie po awarii

Odzyskiwanie sił po niepowodzeniach wymaga czasu i nie bez powodu. Na przychodzący strumień miliona rekordów na sekundę musiałem czekać około 25 minut, aż odzyskiwanie zostało wykonane z uwzględnieniem dysku SSD.

level=info ts=2018-09-13T13:38:14.09650965Z caller=main.go:222 msg="Starting Prometheus" version="(version=2.3.2, branch=v2.3.2, revision=71af5e29e815795e9dd14742ee7725682fa14b7b)"
level=info ts=2018-09-13T13:38:14.096599879Z caller=main.go:223 build_context="(go=go1.10.1, user=Jenkins, date=20180725-08:58:13OURCE)"
level=info ts=2018-09-13T13:38:14.096624109Z caller=main.go:224 host_details="(Linux 4.15.0-32-generic #35-Ubuntu SMP Fri Aug 10 17:58:07 UTC 2018 x86_64 1bee9e9b78cf (none))"
level=info ts=2018-09-13T13:38:14.096641396Z caller=main.go:225 fd_limits="(soft=1048576, hard=1048576)"
level=info ts=2018-09-13T13:38:14.097715256Z caller=web.go:415 component=web msg="Start listening for connections" address=:9090
level=info ts=2018-09-13T13:38:14.097400393Z caller=main.go:533 msg="Starting TSDB ..."
level=info ts=2018-09-13T13:38:14.098718401Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536530400000 maxt=1536537600000 ulid=01CQ0FW3ME8Q5W2AN5F9CB7R0R
level=info ts=2018-09-13T13:38:14.100315658Z caller=web.go:467 component=web msg="router prefix" prefix=/prometheus
level=info ts=2018-09-13T13:38:14.101793727Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536732000000 maxt=1536753600000 ulid=01CQ78486TNX5QZTBF049PQHSM
level=info ts=2018-09-13T13:38:14.102267346Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536537600000 maxt=1536732000000 ulid=01CQ78DE7HSQK0C0F5AZ46YGF0
level=info ts=2018-09-13T13:38:14.102660295Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536775200000 maxt=1536782400000 ulid=01CQ7SAT4RM21Y0PT5GNSS146Q
level=info ts=2018-09-13T13:38:14.103075885Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536753600000 maxt=1536775200000 ulid=01CQ7SV8WJ3C2W5S3RTAHC2GHB
level=error ts=2018-09-13T14:05:18.208469169Z caller=wal.go:275 component=tsdb msg="WAL corruption detected; truncating" err="unexpected CRC32 checksum d0465484, want 0" file=/opt/prometheus/data/.prom2-data/wal/007357 pos=15504363
level=info ts=2018-09-13T14:05:19.471459777Z caller=main.go:543 msg="TSDB started"
level=info ts=2018-09-13T14:05:19.471604598Z caller=main.go:603 msg="Loading configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499156711Z caller=main.go:629 msg="Completed loading of configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499228186Z caller=main.go:502 msg="Server is ready to receive web requests."

Głównym problemem procesu odzyskiwania jest duże zużycie pamięci. Pomimo tego, że w normalnej sytuacji serwer może pracować stabilnie przy tej samej ilości pamięci, w przypadku awarii może nie zostać odzyskany ze względu na OOM. Jedynym rozwiązaniem, jakie znalazłem, było wyłączenie gromadzenia danych, uruchomienie serwera, umożliwienie mu odzyskania danych i ponowne uruchomienie z włączoną funkcją zbierania.

Rozgrzewka

Kolejnym zachowaniem, o którym należy pamiętać podczas rozgrzewki, jest związek między niską wydajnością a dużym zużyciem zasobów zaraz po starcie. Podczas niektórych, ale nie wszystkich uruchomień, zaobserwowałem poważne obciążenie procesora i pamięci.

Analiza TSDB w Prometheusie 2

Analiza TSDB w Prometheusie 2

Luki w wykorzystaniu pamięci wskazują, że Prometheus nie może od początku skonfigurować wszystkich kolekcji i część informacji zostaje utracona.
Nie odkryłem dokładnych przyczyn wysokiego obciążenia procesora i pamięci. Podejrzewam, że jest to spowodowane tworzeniem się nowych szeregów czasowych w bloku czołowym z dużą częstotliwością.

Skoki obciążenia procesora

Oprócz zagęszczenia, które powoduje dość duże obciążenie we/wy, zauważyłem poważne skoki obciążenia procesora co dwie minuty. Impulsy są dłuższe, gdy przepływ wejściowy jest wysoki i wydają się być spowodowane przez moduł zbierający elementy bezużyteczne Go, przy czym przynajmniej niektóre rdzenie są w pełni obciążone.

Analiza TSDB w Prometheusie 2

Analiza TSDB w Prometheusie 2

Te skoki nie są takie małe. Wydaje się, że w takim przypadku wewnętrzny punkt wejścia i metryki Prometheusa stają się niedostępne, co powoduje luki w danych w tych samych okresach.

Analiza TSDB w Prometheusie 2

Można również zauważyć, że eksporter Prometheus wyłącza się na jedną sekundę.

Analiza TSDB w Prometheusie 2

Można zauważyć korelacje z zbieraniem śmieci (GC).

Analiza TSDB w Prometheusie 2

wniosek

TSDB w Prometheusie 2 jest szybka, jest w stanie obsłużyć miliony szeregów czasowych i jednocześnie tysiące rekordów na sekundę przy użyciu dość skromnego sprzętu. Imponujące jest również wykorzystanie procesora i wejść/wyjść dysku. Mój przykład pokazał do 200 000 metryk na sekundę na używany rdzeń.

Planując rozbudowę trzeba pamiętać o wystarczającej ilości pamięci i to musi być pamięć realna. Ilość wykorzystanej pamięci, którą zaobserwowałem, wynosiła około 5 GB na 100 000 rekordów na sekundę przychodzącego strumienia, co razem z pamięcią podręczną systemu operacyjnego dawało około 8 GB zajętej pamięci.

Oczywiście nadal jest wiele do zrobienia, aby okiełznać skoki we/wy procesora i dysku, co nie jest zaskakujące, biorąc pod uwagę porównanie młodego TSDB Prometheus 2 z InnoDB, TokuDB, RocksDB, WiredTiger, ale wszystkie miały podobne problemy na początku ich cyklu życia.

Źródło: www.habr.com

Dodaj komentarz