Prechod od monolitu k mikroslužbám: história a prax

V tomto článku budem hovoriť o tom, ako sa projekt, na ktorom pracujem, pretransformoval z veľkého monolitu na súbor mikroslužieb.

Projekt začal svoju históriu pomerne dávno, začiatkom roku 2000. Prvé verzie boli napísané vo Visual Basic 6. Postupom času sa ukázalo, že vývoj v tomto jazyku bude v budúcnosti ťažko podporiteľný, keďže IDE a samotný jazyk sú slabo rozvinuté. Na konci roku 2000 bolo rozhodnuté prejsť na sľubnejší C#. Nová verzia sa písala súbežne s revíziou starej, postupne sa v .NET písalo viac a viac kódu. Backend v C# bol spočiatku zameraný na architektúru služieb, ale počas vývoja boli použité bežné knižnice s logikou a služby boli spustené v jedinom procese. Výsledkom bola aplikácia, ktorú sme nazvali „monolit služby“.

Jednou z mála výhod tejto kombinácie bola schopnosť služieb volať sa navzájom cez externé API. Pre prechod na korektnejšiu službu a v budúcnosti aj mikroservisnú architektúru boli jasné predpoklady.

S prácou na rozklade sme začali okolo roku 2015. Ešte sme sa nedostali do ideálneho stavu – stále sú tu časti veľkého projektu, ktoré sa len ťažko dajú nazvať monolitmi, no ani na mikroslužby nevyzerajú. Napriek tomu je pokrok výrazný.
Budem o tom hovoriť v článku.

Prechod od monolitu k mikroslužbám: história a prax

Obsah

Architektúra a problémy existujúceho riešenia


Spočiatku architektúra vyzerala nasledovne: UI je samostatná aplikácia, monolitická časť je napísaná vo Visual Basic 6, aplikácia .NET je súbor súvisiacich služieb pracujúcich s pomerne veľkou databázou.

Nevýhody predchádzajúceho riešenia

Jediný bod zlyhania
Mali sme jediný bod zlyhania: aplikácia .NET bežala v jedinom procese. Ak niektorý modul zlyhal, zlyhala celá aplikácia a bolo potrebné ju reštartovať. Keďže automatizujeme veľké množstvo procesov pre rôznych používateľov, kvôli poruche jedného z nich nemohli všetci nejaký čas pracovať. A v prípade softvérovej chyby nepomohlo ani zálohovanie.

Rad vylepšení
Táto nevýhoda je skôr organizačná. Naša aplikácia má veľa zákazníkov a všetci ju chcú čo najskôr vylepšiť. Predtým to nebolo možné robiť paralelne a všetci zákazníci stáli v rade. Tento proces bol pre podniky negatívny, pretože museli dokázať, že ich úloha je hodnotná. A vývojový tím strávil čas organizovaním tohto frontu. To si vyžiadalo veľa času a úsilia a produkt sa nakoniec nemohol zmeniť tak rýchlo, ako by chceli.

Suboptimálne využitie zdrojov
Pri hosťovaní služieb v jednom procese sme vždy kompletne skopírovali konfiguráciu zo servera na server. Najviac zaťažené služby sme chceli umiestniť oddelene, aby sme neplytvali zdrojmi a získali flexibilnejšiu kontrolu nad našou schémou nasadenia.

Je ťažké implementovať moderné technológie
Problém známy všetkým vývojárom: existuje túžba zaviesť do projektu moderné technológie, ale nie je príležitosť. Pri veľkom monolitickom riešení sa akákoľvek aktualizácia súčasnej knižnice, nehovoriac o prechode na novú, mení na dosť netriviálnu úlohu. Dokázať vedúcemu tímu, že to prinesie viac bonusov ako premrhaných nervov, trvá dlho.

Ťažkosti pri vydávaní zmien
Toto bol najvážnejší problém - vydávali sme vydania každé dva mesiace.
Každé vydanie sa pre banku stalo skutočnou katastrofou, napriek testovaniu a úsiliu vývojárov. Podnik pochopil, že začiatkom týždňa niektoré jeho funkcionality nebudú fungovať. A vývojári pochopili, že ich čaká týždeň vážnych incidentov.
Všetci mali túžbu zmeniť situáciu.

