Co wiemy o mikroserwisach

Cześć! Nazywam się Vadim Madison i kieruję rozwojem Platformy Systemowej Avito. Niejednokrotnie mówiono o tym, jak w firmie przechodzimy od architektury monolitycznej do architektury mikroserwisowej. Czas podzielić się tym, jak przekształciliśmy naszą infrastrukturę, aby jak najlepiej wykorzystać mikrousługi i zapobiec zagubieniu się w nich. Jak PaaS nam w tym pomaga, jak uprościliśmy wdrożenie i ograniczyliśmy tworzenie mikroserwisu do jednego kliknięcia – czytaj dalej. Nie wszystko, o czym piszę poniżej, jest w pełni zaimplementowane w Avito, część z tego wynika z tego, jak rozwijamy naszą platformę.

(Na końcu tego artykułu opowiem o możliwości wzięcia udziału w trzydniowym seminarium prowadzonym przez eksperta architektury mikrousług, Chrisa Richardsona).

Co wiemy o mikroserwisach

Jak dotarliśmy do mikroserwisów

Avito to jeden z największych serwisów ogłoszeniowych na świecie, codziennie pojawia się na nim ponad 15 milionów nowych ogłoszeń. Nasz backend akceptuje ponad 20 tysięcy żądań na sekundę. Obecnie mamy kilkaset mikroserwisów.

Od kilku lat budujemy architekturę mikroserwisową. Jak dokładnie - szczegółowo nasi koledzy powiedział w naszej sekcji na RIT++ 2017. Na CodeFest 2017 (patrz. wideo), Siergiej Orłow i Michaił Prokopczuk szczegółowo wyjaśnili, dlaczego potrzebowaliśmy przejścia na mikroserwisy i jaką rolę odegrał tutaj Kubernetes. Cóż, teraz robimy wszystko, aby zminimalizować koszty skalowania, które są nieodłącznym elementem takiej architektury.

Początkowo nie tworzyliśmy ekosystemu, który kompleksowo pomógłby nam w rozwoju i uruchomieniu mikroserwisów. Po prostu zebrali rozsądne rozwiązania open source, uruchomili je w domu i zaprosili programistę, aby się nimi zajął. W rezultacie odwiedził kilkanaście miejsc (dashboardy, usługi wewnętrzne), po czym utwierdził się w pragnieniu wycinania kodu po staremu, w monolicie. Kolor zielony na poniższych schematach wskazuje, co programista robi w ten czy inny sposób własnymi rękami, a kolor żółty oznacza automatyzację.

Co wiemy o mikroserwisach

Teraz w narzędziu PaaS CLI za pomocą jednego polecenia tworzona jest nowa usługa, a następnie dodawana jest nowa baza danych z dwoma kolejnymi i wdrażana na platformie Stage.

Co wiemy o mikroserwisach

Jak pokonać erę „fragmentacji mikroserwisów”

Przy monolitycznej architekturze, w trosce o spójność zmian w produkcie, programiści zmuszeni byli dowiedzieć się, co dzieje się u ich sąsiadów. Podczas pracy nad nową architekturą konteksty usług nie są już od siebie zależne.

Dodatkowo, aby architektura mikroserwisowa była efektywna, należy ustalić wiele procesów, a mianowicie:

• Logowanie;
• śledzenie żądań (Jaeger);
• agregacja błędów (Sentry);
• statusy, komunikaty, zdarzenia z Kubernetes (Event Stream Processing);
• limit wyścigu / wyłącznik automatyczny (możesz użyć Hystrix);
• kontrola łączności usług (używamy Netramesh);
• monitoring (Grafana);
• montaż (TeamCity);
• komunikacja i powiadamianie (Slack, e-mail);
• śledzenie zadań; (Jira)
• przygotowanie dokumentacji.

Aby system nie utracił swojej integralności i pozostał efektywny podczas skalowania, ponownie przemyśleliśmy organizację mikroserwisów w Avito.

Jak zarządzamy mikroserwisami

Poniższa pomoc we wdrożeniu ujednoliconej „polityki partyjnej” wśród wielu mikroserwisów Avito:

  • podział infrastruktury na warstwy;
  • Koncepcja platformy jako usługi (PaaS);
  • monitorowanie wszystkiego, co dzieje się z mikroserwisami.

