Dziś w naszym projekcie oprócz monolitycznego kodu działają dziesiątki mikrousług. Każda z nich wymaga monitorowania. Robienie tego w tak dużych ilościach przez inżynierów DevOps jest problematyczne. Opracowaliśmy system monitorowania, który działa jako usługa dla programistów. Mogą oni samodzielnie przesyłać metryki do systemu monitorowania, korzystać z nich, tworzyć na ich podstawie pulpity nawigacyjne oraz podłączać alerty, które będą się aktywować po osiągnięciu wartości progowych. Inżynierowie DevOps zajmują się tylko infrastrukturą i dokumentacją.
Ten post to transkrypcja mojego wystąpienia z naszej na RIT++. Wiele osób prosiło nas o stworzenie tekstowych wersji prezentacji stamtąd. Jeśli byłeś na konferencji lub oglądałeś wideo, nie znajdziesz nic nowego. A wszystkich innych zapraszam pod kat. Opowiem, jak doszliśmy do takiego systemu, jak on działa i jak planujemy go aktualizować.

Przeszłość: schematy i plany
Jak doszliśmy do istniejącego systemu monitorowania? Aby odpowiedzieć na to pytanie, trzeba cofnąć się do 2015 roku. Oto jak to wyglądało wówczas:

Posiadaliśmy około 24 węzłów odpowiedzialnych za monitorowanie. Istniało całe mnóstwo różnych cronów, skryptów, demonów, które monitorowały coś gdzieś w jakiś sposób, wysyłały wiadomości i realizowały funkcje. Pomyśleliśmy, że im dalej, tym mniej taka system będzie żywotny. Nie ma sensu rozwijać go: zbyt rozbudowany.
Postanowiliśmy wybrać te elementy monitoringu, które zachowamy i będziemy rozwijać, oraz te, z których zrezygnujemy. Okazało się ich 19. Pozostały tylko grafity, agregatory i Grafana jako pulpit nawigacyjny. Ale jak będzie wyglądać nowy system? Oto tak:

Mamy magazyn metryk: to grafity, które będą oparte na szybkich dyskach SSD, to określone agregatory dla metryk. Następnie - Grafana do wyświetlania pulpitów nawigacyjnych oraz Moira jako system alarmowy. Chcieliśmy także opracować system do wykrywania anomalii.
Standard: Monitorowanie 2.0
Tak wyglądały plany w 2015 roku. Ale musieliśmy przygotować nie tylko infrastrukturę i samą usługę, ale także dokumentację do niej. Opracowaliśmy dla siebie standard korporacyjny, który nazwaliśmy monitorowaniem 2.0. Jakie były wymagania wobec systemu?
- stała dostępność;
- interwał przechowywania metryk = 10 sekund;
- strukturalne przechowywanie metryk i pulpitów nawigacyjnych;
- SLA > 99,99%
- zbieranie metryk zdarzeń przez UDP (!).
Potrzebowaliśmy UDP, ponieważ mamy dużą ilość ruchu i zdarzeń, które generują metryki. Jeśli zapiszemy je wszystkie od razu do Graphite, przechowywanie się zawiesi. Wybraliśmy też prefiksy pierwszego poziomu dla wszystkich metryk.

