Přechod od monolitu k mikroslužbám: historie a praxe

V tomto článku budu mluvit o tom, jak se projekt, na kterém pracuji, proměnil z velkého monolitu na sadu mikroslužeb.

Projekt začal svou historii již poměrně dávno, na začátku roku 2000. První verze byly napsány ve Visual Basic 6. Postupem času se ukázalo, že vývoj v tomto jazyce bude v budoucnu obtížně podporovatelný, protože IDE a jazyk sám je špatně vyvinut. Na konci roku 2000 bylo rozhodnuto přejít na slibnější C#. Nová verze se psala souběžně s revizí té staré, postupně se v .NET psal další a další kód. Backend v C# byl zpočátku zaměřen na architekturu služeb, ale během vývoje byly použity běžné knihovny s logikou a služby byly spouštěny v jediném procesu. Výsledkem byla aplikace, kterou jsme nazvali „monolit služby“.

Jednou z mála výhod této kombinace byla schopnost služeb volat se navzájem přes externí API. Pro přechod na korektnější službu a v budoucnu i architekturu mikroslužeb byly jasné předpoklady.

S naší prací na rozkladu jsme začali kolem roku 2015. Ještě jsme nedosáhli ideálního stavu – stále existují části velkého projektu, které lze jen stěží nazvat monolity, ale jako mikroslužby také nevypadají. Přesto je pokrok výrazný.
Budu o tom mluvit v článku.

Přechod od monolitu k mikroslužbám: historie a praxe

Obsah

Architektura a problémy stávajícího řešení


Zpočátku architektura vypadala takto: UI je samostatná aplikace, monolitická část je napsána ve Visual Basic 6, aplikace .NET je sada souvisejících služeb pracujících s poměrně velkou databází.

Nevýhody předchozího řešení

Jediný bod selhání
Měli jsme jediný bod selhání: aplikace .NET běžela v jediném procesu. Pokud některý modul selhal, selhala celá aplikace a bylo nutné ji restartovat. Vzhledem k tomu, že automatizujeme velké množství procesů pro různé uživatele, kvůli výpadku jednoho z nich nemohli všichni nějakou dobu pracovat. A v případě softwarové chyby nepomohlo ani zálohování.

Fronta vylepšení
Tato nevýhoda je spíše organizační. Naše aplikace má mnoho zákazníků a všichni ji chtějí co nejdříve vylepšit. Dříve to nebylo možné dělat paralelně a všichni zákazníci stáli ve frontě. Tento proces byl pro podniky negativní, protože musely prokázat, že jejich úkol je hodnotný. A vývojový tým strávil čas organizováním této fronty. To vyžadovalo spoustu času a úsilí a produkt se nakonec nemohl změnit tak rychle, jak by si přáli.

Neoptimální využití zdrojů
Při hostování služeb v jediném procesu jsme vždy kompletně zkopírovali konfiguraci ze serveru na server. Chtěli jsme umístit nejvíce zatížené služby odděleně, abychom neplýtvali prostředky a získali flexibilnější kontrolu nad naším schématem nasazení.

Obtížná implementace moderních technologií
Problém známý všem vývojářům: existuje touha zavést do projektu moderní technologie, ale není příležitost. U velkého monolitického řešení se jakákoliv aktualizace stávající knihovny, nemluvě o přechodu na novou, mění v docela netriviální úkol. Dokázat vedoucímu týmu, že to přinese více bonusů než promarněné nervy, trvá dlouho.

Obtížnost vydávání změn
To byl nejvážnější problém – vydávali jsme vydání každé dva měsíce.
Každé vydání se pro banku stalo skutečnou katastrofou, navzdory testování a úsilí vývojářů. Podnik pochopil, že na začátku týdne některé jeho funkce nebudou fungovat. A vývojáři pochopili, že je čeká týden vážných incidentů.
Všichni měli touhu situaci změnit.

Očekávání od mikroslužeb