Očakávania od mikroslužieb


Vydanie komponentov, keď sú pripravené. Dodanie komponentov, keď sú pripravené, rozkladom roztoku a oddelením rôznych procesov.

Malé produktové tímy. Je to dôležité, pretože veľký tím pracujúci na starom monolite bolo ťažké zvládnuť. Takýto tím bol nútený pracovať podľa prísneho procesu, no chceli viac kreativity a nezávislosti. Toto si mohli dovoliť len malé tímy.

Izolácia služieb v samostatných procesoch. V ideálnom prípade som ho chcel izolovať v kontajneroch, no veľké množstvo služieb napísaných v .NET Framework beží len na Windowse. Teraz sa objavujú služby založené na .NET Core, ale zatiaľ je ich málo.

Flexibilita nasadenia. Chceli by sme kombinovať služby tak, ako to potrebujeme, a nie tak, ako si to kód vynucuje.

Využitie nových technológií. Toto je zaujímavé pre každého programátora.

Problémy s prechodom


Samozrejme, ak by bolo jednoduché rozbiť monolit na mikroslužby, nebolo by potrebné o tom hovoriť na konferenciách a písať články. V tomto procese je veľa úskalí, opíšem tie hlavné, ktoré nám prekážali.

Prvý problém typické pre väčšinu monolitov: súdržnosť obchodnej logiky. Keď píšeme monolit, chceme znovu použiť naše triedy, aby sme nepísali zbytočný kód. A pri prechode na mikroslužby sa to stáva problémom: celý kód je dosť pevne prepojený a je ťažké oddeliť služby.

V čase začatia prác malo úložisko viac ako 500 projektov a viac ako 700 tisíc riadkov kódu. Toto je dosť veľké rozhodnutie a druhý problém. Nebolo možné to jednoducho zobrať a rozdeliť na mikroslužby.

Tretí problém — nedostatok potrebnej infraštruktúry. V skutočnosti sme manuálne kopírovali zdrojový kód na servery.

Ako prejsť od monolitu k mikroslužbám


Poskytovanie mikroslužieb

Po prvé, okamžite sme sa sami rozhodli, že oddelenie mikroslužieb je iteratívny proces. Vždy sa od nás vyžadovalo, aby sme obchodné problémy rozvíjali paralelne. Ako to zrealizujeme technicky, je už náš problém. Preto sme sa pripravili na iteračný proces. Nebude to fungovať inak, ak máte veľkú aplikáciu a nie je pôvodne pripravená na prepísanie.

Aké metódy používame na izoláciu mikroslužieb?

Prvý spôsob — presunúť existujúce moduly ako služby. V tomto ohľade sme mali šťastie: už boli zaregistrované služby, ktoré fungovali pomocou protokolu WCF. Boli rozdelené do samostatných celkov. Portovali sme ich samostatne a ku každej zostave sme pridali malý spúšťač. Bol napísaný pomocou nádhernej knižnice Topshelf, ktorá vám umožňuje spustiť aplikáciu ako službu aj ako konzolu. To je vhodné na ladenie, pretože v riešení nie sú potrebné žiadne ďalšie projekty.

Služby boli prepojené podľa obchodnej logiky, keďže používali spoločné zostavy a pracovali so spoločnou databázou. Len ťažko by sa dali nazvať mikroslužbami v ich čistej forme. Tieto služby by sme však mohli poskytovať oddelene, v rôznych procesoch. To samo osebe umožnilo znížiť ich vzájomný vplyv, čím sa zmenšil problém s paralelným vývojom a jediným bodom zlyhania.

Zostavenie s hostiteľom je len jeden riadok kódu v triede Program. Prácu s Topshelf sme ukryli v pomocnej triede.

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

       }
    }
}

Druhý spôsob prideľovania mikroslužieb je: vytvoriť ich na riešenie nových problémov. Ak zároveň monolit nerastie, je to už vynikajúce, čo znamená, že ideme správnym smerom. Na vyriešenie nových problémov sme sa pokúsili vytvoriť samostatné služby. Ak by takáto príležitosť bola, vytvorili sme viac „kanonických“ služieb, ktoré kompletne spravujú svoj vlastný dátový model, samostatnú databázu.