Każdy z prefiksów ma jakieś właściwości. Są metryki związane z serwerami, sieciami, kontenerami, zasobami, aplikacjami itd. Zrealizowaliśmy jasne, precyzyjne, typowane filtrowanie, gdzie przyjmujemy metryki pierwszego poziomu, a pozostałe po prostu odrzucamy. Tak planowaliśmy ten system w 2015 roku. A co w teraźniejszości?
Teraźniejszość: schemat interakcji komponentów monitorujących
Przede wszystkim monitorujemy aplikacje: nasz kod PHP, aplikacje i mikroserwisy — krótko mówiąc, wszystko, co piszą nasi programiści. Wszystkie aplikacje wysyłają metryki do agregatora Brubeck (statsd, przepisany w C) przez UDP. Okazał się najszybszy w wyniku testów syntetycznych. I on wysyła już zaggregowane metryki do Graphite przez TCP.
Ma taki typ metryk, jak timery. To bardzo przydatna rzecz. Na przykład, na każde połączenie użytkownika z usługą wysyłasz do Brubeck metrykę z czasem odpowiedzi. Otrzymujesz milion odpowiedzi, a agregator wydaje tylko 10 metryk. Masz liczbę przybyłych osób, maksymalny, minimalny oraz średni czas odpowiedzi, medianę i 4 percentyle. Następnie dane są przesyłane do Graphite i widzimy je wszystkie na żywo.
Mamy również agregację dla metryk sprzętowych, programowych, systemowych oraz naszego starego systemu monitorowania Munin (działał u nas do 2015 roku). Wszystko to zbieramy przez demon CollectD (wbudowane jest w nim wiele różnych wtyczek, potrafi on monitorować wszystkie zasoby systemu hosta, na którym jest zainstalowany — wystarczy wskazać w konfiguracji, gdzie zapisywać dane) i zapisujemy przez niego dane w Graphite. Obsługuje również wtyczki Python i skrypty powłoki, więc możesz pisać własne niestandardowe rozwiązania: CollectD będzie zbierać te dane z lokalnego lub zdalnego hosta (przykładowo, są Curl) i wysyłać je do Graphite.
Następnie wszystkie metryki, które zebraliśmy, wysyłamy do Carbon-c-relay. To rozwiązanie Carbon Relay od Graphite, dostosowane do języka C. To router, który zbiera wszystkie metryki, które wysyłamy z naszych agregatorów, i przekierowuje je do węzłów. Również na etapie routingu sprawdza ważność metryki. Po pierwsze, muszą one odpowiadać schematowi z prefiksami, który wcześniej przedstawiłem, a po drugie, być ważne dla Graphite. W przeciwnym razie są odrzucane.
Następnie Carbon-c-relay wysyła metryki do klastra Graphite. Używamy jako głównego magazynu metryk Carbon-cache, przepisanym w Go. Go-carbon z powodu swojej wielowątkowości znacznie przewyższa wydajnością Carbon-cache. Przyjmuje dane i zapisuje je na dyskach za pomocą pakietu whisper (standardowego, napisanego w Pythonie). Aby odczytać dane z naszych magazynów, używamy Graphite API. Działa znacznie szybciej niż standardowy Graphite WEB. Co się dzieje z danymi dalej?
Przechodzą do Grafana. Jako główne źródło danych wykorzystujemy nasze klastry Graphite, dodatkowo mamy Grafana jako interfejs webowy, do wyświetlania metryk i tworzenia pulpitów nawigacyjnych. Dla każdej usługi programiści zakładają własny pulpit. Następnie budują na ich podstawie wykresy, na których wyświetlane są metryki, które zapisują z swoich aplikacji. Oprócz Grafana mamy również SLAM. To demon w Pythonie, który oblicza SLA na podstawie danych z Graphite. Jak już wspomniałem, mamy kilka dziesiątek mikrousług, z których każda ma swoje wymagania. Dzięki SLAM porównujemy dokumentację z tym, co jest w Graphite i sprawdzamy, jak wymagania odpowiadają dostępności naszych usług.
Idziemy dalej: alerting. Jest zorganizowany za pomocą silnego systemu - Moira. Jest niezależna, ponieważ ma pod maską własny Graphite. Została opracowana przez zespół z SKB "Kontur", napisana w Pythonie i Go, w pełni open-source. Moira przyjmuje cały ten sam strumień, który wpływa do Graphite. Jeśli z jakiegoś powodu Twoje magazynowanie przestanie działać, to Twój alerting będzie działał.
Moira została uruchomiona w Kubernetes jako główna baza danych, korzystając z klastra serwerów Redis. W końcu otrzymaliśmy system odporny na awarie. Porównuje ona strumień metryk z listą wyzwalaczy: jeśli nie ma ich wzmianki, odrzuca metrykę. Dzięki temu jest w stanie przetwarzać gigabajty metryk na minutę.
Dołączyliśmy również do niej korporacyjny LDAP, za pomocą którego każdy użytkownik systemu korporacyjnego może tworzyć powiadomienia dla istniejących (lub nowo utworzonych) wyzwalaczy. Ponieważ Moira zawiera w sobie Graphite, obsługuje wszystkie jego funkcje. Najpierw bierzesz wiersz i kopiujesz go do Grafana. Sprawdzasz, jak dane są wyświetlane na wykresach. Następnie bierzesz ten sam wiersz i kopiujesz go do Moira. Przypinasz do niego limity i otrzymujesz alerting. Aby to wszystko zrealizować, nie potrzebujesz żadnej specyficznej wiedzy. Moira potrafi wysyłać alerty przez SMS, e-mail, w Jira, Slacku… Obsługuje również wykonywanie skryptów niestandardowych. Gdy wystąpi wyzwalacz i jest ona subskrybowana na skrypt niestandardowy lub binarkę, uruchamia go i przekazuje do stdin tej binarki JSON. Twoja aplikacja musi go zanalizować. Co zrobisz z tym JSON-em — decyduj sam. Chcesz — wysyłaj do Telegrama, chcesz — otwieraj zadania w Jira, rób, co chcesz.
Do alertingu używamy również własnego rozwiązania — Imagotag. Dostosowaliśmy panel, który zwykle jest stosowany do elektronicznych etykiet w sklepach, do naszych potrzeb. Wyprowadzone zostały z niego wyzwalacze z Moira. Znajdziesz tam informacje o ich stanie i czasie wystąpienia. Część ludzi z zespołu deweloperskiego zrezygnowała z powiadomień w Slacku i e-mailu na rzecz tego panelu.