Výdej komponent, když jsou připraveny. Dodávka komponent, jakmile jsou připraveny, rozkladem roztoku a oddělením různých procesů.

Malé produktové týmy. To je důležité, protože velký tým pracující na starém monolitu bylo obtížné řídit. Takový tým byl nucen pracovat podle přísného procesu, ale chtěli více kreativity a nezávislosti. To si mohly dovolit jen malé týmy.

Izolace služeb v samostatných procesech. V ideálním případě jsem to chtěl izolovat v kontejnerech, ale velké množství služeb napsaných v .NET Framework běží pouze na Windows. Nyní se objevují služby založené na .NET Core, ale zatím je jich málo.

Flexibilita nasazení. Rádi bychom spojovali služby tak, jak to potřebujeme, a ne tak, jak si to kód vynucuje.

Využití nových technologií. To je zajímavé pro každého programátora.

Problémy s přechodem


Samozřejmě, pokud by bylo snadné rozbít monolit na mikroslužby, nebylo by třeba o tom mluvit na konferencích a psát články. V tomto procesu je mnoho úskalí, popíšu ty hlavní, které nás brzdily.

První problém typické pro většinu monolitů: soudržnost obchodní logiky. Když píšeme monolit, chceme znovu použít naše třídy, abychom nepsali zbytečný kód. A při přechodu na mikroslužby se to stává problémem: veškerý kód je poměrně pevně propojen a je obtížné oddělit služby.

V době zahájení prací mělo úložiště více než 500 projektů a více než 700 tisíc řádků kódu. To je docela velké rozhodnutí a druhý problém. Nebylo možné to jednoduše vzít a rozdělit na mikroslužby.

Třetí problém — nedostatek potřebné infrastruktury. Ve skutečnosti jsme ručně kopírovali zdrojový kód na servery.

Jak přejít od monolitu k mikroslužbám


Poskytování mikroslužeb

Zaprvé jsme se sami okamžitě rozhodli, že oddělení mikroslužeb je iterativní proces. Vždy jsme byli povinni vyvíjet obchodní problémy paralelně. Jak to provedeme technicky, je již náš problém. Proto jsme se připravili na iterativní proces. Nebude to fungovat jinak, pokud máte velkou aplikaci a není zpočátku připravena k přepsání.

Jaké metody používáme k izolaci mikroslužeb?

První cesta — přesunout stávající moduly jako služby. V tomto ohledu jsme měli štěstí: již byly registrované služby, které fungovaly pomocí protokolu WCF. Byli rozděleni do samostatných celků. Portovali jsme je samostatně a do každého sestavení jsme přidali malý launcher. Byla napsána pomocí nádherné knihovny Topshelf, která umožňuje spouštět aplikaci jako službu i jako konzoli. To je vhodné pro ladění, protože řešení nevyžaduje žádné další projekty.

Služby byly propojeny podle obchodní logiky, protože využívaly společné sestavy a pracovaly se společnou databází. Stěží by se daly nazvat mikroslužbami v jejich čisté podobě. Tyto služby bychom však mohli poskytovat samostatně, v různých procesech. To samo o sobě umožnilo snížit jejich vzájemný vliv, snížit problém s paralelním vývojem a jediným bodem selhání.

Sestavení s hostitelem je pouze jeden řádek kódu ve třídě Program. Práci s Topshelf jsme schovali do pomocné třídy.

namespace RBA.Services.Accounts.Host
{
   internal class Program
   {
      private static void Main(string[] args)
      {
        HostRunner<Accounts>.Run("RBA.Services.Accounts.Host");

       }
    }
}

Druhý způsob alokace mikroslužeb je: vytvořit je k řešení nových problémů. Pokud zároveň monolit neroste, je to již vynikající, což znamená, že jdeme správným směrem. Pro řešení nových problémů jsme se pokusili vytvořit samostatné služby. Pokud by taková příležitost byla, pak jsme vytvořili více „kanonických“ služeb, které kompletně spravují svůj vlastní datový model, samostatnou databázi.

