Mikroserwisy – makroproblemy
Mikroserwisy są tematem, który przewija się przez wszystkie konferencje IT i coraz więcej firm zaczyna rozmowy o projektach od „robimy mikroserwisy i słucham co dalej”. Ale czy to faktycznie jest taki złoty środek na wszystko i warto je stosować w każdej firmie?
Absolutnie nie.
Boom na mikro
Idea obecna w mikroserwisach jest starsza niż może Ci się wydawać. Bo początkowo pomysł na rozproszone serwisy współpracujące ze sobą został już wykorzystany przy okazji architektury SOA. A więc mówimy o początku lat 90. To z SOA wyewoluowały mikroserwisy. Pierwszy raz ten termin został użyty w 2005 roku. Jednak popularność zaczęły zyskiwać od około 2011 roku. Zdecydowanie pomogło im pokazanie światu Dockera w 2013 roku. Do tego Netflix i Amazon, jako prekursorzy w korzystaniu z tego podejścia, bardzo chętnie pokazywali zalety tej architektury.
Do tej pory Netflix działający na chmurze AWS jest wręcz wzorcowym przykładem wykorzystania mikroserwisów i wyciśnięcia z nich wszystkich zalet. Ale osiągnęli to m.in. dzięki temu, że stworzyli masę narzędzi, które im w tym pomagają. Jak chociażby software „psujący” losowo serwisy na produkcji, który nazywa się Chaos Monkey (dostępny na GitHubie). Po co takie coś? Bo tylko w ten sposób Netflix ma pewność, że robią mikroserwisy dobrze i są przygotowani na awarię. A cechą mikroserwisów jest to, że permanentnie któreś nie działają.
Tak więc mikroserwisy to temat ostatnich kilku lat. Chociaż w ostatnim roku na konferencjach dostrzegłem już nadejście czasów „post-mikroserwisowych”. Narracja zmieniła się z „wszystko w mikroserwisach” na „jak wyjść z mikroserwisów”.
Gdzie leży problem?
Dlaczego mikroserwisy są tak ciężkie w zastosowaniu i nie warto w mnie wchodzić pochopnie? Oto kilka przykładów problemów z jakimi musi się zmierzyć Twój zespół jeżeli planujecie wprowadzić mikroserwisy.
Podział odpowiedzialności
Podstawa od której właściwie musi się zacząć wprowadzanie mikroserwisów. Czy potraficie w swoim projekcie wydzielić obszary, domeny, które są niezależne od siebie i tworzą zamkniętą całość?
Z tym tematem nie radzą sobie nawet zespoły pracujące z monolitem. Jest to temat trudny i wymagający współpracy programistów i biznesu. Bo Wasze mikroserwisy to będą właśnie te odrębne poddomeny, moduły. Nie może być tak, że macie co prawda podział i gdzieś tam jest fakturowanie, a jeszcze gdzieś indziej zamówienia ale jak się tak spojrzy w kod to faktury korzystają z jakichś funkcji z zamówień, a zamówienia to są połączone właściwie z magazynem. O ile w monolicie takie coś jeszcze przejdzie, bo będzie co najwyżej brzydkie jak się na kod spojrzy tak w mikroserwisach jest to niedopuszczalne. Bo wprowadzanie jakichkolwiek silnych zależności pomiędzy serwisami to proszenie się o kłopoty i ogromny wzrost skomplikowania. Bo teraz już nie macie po prostu osobnego pliku do którego się odwołacie. Teraz trzeba nagle synchronizować pracę tak naprawdę niezależnych aplikacji, które pewnie mają osobne bazy i mogą być w różnych wersjach rozwijanych przez różne zespoły!
W tym temacie warto zacząć chociażby od zagadnienia Domain Driven Design.
Niezależne wdrożenia i odporność na brak któregoś serwisu
Ten punkt to jednej z ważniejszych elementów pracy z mikroserwisami. Bo podstawową zaletą korzystania z nich jest zwiększenie dostępności. Czyli zapewnienie działania usługi nawet jeżeli część z jej elementów przestała działać.
Jedną z zalet pójścia w mikroserwisy jest możliwość niezależnego wdrażania każdego serwisu. Co więcej zwykle robionego przez inny zespół albo w innej technologii. Tylko w tym momencie zależności, o których mowa w poprzednim punkcie od razu muszą być wyeliminowane. Kolejna rzecz to wprowadzanie czy wypracowanie kontraktów pomiędzy serwisami. Bo z jednej strony możemy wdrażać wszystko niezależnie ale jednak nadal serwisy musza móc ze sobą „rozmawiać” mimo, że jeden z nich dostał aktualizację.
No i najważniejsza rzecz w tym punkcie – reakcja na problemy z innymi serwisami. Kiedy mamy zwykłą aplikację to sprawa jest prosta – coś się zepsuło to aplikacja pada, robimy restart i wszystko znowu śmiga. A w przypadku mikroserwisów dochodzimy bardzo szybko do etapu kiedy musimy mieć przewidzianą strategię reakcji na błędy. Bo co zrobimy kiedy jeden serwis już zapisał dane, a drugi akurat „umarł” zanim je też przetworzył? No to jakieś rozproszone transakcje. Albo może próba ponawiania połączenia? A może strategia „mam to gdzieś, lecimy dalej”? To wszystko to decyzje, które trzeba podjąć. Tak samo jak decyzją i koniecznością jest wprowadzenie usługi, która będzie pozwalała odnaleźć pozostałe serwisy. Bo mikroserwisy są, a za chwilę ich nie ma. I teraz skąd mamy wiedzieć czy umarły czy po prostu zmieniły adres IP bo dostawca chmury nam je przełączył na inny node? Więc mamy kolejną rzecz, która wymaga przemyślenia, uruchomienia i konfiguracji.
W tym temacie warto poznać chociażby takie zagadnienia:
- rozproszone transakcje / sagi (https://developers.redhat.com/blog/2018/10/01/patterns-for-distributed-transactions-within-a-microservices-architecture/)
- service discovery (https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/)
Monitoring
W przypadku aplikacji monolitycznej wystarczy dodać logowanie i ewentualnie monitorowanie ruchu do i z aplikacji. Kiedy coś się stanie to po prostych logach z treścią wyjątku możemy dojść do tego co się stało.
Jednak jeżeli mówimy o mikroserwisach to mówimy o konieczności analizowania logów z wielu serwisów. I teraz w jaki sposób będziesz wiedział, że błąd, który pojawił się w dwóch serwisach dotyczy tej samej akcji? Albo czy dane, które użytkownik wpuścił w jednym serwisie dotarły do kolejnych i gdzie właściwie są? Żeby to ogarnąć trzeba wprowadzać kolejne mechanizmy takie jak chociażby correlation id, które pozwala dowiedzieć się czy dane w dwóch serwisach dotyczyły tej samej akcji. To powoduje, że mamy kolejny mechanizm do wdrożenia i utrzymania, który wynika tylko z faktu, że mamy rozproszone serwisy.
Drugą sprawą w kontekście monitoringu jest sam monitoring działających serwisów jako maszyn i usług. Jeżeli pójdziemy w dużą automatyzację (która jest konieczna, żeby mikroserwisy nie przyprawiały o ból głowy zbyt mocno) to tak naprawdę powierzamy skryptom pilnowanie pod jakim adresem działa teraz nasz serwis i ile jego kopii w ogóle jest dostępnych. Bo mamy mechanizmy autoskalowania, które reagują na ruch i np. zwiększają liczbę kontenerów kiedy przyjdzie więcej użytkowników. Mamy też awarie sieci i przełączanie maszyn w chmurach więc adresy naszych serwisów zmieniają się dynamicznie. Dlatego monitoring musi być dobrze zaplanowany i złożony.
Tutaj podstawowy temat to np. correlation id.
Testowanie
To jest „ulubiony” temat wszystkich osób mówiących o mikroserwisach. W przypadku zwykłej aplikacji testy integracyjne czy tym bardziej end-to-end są proste. Mamy endpointy. Mamy bazę. Nawet w najgorszym crapie możemy takie testy dodać i sprawdzić co jakiś czas czy aplikacja działa z perspektywy użytkownika poprawnie.
A teraz dokładamy do tego zawodną sieć i serwisy, które aktualizują się w dowolnym momencie.
Szansa, że będziemy mieli w pełni działające całe środowisko testowe, ze wszystkimi serwisami jest równie duża co koniunkcja wszystkich planet w układzie słonecznym. Jak jeden serwis zadziała to drugi akurat się zaktualizował. Jeżeli oba działają to sieć zawiodła. Jak dokładamy trzeci to dwa pierwsze zmieniły adres. I tak dalej.
Więc testowanie mikroserwisów to naprawdę duże wyzwanie i sztuka wypracowania kompromisów i mechanizmów pozwalających być w miarę pewnym działania całości w oparciu o testy mniejszych wycinków naszego systemu.
Eventual consistency
Mikroserwisy to komunikacja asynchroniczna. W dodatku z wykorzystaniem sieci. Jeżeli coś można powiedzieć na pewno o sieci to to, że lubi nie działać. Więc w zależności od tego jak zareagujemy nasze dane mogą się w ogóle nie zapisać albo przez jakiś czas byś w różnym stanie w różnych serwisach. Bo jeden już je sobie przerobił, a drugi to umarł i dopiero za minutę je odbierze z kolejki. Chyba, że kolejka akurat też umrze to przetworzy je za kilka minut i dopiero wtedy użytkownik zobaczy na stronie albo w skrzynce mailowej to co dodał w formularzu.
Eventual consistency w przypadku mikroserwisów to nie jest coś co wprowadzamy lub nie. To jest coś co po prostu jest obecne w mniejszym lub większym stopni. Jedyne co możemy zrobić to wypracować strategię radzenia sobie z tym zjawiskiem albo w porozumieniu z biznesem uznać, że nic się nie stanie jak jakieś dane będą niespójne.
Szybkość komunikacji
Coś na co pewnie niewiele osób zwraca uwagę. Mikroserwisy to usługi, które komunikują się ze sobą np. za pomocą RESTa, a ogólnie mówiąc poprzez sieć. Więc każde połączenie dwóch serwisów nie jest natychmiastowe jak w przypadku monolitu gdzie wywołanie funkcji z innego obiektu jest wręcz natychmiastowe. Tutaj każda zależność pomiędzy serwisami powoduje zauważalne spadki szybkości przetwarzania danych. I jest to kolejny powód dla którego tak ważne jest rozdzielenie odpowiedzialności. Bo jeżeli do przetworzenia jednego zapytania będziemy musieli wysłać 100 requestów pomiędzy wszystkimi serwisami to sumarycznie aplikacja będzie sprawiała wrażenie powolnej albo po prostu nie będzie w stanie obsłużyć tak dużej liczby użytkowników jak zakładaliśmy.
Podsumowanie
Tematy powyżej to są podstawowe kwestie i od przemyślenia tych spraw i zagłębienia się w te tematy dopiero powinniście zaczynać rozważania czy warto iść w to rozwiązanie. Bo mikroserwisy są ciężką w opanowaniu architekturą, a ich zalety widać dopiero od pewnej skali problemu jaki rozwiązujecie.
Najważniejsza sprawa we wprowadzaniu mikroserwisów – NIGDY nie zaczynamy projektu od mikroserwisów. Nawet jak planujemy naprawdę dużą usługę. Bo przeciętna, monolityczna aplikacja spokojnie obsłuży przy dobrej konfiguracji dziesiątki czy setki tysięcy użytkowników, a koszt i czas jej wdrożenia będą mniejsze niż samo przygotowanie środowiska do pracy z mikroserwisami.
I na koniec warto zapamiętać – w mikroserwisach nie wolno zakładać, że jakiś element na pewno się nie zepsuje. Bo prawdopodobnie będzie to pierwsza rzecz jaka przestanie działać. Czy to sieć, czy to kontenery, czy chmura albo baza danych lub kolejka. Każdy kolejny klocek w tej układance to kolejna rzecz, która może zawieść. I trzeba być na to gotowym.
Hej Marek,
Właśnie dzisiaj tworzyłem artykuł na niemalże ten sam temat, rozbudowany o Dockera.
Publikacja w czwartek. Widzę u Ciebie sporo wspólnych przekonań i poglądów
Pozdrawiam
Damian