Jak nedělat mikroservisy
Mikroservisy představují aktuální téma v návrhu a implementaci aplikací, a proto bych se rád podělil v tomto článku o negativní zkušenosti, na které jsme my sami narazili nebo získali od jiných. V článku najdete několik témat, na které je vhodné si během realizace mikroservis dát pozot, pokud si chcete ušetřit starosti.
Pro mikroservisy musí být důvod
S tímto tématem se už asi opakuji, ale jsem přesvědčený o tom, že ve většině případů spíše vítězí technické nadšení se pustit do realizace mikroservis než skutečné důvody, že by tomu tak mělo být. My technici se rádi učíme nové technologie, technické výzvy máme rádi, Kubernetes je konečně ta správná platforma atd. — buďme upřímní, pokud by bylo jen na nás, tak se do toho pustíme hned.
Bohužel realita je taková, že ne všichni dokážou předem dohlédnout dopady takového rozhodnutí a uvědomit si negativa spojená s komplexitou řešení, pracností a termíny.
Pro mě pořád zůstavají dva zásadní důvody, proč se pustit do mikroservis:
- technické důvody jako např. škálovatelnost, performance, deployment/release nebo i různě vhodné technologie/jazyky pro řešení různých funkčních domén. Osobně je pro mě “killer” důvod ta škálovatelnost společně s výsokou výkonností, protože pokud opravdu takový požadavek existuje, tak se to monolitickou aplikací bude řešit hodně těžko (možná to ani nepůjde, protože vše má své limity).
- organizační důvody jako např. rozdělení do týmů. Organizace do týmů a architektura aplikací jdou ruku v ruce, proto nelze nad tímto (netechnickým) důvodem jen mávnout rukou. Čím dál více společností prochází agilní transformací, vznikají autonomní týmy se svojí svobodou, ale i odpovědností a mikroservisní architektura jde těmto požadavkům resp. realitě naproti.
Ale není to jen o monolitech a mikroservisách, těch možností “mezi” nebo zcela jiných architekturních přístupů (např. SOA) je více a je potřeba si umět vybrat. Tedy znát funkční a nefunkční požadavky na cílové řešení a podle toho zvolit vhodnou architekturu, klidně i monolit, když to dává největší smysl.
Zkušenosti k nezaplacení
Žádná jiná architektura nevyžaduje přítomnost seniorních a zkušených lidí jako ta mikroservisní. Pokud v monolitu něco nedomyslíte nebo implementace se zrovna nepovede, tak si můžete pomoci refaktoringem a nebo při nejhorším budete trpět na výkonnosti a budete mít nějaký overhead při správě a dalším rozvoji aplikace. Pokud to samé vás potká při realizaci mikroservis, tak to jsou vše problémy na druhou — udělali jste špatně dekompozici mikroslužeb? Společný kód sdílíte mezi mikroservisami tak, že z toho máte více starostí než užitku? Logování a monitoring necháváte až na konec, to se vždy dá nějak dodělat? Zjišťujete, že zabezpečení tolika API v oddělených mikroslužbách je přeci jen jiná úroveň než jak to bylo v jedné aplikaci? Zvolili jste asynchronní způsob komunikace mezi mikroservisami, který ze začátku fungoval, ale postupem času se z každé změny přenášených dat stala noční můra? Strategii zpracování a propagaci výjímek zatím neřešíte, pořešíte až na konci vývoje? atd.
Pokud na začátku uděláte něco špatně, tak vás to v průběhu realizace bude trápit mnohem více než u monilitu a náklady spojené s nápravou budou mnohem větší.
Proto více než jinde je dobré mít v týmu někoho, kdo ví — ideálně kdo má reálné zkušenosti nebo aspoň načteno a má představu o celkové komplexnosti řešení včetně možných dopadů jednotlivých rozhodnutí.
Vývoj mikroslužeb je ze začátku pomalejší
Jednou z výhod mikroservisního přístupu je rychlejší dodávka — týmy jsou malé, každý své oblasti perfektně rozumí, zvolil se takový jazyk, ve kterém se řešená oblast bude nejlépe implementovat. Toto ale neplatí pokud s mikroservisami začínáte. Overhead spojený s návrhem architektury řešení, s přípravou běhové platformy včetně CI/CD a s nastavením všech funkcí přes jednotlivé mikroservisy (logování, monitoring, zabezpečení, komunikace atd.) je značný. Proto nelze čekat, že na začátku bude tým schopný dodávat jednu funkci za druhou.
Bohužel často je očekávání businessu opačné, chce vidět co nejrychlejší návratnost své investice (ROI). Navíc pokud si toto nepřizná ani samotný tým, pak je oheň na streše. Zde existuje asi jen jedno doporučení — dopředu vše transparetně businessu komunikovat, vysvětlit výhody a nevýhody mikroservis a hlavně si neříkat, že “my to zvládneme lépe než ostatní” nebo “logování a zabezpečení pořešíme až bude více klidu”.
Pozor na mikrolity
Podstatou mikroservisní architektury není jen oddělení aplikačního kódu do samostatných mikroservis, ale zejména plné oddělení na úrovni fungování každé mikroservisy, tedy nezávislost na vnitřním stavu, na způsobu ukládání dat nebo i na způsobu komunikace s okolím.
Tvorbou mikroservis člověk netvoří nic jiného než distribuovaný systém, který ze samotné podstaty není lehkou úlohou, protože se nejedná o lokální komunikaci v rámci jedné aplikace, ale o vzdálenou komunikaci mezi více nezávislými aplikacemi skrz infrastrukturní prostředí, které samo o sobě přidává další úroveň složitosti (latence, spolehlivost spojení, zabezpečení atd.).
Proto posun od monolitu k mikrolitu nepřinese žádné zásadnější zlepšení, spíše naopak. Je potřeba se vyvarovat synchronní / blokující komunikaci mezi mikroslužbami, protože takový způsob komunikace funguje pouze v ideálních podmínkach, nikoliv v realném produkčním prostředí.
Začneme modulárně, skončíme mikroservisně
Ne vždy je na začátku jasné, zda mikroservisy budou opravdu nutné — co s tím v takovém případě? Jako vhodné řešení se zdá být to, že půjdeme cestou modulárního monolitu a pokud se pak v budoucnu opravdu ukáže potřeba mikroservis (většinou z důvodu škálovatelnosti a výkonnosti), tak to doděláme.
Není to asi úplně mimo, ale těch úskalí je tam celkem hodně a nejsem si úplně jistý, že člověk pak nenarazí, když začne “předělávat” na mikroservisy:
- musíte se donutit mít opravdu moduly, které mezi sebou komunikují přes domluvený kontrakt (API), které sice bude mít podobu rozhraní (interface), ale bude to zcela jasně oddělovat modul od modulu.
- uložení dat musí být nezávislé. Tím neříkám, že co modul to jiná databáze nebo jiné schéma v DB, ale musíte být schopni data oddělit, když na to přijde. Tedy mít společnou entitu User s referencí na všechny další entity nepřichází v úvahu. Tento bod je o to více důležitý, protože nejčastěji to bude výkonnost a škálovatelnost, proč se pustíte do mikroservis a to se sdíleným úložištěm dohromady nejde. Toto je skoro vždy ta nejtěžší část při refaktoringu monolitu na mikroservisy.
- moduly mezi sebou budou komunikovat synchronně, ale z důvodu škálovatelnosti mikroslužeb budete muset přejít na asynchronní komunikaci a to není jen o změně implementace rozhraní, to má vliv i na vnitřní logiku modulu.
Z tohoto pohledu se zdá být nakonec lepší přístup, kdy z monolitu postupným “ukrajováním” funkčností vznikají zcela nové mikroservisy. Je to takový “kočkopes”, kdy máte monolit, ale musíte ho implementovat s tím, že jednou to možná budou mikroservisy, ale bohužel pokud to nikdy nebude, tak jste vynaložili nemalý overhead na něco, co je zbytečné, navíc vás to po celou dobu bude brzdit. A nebudou nakonec bohatě stačit makroservisy nebo miniservisy?
Testování v produkci
Pro řadu “klasických” testerů a manažerů je to stále nepředstavitelná věc, aby na produkci šly ne zcela otestované funkce, ale bohužel v mikroservisním světě to jinak nejde. Když pominu skutečnost, že testy mě dávají spíše pocit jistoty než záruku, že tam žádné chyby nebudou, tak hlavně z podstavy složitosti mikroservis nejsem často vůbec schopen danou funkčnost otestovat jinde než na produkčním prostředí (nebo aspoň s rozumným úsilím).
Toto je nutné přijmout jako fakt a podle toho změnit přístup k testování a vlastně i k samotnému vývoji. Je potřeba si osvojit techniky jako A/B testování, Canary release nebo Blue-green deployment. Začít používat feature flags, které mě umožní zapínání/vypínání funkcí za běhu aplikace. A samozřejmě je nezbytné mít úroveň logování a monitoringu mikroslužeb na takové úrovni, abych vše dokázal sledovat a měřit.
Shrnutí
- mikroservisy ano, ale musí pro to být důvod
- lidi se zkušenostmi a znalostmi z mikroservisního světa se vám bohatě zaplatí
- pokud s mikroservisami začínáte, tak počítejte s tím, že něž se to celé rozjede, tak to chvíli bude trvat
- pokud si nejste jisti, zda mikroservisy budete v budoucnu potřebovat, tak raději začněte psát “hezký a efektivní” monolit než se od začátku omezovat budoucím a nejistým přechodem na mikroservisy
- komunikace mezi servisami musí být z povahy asynchronní, což často znamená zcela jiný pohled na implementaci různých “standardních” funkčností, vzláště ve srovnání s monolitickým světem
- bez testování to nejde, ale s “klasickým” testováním také ne