Jako mnozí jsme začali s autentizačními a autorizačními službami. Jsou na to jako stvořené. Jsou nezávislé, zpravidla mají samostatný datový model. Oni sami s monolitem neinteragují, pouze se na ně obrací, aby vyřešili nějaké problémy. Pomocí těchto služeb můžete zahájit přechod na novou architekturu, odladit na nich infrastrukturu, vyzkoušet některé přístupy související se síťovými knihovnami atd. V naší organizaci nemáme žádné týmy, které by nedokázaly vytvořit ověřovací službu.

Třetí způsob alokace mikroslužebTen, který používáme, je pro nás trochu specifický. Jedná se o odstranění obchodní logiky z vrstvy uživatelského rozhraní. Naší hlavní aplikací uživatelského rozhraní je desktop; stejně jako backend je napsán v C#. Vývojáři pravidelně dělali chyby a přenášeli části logiky do uživatelského rozhraní, které měly existovat v backendu a být znovu použity.

Pokud se podíváte na skutečný příklad z kódu části uživatelského rozhraní, můžete vidět, že většina tohoto řešení obsahuje skutečnou obchodní logiku, která je užitečná v jiných procesech, nejen pro vytváření formuláře uživatelského rozhraní.

Přechod od monolitu k mikroslužbám: historie a praxe

Skutečná logika uživatelského rozhraní je pouze v posledních několika řádcích. Přenesli jsme jej na server, aby jej bylo možné znovu použít, čímž jsme snížili uživatelské rozhraní a dosáhli správné architektury.

Čtvrtý a nejdůležitější způsob izolace mikroslužeb, která umožňuje zmenšit monolit, je odstranění stávajících služeb se zpracováním. Když vyjmeme stávající moduly tak, jak jsou, výsledek se ne vždy vývojářům líbí a obchodní proces může být od doby vytvoření funkce zastaralý. Díky refaktoringu můžeme podpořit nový obchodní proces, protože obchodní požadavky se neustále mění. Dokážeme vylepšit zdrojový kód, odstranit známé defekty a vytvořit lepší datový model. Existuje mnoho výhod.

Oddělení služeb od zpracování je neoddělitelně spjato s konceptem ohraničeného kontextu. Jedná se o koncept od Domain Driven Design. Znamená to část doménového modelu, ve které jsou jednoznačně definovány všechny termíny jednoho jazyka. Podívejme se jako příklad na kontext pojištění a účtů. Máme monolitickou aplikaci a potřebujeme pracovat s účtem v pojištění. Očekáváme, že vývojář najde existující třídu účtu v jiném sestavení, odkáže na ni z třídy pojištění a my budeme mít funkční kód. Bude respektován princip DRY, úkol bude proveden rychleji pomocí stávajícího kódu.

Ve výsledku se ukazuje, že souvislosti účtů a pojištění jsou propojeny. Jakmile se objeví nové požadavky, toto spojení bude narušovat vývoj, čímž se zvýší složitost již tak složité obchodní logiky. Chcete-li tento problém vyřešit, musíte najít hranice mezi kontexty v kódu a odstranit jejich porušení. Například v souvislosti s pojištěním je docela možné, že bude stačit 20místné číslo účtu centrální banky a datum otevření účtu.

Abychom oddělili tyto ohraničené kontexty od sebe a zahájili proces oddělení mikroslužeb od monolitického řešení, použili jsme přístup, jako je vytváření externích API v rámci aplikace. Pokud jsme věděli, že by se z nějakého modulu měla stát mikroslužba, nějak upravená v rámci procesu, tak jsme okamžitě pomocí externích volání volali logiku, která patří do jiného omezeného kontextu. Například přes REST nebo WCF.

