Ciach! Ciach! Szalone nożyczki. Czyli jak (nie) ciąć (mikro)serwisy
Cześć!
Dzisiaj w jednej ręce trzymam klawiaturę, w drugiej nożyczki? Po co mi te nożyczki? Zgodnie z tematem maila chciałbym pogadać z Tobą o cięciu (mikro)serwisów.
Ciekawą drogę przeszliśmy od momentu gdy powiedzenie “robisz monolit” brzmiało jak “twoja stara klaszcze u Rubika” do czasów gdy wiele ludzi wzbrania się od mówienia słowa “mikroserwisy”. Jak to? W świecie IT regularnie pojawiają się “buzz words”. Tak jak w ubieraniu się tak i w programowaniu moda to ważna rzecz. Słowa klucze. To jest też ciekawe jak oryginalnie dobre pomysły i wzorce przemielone przez papkę blogosferową potrafią wyewoluować w coś niezjadliwego. No ale, do konkretów!
Uważam, że mikroserwisy teraz są często źle rozumiane w dwóch kategoriach:
- technicznej - sprowadzane są do samego hostowania, dockera, kubernetowego podsa. Mikroserwis to coś co wystawiamy osobno i już,
- socjologicznej - do robienia bo inni tak robią. O zgrozo, często liderzy techniczni, project managerowie wybierają taką drogę, bo nie mogą znaleźć pracowników, bo każdy chce robić mikroserwisy - serio! Owczy pęd. Szalone nożyczki - ciach, ciach, ciach.
Skupmy się dzisiaj na części technicznej. A konkretnie na jednostce wdrożeniowej - co to takiego? Jest to jedna aplikacja hostowana w ramach jednego systemu uruchomieniowego - czyli np. web api. Tak, często taką jednostkę wdrożeniową nazywa się mikroserwisem. Czy słusznie?
Kiedy wg mnie warto mieć w aplikacji więcej “jednostek wdrożeniowych”?
- Różnica w ruchu w poszczególnych fragmentach aplikacji - załóżmy, że robimy system do sprzedaży ebooków. W takim systemie moduł zarządzania i dodawania nowych pozycji będzie miał dużo mniejsze obłożenie niż wyszukiwarka pozycji czy też nawet moduł obsługi zakupów. Dla tego nawet jeśli nie robimy load balancingu tylko skalujemy wzwyż (vertical scalling) zwiększając moc serwerów warto mieć je na osobnych maszynach, dzięki temu na wyszukiwarkę będzie można dać większą maszynkę, na panel zarządzający jaką malutką, a na zakupy średniej wielkości. Pozwoli to nam lepiej zutylizować zasoby, a co za tym idzie oszczędzić pieniądze.
- Zwiększenie przepustowości - jeżeli chcemy móc dynamicznie przesuwać suwaki, bo np. ciężko nam ocenić jaki będzie “load” w naszej aplikacji, albo może nam się dynamicznie zmieniać - np. w przywołanym systemie ebooków przy okazji Black Friday, kupowania prezentów na Gwiazdkę może dojść do wysokich “peaków” ruchu, warto móc sobie przeskalować to wszerz (horizontal scalling) dorzucając równolegle maszynki. Co to w ogóle znaczy “dorzucając maszynki”? Włączamy wtedy tak naprawdę klony tego samego serwisu, które w dalszym ciągu są powiązane ze sobą bo strzelają (zwykle) do tej samej bazy. Powinny takie klony być bezstanowe, bo jeden klon o drugim nic nie wie i stanu nie odczyta. System i storage powinny też być przygotowane na to, że dwie instancje mogą robić to samo jednocześnie.
- Multi tenancy - Jeśli robimy rozwiązanie gdzie sprzedajemy ten sam system wielu klientom to warto zadbać o to, żeby praca i obciążenie jaki robi jeden klient nie miało wpływu na działanie systemu dla innego klienta. Poza samą izolacją danych, która jest oczywista problemem może być to, że np. jeden klient ma wyraźnie większy ruch niż inni lub po prostu, że coś u niego się wydarzyło nieprzewidzianego (np. nagle wzmożony ruch, jakiś atak ddos, spam na api przez to, że źle je wywoływał). Najgorsze co byśmy chcieli to musieć się tłumaczyć jakiemuś klientowi, że jego system działa, bo jego konkurent co też korzysta z naszego systemu ma wzmożony ruch. Aby to zrobić podobnie jak w poprzednim punkcie robimy klony naszego serwisu z tym, że tym razem również z osobną bazą danych. Takie podejście nazywamy shardingiem - czyli stawianie zupełnie autonomicznych niepowiązanych klonów serwisu.
- Multi region - nasza aplikacja musi działać w wielu regionach geograficznych. Wiele osób nie zdaje sobie sprawę, ale przez Ocean Atlantycki pociągnięte są grube zwoje światłowodów i to nimi pędzi ten Internet. Pomiędzy innymi kontynentami podobnie. Wbrew pozorom nawet dla nich ma to znaczenie. Na co dzień o tym nie myślimy, ale fizyczne odległości też mają znaczenie. Dlatego jeśli chcemy mieć aplikację, która jest wydajna nie tylko w naszym lokalnym regionie geograficznym to musimy to też fizycznie obsłużyć poprzez postawienie serwerów znajdujących się blisko naszego odbiorcy. Takie coś wymaga zwykle połączeniu działań opisanych w pkt 2 i 3 plus replikacji i jeszcze sprawnego podziałowi, które dane mają być dzielone (i jak) pomiędzy regionami.
Co wg mnie jest nietrafnym lub co najmniej nadużywanym argumentem?
- Konieczność ciągłej dostępności serwisu - faktycznie fajnie by było mieć “rolling deploy” czyli że stawiamy kopię serwisu, następnie przepinamy ruch do niego i wyłączamy starą wersję. Z tym, że w praktyce po ponad 12 latach kariery nie pracowałem w systemie gdzie biznes by się nie dał namówić na rozsądne okienko serwisowe. Zwykle w takich systemach przy jakich pracujemy jest to więcej zachodu niż zysku. Zapewnienie tego naprawdę mocno komplikuje i architekturę developerską i operacyjną. Wiadomo można to zrobić, ale zwykle wypadało się zapytać “a po co?“. Czyli upewnijmy się najpierw czy faktycznie dla naszego klienta to must have.
- Moduły używają inny storage - kiedyś (?) gdy zwykle bazy danych były stawiane na tym samym serwerze miało to znaczenie, ale w dzisiejszej dobie chmur, dockerów tak naprawdę nasza baza danych stoi zwykle w zupełnie innym miejscu niż nasza aplikacja. Jeśli mamy 2 bazy z jakiś powodów (nawet 2 różne typu Elastic i Postgres) to czy na pewno nasz serwer aplikacji musi być pocięty? Jeśli ruch jest w każdym jego fragmencie ten sam, nie ma konieczności robienia żadnego z tych “case’ów”, które opisałem powyżej to po co to ciąć? Czemu jedna klasa by nie mogła strzelać do Elastica a druga do Postgresa?
O kwestii socjologiczno widzimisiowej chętnie opowiem kiedy indziej - może za tydzień?
A jakie jest zdanie na ten temat? Zgadzasz się ze mną, czy nie bardzo? Chętnie podyskutuję. Czekam na odpowiedź z Twoim zdaniem!
Pozdrawiam serdecznie!
Oskar