A ponieważ jesteśmy nowoczesną firmą, monitorujemy w tym systemie również Kubernetes. Włączono go do systemu za pomocą Heapster, który zainstalowaliśmy w klastrze, zbiera dane i wysyła je do Graphite. Ostatecznie schemat wygląda tak:

Komponenty monitorowania
Oto lista linków do komponentów, które wykorzystaliśmy do tego zadania. Wszystkie są open-source.
Graphite:
- go-carbon:
- whisper:
- graphite-api:
Carbon-c-relay:
Brubeck:
Collectd:
Moira:
Grafana:
Heapster:
Statystyka
A oto kilka liczb dotyczących działania naszego systemu.
Agregator (brubeck)
Liczba metryk: ~ 300 000 / sek
Czas wysyłania metryk do Graphite: 30 sek
Zużycie zasobów serwera: ~ 6% CPU (mowa o pełnoprawnych serwerach); ~ 1Gb RAM; ~ 3 Mbps LAN
Graphite (go-carbon)
Liczba metryk: ~ 1 600 000 / min
Czas aktualizacji metryk: 30 sek
Schemat przechowywania metryk: 30 sek 35 dni, 5 min 90 dni, 10 min 365 dni (daje zrozumienie tego, co się dzieje z usługą w długim okresie czasu)
Zużycie zasobów serwera: ~ 10% CPU; ~ 20Gb RAM; ~ 30 Mbps LAN
Elastyczność
W Avito naprawdę cenimy elastyczność w naszym systemie monitorowania. Dlaczego on taki jest? Po pierwsze, jego składniki są wymienne: zarówno komponenty, jak i ich wersje. Po drugie — utrzymywany. Cały projekt oparty jest na open source, więc możesz samodzielnie edytować kod, wprowadzać zmiany i realizować funkcje, które nie są dostępne od razu. Używane są stosunkowo powszechne technologie, głównie Go i Python, dlatego jest to dość proste.
Oto przykład rzeczywistego problemu, który się pojawił. Metryka w Graphite to plik. Ma swoją nazwę. Nazwa pliku = nazwa metryki. I jest ścieżka do niego. Nazwy plików w Linux są ograniczone do 255 znaków. A my mamy (jako "wewnętrzni zamawiający") chłopaków z działu baz danych. Mówią nam: "Chcemy monitorować nasze zapytania SQL. A one — to nie 255 znaków, a 8 MB każde. Chcemy je wyświetlać w Grafana, widzieć parametry dla tego zapytania, a jeszcze lepiej, chcemy widzieć top takich zapytań. Byłoby wspaniale, gdyby były wyświetlane w czasie rzeczywistym. A jeszcze lepiej, gdyby można je było wrzucić do alertów."