Ako mnohí sme začali so službami autentifikácie a autorizácie. Sú na to ako stvorené. Sú nezávislé, spravidla majú samostatný dátový model. Oni sami neinteragujú s monolitom, len sa na nich obracia, aby vyriešili nejaké problémy. Pomocou týchto služieb môžete začať s prechodom na novú architektúru, odladiť infraštruktúru na nich, vyskúšať niektoré prístupy súvisiace so sieťovými knižnicami atď. V našej organizácii nemáme žiadne tímy, ktoré by nedokázali vytvoriť službu overovania.

Tretí spôsob prideľovania mikroslužiebTen, ktorý používame, je pre nás trochu špecifický. Ide o odstránenie obchodnej logiky z vrstvy používateľského rozhrania. Naša hlavná aplikácia používateľského rozhrania je desktop; rovnako ako backend je napísaná v jazyku C#. Vývojári pravidelne robili chyby a do používateľského rozhrania prenášali časti logiky, ktoré mali existovať v backende a mali byť opätovne použité.

Ak sa pozriete na skutočný príklad z kódu časti používateľského rozhrania, môžete vidieť, že väčšina tohto riešenia obsahuje skutočnú obchodnú logiku, ktorá je užitočná v iných procesoch, nielen pri vytváraní formulára používateľského rozhrania.

Prechod od monolitu k mikroslužbám: história a prax

Skutočná logika používateľského rozhrania sa nachádza až v posledných niekoľkých riadkoch. Preniesli sme ho na server, aby sa dal znova použiť, čím sme znížili používateľské rozhranie a dosiahli správnu architektúru.

Štvrtý a najdôležitejší spôsob izolácie mikroslužieb, ktorý umožňuje zmenšiť monolit, je odstránenie existujúcich služieb so spracovaním. Keď odstránime existujúce moduly tak, ako sú, výsledok nie je vždy podľa predstáv vývojárov a obchodný proces môže byť od vytvorenia funkcie zastaraný. Refaktoringom môžeme podporiť nový obchodný proces, pretože obchodné požiadavky sa neustále menia. Dokážeme vylepšiť zdrojový kód, odstrániť známe defekty a vytvoriť lepší dátový model. Pribúda veľa výhod.

Oddelenie služieb od spracovania je neoddeliteľne spojené s pojmom ohraničený kontext. Ide o koncept od Domain Driven Design. Znamená to časť doménového modelu, v ktorej sú jednoznačne definované všetky výrazy jedného jazyka. Pozrime sa ako príklad na kontext poistenia a účtov. Máme monolitickú aplikáciu a potrebujeme pracovať s účtom v poistení. Očakávame, že vývojár nájde existujúcu triedu účtu v inej zostave, odkáže na ňu z triedy poistenia a budeme mať funkčný kód. Princíp DRY bude rešpektovaný, úloha bude vykonaná rýchlejšie s použitím existujúceho kódu.

V dôsledku toho sa ukazuje, že súvislosti účtov a poistenia sú prepojené. Keď sa objavia nové požiadavky, toto spojenie bude zasahovať do vývoja, čím sa zvýši zložitosť už aj tak zložitej obchodnej logiky. Ak chcete vyriešiť tento problém, musíte nájsť hranice medzi kontextami v kóde a odstrániť ich porušenia. Napríklad v súvislosti s poistením je celkom možné, že 20-miestne číslo účtu centrálnej banky a dátum otvorenia účtu budú postačovať.

Na oddelenie týchto ohraničených kontextov od seba a začatie procesu oddeľovania mikroslužieb od monolitického riešenia sme použili prístup, ako je vytváranie externých API v rámci aplikácie. Ak sme vedeli, že z nejakého modulu by sa mala stať mikroslužba, nejako upravená v rámci procesu, tak sme okamžite cez externé volania volali logiku, ktorá patrí do iného obmedzeného kontextu. Napríklad cez REST alebo WCF.

Pevne sme sa rozhodli, že sa nevyhneme kódu, ktorý by vyžadoval distribuované transakcie. V našom prípade sa ukázalo byť celkom jednoduché dodržať toto pravidlo. Ešte sme sa nestretli so situáciami, kedy by boli striktne distribuované transakcie naozaj potrebné – výsledná konzistencia medzi modulmi je úplne dostatočná.