Warstwy abstrakcji infrastruktury obejmują trzy warstwy. Przejdźmy od góry do dołu.

A. Góra - siatka serwisowa. Na początku próbowaliśmy Istio, ale okazało się, że zużywa zbyt wiele zasobów, co jest zbyt drogie dla naszych woluminów. Dlatego starszy inżynier w zespole architektonicznym Alexander Lukyanchenko opracował własne rozwiązanie - Netramesh (dostępny w Open Source), z którego obecnie korzystamy w produkcji i który zużywa kilkukrotnie mniej zasobów niż Istio (ale nie robi wszystkiego, czym Istio może się pochwalić).
B. Średni – Kubernetes. Wdrażamy i obsługujemy na nim mikroserwisy.
C. Dół - goły metal. Nie używamy chmur ani rzeczy takich jak OpenStack, ale całkowicie polegamy na gołym metalu.

Wszystkie warstwy są łączone poprzez PaaS. A ta platforma z kolei składa się z trzech części.

I. Generatory, kontrolowane za pomocą narzędzia CLI. To ona pomaga programiście stworzyć mikroserwis we właściwy sposób i przy minimalnym wysiłku.

II. Skonsolidowany kolektor z kontrolą wszystkich narzędzi poprzez wspólny pulpit nawigacyjny.

III. Składowanie. Łączy się z programami planującymi, które automatycznie ustawiają wyzwalacze dla znaczących działań. Dzięki takiemu systemowi żadne zadanie nie zostanie pominięte tylko dlatego, że ktoś zapomniał ustawić zadanie w Jirze. Używamy do tego wewnętrznego narzędzia o nazwie Atlas.

Co wiemy o mikroserwisach

Wdrożenie mikroserwisów w Avito również odbywa się według jednego schematu, co ułatwia kontrolę nad nimi na każdym etapie rozwoju i wydania.

Jak działa standardowy potok rozwoju mikrousług?

Ogólnie łańcuch tworzenia mikrousług wygląda następująco:

CLI-push → Ciągła integracja → Pieczenie → Wdrażanie → Sztuczne testy → Testy Canary → Testy ściskania → Produkcja → Konserwacja.

Przeanalizujmy to dokładnie w tej kolejności.

Push CLI

• Tworzenie mikroserwisu.
Długo staraliśmy się nauczyć każdego programistę, jak wykonywać mikrousługi. Obejmowało to napisanie szczegółowych instrukcji w Confluence. Ale schematy uległy zmianie i zostały uzupełnione. Efekt jest taki, że już na początku podróży pojawiło się wąskie gardło: uruchomienie mikroserwisów trwało znacznie dłużej, a mimo to często pojawiały się problemy podczas ich tworzenia.

Na koniec zbudowaliśmy proste narzędzie CLI, które automatyzuje podstawowe kroki podczas tworzenia mikroserwisu. W rzeczywistości zastępuje pierwszy git push. Oto, czym dokładnie się zajmuje.

— Tworzy usługę według szablonu — krok po kroku, w trybie „kreatora”. W backendzie Avito posiadamy szablony dla głównych języków programowania: PHP, Golang i Python.

- Jedno polecenie na raz wdraża środowisko do lokalnego programowania na określonej maszynie - Uruchamia się Minikube, automatycznie generowane są wykresy Helm i uruchamiane w lokalnym kubernetesie.

— Łączy wymaganą bazę danych. Programista nie musi znać adresu IP, loginu i hasła, aby uzyskać dostęp do potrzebnej mu bazy danych – czy to lokalnie, na Stage, czy w produkcji. Co więcej, baza danych jest natychmiast wdrażana w konfiguracji odpornej na błędy i z równoważeniem.

— Sam wykonuje montaż na żywo. Załóżmy, że programista poprawił coś w mikroserwisie za pośrednictwem swojego IDE. Narzędzie widzi zmiany w systemie plików i na ich podstawie przebudowuje aplikację (dla Golang) i uruchamia się ponownie. W przypadku PHP po prostu przekazujemy katalog wewnątrz kostki i tam „automatycznie” następuje przeładowanie na żywo.