Przykład zapytania SQL podano jako przykład z
Uruchamiamy serwer Redis z naszymi wtyczkami Collectd, które łączą się z Postgresem i pobierają z niego wszystkie dane, wysyłając metryki do Graphite. Zmieniamy jednak nazwę metryki na hashe. Ten sam hash przesyłamy do Redis jako klucz, a całe zapytanie SQL jako wartość. Pozostało nam tylko skonfigurować Grafanę, aby mogła uzyskiwać tę informację z Redis. Otwieramy API Graphite, ponieważ jest to główny interfejs komunikacji wszystkich komponentów monitoringu z Graphite, i wpisujemy tam nową funkcję o nazwie aliasByHash() — otrzymujemy nazwę metryki od Grafana i używamy jej w zapytaniu do Redis jako klucza, aby w odpowiedzi uzyskać wartość tego klucza, którą jest nasze „zapytanie SQL”. W ten sposób wyświetliliśmy w Grafanie zapytanie SQL, które normalnie nie mogłoby się tam pojawić, wraz z jego statystyką (calls, rows, total_time, …).
Podsumowanie
Dostępność. Nasz serwis monitoringu jest dostępny 24/7 z każdej aplikacji i dowolnego kodu. Jeśli masz dostęp do magazynów, możesz przekazywać dane do serwisu. Język nie ma znaczenia, rozwiązania nie są istotne. Musisz tylko wiedzieć, jak otworzyć gniazdo, wrzucić tam metrykę i zamknąć gniazdo.
Niezawodność. Wszystkie komponenty są odporne na awarie i dobrze radzą sobie z naszymi obciążeniami.
Niski próg wejścia. Aby skorzystać z tego systemu, nie musisz uczyć się języków programowania ani zapytań w Grafanie. Po prostu otwierasz swoją aplikację, wpisujesz w niej gniazdo, które będzie wysyłać metryki do Graphite, zamykasz je, otwierasz Grafanę, tworzysz tam pulpit nawigacyjny i obserwujesz zachowanie swoich metryk, otrzymując powiadomienia przez Moira.
Samodzielność. Możesz to wszystko robić samodzielnie, bez pomocy inżynierów DevOps. To jest overkill, ponieważ możesz monitorować swój projekt teraz, nie musząc nikogo prosić — ani na początku pracy, ani przy zmianach.
Do czego dążymy?
Wszystko, co wymienione poniżej, to nie tylko abstrakcyjne myśli, ale to, do czego zrobiono przynajmniej pierwsze kroki.
- Detektor anomalii. Chcemy stworzyć usługę, która będzie przeszukiwać nasze magazyny Graphite i sprawdzać każdą metrykę za pomocą różnych algorytmów. Mamy już algorytmy, które chcemy zdiagnozować, mamy dane i potrafimy je przetwarzać.
- Metadane. Mamy wiele usług, które w miarę upływu czasu się zmieniają, tak samo jak ludzie, którzy z nimi pracują. Ciągłe prowadzenie dokumentacji ręcznie to nie opcja. Dlatego teraz nasze mikroserwisy wyposażane są w metadane. Opisano tam, kto go stworzył, z jakimi językami współpracuje, wymagania dotyczące SLA oraz dokąd i do kogo wysyłać powiadomienia. Podczas wdrażania usługi wszystkie dane encji są tworzone automatycznie. W efekcie otrzymujesz dwa linki — jeden do wyzwalaczy, a drugi do pulpitów w Grafana.
- Monitoring w każdym domu. Uważamy, że z takiego systemu powinni korzystać wszyscy programiści. W ten sposób zawsze wiesz, gdzie jest Twój ruch, co się z nim dzieje, gdzie ma problemy, gdzie są jego słabe punkty. Jeśli na przykład coś przyjdzie i zablokuje Twój serwis, dowiesz się o tym nie podczas telefonu od menedżera, lecz od alertu, i od razu będziesz mógł otworzyć świeże logi i sprawdzić, co się wydarzyło.
- Wysoka wydajność. Nasz projekt nieustannie rośnie i dziś przetwarza około 2 000 000 wartości metryk na minutę. Rok temu ten wskaźnik wynosił 500 000. A wzrost trwa, co oznacza, że w pewnym momencie Graphite (whisper) zacznie mocno obciążać system dyskowy. Jak już mówiłem, ten system monitorowania jest dość uniwersalny dzięki wymienności komponentów. Niektórzy specjalnie dla Graphite obsługują i nieustannie rozszerzają swoją infrastrukturę, ale postanowiliśmy pójść inną drogą: wykorzystać jako magazyn naszych metryk. Ten proces przejścia jest niemal zakończony i wkrótce opowiem dokładniej, jak to zostało zrobione: jakie były trudności i jak je przezwyciężono, jak przebiegał proces migracji, opiszę wybrane komponenty jako powiązania oraz ich konfiguracje.
Dziękuję za uwagę! Zadawajcie pytania na ten temat, postaram się odpowiedzieć tutaj lub w następnych postach. Być może ktoś ma doświadczenie w budowaniu takiego systemu monitorowania lub w przechodzeniu na Clickhouse w podobnej sytuacji — podzielcie się nim w komentarzach.
Źródło: habr.com