Pozrime sa na konkrétny príklad. Máme koncept orchestrátora – potrubia, ktoré spracováva entitu „aplikácie“. Postupne vytvorí klienta, účet a bankovú kartu. Ak je klient a účet úspešne vytvorený, ale vytvorenie karty zlyhá, aplikácia neprejde do stavu „úspešná“ a zostane v stave „karta nevytvorená“. V budúcnosti to aktivita na pozadí vyberie a dokončí. Systém je už nejaký čas v nekonzistentnom stave, ale vo všeobecnosti sme s tým spokojní.

Ak nastane situácia, že je potrebné dôsledne ukladať časť údajov, s najväčšou pravdepodobnosťou pristúpime ku konsolidácii služby, aby sme ju spracovali v jednom procese.

Pozrime sa na príklad pridelenia mikroslužby. Ako ho relatívne bezpečne priviesť do výroby? V tomto príklade máme samostatnú časť systému – modul mzdovej agendy, ktorej jednu z kódových sekcií by sme chceli urobiť mikroservisom.

Prechod od monolitu k mikroslužbám: história a prax

V prvom rade si prepísaním kódu vytvoríme mikroslužbu. Vylepšujeme niektoré aspekty, s ktorými sme neboli spokojní. Implementujeme nové obchodné požiadavky od zákazníka. Do prepojenia medzi UI a backendom pridávame API Gateway, ktorá zabezpečí presmerovanie hovorov.

Prechod od monolitu k mikroslužbám: história a prax

Ďalej túto konfiguráciu uvoľníme do prevádzky, ale v pilotnom stave. Väčšina našich používateľov stále pracuje so starými obchodnými procesmi. Pre nových používateľov vyvíjame novú verziu monolitickej aplikácie, ktorá už tento proces neobsahuje. V podstate máme kombináciu monolitu a mikroslužby, ktorá funguje ako pilot.

Prechod od monolitu k mikroslužbám: história a prax

Po úspešnom pilotnom teste chápeme, že nová konfigurácia je skutočne funkčná, môžeme odstrániť starý monolit z rovnice a ponechať novú konfiguráciu namiesto starého riešenia.

Prechod od monolitu k mikroslužbám: história a prax

Celkovo využívame takmer všetky existujúce metódy na rozdelenie zdrojového kódu monolitu. Všetky nám umožňujú zmenšiť veľkosť častí aplikácie a preložiť ich do nových knižníc, čím sa zlepší zdrojový kód.

Práca s databázou


Databázu je možné deliť horšie ako zdrojový kód, keďže obsahuje nielen aktuálnu schému, ale aj nahromadené historické údaje.

Naša databáza, podobne ako mnohé iné, mala ešte jednu dôležitú nevýhodu – jej obrovskú veľkosť. Táto databáza bola navrhnutá podľa zložitej obchodnej logiky monolitu a vzťahov nahromadených medzi tabuľkami rôznych ohraničených kontextov.

V našom prípade sa k všetkým problémom (veľká databáza, veľa spojení, niekedy nejasné hranice medzi tabuľkami) objavil problém, ktorý sa vyskytuje v mnohých veľkých projektoch: použitie šablóny zdieľanej databázy. Údaje boli prevzaté z tabuliek prostredníctvom zobrazenia, prostredníctvom replikácie a odoslané do iných systémov, kde bola táto replikácia potrebná. V dôsledku toho sme nemohli presunúť tabuľky do samostatnej schémy, pretože sa aktívne používali.

Rovnaké rozdelenie na obmedzené kontexty v kóde nám pomáha pri separácii. Zvyčajne nám to dáva celkom dobrú predstavu o tom, ako rozdeľujeme údaje na úrovni databázy. Rozumieme, ktoré tabuľky patria do jedného ohraničeného kontextu a ktoré do iného.

Použili sme dve globálne metódy delenia databázy: delenie existujúcich tabuliek a delenie so spracovaním.

Oddelenie existujúcich tabuliek je vhodná metóda, ak je dátová štruktúra dobrá, spĺňa obchodné požiadavky a všetci sú s ňou spokojní. V tomto prípade môžeme existujúce tabuľky oddeliť do samostatnej schémy.