Pevně ​​jsme se rozhodli, že se nevyhneme kódu, který by vyžadoval distribuované transakce. V našem případě se ukázalo být docela snadné toto pravidlo dodržet. Ještě jsme se nesetkali se situacemi, kdy by byly striktně distribuované transakce opravdu potřeba – výsledná konzistence mezi moduly je zcela dostatečná.

Podívejme se na konkrétní příklad. Máme koncept orchestrátoru – potrubí, které zpracovává entitu „aplikace“. Postupně vytvoří klienta, účet a bankovní kartu. Pokud jsou klient a účet úspěšně vytvořeny, ale vytvoření karty se nezdaří, aplikace nepřejde do stavu „úspěšná“ a zůstane ve stavu „karta nevytvořena“. V budoucnu to aktivita na pozadí vyzvedne a dokončí. Systém je již nějakou dobu ve stavu nekonzistence, ale jsme s tím vesměs spokojeni.

Pokud nastane situace, kdy je potřeba důsledně ukládat část dat, půjdeme nejspíše do konsolidace služby, abychom ji zpracovali v jednom procesu.

Podívejme se na příklad alokace mikroslužby. Jak to můžete relativně bezpečně přivést do výroby? V tomto příkladu máme samostatnou část systému - modul mzdové agendy, jehož jednu z kódových sekcí bychom rádi udělali mikroservis.

Přechod od monolitu k mikroslužbám: historie a praxe

V první řadě vytvoříme mikroslužbu přepsáním kódu. Vylepšujeme některé aspekty, se kterými jsme nebyli spokojeni. Implementujeme nové obchodní požadavky od zákazníka. K propojení mezi UI a backendem přidáváme API Gateway, která zajistí přesměrování hovorů.

Přechod od monolitu k mikroslužbám: historie a praxe

Dále tuto konfiguraci uvolníme do provozu, ale v pilotním stavu. Většina našich uživatelů stále pracuje se starými obchodními procesy. Pro nové uživatele vyvíjíme novou verzi monolitické aplikace, která již tento proces neobsahuje. V podstatě máme kombinaci monolitu a mikroslužby fungující jako pilot.

Přechod od monolitu k mikroslužbám: historie a praxe

S úspěšným pilotním testem chápeme, že nová konfigurace je skutečně funkční, můžeme z rovnice odstranit starý monolit a ponechat novou konfiguraci místo starého řešení.

Přechod od monolitu k mikroslužbám: historie a praxe

Celkově využíváme téměř všechny existující metody pro rozdělení zdrojového kódu monolitu. Všechny nám umožňují zmenšit velikost částí aplikace a přeložit je do nových knihoven, čímž se zlepší zdrojový kód.

Práce s databází


Databáze se dá dělit hůře než zdrojový kód, protože obsahuje nejen aktuální schéma, ale i nashromážděná historická data.

Naše databáze, stejně jako mnoho jiných, měla ještě jednu důležitou nevýhodu – její obrovskou velikost. Tato databáze byla navržena podle složité obchodní logiky monolitu a vztahů nahromaděných mezi tabulkami různých ohraničených kontextů.

V našem případě se ke všem problémům (velká databáze, mnoho spojení, někdy nejasné hranice mezi tabulkami) objevil problém, který se vyskytuje v mnoha velkých projektech: použití šablony sdílené databáze. Data byla převzata z tabulek prostřednictvím zobrazení, prostřednictvím replikace a odeslána do jiných systémů, kde byla tato replikace potřebná. V důsledku toho jsme nemohli přesunout tabulky do samostatného schématu, protože byly aktivně používány.

V separaci nám pomáhá stejné rozdělení do omezených kontextů v kódu. Obvykle nám dává docela dobrou představu o tom, jak rozkládáme data na úrovni databáze. Rozumíme, které tabulky patří do jednoho ohraničeného kontextu a které do jiného.

Použili jsme dvě globální metody dělení databáze: dělení existujících tabulek a dělení se zpracováním.