— Generuje autotesty. W postaci półfabrykatów, ale całkiem nadaje się do użycia.

• Wdrożenie mikrousług.

Wdrożenie mikroserwisu było dla nas nie lada wyzwaniem. Wymagane były:

I. Plik Dockera.

II. Konfig.
III. Wykres steru, który sam w sobie jest uciążliwy i zawiera:

— same wykresy;
— szablony;
— konkretne wartości, biorąc pod uwagę różne środowiska.

Usunęliśmy problem przeróbki manifestów Kubernetesa, dzięki czemu są one teraz generowane automatycznie. Ale co najważniejsze, uprościli wdrożenie do granic możliwości. Od tego momentu mamy plik Dockerfile, a programista zapisuje całą konfigurację w jednym, krótkim pliku app.toml.

Co wiemy o mikroserwisach

Tak, a w samym app.toml nie ma nic do zrobienia przez minutę. Określamy gdzie i ile kopii usługi ma powstać (na serwerze deweloperskim, na stagingu, w produkcji) oraz wskazujemy jej zależności. Zwróć uwagę na rozmiar linii = „small” w bloku [engine]. Jest to limit, który zostanie przydzielony usłudze poprzez Kubernetes.

Następnie na podstawie konfiguracji automatycznie generowane są wszystkie niezbędne wykresy Helma i tworzone są połączenia z bazami danych.

• Podstawowa walidacja. Takie kontrole są również zautomatyzowane.
Trzeba śledzić:
— czy istnieje plik Dockerfile;
— czy istnieje aplikacja.toml;
— czy dostępna jest dokumentacja?
— czy zależność jest prawidłowa?
— czy ustawiono reguły ostrzegania.
Wracając do ostatniego punktu: właściciel serwisu sam określa, jakie metryki produktu ma monitorować.

• Przygotowanie dokumentacji.
Nadal jest to obszar problemowy. Wydaje się to najbardziej oczywiste, ale jednocześnie jest to zapis „często zapominany”, a przez to wrażliwe ogniwo łańcucha.
Konieczne jest posiadanie dokumentacji dla każdego mikroserwisu. Zawiera następujące bloki.

I. Krótki opis usługi. Dosłownie kilka zdań o tym co robi i dlaczego jest potrzebny.

II. Link do schematu architektury. Ważne jest, aby już na pierwszy rzut oka łatwo było zrozumieć, na przykład, czy używasz Redis do buforowania, czy jako głównego magazynu danych w trybie trwałym. W Avito na razie jest to link do Confluence.

III. Element Runbook. Krótki poradnik dotyczący uruchomienia usługi i zawiłości jej obsługi.

IV. Często zadawane pytania, gdzie dobrze byłoby przewidzieć problemy, jakie mogą napotkać Twoi współpracownicy podczas pracy z serwisem.

V. Opis punktów końcowych dla API. Jeśli nagle nie określisz miejsc docelowych, koledzy, których mikrousługi są powiązane z Twoimi, prawie na pewno za to zapłacą. Teraz używamy do tego Swaggera i naszego rozwiązania o nazwie brief.

VI. Etykiety. Lub znaczniki pokazujące, do jakiego produktu, funkcjonalności lub działu strukturalnego firmy należy dana usługa. Pomagają Ci szybko zrozumieć, na przykład, czy ograniczasz funkcjonalność, którą Twoi współpracownicy wdrożyli dla tej samej jednostki biznesowej tydzień temu.

VII. Właściciel lub właściciele serwisu. W większości przypadków to — lub one — można określić automatycznie za pomocą PaaS, ale dla bezpieczeństwa wymagamy od programisty określenia ich ręcznie.

Wreszcie, dobrą praktyką jest przeglądanie dokumentacji, podobnie jak w przypadku przeglądu kodu.