Oddelenie so spracovaním je potrebné vtedy, keď sa veľmi zmenil obchodný model a tabuľky nás už vôbec neuspokojujú.

Rozdelenie existujúcich tabuliek. Musíme si určiť, čo oddelíme. Bez týchto znalostí nebude fungovať nič a tu nám pomôže oddelenie ohraničených kontextov v kóde. Spravidla, ak rozumiete hraniciam kontextov v zdrojovom kóde, je jasné, ktoré tabuľky by mali byť zahrnuté do zoznamu pre oddelenie.

Predstavme si, že máme riešenie, v ktorom dva monolitné moduly interagujú s jednou databázou. Musíme sa uistiť, že iba jeden modul interaguje so sekciou oddelených tabuliek a druhý s ňou začne interagovať cez API. Na začiatok stačí, aby sa cez API vykonávalo iba nahrávanie. To je nevyhnutná podmienka, aby sme hovorili o nezávislosti mikroslužieb. Pripojenie na čítanie môže zostať, pokiaľ nenastane žiadny veľký problém.

Prechod od monolitu k mikroslužbám: história a prax

Ďalším krokom je, že môžeme oddeliť časť kódu, ktorá pracuje s oddelenými tabuľkami, so spracovaním alebo bez neho, do samostatnej mikroslužby a spustiť ju v samostatnom procese, kontajneri. Pôjde o samostatnú službu s napojením na monolitnú databázu a tie tabuľky, ktoré sa jej priamo netýkajú. Monolit stále interaguje pri čítaní s odnímateľnou časťou.

Prechod od monolitu k mikroslužbám: história a prax

Neskôr toto spojenie odstránime, to znamená, že čítanie údajov z monolitickej aplikácie z oddelených tabuliek sa prenesie aj do API.

Prechod od monolitu k mikroslužbám: história a prax

Ďalej si zo všeobecnej databázy vyberieme tabuľky, s ktorými pracuje len nová mikroslužba. Tabuľky môžeme presunúť do samostatnej schémy alebo aj do samostatnej fyzickej databázy. Medzi mikroslužbou a monolitickou databázou stále existuje čítacie spojenie, ale nie je sa čoho obávať, v tejto konfigurácii môže žiť pomerne dlho.

Prechod od monolitu k mikroslužbám: história a prax

Posledným krokom je úplné odstránenie všetkých pripojení. V tomto prípade možno budeme musieť migrovať údaje z hlavnej databázy. Niekedy chceme znova použiť niektoré údaje alebo adresáre replikované z externých systémov vo viacerých databázach. Toto sa nám pravidelne stáva.

Prechod od monolitu k mikroslužbám: história a prax

Spracovateľské oddelenie. Táto metóda je veľmi podobná prvej, len v opačnom poradí. Okamžite alokujeme novú databázu a novú mikroslužbu, ktorá interaguje s monolitom cez API. Zároveň však zostáva súbor databázových tabuliek, ktoré chceme v budúcnosti vymazať. Už ho nepotrebujeme, nahradili sme ho v novom modeli.

Prechod od monolitu k mikroslužbám: história a prax

Aby táto schéma fungovala, pravdepodobne budeme potrebovať prechodné obdobie.

Potom existujú dva možné prístupy.

Prvé: duplikujeme všetky údaje v novej a starej databáze. V tomto prípade máme redundanciu údajov a môžu nastať problémy so synchronizáciou. Môžeme však vziať dvoch rôznych klientov. Jeden bude fungovať s novou verziou, druhý so starou verziou.

Druhý: údaje delíme podľa niektorých obchodných kritérií. V systéme sme mali napríklad 5 produktov, ktoré boli uložené v starej databáze. Šiestu v rámci novej obchodnej úlohy umiestňujeme do novej databázy. Budeme ale potrebovať API Gateway, ktorá tieto dáta zosynchronizuje a ukáže klientovi odkiaľ a čo má získať.

Oba prístupy fungujú, vyberte si v závislosti od situácie.

Keď sme si istí, že všetko funguje, časť monolitu, ktorá pracuje so starými databázovými štruktúrami, môže byť deaktivovaná.