Oddělení existujících tabulek je dobrou metodou, pokud je datová struktura dobrá, splňuje obchodní požadavky a všichni jsou s ní spokojeni. V tomto případě můžeme oddělit existující tabulky do samostatného schématu.

Oddělení se zpracováním je potřeba, když se obchodní model výrazně změnil a tabulky už nás vůbec neuspokojují.

Rozdělení stávajících tabulek. Musíme si určit, co budeme oddělovat. Bez této znalosti nebude nic fungovat a zde nám pomůže oddělení ohraničených kontextů v kódu. Zpravidla platí, že pokud rozumíte hranicím kontextů ve zdrojovém kódu, je jasné, které tabulky by měly být zařazeny do seznamu pro oddělení.

Představme si, že máme řešení, ve kterém dva monolitní moduly interagují s jednou databází. Musíme se ujistit, že pouze jeden modul komunikuje se sekcí oddělených tabulek a druhý s ní začne interagovat přes API. Pro začátek postačí, že se přes API provádí pouze záznam. To je nezbytná podmínka, abychom mohli mluvit o nezávislosti mikroslužeb. Čtecí spojení může zůstat, dokud nenastane žádný velký problém.

Přechod od monolitu k mikroslužbám: historie a praxe

Dalším krokem je, že můžeme oddělit část kódu, která pracuje s oddělenými tabulkami, se zpracováním nebo bez něj, do samostatné mikroslužby a spustit ji v samostatném procesu, kontejneru. Půjde o samostatnou službu s napojením na monolitní databázi a ty tabulky, které se jí přímo netýkají. Monolit stále interaguje pro čtení s odnímatelnou částí.

Přechod od monolitu k mikroslužbám: historie a praxe

Později toto spojení odstraníme, to znamená, že čtení dat z monolitické aplikace z oddělených tabulek bude také přeneseno do API.

Přechod od monolitu k mikroslužbám: historie a praxe

Dále z obecné databáze vybereme tabulky, se kterými pracuje pouze nová mikroslužba. Tabulky můžeme přesunout do samostatného schématu nebo i do samostatné fyzické databáze. Mezi mikroslužbou a monolitní databází stále existuje čtecí spojení, ale není se čeho obávat, v této konfiguraci může žít poměrně dlouho.

Přechod od monolitu k mikroslužbám: historie a praxe

Posledním krokem je úplné odstranění všech připojení. V tomto případě možná budeme muset migrovat data z hlavní databáze. Někdy chceme znovu použít některá data nebo adresáře replikované z externích systémů v několika databázích. To se nám pravidelně stává.

Přechod od monolitu k mikroslužbám: historie a praxe

Oddělení zpracování. Tato metoda je velmi podobná té první, pouze v opačném pořadí. Okamžitě alokujeme novou databázi a novou mikroslužbu, která interaguje s monolitem prostřednictvím API. Zároveň ale zůstává sada databázových tabulek, které chceme v budoucnu smazat. Již ji nepotřebujeme, nahradili jsme ji v novém modelu.

Přechod od monolitu k mikroslužbám: historie a praxe

Aby toto schéma fungovalo, budeme pravděpodobně potřebovat přechodné období.

Pak existují dva možné přístupy.

První: duplikujeme všechna data v nové a staré databázi. V tomto případě máme redundanci dat a mohou nastat problémy se synchronizací. Můžeme ale vzít dva různé klienty. Jedna bude fungovat s novou verzí, druhá se starou.

Druhý: data rozdělujeme podle některých obchodních kritérií. Například jsme měli v systému 5 produktů, které byly uloženy ve staré databázi. Šestého v rámci nového obchodního úkolu umístíme do nové databáze. Budeme ale potřebovat API Gateway, která bude tato data synchronizovat a ukáže klientovi odkud a co má získat.

Oba přístupy fungují, vybírejte podle situace.

Poté, co jsme si jisti, že vše funguje, lze část monolitu, která pracuje se starými databázovými strukturami, zakázat.