Ciągła integracja

  • Przygotowanie repozytoriów.
  • Tworzenie potoku w TeamCity.
  • Ustawianie praw.
  • Wyszukaj właścicieli usług. Istnieje tutaj schemat hybrydowy - ręczne znakowanie i minimalna automatyzacja z PaaS. W pełni automatyczny schemat zawodzi, gdy usługi są przekazywane do wsparcia innemu zespołowi programistów lub, na przykład, jeśli twórca usługi odchodzi.
  • Rejestracja usługi w Atlasie (patrz wyżej). Ze wszystkimi jego właścicielami i zależnościami.
  • Sprawdzanie migracji. Sprawdzamy, czy któreś z nich nie jest potencjalnie niebezpieczne. Przykładowo w jednym z nich wyskakuje tablica alter lub coś innego, co może zakłócić kompatybilność schematu danych pomiędzy różnymi wersjami usługi. Wtedy migracja nie jest wykonywana, tylko umieszczana w abonamencie – PaaS musi sygnalizować właścicielowi usługi, kiedy będzie można bezpiecznie z niego korzystać.

Piec

Kolejnym etapem są usługi pakowania przed wdrożeniem.

  • Budowanie aplikacji. Według klasyki – w obrazie Dockera.
  • Generowanie wykresów Helma dla samej usługi i powiązanych zasobów. W tym dla baz danych i pamięci podręcznej. Tworzą się automatycznie zgodnie z konfiguracją app.toml, która została wygenerowana na etapie CLI-push.
  • Tworzenie biletów dla administratorów na otwieranie portów (gdy wymagane).
  • Uruchamianie testów jednostkowych i obliczanie pokrycia kodu. Jeśli pokrycie kodu będzie poniżej określonego progu, najprawdopodobniej usługa nie pójdzie dalej - do wdrożenia. Jeśli będzie na granicy akceptowalności, usłudze zostanie przypisany współczynnik „pesymizujący”: wówczas, jeśli z czasem nie nastąpi poprawa wskaźnika, programista otrzyma powiadomienie, że nie ma postępu w testach ( i trzeba coś z tym zrobić).
  • Uwzględnianie ograniczeń pamięci i procesora. Mikroserwisy piszemy głównie w Golangu i uruchamiamy je w Kubernetesie. Stąd jedna subtelność związana ze specyfiką języka Golang: domyślnie podczas uruchamiania używane są wszystkie rdzenie maszyny, jeśli nie ustawisz jawnie zmiennej GOMAXPROCS, a gdy na tej samej maszynie zostanie uruchomionych kilka takich usług, rozpoczynają się konkurować o zasoby, zakłócając się nawzajem. Poniższe wykresy pokazują, jak zmienia się czas wykonania, jeśli uruchomisz aplikację bez rywalizacji i w trybie wyścigu o zasoby. (Źródła wykresów to tutaj).

Co wiemy o mikroserwisach

Czas realizacji, mniej znaczy lepiej. Maksymalnie: 643 ms, minimalnie: 42 ms. Zdjęcie można kliknąć.

Co wiemy o mikroserwisach

Czas na operację, mniej znaczy lepiej. Maksymalnie: 14091 ns, minimalnie: 151 ns. Zdjęcie można kliknąć.

Na etapie przygotowania montażu możesz ustawić tę zmienną jawnie lub możesz skorzystać z biblioteki automaxprocs od chłopaków z Ubera.

Wdrożyć

• Sprawdzanie konwencji. Zanim zaczniesz dostarczać zestawy usług do zamierzonych środowisk, musisz sprawdzić następujące kwestie:
- Punkty końcowe API.
— Zgodność odpowiedzi punktów końcowych API ze schematem.
— Format dziennika.
— Ustawianie nagłówków żądań do usługi (obecnie robi to netramesh)
— Ustawianie tokena właściciela podczas wysyłania komunikatów do magistrali zdarzeń. Jest to potrzebne do śledzenia łączności usług w całej magistrali. Do magistrali można wysyłać zarówno dane idempotentne, co nie zwiększa łączności usług (co jest dobre), jak i dane biznesowe, które wzmacniają łączność usług (co jest bardzo złe!). A w momencie, gdy łączność staje się problemem, zrozumienie, kto pisze i czyta autobus, pomaga właściwie oddzielić usługi.

W Avito nie ma jeszcze zbyt wielu konwencji, ale ich pula się powiększa. Im więcej takich umów jest dostępnych w formie zrozumiałej i zrozumiałej dla zespołu, tym łatwiej jest zachować spójność pomiędzy mikrousługami.

Testy syntetyczne

