Tradycją w Polsce jest wykorzystanie urlopu/chorobowego do prowadzenia remontów. Jako że siedzę na L4, to jest to najlepsza okazja, by zadbać o blogaska. Przy okazji przedstawić wam pewną ciekawą technologię.

Mikroserwisy robione bez sensu

W epoce mikroserwisów coraz częściej można spotkać aplikacje, które są tak naprawdę „mikroserwisami”, bo nie posiadają cech, wymaganych dla tej architektury. Z moich obserwacji wynika, że są dwa główne źródła takiego stanu rzeczy.

  • Źródło marketingowe – zazwyczaj typowe BDD (Buzzword Driven Development). Skoro wszyscy robią mikroserwisy i to się sprzedaje, to my też. Jest to bardzo stara przypadłość.
  • Źródło biznesowe – dokładnie, bardzo prosta domena biznesowa. Jeżeli mamy dwa przypadki użycia na krzyż albo niewielką ilość encji, a jednocześnie potrzebujemy pewnych cech, które dają mikroserwisy np. dynamicznej skalowalności, czy ciągłości działania.

Przy czym mam świadomość, że jak w przypadku wszelkich prób opisania świata przez pryzmat własnych doświadczeń, będzie to opis niepełny.

Pierwszy punkt jest oczywisty i co kilka lat przewija się w takiej czy innej formie. Znacznie ciekawszy jest punkt drugi. Wybór architektury mikroserwisowej wynika tu z braku znajomości innych rozwiązań np. mod_cluster (który zazwyczaj wystarcza do zapewnienia dostępności i skalowalności) czy wiary, że „mikroserwisy to same to ogarną”. Jednocześnie ignoruje się dwie cechy, które powinien spełniać projekt, by w ogóle podjąć próbę użycia tego rodzaju architektury:

  • Niezależność zespołów
  • Niezależność technologii

W obu przypadkach z naciskiem na liczbę mnogą. Pozatechniczną cechą architektury mikroserwisowej jest możliwość budowania aplikacji w oparciu o rozproszone zespoły, które pracują niezależnie od siebie. Zespoły te mogą używać różnych, całkowicie niekompatybilnych, technologii. Na przykład tworząc aplikację do e-learningu, użyję innych technologii do obsługi streamingu video, a innych do ogarnięcia płatności. Pierwsze zadanie opędzę np. Elixirem, a to drugie napiszę w Javie (w zasadzie wykorzystam jakiegoś gotowca). Całość oczywiście będzie gadać ze sobą po jakimś uniwersalnym protokole.
Z drugiej strony mając jeden niewielki zespół, do stworzenia takiej aplikacji nie mogę wymagać, że będzie on ogarniać kilku różnych technologii. Muszę wybrać takie rozwiązanie architektoniczne, które pozwoli mi na jak najlepszą wydajność pracy. Być może okaże się, że odpowiednio napisany monolit będzie tu lepszy. Co ciekawe (też z własnych obserwacji), nawet jeżeli mamy architekturę mikroserwisową, która poniekąd wymusza separację komponentów, to jeżeli mamy mały zespół, to i tak zacznie powstawać, albo jakieś dziwne spaghetti pomiędzy komponentami, albo zespół będzie kodował metodami Kopiego-Pasty wszędzie tam, gdzie kod się powtarza. Dokładnie w ten sam sposób jak ma to miejsce w przypadku monolitu.

UWAGA!

Dobrze napisany monolit wymaga świadomego zespołu, który potrafi wykorzystywać clean architecture oraz opiera się nieuprawnionym operacjom.

Inne problemy z mikroserwisami

Poza wyżej wymienionymi problemami, które wynikają z niedopasowania architektury do problemu, jest też jeszcze kilka innych, które zazwyczaj umykają przy wyborze architektury.

Koszty sprzętu