Prechod od monolitu k mikroslužbám: história a prax

Posledným krokom je odstránenie starých dátových štruktúr.

Prechod od monolitu k mikroslužbám: história a prax

Aby sme to zhrnuli, môžeme povedať, že máme problémy s databázou: v porovnaní so zdrojovým kódom sa s ňou ťažko pracuje, je náročnejšie na zdieľanie, ale dá sa a treba. Našli sme niekoľko spôsobov, ktoré nám to umožňujú celkom bezpečne, ale stále je jednoduchšie robiť chyby s údajmi ako so zdrojovým kódom.

Práca so zdrojovým kódom


Takto vyzeral diagram zdrojového kódu, keď sme začali analyzovať monolitický projekt.

Prechod od monolitu k mikroslužbám: história a prax

Dá sa rozdeliť zhruba na tri vrstvy. Ide o vrstvu spustených modulov, pluginov, služieb a jednotlivých aktivít. V skutočnosti to boli vstupné body v rámci monolitického riešenia. Všetky boli tesne utesnené spoločnou vrstvou. Malo to obchodnú logiku, ktorú služby zdieľali, a veľa prepojení. Každá služba a doplnok využívali až 10 alebo viac bežných zostáv, v závislosti od ich veľkosti a svedomia vývojárov.

Mali sme šťastie na knižnice infraštruktúry, ktoré sa dali používať samostatne.

Niekedy nastala situácia, keď niektoré bežné objekty v skutočnosti nepatrili do tejto vrstvy, ale boli infraštruktúrnymi knižnicami. Vyriešilo sa to premenovaním.

Najväčšie obavy vyvolávali ohraničené kontexty. Stávalo sa, že v jednom spoločnom zhromaždení boli zmiešané 3-4 kontexty a navzájom sa používali v rámci rovnakých obchodných funkcií. Bolo potrebné pochopiť, kde sa to dá rozdeliť a po akých hraniciach a čo ďalej robiť s mapovaním tohto rozdelenia do zostáv zdrojového kódu.

Pre proces rozdelenia kódu sme sformulovali niekoľko pravidiel.

Prvé: Už sme nechceli zdieľať obchodnú logiku medzi službami, aktivitami a pluginmi. Chceli sme osamostatniť obchodnú logiku v rámci mikroslužieb. Mikroslužby sú na druhej strane ideálne považované za služby, ktoré existujú úplne nezávisle. Domnievam sa, že tento prístup je trochu zbytočný a je ťažké ho dosiahnuť, pretože napríklad služby v C# budú v každom prípade prepojené štandardnou knižnicou. Náš systém je napísaný v C#, iné technológie sme zatiaľ nepoužili. Preto sme sa rozhodli, že si môžeme dovoliť použiť bežné technické zostavy. Hlavná vec je, že neobsahujú žiadne fragmenty obchodnej logiky. Ak máte nad ORM, ktorý používate, pohodlnú obálku, potom je jej kopírovanie zo služby do služby veľmi drahé.

Náš tím je fanúšikom dizajnu riadeného doménou, takže cibuľová architektúra sa nám skvele hodila. Základom našich služieb nie je dátová prístupová vrstva, ale zostava s doménovou logikou, ktorá obsahuje len obchodnú logiku a nemá žiadne prepojenie s infraštruktúrou. Zároveň môžeme nezávisle modifikovať zostavu domény na riešenie problémov súvisiacich s frameworkami.

V tejto fáze sme narazili na náš prvý vážny problém. Služba sa musela odvolávať na jednu doménovú zostavu, chceli sme osamostatniť logiku a zásada DRY nám tu veľmi prekážala. Vývojári chceli znova použiť triedy zo susedných zostáv, aby sa vyhli duplicite, a v dôsledku toho sa domény začali opäť spájať. Analyzovali sme výsledky a rozhodli sme sa, že problém možno spočíva aj v oblasti zariadenia na ukladanie zdrojového kódu. Mali sme veľké úložisko obsahujúce všetky zdrojové kódy. Riešenie pre celý projekt bolo veľmi náročné zostaviť na lokálnom stroji. Pre časti projektu preto vznikali samostatné malé riešenia a nikto nezakazoval pridať k nim nejakú spoločnú alebo doménovú zostavu a znovu ich použiť. Jediný nástroj, ktorý nám to neumožnil, bola kontrola kódu. Ale niekedy to tiež zlyhalo.