Přechod od monolitu k mikroslužbám: historie a praxe

Posledním krokem je odstranění starých datových struktur.

Přechod od monolitu k mikroslužbám: historie a praxe

Abychom to shrnuli, můžeme říci, že máme problémy s databází: je obtížné s ní pracovat ve srovnání se zdrojovým kódem, je obtížnější ji sdílet, ale lze a mělo by se to udělat. Našli jsme několik způsobů, které nám to umožňují docela bezpečně, ale stále je snazší dělat chyby s daty než se zdrojovým kódem.

Práce se zdrojovým kódem


Takto vypadal diagram zdrojového kódu, když jsme začali analyzovat monolitický projekt.

Přechod od monolitu k mikroslužbám: historie a praxe

Dá se zhruba rozdělit do tří vrstev. Jedná se o vrstvu spuštěných modulů, pluginů, služeb a jednotlivých aktivit. Ve skutečnosti to byly vstupní body v rámci monolitického řešení. Všechny byly těsně utěsněny společnou vrstvou. Mělo to obchodní logiku, kterou služby sdílely, a spoustu spojení. Každá služba a plugin používaly až 10 nebo více běžných sestavení v závislosti na jejich velikosti a svědomí vývojářů.

Měli jsme štěstí na knihovny infrastruktury, které bylo možné používat samostatně.

Občas nastala situace, kdy některé běžné objekty do této vrstvy ve skutečnosti nepatřily, ale byly infrastrukturními knihovnami. To bylo vyřešeno přejmenováním.

Největší obavou byly ohraničené kontexty. Stalo se, že v jednom společném sestavení byly smíchány 3-4 kontexty a vzájemně se používaly v rámci stejných obchodních funkcí. Bylo nutné pochopit, kde se to dá rozdělit a po jakých hranicích, a co dál s mapováním tohoto rozdělení na sestavy zdrojového kódu.

Pro proces dělení kódu jsme formulovali několik pravidel.

První: Už jsme nechtěli sdílet obchodní logiku mezi službami, aktivitami a pluginy. Chtěli jsme učinit obchodní logiku nezávislou v rámci mikroslužeb. Mikroslužby jsou naproti tomu v ideálním případě chápány jako služby, které existují zcela nezávisle. Domnívám se, že tento přístup je poněkud marnotratný a těžko dosažitelný, protože například služby v C# budou v každém případě propojeny standardní knihovnou. Náš systém je napsán v C#, jiné technologie jsme zatím nepoužili. Proto jsme se rozhodli, že si můžeme dovolit použít běžné technické sestavy. Hlavní věc je, že neobsahují žádné fragmenty obchodní logiky. Pokud máte na ORM, který používáte, pohodlný obal, pak je jeho kopírování ze služby do služby velmi drahé.

Náš tým je fanouškem designu řízeného doménou, takže cibulová architektura se nám skvěle hodila. Základem našich služeb není datová přístupová vrstva, ale sestava s doménovou logikou, která obsahuje pouze obchodní logiku a nemá žádné propojení s infrastrukturou. Zároveň můžeme nezávisle upravovat sestavení domény pro řešení problémů souvisejících s frameworky.

V této fázi jsme narazili na náš první vážný problém. Služba se musela odkazovat na jedno doménové sestavení, chtěli jsme osamostatnit logiku a princip DRY nás zde značně brzdil. Vývojáři chtěli znovu použít třídy ze sousedních sestavení, aby se předešlo duplicitě, a v důsledku toho se domény začaly znovu propojovat. Analyzovali jsme výsledky a rozhodli jsme se, že možná problém spočívá také v oblasti zařízení pro ukládání zdrojového kódu. Měli jsme velké úložiště obsahující veškerý zdrojový kód. Řešení celého projektu bylo velmi obtížné sestavit na lokálním stroji. Pro části projektu proto vznikala samostatná malá řešení a nikdo nezakazoval k nim přidávat nějaké společné nebo doménové sestavení a znovu je používat. Jediný nástroj, který nám to neumožnil, byla kontrola kódu. Někdy se to ale také nepovedlo.