• Testowanie w pętli zamkniętej. W tym celu używamy teraz oprogramowania open source Hoverfly.io. Najpierw rejestruje rzeczywiste obciążenie usługi, następnie – już w zamkniętej pętli – je emuluje.

• Test naprężeń. Staramy się, aby wszystkie usługi działały optymalnie. A wszystkie wersje każdej usługi muszą zostać poddane testom obciążeniowym - w ten sposób możemy zrozumieć aktualną wydajność usługi i różnicę w stosunku do poprzednich wersji tej samej usługi. Jeśli po aktualizacji usługi jej wydajność spadła półtorakrotnie, jest to wyraźny sygnał dla jej właścicieli: trzeba zagłębić się w kod i poprawić sytuację.
Zebrane dane wykorzystujemy np. do prawidłowego wdrożenia autoskalowania i ostatecznie ogólnie rozumiemy, jak skalowalna jest usługa.

Podczas testów obciążeniowych sprawdzamy, czy zużycie zasobów mieści się w ustalonych limitach. A my skupiamy się przede wszystkim na skrajnościach.

a) Patrzymy na całkowite obciążenie.
- Za mały - najprawdopodobniej coś w ogóle nie będzie działać, jeśli ładunek nagle spadnie kilka razy.
- Zbyt duży - wymagana optymalizacja.

b) Patrzymy na punkt odcięcia według RPS.
Tutaj patrzymy na różnicę między obecną wersją a poprzednią i całkowitą ilość. Przykładowo, jeśli usługa produkuje 100 rps, to albo jest źle napisana, albo taka jest jej specyfika, ale w każdym razie jest to powód, aby przyjrzeć się usłudze bardzo uważnie.
Jeśli wręcz przeciwnie, jest zbyt wiele RPS, być może wystąpił jakiś błąd i niektóre punkty końcowe przestały wykonywać ładunek, ale inny został po prostu uruchomiony return true;

Testy na kanarkach

Po przejściu testów syntetycznych testujemy mikroserwis na małej liczbie użytkowników. Zaczynamy ostrożnie, z niewielkim udziałem docelowej grupy odbiorców serwisu – poniżej 0,1%. Na tym etapie bardzo ważne jest, aby w monitoringu uwzględnić prawidłowe metryki techniczne i produktowe, aby jak najszybciej pokazać problem w serwisie. Minimalny czas testu na kanarkach to 5 minut, główny to 2 godziny. W przypadku usług skomplikowanych czas ustalamy ręcznie.
Analizujemy:
— metryki specyficzne dla języka, w szczególności procesy robocze php-fpm;
— błędy w Sentry;
— statusy odpowiedzi;
— czas reakcji, dokładny i średni;
- czas oczekiwania;
— wyjątki, przetworzone i nieobsługiwane;
– metryki produktu.

Testowanie ściskania

Test ściskania nazywany jest także testem ściskania. Nazwa techniki została wprowadzona do Netflixa. Jej istota polega na tym, że najpierw zapełniamy jedną instancję rzeczywistym ruchem aż do momentu awarii i w ten sposób ustalamy jego limit. Następnie dodajemy kolejną instancję i ładujemy tę parę - ponownie do maksimum; widzimy ich sufit i deltę przy pierwszym „ściśnięciu”. Dlatego łączymy po jednej instancji na raz i obliczamy wzór zmian.
Dane testowe poprzez „ściskanie” wpływają również do wspólnej bazy danych metryk, gdzie albo wzbogacamy nimi wyniki sztucznego obciążenia, albo nawet zastępujemy nimi „syntetyki”.

Produkcja

• Skalowanie. Kiedy wdrażamy usługę na produkcję, monitorujemy jej skalowanie. Z naszego doświadczenia wynika, że ​​monitorowanie wyłącznie wskaźników CPU jest nieskuteczne. Automatyczne skalowanie za pomocą benchmarku RPS w czystej postaci działa, ale tylko w przypadku niektórych usług, takich jak streaming online. Dlatego najpierw przyglądamy się metrykom produktu specyficznym dla aplikacji.

W rezultacie podczas skalowania analizujemy:
- Wskaźniki procesora i pamięci RAM,
— liczba żądań w kolejce,
- czas odpowiedzi,
— prognoza oparta na zgromadzonych danych historycznych.