Potom sme začali prechádzať na model so samostatnými úložiskami. Obchodná logika už neplynie zo služby do služby, domény sa skutočne stali nezávislými. Ohraničené kontexty sú podporované jasnejšie. Ako opätovne využívame knižnice infraštruktúry? Oddelili sme ich do samostatného úložiska, potom sme ich vložili do balíčkov Nuget, ktoré sme vložili do Artifactory. Pri akejkoľvek zmene dôjde k zostaveniu a zverejneniu automaticky.

Prechod od monolitu k mikroslužbám: história a prax

Naše služby začali odkazovať na interné infraštruktúrne balíky rovnako ako na externé. Sťahujeme externé knižnice z Nugetu. Na prácu s Artifactory, kde sme tieto balíky umiestnili, sme použili dvoch správcov balíkov. V malých úložiskách sme používali aj Nuget. V úložiskách s viacerými službami sme použili Paket, ktorý poskytuje väčšiu konzistenciu verzií medzi modulmi.

Prechod od monolitu k mikroslužbám: história a prax

Prácou na zdrojovom kóde, miernou zmenou architektúry a oddelením repozitárov teda robíme naše služby nezávislejšie.

Problémy s infraštruktúrou


Väčšina nevýhod prechodu na mikroslužby súvisí s infraštruktúrou. Budete potrebovať automatizované nasadenie, budete potrebovať nové knižnice na spustenie infraštruktúry.

Manuálna inštalácia v prostrediach

Spočiatku sme riešenie pre prostredia nainštalovali manuálne. Na automatizáciu tohto procesu sme vytvorili CI/CD pipeline. Proces kontinuálneho doručovania sme zvolili preto, lebo kontinuálne nasadzovanie je pre nás zatiaľ z pohľadu obchodných procesov neprijateľné. Preto sa odosielanie na prevádzku vykonáva pomocou tlačidla a na testovanie - automaticky.

Prechod od monolitu k mikroslužbám: história a prax

Na ukladanie zdrojového kódu používame Atlassian, Bitbucket a na stavbu Bamboo. Radi píšeme zostavovacie skripty v Cake, pretože je to rovnaké ako C#. Do Artifactory prichádzajú hotové balíčky a Ansible sa automaticky dostane na testovacie servery, po ktorých je možné ich okamžite otestovať.

Prechod od monolitu k mikroslužbám: história a prax

Samostatné protokolovanie


Kedysi bolo jednou z myšlienok monolitu poskytnúť zdieľanú ťažbu dreva. Potrebovali sme tiež pochopiť, čo robiť s jednotlivými logami, ktoré sú na diskoch. Naše protokoly sa zapisujú do textových súborov. Rozhodli sme sa použiť štandardný zásobník ELK. Nepísali sme spoločnosti ELK priamo cez poskytovateľov, ale rozhodli sme sa, že upravíme textové protokoly a zapíšeme do nich ID sledovania ako identifikátor s pridaním názvu služby, aby bolo možné tieto protokoly neskôr analyzovať.

Prechod od monolitu k mikroslužbám: história a prax

Pomocou Filebeat máme príležitosť zbierať naše protokoly zo serverov, potom ich transformovať, používať Kibana na vytváranie dopytov v používateľskom rozhraní a vidieť, ako prebiehal hovor medzi službami. Trace ID s tým veľmi pomáha.

Služby súvisiace s testovaním a ladením


Spočiatku sme úplne nerozumeli tomu, ako ladiť vyvíjané služby. S monolitom bolo všetko jednoduché, spustili sme ho na lokálnom stroji. Najprv sa pokúšali urobiť to isté s mikroslužbami, ale niekedy na úplné spustenie jednej mikroslužby musíte spustiť niekoľko ďalších, čo je nepohodlné. Uvedomili sme si, že musíme prejsť na model, kde necháme na lokálnom počítači len službu alebo služby, ktoré chceme ladiť. Zostávajúce služby sa používajú zo serverov, ktoré zodpovedajú konfigurácii s prod. Po ladení, počas testovania, pre každú úlohu sa testovaciemu serveru odošlú iba zmenené služby. Riešenie sa teda testuje v podobe, v akej sa v budúcnosti objaví vo výrobe.