Poté jsme začali přecházet na model s oddělenými repozitáři. Obchodní logika již nepřechází ze služby do služby, domény se skutečně staly nezávislými. Ohraničené kontexty jsou podporovány jasněji. Jak znovu využíváme knihovny infrastruktury? Oddělili jsme je do samostatného úložiště, pak jsme je vložili do balíčků Nuget, které jsme vložili do Artifactory. Při jakékoli změně dojde k sestavení a zveřejnění automaticky.

Přechod od monolitu k mikroslužbám: historie a praxe

Naše služby začaly odkazovat na balíčky interní infrastruktury stejně jako na externí. Externí knihovny stahujeme z Nugetu. Pro práci s Artifactory, kam jsme tyto balíčky umístili, jsme použili dva správce balíčků. V malých repozitářích jsme také používali Nuget. V úložištích s více službami jsme použili Paket, který poskytuje větší konzistenci verzí mezi moduly.

Přechod od monolitu k mikroslužbám: historie a praxe

Tím, že pracujeme na zdrojovém kódu, mírně měníme architekturu a oddělujeme repozitáře, činíme naše služby nezávislejšími.

Problémy s infrastrukturou


Většina nevýhod přechodu na mikroslužby souvisí s infrastrukturou. Budete potřebovat automatizované nasazení, budete potřebovat nové knihovny pro provoz infrastruktury.

Ruční instalace v prostředí

Zpočátku jsme řešení pro prostředí nainstalovali ručně. Pro automatizaci tohoto procesu jsme vytvořili potrubí CI/CD. Proces kontinuálního dodávání jsme zvolili proto, že kontinuální nasazení pro nás není z pohledu obchodních procesů zatím přijatelné. Odeslání do provozu se proto provádí pomocí tlačítka a pro testování - automaticky.

Přechod od monolitu k mikroslužbám: historie a praxe

Pro ukládání zdrojového kódu používáme Atlassian, Bitbucket a pro stavbu Bamboo. Rádi píšeme sestavovací skripty v Cake, protože je to stejné jako C#. Hotové balíčky přicházejí do Artifactory a Ansible se automaticky dostane na testovací servery, po kterých mohou být okamžitě testovány.

Přechod od monolitu k mikroslužbám: historie a praxe

Samostatné protokolování


Jednou z myšlenek monolitu bylo poskytovat sdílenou těžbu dřeva. Potřebovali jsme také pochopit, co dělat s jednotlivými logy, které jsou na discích. Naše protokoly jsou zapisovány do textových souborů. Rozhodli jsme se použít standardní zásobník ELK. Nepsali jsme do ELK přímo přes poskytovatele, ale rozhodli jsme se, že dokončíme textové protokoly a zapíšeme do nich trasovací ID jako identifikátor a přidáme název služby, aby bylo možné tyto protokoly později analyzovat.

Přechod od monolitu k mikroslužbám: historie a praxe

Pomocí Filebeat máme příležitost shromažďovat naše protokoly ze serverů, poté je transformovat, používat Kibana k vytváření dotazů v uživatelském rozhraní a sledovat, jak hovor probíhal mezi službami. Trace ID s tím hodně pomáhá.

Testování a ladění souvisejících služeb


Zpočátku jsme úplně nechápali, jak ladit vyvíjené služby. S monolitem bylo všechno jednoduché, provozovali jsme to na místním stroji. Nejprve se pokusili udělat totéž s mikroslužbami, ale někdy je k úplnému spuštění jedné mikroslužby potřeba spustit několik dalších, což je nepohodlné. Uvědomili jsme si, že musíme přejít na model, kdy na lokálním počítači ponecháme pouze službu nebo služby, které chceme ladit. Zbývající služby jsou využívány ze serverů, které odpovídají konfiguraci s prod. Po ladění, během testování, jsou pro každou úlohu testovacímu serveru vydány pouze změněné služby. Řešení je tedy testováno v podobě, v jaké se v budoucnu objeví ve výrobě.

