Zbierasz więc metryki. Tak jak my. Zbieramy także metryki. Oczywiście niezbędne w interesach. Dzisiaj porozmawiamy o pierwszym ogniwie naszego systemu monitorowania - serwerze agregującym kompatybilnym ze statystyką
Z naszych poprzednich artykułów (
Zastrzeżenie 1. Github, twórca projektu, przestał go wspierać: publikować łatki i poprawki, akceptować nasz i (nie tylko nasz) PR. W ciągu ostatnich kilku miesięcy (gdzieś od lutego-marca 2018 r.) działalność została wznowiona, ale wcześniej panował prawie 2 lata całkowitego spokoju. Ponadto projekt jest w trakcie opracowywania
Zastrzeżenie 2. Dokładność obliczeń. Brubeck zbiera w sumie 65536 wartości do agregacji. W naszym przypadku dla niektórych metryk w okresie agregacji (30 sekund) może pojawić się znacznie więcej wartości (w szczycie 1 527 392). W wyniku tego próbkowania wartości maksymalne i minimalne wydają się bezużyteczne. Na przykład tak:
Tak jak było
Jak to powinno wyglądać
Z tego samego powodu kwoty są na ogół obliczane nieprawidłowo. Dodaj tutaj błąd z 32-bitowym przepełnieniem float, który zazwyczaj wysyła serwer do segfault po otrzymaniu pozornie niewinnej metryki i wszystko staje się świetne. Swoją drogą błąd nie został naprawiony.
Oraz, w końcu, Roszczenie X. W chwili pisania tego tekstu jesteśmy gotowi zaprezentować go wszystkim 14 mniej więcej działającym wdrożeniom statystyk, jakie udało nam się znaleźć. Wyobraźmy sobie, że jakaś pojedyncza infrastruktura rozrosła się tak bardzo, że akceptacja 4 milionów MPS już nie wystarczy. Albo nawet jeśli jeszcze nie urósł, ale wskaźniki są już dla Ciebie na tyle ważne, że nawet krótkie, 2-3-minutowe spadki na wykresach mogą już stać się krytyczne i spowodować napady nieprzezwyciężonej depresji wśród menedżerów. Ponieważ leczenie depresji jest niewdzięcznym zadaniem, potrzebne są rozwiązania techniczne.
Po pierwsze, odporność na awarie, aby nagły problem na serwerze nie spowodował psychiatrycznej apokalipsy zombie w biurze. Po drugie skalowanie, aby móc przyjąć ponad 4 miliony MPS, bez wdzierania się głęboko w stos sieciowy Linuksa i spokojnego powiększania „wszerz” do wymaganego rozmiaru.
Ponieważ mieliśmy miejsce na skalowanie, postanowiliśmy zacząć od odporności na błędy. "O! Tolerancja na błędy! To proste, damy radę” – pomyśleliśmy i uruchomiliśmy 2 serwery, na każdym umieszczając kopię brubecka. Aby to zrobić, musieliśmy skopiować ruch z metrykami na oba serwery, a nawet napisać do tego
Jeśli pomyślisz trochę o problemie i jednocześnie odkopiesz śnieg łopatą, może przyjść Ci do głowy następujący oczywisty pomysł: potrzebujesz statystyki, która może działać w trybie rozproszonym. Oznacza to, że implementuje synchronizację między węzłami pod względem czasu i metryk. „Oczywiście, takie rozwiązanie prawdopodobnie już istnieje” – powiedzieliśmy i udaliśmy się do Google…. I nic nie znaleźli. Po przejrzeniu dokumentacji dla różnych statystyk (
I wtedy przypomnieliśmy sobie o „zabawkowych” statystykach – bioyino, które powstały podczas hackatonu Just for Fun (nazwa projektu została wygenerowana przez skrypt przed rozpoczęciem hackatonu) i zdaliśmy sobie sprawę, że pilnie potrzebujemy własnych statystyk. Po co?
- ponieważ na świecie jest zbyt mało klonów statystyk,
- ponieważ możliwe jest zapewnienie pożądanej lub zbliżonej do pożądanej odporności na awarie i skalowalności (w tym synchronizacja zagregowanych metryk pomiędzy serwerami i rozwiązywanie problemu konfliktów przesyłania),
- ponieważ możliwe jest obliczanie wskaźników dokładniej niż robi to Brubeck,
- ponieważ możesz samodzielnie zbierać bardziej szczegółowe statystyki, których Brubeck praktycznie nam nie udostępniał,
- ponieważ miałem okazję zaprogramować własną hiperwydajną aplikację w skali rozproszonej, która nie będzie całkowicie powtarzać architektury innego podobnego hiperbo... cóż, to wszystko.
Na czym pisać? Oczywiście w Rust. Dlaczego?
- ponieważ istniało już rozwiązanie prototypowe,
- ponieważ autor artykułu znał już wtedy Rusta i bardzo chciał napisać w nim coś na produkcję z możliwością umieszczenia go w open-source,
- ponieważ języki z GC nie są dla nas odpowiednie ze względu na charakter odbieranego ruchu (prawie w czasie rzeczywistym), a przerwy w GC są praktycznie nie do przyjęcia,
- ponieważ potrzebujesz maksymalnej wydajności porównywalnej z C
- ponieważ Rust zapewnia nam nieustraszoną współbieżność i gdybyśmy zaczęli pisać go w C/C++, zgarnęlibyśmy jeszcze więcej luk w zabezpieczeniach, przepełnienia bufora, warunki wyścigowe i inne przerażające słowa niż brubeck.
Był też argument przeciwko Rustowi. Firma nie miała doświadczenia w tworzeniu projektów w Rust, a teraz też nie planujemy używać go w głównym projekcie. Dlatego były poważne obawy, że nic nie wyjdzie, ale postanowiliśmy zaryzykować i spróbować.
Czas minął...
Wreszcie, po kilku nieudanych próbach, pierwsza działająca wersja była gotowa. Co się stało? To jest to, co się stało.
Każdy węzeł otrzymuje swój własny zestaw metryk i gromadzi je, ale nie agreguje metryk dla tych typów, w przypadku których do ostatecznej agregacji wymagany jest ich pełny zestaw. Węzły są połączone ze sobą za pomocą pewnego rodzaju protokołu blokady rozproszonej, który pozwala wybrać spośród nich jedyny (tutaj płakaliśmy), który jest godny przesłania metryk do Wielkiego. Problem ten jest obecnie rozwiązywany przez
Pakiety UDP z metrykami są niezrównoważone pomiędzy węzłami sprzętu sieciowego poprzez proste działanie okrężne. Oczywiście sprzęt sieciowy nie analizuje zawartości pakietów i dlatego może pobrać znacznie więcej niż 4M pakietów na sekundę, nie mówiąc już o wskaźnikach, o których w ogóle nic nie wie. Jeśli weźmiemy pod uwagę, że metryki nie przychodzą pojedynczo w każdym pakiecie, to nie przewidujemy w tym miejscu żadnych problemów z wydajnością. W przypadku awarii serwera urządzenie sieciowe szybko (w ciągu 1-2 sekund) wykrywa ten fakt i usuwa uszkodzony serwer z rotacji. Dzięki temu węzły pasywne (czyli nie wiodące) można włączać i wyłączać praktycznie bez zauważania spadków na wykresach. Maksymalna strata jest częścią wskaźników, które pojawiły się w ostatniej sekundzie. Nagła utrata/wyłączenie/zmiana lidera nadal spowoduje niewielką anomalię (30-sekundowy interwał nadal nie jest zsynchronizowany), ale jeśli między węzłami istnieje komunikacja, problemy te można zminimalizować, na przykład wysyłając pakiety synchronizacyjne .
Trochę o strukturze wewnętrznej. Aplikacja jest oczywiście wielowątkowa, jednak architektura wątków różni się od tej stosowanej w brubecku. Wątki w brubeck są takie same - każdy z nich odpowiada zarówno za zbieranie, jak i agregację informacji. W bioyino pracownicy dzielą się na dwie grupy: osoby odpowiedzialne za sieć i osoby odpowiedzialne za agregację. Podział ten pozwala na bardziej elastyczne zarządzanie aplikacją w zależności od rodzaju metryk: tam, gdzie wymagana jest intensywna agregacja, można dodać agregatory, tam, gdzie jest duży ruch sieciowy, można dodać liczbę przepływów sieciowych. W tej chwili na naszych serwerach pracujemy w 8 przepływach sieciowych i 4 agregacyjnych.
Część licząca (odpowiedzialna za agregację) jest dość nudna. Bufory wypełnione przepływami sieciowymi rozdzielane są pomiędzy przepływy zliczające, gdzie są następnie analizowane i agregowane. Na żądanie podawane są metryki do przesłania do innych węzłów. Wszystko to, łącznie z przesyłaniem danych pomiędzy węzłami i pracą z Consulem, odbywa się asynchronicznie, działając na frameworku
Znacznie więcej problemów w fazie rozwoju sprawiała część sieciowa odpowiedzialna za odbieranie metryk. Głównym celem rozdzielenia przepływów sieciowych na odrębne podmioty była chęć skrócenia czasu spędzanego przez przepływ nie do odczytania danych z gniazda. Opcje wykorzystujące asynchroniczny UDP i zwykły recvmsg szybko zniknęły: pierwsza zużywa zbyt dużo procesora przestrzeni użytkownika do przetwarzania zdarzeń, druga wymaga zbyt wielu przełączeń kontekstu. Dlatego jest teraz używany
Operacja
W ustawieniach domyślnych rozmiar bufora jest ustawiony na dość duży. Jeśli nagle zdecydujesz się na samodzielne wypróbowanie serwera, możesz spotkać się z faktem, że po wysłaniu niewielkiej liczby metryk nie dotrą one w Graphite, pozostając w buforze strumienia sieciowego. Aby pracować z małą liczbą metryk, musisz ustawić bufsize i task-queue-size na mniejsze wartości w konfiguracji.
Na koniec kilka wykresów dla miłośników wykresów.
Statystyki dotyczące liczby przychodzących metryk dla każdego serwera: ponad 2 miliony MPS.
Wyłączenie jednego z węzłów i redystrybucja przychodzących metryk.
Statystyki dotyczące metryk wychodzących: zawsze wysyła tylko jeden węzeł – szef rajdu.
Statystyka pracy każdego węzła z uwzględnieniem błędów w różnych modułach systemu.
Szczegóły przychodzących metryk (nazwy metryk są ukryte).
Co mamy zamiar z tym wszystkim dalej zrobić? Oczywiście, napisz kod, cholera…! Pierwotnie projekt miał mieć charakter open source i tak pozostanie przez cały okres jego istnienia. Nasze najbliższe plany obejmują przejście na własną wersję Raft, zmianę protokołu równorzędnego na bardziej przenośny, wprowadzenie dodatkowych statystyk wewnętrznych, nowych typów metryk, poprawki błędów i inne ulepszenia.
Oczywiście każdy może pomóc w rozwoju projektu: stworzyć PR, Issues, w miarę możliwości odpowiemy, ulepszymy itp.
Mając to na uwadze, to wszystko, kupcie nasze słonie!
Źródło: www.habr.com