Na początku mało kto zdaje sobie sprawę ile może kosztować wdrożenie mikroserwisów. Cztery jednostki m4.large w amazonowym AWS kosztują obecnie 0,60$/h jednocześnie czterokrotnie większa jednostka m4.2xlarge (4x więcej rdzeni i pamięci RAM) kosztuje 1,44$/h. Często padające hasło „mikroserwisy można wdrażać na mniejszych i tańszych serwerach” może okazać się znacznie kosztowniejsze w porównaniu z wykorzystaniem monolitu na mocniejszej maszynie.

Wydajność

Bardzo ciekawy argument, bo często mikroserwisy przedstawia się jako remedium na problemy z wydajnością. A to nie tak. Oczywiście dzięki znacznie lepszej skalowalności można poprawić wydajność w pewnych rejonach np. można przyjąć więcej żądań. Z drugiej strony mogą pojawić się nowe problemy związane z tempem komunikacji. Wywołanie zdalne nie będzie tak szybkie jak wywołanie lokalne. Nawet jeżeli jest robione w obrębie pojedynczej maszyny. Po prostu komunikacja trwa.

Koszty technologii (i ludzi)

Wprowadzając architekturę mikroserwisową do naszego świata, musimy zapewnić odpowiednie zaplecze. Jak wcześniej wspomniałem, koszty sprzętowe mogą być znaczące, dlatego chcemy mieć kontrolę nad sprzętem. Tym samym wprowadzamy różnego rodzaju narzędzia, które pozwolą nam na atomizację komponentów i niezależne zarządzanie nimi. Poszczególne mikroserwisy pakujemy w kontenery dockerowe, te rozrzucamy na środowiska przy pomocy managera w rodzaju Kubernetes, na którym zmiany w boxach są zarządzane za pomocą Ansible, a same boxy są zwirtualizowane z użyciem Mesos, dzięki czemu możemy tworzyć „dziwne” konfiguracje sprzętowe (wirtualne). Do tego procesy deploymentu są zarządzane z poziomu Jenkinsa i jego pipelien-ów. BDD pełną gębą. A przecież, ktoś to musi ogarnąć.
Koszty technologii to nie tylko koszty licencji, ale przede wszystkim koszty ludzi, którzy będą je później utrzymywać. W dodatku nasi ludzie muszą mieć wiedzę z wielu dziedzin, które niekoniecznie idą w parze. Dla dużych organizacji zatrudnienie „gościa od buildów”, którego zadaniem będzie jedynie analizowanie zepsutych zadań jenkinsowych, pod kątem potencjalnych błędów w konfiguracji nie jest dużym kosztem. Dla małej firmy może okazać się to zabójcze. Zresztą tu pojawia się pojęcie DevOps, które oznacza metodykę, ale też pewien zestaw umiejętności i wiedzy. Te z natury muszą być bardzo rozległe. Obejmują znajomość narzędzi, technik programowania, zagadnień z zakresu testowania, bezpieczeństwa, ale też całą masę umiejętności miękkich potrzebnych do pracy z wieloma różnymi ludźmi począwszy od programistów, poprzez managerów, osoby ze wsparcia technicznego, a na klientach kończąc. Wierzę, że można to ogarnąć, ale nie wierzę, że ogarniesz to w przeciągu kilku tygodni czy nawet miesięcy. W dodatku jak je już opanujesz, to może okazać się, że po zmianie organizacji tylko wiedza techniczna (i ogólne umiejętności miękkie) będzie przydatna. Nowa firma, inne procesy, inne standardy.

Typowy system javowy