Podczas skalowania usługi ważne jest również monitorowanie jej zależności, aby nie skalować pierwszej usługi w łańcuchu, a te, do których uzyskuje dostęp, zawiodą pod obciążeniem. Aby ustalić akceptowalne obciążenie całej puli usług, przeglądamy dane historyczne „najbliższej” zależnej usługi (w oparciu o kombinację wskaźników procesora i pamięci RAM w połączeniu ze wskaźnikami specyficznymi dla aplikacji) i porównujemy je z danymi historycznymi usługi inicjującej i tak dalej w całym „łańcuchu zależności”, od góry do dołu.

Serwis

Po uruchomieniu mikroserwisu możemy do niego dołączyć wyzwalacze.

Oto typowe sytuacje, w których występują wyzwalacze.
— Wykryto potencjalnie niebezpieczne migracje.
— Wydano aktualizacje zabezpieczeń.
— Sama usługa nie była aktualizowana od dłuższego czasu.
— Obciążenie usługi wyraźnie spadło lub niektóre wskaźniki produktu wykraczają poza normalny zakres.
— Usługa nie spełnia już nowych wymagań platformy.

Część z wyzwalaczy odpowiada za stabilność działania, część – w wyniku konserwacji systemu – np. część usługi nie była wdrażana od dłuższego czasu i jej obraz bazowy przestał przechodzić kontrolę bezpieczeństwa.

Panel

Krótko mówiąc, dashboard to panel kontrolny całego naszego PaaS.

  • Pojedynczy punkt informacji o usłudze, zawierający dane o jej zasięgu testowym, liczbie jej obrazów, liczbie egzemplarzy produkcyjnych, wersjach itp.
  • Narzędzie do filtrowania danych po usługach i etykietach (oznaczenia przynależności do jednostek biznesowych, funkcjonalności produktów itp.)
  • Narzędzie do integracji z narzędziami infrastrukturalnymi do śledzenia, rejestrowania i monitorowania.
  • Dokumentacja serwisowa w jednym punkcie.
  • Jeden punkt widzenia na wszystkie zdarzenia w ramach usług.

Co wiemy o mikroserwisach
Co wiemy o mikroserwisach
Co wiemy o mikroserwisach
Co wiemy o mikroserwisach

Razem

Przed wprowadzeniem PaaS nowy programista może spędzić kilka tygodni na zapoznawaniu się ze wszystkimi narzędziami niezbędnymi do uruchomienia mikrousługi w środowisku produkcyjnym: Kubernetes, Helm, nasze wewnętrzne funkcje TeamCity, konfigurowanie połączeń z bazami danych i pamięciami podręcznymi w sposób odporny na awarie itp. Teraz jest to możliwe. Przeczytanie przewodnika Szybki Start i utworzenie samej usługi zajmuje kilka godzin.

Dałem reportaż na ten temat dla HighLoad++ 2018, możecie go obejrzeć wideo и prezentacja.

Bonusowy utwór dla tych, którzy przeczytali do końca

W Avito organizujemy wewnętrzne trzydniowe szkolenie dla programistów z Chrisa Richardsona, ekspert w dziedzinie architektury mikroserwisowej. Chcielibyśmy dać możliwość wzięcia w nim udziału jednemu z czytelników tego wpisu. Tutaj Program szkolenia został opublikowany.

Szkolenie odbędzie się w dniach 5-7 sierpnia w Moskwie. Są to dni robocze, które będą w pełni zajęte. Obiad i szkolenie odbędzie się w naszym biurze, a wybrany uczestnik sam opłaci podróż i zakwaterowanie.

Można zgłaszać chęć udziału w tym formularzu Google. Od Ciebie - odpowiedź na pytanie dlaczego warto wziąć udział w szkoleniu oraz informacje jak się z Tobą skontaktować. Odpowiedz po angielsku, ponieważ Krzysiek sam wybierze uczestnika, który weźmie udział w szkoleniu.
Imię i nazwisko uczestnika szkolenia ogłosimy w aktualizacji tego wpisu oraz na portalach społecznościowych Avito dla programistów (AvitoTech w Фейсбуке, VKontakte, Twitter) nie później niż 19 lipca.

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

Dodaj komentarz