Existujú servery, ktoré prevádzkujú iba produkčné verzie služieb. Tieto servery sú potrebné v prípade incidentov, na kontrolu dodávky pred nasadením a na interné školenia.

Pridali sme automatizovaný proces testovania pomocou populárnej knižnice Specflow. Testy sa spúšťajú automaticky pomocou NUnit ihneď po nasadení z Ansible. Ak je pokrytie úloh plne automatické, nie je potrebné manuálne testovanie. Aj keď niekedy je stále potrebné dodatočné manuálne testovanie. V Jira používame značky na určenie, ktoré testy sa majú spustiť pre konkrétny problém.

Okrem toho sa zvýšila potreba záťažových testov, ktoré sa predtým vykonávali len v ojedinelých prípadoch. Používame JMeter na spustenie testov, InfluxDB na ich ukladanie a Grafana na vytváranie procesných grafov.

čo sme dosiahli?


Po prvé, zbavili sme sa konceptu „uvoľnenia“. Preč sú dvojmesačné monštruózne vydania, keď bol tento kolos nasadený v produkčnom prostredí, čo dočasne narušilo obchodné procesy. Teraz nasadzujeme služby v priemere každých 1,5 dňa, pričom ich zoskupujeme, pretože idú do prevádzky po schválení.

V našom systéme nie sú žiadne fatálne zlyhania. Ak uvoľníme mikroslužbu s chybou, funkcie s ňou spojené budú poškodené a všetky ostatné funkcie nebudú ovplyvnené. To výrazne zlepšuje používateľskú skúsenosť.

Môžeme ovládať vzor nasadenia. V prípade potreby si môžete vybrať skupiny služieb oddelene od zvyšku riešenia.

Okrem toho sme výrazne znížili problém s veľkým radom vylepšení. Teraz máme samostatné produktové tímy, ktoré pracujú s niektorými službami nezávisle. Scrum proces sem dobre zapadá. Konkrétny tím môže mať samostatného vlastníka produktu, ktorý mu prideľuje úlohy.

Zhrnutie

  • Mikroslužby sú vhodné na rozklad zložitých systémov. V tomto procese začíname chápať, čo je v našom systéme, aké sú tam obmedzené súvislosti, kde ležia ich hranice. To vám umožní správne distribuovať vylepšenia medzi moduly a zabrániť zmätku kódu.
  • Mikroslužby poskytujú organizačné výhody. Často sa o nich hovorí len ako o architektúre, ale akákoľvek architektúra je potrebná na riešenie obchodných potrieb, a nie sama o sebe. Preto môžeme povedať, že mikroslužby sú vhodné na riešenie problémov v malých tímoch, keďže Scrum je teraz veľmi populárny.
  • Separácia je iteratívny proces. Nemôžete vziať aplikáciu a jednoducho ju rozdeliť na mikroslužby. Je nepravdepodobné, že by výsledný produkt bol funkčný. Pri dedikácii mikroslužieb je výhodné prepísať existujúce dedičstvo, teda premeniť ho na kód, ktorý sa nám páči a lepšie vyhovuje potrebám firmy z hľadiska funkčnosti a rýchlosti.

    Malé upozornenie: Náklady na prechod na mikroslužby sú dosť značné. Samotné vyriešenie problému s infraštruktúrou trvalo dlho. Ak teda máte malú aplikáciu, ktorá si nevyžaduje špecifické škálovanie, pokiaľ nemáte veľký počet zákazníkov, ktorí súperia o pozornosť a čas vášho tímu, potom mikroslužby nemusia byť to, čo dnes potrebujete. Je to dosť drahé. Ak proces začnete mikroslužbami, potom budú náklady na začiatku vyššie, ako keby ste rovnaký projekt začali s vývojom monolitu.

    PS Emotivnejší príbeh (a akoby pre vás osobne) - podľa odkaz.
    Tu je plná verzia správy.

Zdroj: hab.com

Pridať komentár