Spróbujmy popatrzeć na ten problem inaczej. Po pierwsze, jeżeli nasz system jest w całości napisany pod JVM-a, to możemy pokusić się o użycie natywnych dla JVM-a rozwiązań. Po drugie, jeżeli idziemy w mikroserwisy i potrafimy pociąć naszą domenę na mikroserwisy, to zazwyczaj będą one odpowiadać poszczególnym aplikacjom. Po trzecie potrafimy jako tako w CI/CD i umiemy zapewnić sobie jako taką stabilność, to zapewne efekt będzie następujący. Kilka aplikacji Spring Bootowych, które gadają za pomocą Http REST API, bo Spring ma zarówno klienta, jak i pozwala na generowanie końcówek (endpointów) REST-owych. Oczywiście to nie jest takie proste jak się wydaje. W systemach tego rodzaju możemy wyróżnić dwa rodzaje interfejsów komunikacyjnych. Pierwszy to interfejs zewnętrzny, który jest wykorzystywany przez naszych klientów. Ten prawie na pewno powinien być zestandaryzowany. Jakiś REST, może SOAP, może całość napędzane na CORBA/kę. Drugi interfejs to wewnętrzny interfejs komunikacji. Służy on do komunikacji pomiędzy mikroserwisami. Tu zazwyczaj używamy jakiegoś binarnego ustrojstwa w rodzaju gRPC albo Aerona.

I tu wchodzi JLupin…

Największą bolączką opisanego wyżej systemu jest komunikacja wewnątrz. Trzeba jakoś dogadywać mikroserwisy, udostępniać je w ramach systemu oraz monitorować ich stan. Dużo roboty, którą dobrze by było oddelegować. JLupin Next Server jest rozwiązaniem, które pomaga ogarnąć cały ten burdel. Jest niezależne od frameworków aplikacyjnych, ale out-of-box dostarcza nam wsparcie dla Springa, a to dlatego, że twórcy wyszli z założenia, że Spring jest najpopularniejszym rozwiązaniem i trzeba je wspierać by JLNS się przyjął. Co nam daje JLNS?

Po pierwsze ogarnia komunikację wewnątrz systemową. Takie rzeczy jak Service Discovery, wewnętrzna komunikacja w oparciu o szybki binarny protokół, czy nadzór nad stanem poszczególnych mikroserwisów mamy od ręki. Ponad. to mamy zapewniony pseudo hot swap, który pozwala zachować ciągłość działania aplikacji. Mamy narzędzia do monitorowania i zarządzania naszej mikroserwisowej infrastruktury. Mamy możliwość podzielenia naszego systemu na strefy, umieszczania mikroserwisów na konkretnych maszynach w określonych strefach (logicznych) oraz co za tym idzie możliwość zarządzania widocznością mikroserwisów. Do tego twórcy przygotowali plugin do mavena, który pozwala na automatyzację deploymentu oraz plugin do IntelliJ, który pozwala na ogarnięcie zależności generowanie powiązań.

Instalacja

Instalacja sprowadza się do pobrania odpowiedniej paczki i jej rozpakowania na dysku. W przypadku wersji community należy jeszcze wyczyścić zawartość katalogu application/, bo siedzi tam przykładowa aplikacja.

Uruchomienie

W katalogu start mamy skrypt start.sh, który po uruchomieniu nie daje śladów życia, ale w katalogu logs/server/main/start będzie sobie leżał plik server.out, a logs/server/main kilka wyspecjalizowanych logów.
Jeżeli w katalogu start uruchomimy skrypt control.sh, to dostaniemy się do konsoli administracyjnej naszego serwera. Tu mamy wgląd w stan naszego noda oraz możemy zarządzać mikroserwisami. Wygląda to tak:

No trochę vintage, ale zawsze lepiej niż konsola weblogica.

Community vs Enterprise

Różnice są trzy. Po pierwsze wersja community jest ograniczona do 15 mikroserwisów na node. Po drugie może być tylko jeden node. Po trzecie nie ma dostępnego narzędzia Control Center, do zarządzania nodami (bo i po co).

Dodatki

Jeżeli chcemy możemy jeszcze dociągnąć sobie plugin do IntelliJ, bo z jakiś nieznanych mi powodów nie ma go w repozytorium JetBrains. Możemy też pobrać plugin do mavena jeżeli nie chcemy łączyć się do repozytorium JLupina.

Podsumowanie

Na dziś starczy. W kolejnych wpisach pobawimy się w tworzenie prostego systemu bankowego, z wykorzystaniem mikroserwisów i JLupina.