Existují servery, které provozují pouze produkční verze služeb. Tyto servery jsou potřeba v případě incidentů, pro kontrolu doručení před nasazením a pro interní školení.

Přidali jsme automatizovaný proces testování pomocí oblíbené knihovny Specflow. Testy se spouštějí automaticky pomocí NUnit ihned po nasazení z Ansible. Pokud je pokrytí úloh plně automatické, není potřeba ruční testování. I když někdy je stále vyžadováno další ruční testování. Značky v Jira používáme k určení, které testy spustit pro konkrétní problém.

Navíc vzrostla potřeba zátěžového testování, které se dříve provádělo jen ve výjimečných případech. Používáme JMeter ke spouštění testů, InfluxDB k jejich ukládání a Grafana k vytváření procesních grafů.

Čeho jsme dosáhli?


Nejprve jsme se zbavili konceptu „uvolnění“. Pryč jsou dvouměsíční monstrózní vydání, kdy byl tento kolos nasazen v produkčním prostředí, což dočasně narušilo obchodní procesy. Nyní nasazujeme služby v průměru každých 1,5 dne, seskupujeme je, protože jdou do provozu po schválení.

V našem systému nejsou žádná fatální selhání. Pokud uvolníme mikroslužbu s chybou, funkce s ní spojené budou nefunkční a všechny ostatní funkce nebudou ovlivněny. To výrazně zlepšuje uživatelský zážitek.

Můžeme ovládat vzor nasazení. V případě potřeby můžete skupiny služeb vybrat odděleně od zbytku řešení.

Navíc jsme výrazně omezili problém s velkou frontou vylepšení. Nyní máme samostatné produktové týmy, které s některými službami pracují nezávisle. Proces Scrum se zde již dobře hodí. Konkrétní tým může mít samostatného vlastníka produktu, který mu přiděluje úkoly.

Shrnutí

  • Mikroslužby se dobře hodí pro rozklad složitých systémů. V procesu začínáme chápat, co je v našem systému, jaké tam jsou omezené kontexty, kde leží jejich hranice. To vám umožní správně distribuovat vylepšení mezi moduly a zabránit záměně kódu.
  • Mikroslužby poskytují organizační výhody. Často se o nich mluví pouze jako o architektuře, ale jakákoli architektura je potřebná k řešení obchodních potřeb, a ne sama o sobě. Můžeme tedy říci, že mikroslužby jsou vhodné pro řešení problémů v malých týmech, vzhledem k tomu, že Scrum je nyní velmi populární.
  • Separace je iterativní proces. Nemůžete vzít aplikaci a jednoduše ji rozdělit na mikroslužby. Výsledný produkt pravděpodobně nebude funkční. Při dedikaci mikroslužeb je výhodné stávající legacy přepsat, to znamená udělat z něj kód, který se nám líbí a lépe odpovídá potřebám firmy z hlediska funkčnosti a rychlosti.

    Malé upozornění: Náklady na přechod na mikroslužby jsou poměrně značné. Samotné vyřešení problému s infrastrukturou trvalo dlouho. Pokud tedy máte malou aplikaci, která nevyžaduje specifické škálování, pokud nemáte velký počet zákazníků, kteří soutěží o pozornost a čas vašeho týmu, pak mikroslužby nemusí být to, co dnes potřebujete. Je to docela drahé. Pokud proces zahájíte mikroslužbami, pak budou náklady zpočátku vyšší, než když stejný projekt zahájíte vývojem monolitu.

    PS Emotivnější příběh (a jakoby pro vás osobně) - dle odkaz.
    Zde je plná verze zprávy.

Zdroj: www.habr.com

Přidat komentář