Tranzicija sa monolita na mikroservise: istorija i praksa

U ovom članku ću govoriti o tome kako je projekat na kojem radim evoluirao iz velikog monolita u skup mikroservisa.

Projekat je svoju istoriju započeo prilično davno, početkom 2000. Prve verzije su napisane u Visual Basicu 6. Vremenom je postalo jasno da će razvoj na ovom jeziku biti teško podržati u budućnosti, jer IDE i sam jezik se slabo razvija. Krajem 2000-ih odlučeno je da se pređe na C# koji obećava. Nova verzija je pisana paralelno sa revizijom stare, postepeno je sve više koda bilo u .NET-u. Pozadinski deo u C# je prvobitno bio fokusiran na arhitekturu servisa, međutim, uobičajene biblioteke sa logikom su korišćene tokom razvoja, a usluge su pokrenute u jednom procesu. Rezultat je bila aplikacija koju smo nazvali „monolit usluge“.

Jedna od rijetkih prednosti takvog paketa bila je mogućnost da se usluge međusobno pozivaju preko vanjskog API-ja. Postojali su jasni preduslovi za prelazak na korektniju uslugu, au budućnosti i mikroservisnu arhitekturu.

Počeli smo sa radom na dekompoziciji oko 2015. godine. Još nismo dostigli idealno stanje - postoje dijelovi velikog projekta koji se teško mogu nazvati monolitima, ali ni oni ne liče na mikroservise. Međutim, napredak je značajan.
O tome ću govoriti u članku.

Tranzicija sa monolita na mikroservise: istorija i praksa

Sadržaj

Arhitektura i problemi postojećeg rješenja


U početku je arhitektura izgledala ovako: UI - zasebna aplikacija, monolitni dio je napisan u Visual Basic-u 6, .NET aplikacija je bila skup povezanih usluga koje rade sa prilično velikom bazom podataka.

Nedostaci prethodnog rješenja

Jedna tačka kvara
Imali smo jednu tačku neuspjeha: .NET aplikacija je radila u jednom procesu. Ako neki od modula nije uspio, cijela aplikacija nije uspjela i morala se ponovo pokrenuti. Budući da automatizujemo veliki broj procesa za različite korisnike, zbog kvara na jednom od njih, svi nisu mogli raditi neko vrijeme. A kod softverske greške ni redundantnost nije pomogla.

Red za poboljšanje
Ovaj nedostatak je više organizacijski. U našoj aplikaciji ima mnogo kupaca i svi žele da je finaliziraju što je prije moguće. Ranije je to bilo nemoguće raditi paralelno, a svi kupci su stajali u redu. Ovaj proces je bio negativan za poslovanje, jer su morali da dokažu da je njihov zadatak vredan. A razvojni tim je proveo vrijeme organizirajući ovaj red čekanja. Bilo je potrebno mnogo vremena i truda, a kao rezultat toga, proizvod se nije mogao promijeniti onoliko brzo koliko bismo željeli.

Neoptimalna upotreba resursa
Kada smo hostirali usluge u jednom procesu, uvijek smo u potpunosti kopirali konfiguraciju sa servera na server. Htjeli smo odvojeno odvojiti najopterećenije usluge kako ne bismo trošili resurse i dobili fleksibilniju kontrolu nad našom šemom implementacije.

Teško je implementirati moderne tehnologije
Problem poznat svim programerima: postoji želja da se u projekat uvedu moderne tehnologije, ali ne postoji način. Uz veliko monolitno rješenje, svako ažuriranje trenutne biblioteke, a da ne spominjemo prelazak na novu, pretvara se u prilično netrivijalan zadatak. Potrebno je dosta vremena da se vođi tima dokaže da će to donijeti više bonusa nego potrošene živce.

Poteškoće sa izdavanjem izmjena
To je bio najozbiljniji problem - izdavali smo saopštenja svaka dva mjeseca.
Svako izdanje pretvaralo se u pravu katastrofu za banku, uprkos testiranju i naporima programera. Poslovanje je shvatilo da neke od funkcionalnosti neće raditi za njega početkom sedmice. I programeri su shvatili da ih čeka sedmica ozbiljnih incidenata.
Svi su imali želju da promene situaciju.

Očekivanja od mikroservisa


Izdavanje komponenti kada su spremni. Izdavanje komponenti kako su gotove dekompozicijom rješenja i odvajanjem različitih procesa.

Mali timovi proizvoda. Ovo je važno jer je velikim timom koji je radio na starom monolitu bilo teško upravljati. Takav tim je bio primoran da radi po strogom procesu, ali je želeo više kreativnosti i nezavisnosti. Samo mali timovi su to mogli priuštiti.

Izolacija usluga u odvojenim procesima. U idealnom slučaju, želeo sam da izolujem u kontejnerima, ali veliki broj usluga napisanih u .NET Framework radi samo pod Windowsom. Sada postoje servisi na .NET Core, ali ih još nema dovoljno.

Fleksibilnost implementacije. Želio bih kombinovati usluge onako kako nam je potrebno, a ne na način na koji kod forsira.

Upotreba novih tehnologija. Ovo je od interesa za svakog programera.

Pitanja tranzicije


Naravno, da je razdvajanje monolita na mikroservise bilo lako, ne bi bilo potrebe o tome pričati na konferencijama i pisati članke. Mnogo je zamki u ovom procesu, opisaću glavne koje su nas omele.

Prvi problem tipično za većinu monolita: povezanost poslovne logike. Kada pišemo monolit, želimo ponovo koristiti naše klase kako ne bismo pisali dodatni kod. A kada se pređe na mikroservise, ovo postaje problem: sav kod je prilično čvrsto povezan i teško je razdvojiti usluge.

U trenutku početka rada, repozitorijum je imao više od 500 projekata i više od 700 hiljada linija koda. Ovo je dovoljno veliko rješenje. drugi problem. Nije bilo moguće jednostavno uzeti i podijeliti na mikroservise.

Treći problem — Nedostatak potrebne infrastrukture. U stvari, bavili smo se ručnim kopiranjem izvornog koda na servere.

Kako preći sa monolita na mikroservise


Dodjela mikroservisa

Prvo, odmah smo za sebe utvrdili da je odvajanje mikroservisa iterativni proces. Od nas se uvijek tražilo da paralelno razvijamo poslovne zadatke. Kako ćemo to tehnički implementirati već je naš problem. Stoga smo se pripremili za iterativni proces. Neće raditi drugačije ako imate veliku aplikaciju i nije u početku spremna za ponovno pisanje.

Koje metode koristimo za izolaciju mikroservisa?

Prvi put - izvaditi postojeće module kao servise. U tom smislu, imali smo sreće: već su postojale formalizovane usluge koje su radile po WCF protokolu. Bili su razdvojeni u posebne skupštine. Portirali smo ih zasebno, dodajući mali pokretač svakoj gradnji. Napisana je pomoću divne biblioteke Topshelf, koja vam omogućava da pokrenete aplikaciju i kao servis i kao konzolu. Ovo je korisno za otklanjanje grešaka jer u rješenju nisu potrebni dodatni projekti.

Servisi su bili povezani poslovnom logikom, jer su koristili zajedničke sklopove i radili sa zajedničkom bazom podataka. Bilo je teško nazvati ih mikroservisima u njihovom najčistijem obliku. Međutim, mogli bismo izdavati ove usluge odvojeno, u različitim procesima. To je već omogućilo smanjenje njihovog uticaja jedni na druge, smanjujući problem paralelnog razvoja i jedne tačke neuspjeha.

Glavni sklop je samo jedan red koda u klasi Program. Rad sa Topshelf-om smo sakrili u pomoćni razred.

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

       }
    }
}

Drugi način za izolaciju mikroservisa: stvoriti ih za rješavanje novih problema. Ako u isto vrijeme monolit ne raste, to je već odlično, što znači da se krećemo u pravom smjeru. Kako bismo riješili nove probleme, pokušali smo kreirati zasebne servise. Ako je postojala takva prilika, onda smo kreirali više „kanonskih“ servisa koji u potpunosti upravljaju svojim modelom podataka, zasebnu bazu podataka.

Mi smo, kao i mnogi drugi, počeli sa uslugama autentifikacije i autorizacije. Savršeni su za ovo. Oni su nezavisni, po pravilu imaju poseban model podataka. Oni sami ne stupaju u interakciju sa monolitom, samo se to odnosi na njih da riješe neke probleme. Na ovim servisima možete započeti prijelaz na novu arhitekturu, otkloniti greške u infrastrukturi na njima, isprobati neke pristupe vezane za mrežne biblioteke itd. U našoj organizaciji nema timova koji ne bi mogli napraviti uslugu autentifikacije.

Treći način za izolaciju mikroservisa, koji koristimo, malo je specifičan za nas. Ovo je uklanjanje poslovne logike sa sloja korisničkog sučelja. Naša glavna UI aplikacija je desktop, ona je, kao i backend, napisana na C#. Programeri su povremeno pravili greške i uklanjali delove logike na korisničkom interfejsu koji je trebalo da postoji u pozadini i ponovo je korišćen.

Ako pogledate pravi primjer iz koda UI dijela, možete vidjeti da većina ovog rješenja sadrži stvarnu poslovnu logiku, koja je korisna u drugim procesima, ne samo za izgradnju UI forme.

Tranzicija sa monolita na mikroservise: istorija i praksa

Prava UI logika postoji samo u zadnjih nekoliko redaka. Prebacili smo ga na server kako bi se mogao ponovo koristiti, čime smo smanjili korisničko sučelje i postigli ispravnu arhitekturu.

Četvrti, najvažniji način izolacije mikroservisa, koji vam omogućava da smanjite monolit, je uklanjanje postojećih usluga uz obradu. Kada izbacimo postojeće module kakve jesu, programerima se ne sviđa uvijek rezultat, a poslovni proces bi mogao zastarjeti od kreiranja funkcionalnosti. Kroz refaktoring možemo podržati novi poslovni proces jer se poslovni zahtjevi stalno mijenjaju. Možemo poboljšati izvorni kod, ukloniti poznate nedostatke, kreirati bolji model podataka. Mnogo je pogodnosti koje možete dobiti.

Odvajanje usluga s preradom ide ruku pod ruku s konceptom ograničenog konteksta. Ovo je koncept domenski orijentisanog dizajna. To znači dio modela domene u kojem su svi pojmovi jednog jezika jedinstveno definirani. Razmotrite kontekst osiguranja i računa kao primjer. Imamo monolitnu aplikaciju, a potrebno je raditi sa računom u osiguranju. Očekujemo da programer pronađe postojeću klasu Account u drugom sklopu, napravi referencu na nju iz klase Insurance, a mi ćemo dobiti radni kod. Poštovaće se princip DRY, zadatak će se obaviti brže korišćenjem postojećeg koda.

Kao rezultat toga, ispada da su konteksti računa i osiguranja povezani. Kako dolaze novi zahtjevi, ovaj odnos će ometati razvoj, dodajući složenost ionako složenoj poslovnoj logici. Da biste riješili ovaj problem, morate pronaći granice između konteksta u kodu i ukloniti njihova kršenja. Na primjer, u kontekstu osiguranja, sasvim je moguće da će 20-cifreni broj računa Centralne banke i datum otvaranja računa biti dovoljni.

Kako bismo odvojili ove ograničene kontekste jedan od drugog i započeli proces izdvajanja mikroservisa iz monolitnog rješenja, koristili smo pristup kao što je kreiranje vanjskih API-ja unutar aplikacije. Ako smo znali da neki modul treba da postane mikroservis, da se nekako promeni unutar procesa, onda bismo eksternim pozivima odmah pozvali logiku koja pripada drugom ograničenom kontekstu. Na primjer, kroz REST ili WCF.

Za sebe smo čvrsto odlučili da nećemo izbjegavati kod koji bi zahtijevao distribuirane transakcije. U našem slučaju, pokazalo se da je prilično lako slijediti ovo pravilo. Do sada se nismo susreli sa takvim situacijama kada su hard distribuirane transakcije zaista potrebne - konačna konzistentnost između modula je sasvim dovoljna.

Razmotrimo konkretan primjer. Imamo koncept orkestratora - cevovod koji obrađuje suštinu "aplikacije". On zauzvrat kreira klijenta, račun i bankovnu karticu. Ako su klijent i račun uspješno kreirani, ali kreiranje kartice ne uspije, aplikacija ne prelazi u status "uspješan" i ostaje u statusu "kartica nije kreirana". U budućnosti će ga pozadinska aktivnost pokupiti i završiti. Sistem je već neko vrijeme u nekonzistentnom stanju, ali smo generalno time zadovoljni.

U slučaju da ipak dođe do situacije kada će biti potrebno dosljedno čuvati dio podataka, najvjerovatnije ćemo ići na proširenje servisa kako bismo to obradili u jednom procesu.

Razmotrimo primjer dodjeljivanja mikroservisa. Kako može biti relativno sigurno dovesti ga u proizvodnju? U ovom primjeru imamo poseban dio sistema - modul platnog spiska, čiji jedan od dijelova koda želimo napraviti mikroservis.

Tranzicija sa monolita na mikroservise: istorija i praksa

Prije svega, kreiramo mikroservis ponovnim pisanjem koda. Popravljamo neke tačke koje nam nisu odgovarale. Implementiramo nove poslovne zahtjeve kupaca. Dodajemo u paket između korisničkog sučelja i pozadinskog dijela API Gatewaya, koji će omogućiti prosljeđivanje poziva.

Tranzicija sa monolita na mikroservise: istorija i praksa

Zatim ovu konfiguraciju puštamo u rad, ali u pilot stanju. Većina naših korisnika još uvijek radi sa starim poslovnim procesima. Za nove korisnike razvijamo novu verziju monolitne aplikacije koju ovaj proces više ne sadrži. U stvari, imamo gomilu monolita i mikroservisa koji rade u obliku pilota.

Tranzicija sa monolita na mikroservise: istorija i praksa

Uz uspješan pilot, razumijemo da nova konfiguracija zaista funkcionira, možemo ukloniti stari monolit iz jednačine i ostaviti novu konfiguraciju na mjestu starog rješenja.

Tranzicija sa monolita na mikroservise: istorija i praksa

Ukupno koristimo gotovo sve postojeće metode za odvajanje izvornog koda monolita. Svi oni nam omogućavaju da smanjimo veličinu dijelova aplikacije i prevedemo ih u nove biblioteke, čineći bolji izvorni kod.

Rad sa bazom podataka


Baza podataka je podložna razdvajanju gore od izvornog koda, jer sadrži ne samo trenutnu šemu, već i akumulirane istorijske podatke.

Naša baza podataka, kao i mnoge druge, imala je još jedan važan nedostatak - veliku veličinu. Ova baza podataka je dizajnirana u skladu sa zamršenom poslovnom logikom monolita, a odnosi su se akumulirali između tabela različitih ograničenih konteksta.

U našem slučaju, povrh svih problema (velika baza podataka, mnogo relacija, ponekad nerazumljive granice između tabela), postojao je problem koji se javlja u mnogim velikim projektima: korištenje obrasca zajedničke baze podataka. Podaci su uzimani iz tabela kroz prikaze, kroz replikaciju i otpremani u druge sisteme gdje je ova replikacija potrebna. Kao rezultat toga, nismo mogli premjestiti tabele u zasebnu šemu, jer su se aktivno koristile.

U podjeli nam pomaže sama podjela na ograničene kontekste u kodu. Obično nam daje prilično dobru ideju o tome kako razbijamo podatke na nivou baze podataka. Razumijemo koje tabele pripadaju jednom ograničenom kontekstu, a koje drugom.

Primijenili smo dva globalna načina za podjelu baze podataka: podijeliti postojeće tablice i podijeliti s obradom.

Razdvajanje postojećih tabela je dobra praksa za korištenje ako je struktura podataka dobra, ispunjava poslovne zahtjeve i svi su zadovoljni njome. U ovom slučaju možemo alocirati postojeće tabele u posebnu šemu.

Filijala sa preradom je potrebna kada se poslovni model dosta promenio, a tabele nas više nimalo ne zadovoljavaju.

Razdvajanje postojećih tabela. Moramo da odredimo šta ćemo odvojiti. Bez ovog znanja ništa neće raditi, a odvajanje ograničenih konteksta u kodu će nam pomoći ovdje. Po pravilu, ako možete razumjeti granice konteksta u izvornom kodu, postaje jasno koje tabele treba uključiti u listu za razdvajanje.

Zamislite da imamo rješenje gdje dva monolitna modula komuniciraju sa istom bazom podataka. Moramo se pobrinuti da samo jedan modul stupi u interakciju sa odjeljkom tabela koje treba odvojiti, a drugi počinje interakciju s njim preko API-ja. Za početak, dovoljno je da se samo snimanje vrši preko API-ja. To je neophodan uslov da bismo mogli govoriti o samostalnosti mikroservisa. Linkovi za čitanje mogu ostati sve dok nema velikog problema.

Tranzicija sa monolita na mikroservise: istorija i praksa

Kao sljedeći korak, već možemo odvojiti dio koda koji radi sa odvojivim tablicama, sa ili bez obrade, u zasebnu mikroservis i pokrenuti u zasebnom procesu, kontejneru. Ovo će biti zasebna usluga sa vezom na monolitnu bazu podataka i one tabele koje nisu direktno povezane s njom. Monolit je i dalje u interakciji sa odvojivim delom za čitanje.

Tranzicija sa monolita na mikroservise: istorija i praksa

Kasnije ćemo ukloniti ovu vezu, odnosno prebaciti čitanje monolitnih podataka aplikacije iz odvojenih tabela u API.

Tranzicija sa monolita na mikroservise: istorija i praksa

Zatim biramo tabele iz zajedničke baze podataka sa kojima radi samo novi mikroservis. Tabele možemo premjestiti u zasebnu shemu ili čak u zasebnu fizičku bazu podataka. Ostaje veza za čitanje između mikroservisa i monolitne baze podataka, ali nema razloga za brigu, u ovoj konfiguraciji može dugo živjeti.

Tranzicija sa monolita na mikroservise: istorija i praksa

Posljednji korak je potpuno uklanjanje svih veza. U ovom slučaju, možda ćemo morati da migriramo podatke iz glavne baze podataka. Ponekad želimo ponovo koristiti neke podatke ili direktorije replicirane iz vanjskih sistema u nekoliko baza podataka. Imamo ovo s vremena na vrijeme.

Tranzicija sa monolita na mikroservise: istorija i praksa

Odeljenje za obradu. Ova metoda je vrlo slična prvoj, samo što ide obrnuto. Odmah imamo novu bazu podataka i novi mikroservis koji komunicira sa monolitom preko API-ja. Ali ovo ostavlja skup tabela baze podataka koje želimo da izbrišemo u budućnosti. Neće nam više trebati, zamenili smo ga u novom modelu.

Tranzicija sa monolita na mikroservise: istorija i praksa

Da bi ova šema funkcionirala, najvjerovatnije će nam trebati prelazni period.

Dalje, postoje dva moguća pristupa.

Prvi: dupliramo sve podatke u novim i starim bazama podataka. U ovom slučaju imamo redundantnost podataka, može doći do problema sa sinhronizacijom. Ali tada možemo uzeti dva različita klijenta. Jedan će raditi sa novom verzijom, drugi sa starom.

Drugi: razdvajanje podataka prema nekom poslovnom atributu. Na primjer, u sistemu smo imali 5 proizvoda koji su pohranjeni u staru bazu podataka. Šestu, u okviru novog poslovnog zadatka, stavljamo u novu bazu podataka. Ali trebamo API Gateway koji sinkronizira ove podatke i pokazuje klijentu gdje i šta treba uzeti.

Oba pristupa funkcionišu, birajte u zavisnosti od situacije.

Nakon što se uvjerimo da sve radi, dio monolita koji radi sa starim strukturama baze podataka može se onemogućiti.

Tranzicija sa monolita na mikroservise: istorija i praksa

Posljednji korak je uklanjanje starih struktura podataka.

Tranzicija sa monolita na mikroservise: istorija i praksa

Sumirajući, možemo reći da imamo problema sa bazom podataka: teško je raditi s njom u poređenju sa izvornim kodom, teže je odvojiti, ali može i treba. Pronašli smo neke načine koji vam omogućavaju da to učinite sasvim sigurno, ali je lakše pogriješiti s podacima nego s izvornim kodom.

Rad sa izvornim kodom


Ovako je izgledao dijagram izvornog koda kada smo počeli da analiziramo monolitni projekat.

Tranzicija sa monolita na mikroservise: istorija i praksa

Može se uslovno podijeliti u tri sloja. Ovo je sloj pokretanih modula, dodataka, usluga i pojedinačnih aktivnosti. Zapravo, to su bile ulazne tačke unutar monolitnog rješenja. Svi su bili čvrsto pričvršćeni zajedničkim slojem. Imao je poslovnu logiku koja je bila podijeljena između službi i mnogih odnosa. Svaki servis i dodatak koristio je do 10 ili više uobičajenih sklopova, ovisno o njihovoj veličini i svijesti programera.

Imali smo sreće, imali smo infrastrukturne biblioteke koje su se mogle koristiti zasebno.

Ponekad je nastala situacija kada neki Common objekti zapravo ne pripadaju ovom sloju, već su infrastrukturne biblioteke. Ovo je riješeno preimenovanjem.

Ograničeni konteksti bili su najveća briga. Dešavalo se da se 3-4 konteksta pomiješaju u jednom zajedničkom sklopu i koriste jedan drugog u okviru istih poslovnih funkcija. Bilo je potrebno razumjeti gdje se može podijeliti i duž kojih granica, te šta dalje raditi sa mapiranjem ovog razdvajanja na sklopove izvornog koda.

Formulirali smo nekoliko pravila za proces razdvajanja koda.

Prvi: Više nismo željeli dijeliti poslovnu logiku između usluga, aktivnosti i dodataka. Željeli smo da poslovnu logiku učinimo nezavisnom unutar mikroservisa. S druge strane, mikroservise se u idealnom slučaju percipiraju kao usluge koje postoje potpuno neovisno. Mislim da je ovaj pristup pomalo rasipnički i teško ostvariv, jer će, na primjer, C# servisi ionako biti povezani standardnom bibliotekom. Naš sistem je napisan na C#, druge tehnologije još nisu korištene. Stoga smo odlučili da možemo priuštiti korištenje uobičajenih tehničkih konstrukcija. Glavna stvar je da oni ne sadrže nikakve fragmente poslovne logike. Ako imate lijep omot oko ORM-a koji koristite, vrlo je skupo kopirati ga iz servisa u servis.

Naš tim je obožavatelj domenskog dizajna, tako da nam je onion arhitektura odlično pristajala. Osnova u našim uslugama nije bio sloj pristupa podacima, već sklop sa domenskom logikom, koji sadrži samo poslovnu logiku i lišen je veza sa infrastrukturom. U isto vrijeme, možemo samostalno precizirati sklop domene kako bismo riješili probleme povezane s okvirima.

U ovoj fazi naišli smo na prvi ozbiljan problem. Servis je morao da se odnosi na jedan sklop domena, želeli smo da učinimo logiku nezavisnom, a DRY princip nas je ovde ometao. Programeri su željeli ponovo koristiti klase iz susjednih sklopova kako bi izbjegli dupliciranje, i kao rezultat toga, domeni su ponovo počeli međusobno komunicirati. Analizirali smo rezultate i zaključili da možda problem leži iu području uređaja za skladištenje izvornog koda. Imali smo veliko spremište koje je sadržavalo sve izvorne kodove. Rješenje za cijeli projekat bilo je vrlo teško izgraditi na lokalnoj mašini. Stoga su za dijelove projekta kreirana zasebna mala rješenja i niko im nije zabranio dodavanje nekog Common ili domenskog sklopa i njihovo ponovno korištenje. Jedini alat koji nam to nije omogućio bio je kod za pregled. Ali ponekad je i on zabrljao.

Zatim smo počeli da prelazimo na model sa odvojenim repozitorijumima. Poslovna logika je prestala da teče od servisa do servisa, domeni su zaista postali nezavisni. Ograničeni konteksti su eksplicitnije podržani. Kako ponovo koristimo infrastrukturne biblioteke? Odvojili smo ih u posebno spremište, a zatim ih stavili u Nuget pakete, koje smo stavili u Artifactory. Sa bilo kojom promjenom, sklapanje i objavljivanje se odvija automatski.

Tranzicija sa monolita na mikroservise: istorija i praksa

Naše usluge su počele da se odnose na interne infrastrukturne pakete na isti način kao i na eksterne. Eksterne biblioteke preuzimamo sa Nugeta. Za rad sa Artifactory-om, gdje smo stavili ove pakete, koristili smo dva menadžera paketa. U malim repozitorijumima koristili smo i Nuget. U repozitorijumima sa više servisa, koristili smo Paket koji obezbeđuje više konzistentnosti verzija između modula.

Tranzicija sa monolita na mikroservise: istorija i praksa

Tako, radeći na izvornom kodu, neznatno mijenjajući arhitekturu i odvajajući spremišta, činimo naše usluge nezavisnijim.

Infrastrukturna pitanja


Većina nedostataka migracije na mikrousluge ima veze sa infrastrukturom. Trebat će vam automatska implementacija, trebat će vam nove biblioteke za pokretanje infrastrukture.

Ručna instalacija u okruženjima

U početku smo ručno instalirali rješenje na okruženja. Da bismo automatizovali ovaj proces, kreirali smo CI/CD cevovod. Odabrali smo kontinuirani proces isporuke, jer nam je kontinuirana implementacija još uvijek neprihvatljiva sa stanovišta poslovnih procesa. Stoga se slanje u rad vrši pomoću dugmeta, a za testiranje - automatski.

Tranzicija sa monolita na mikroservise: istorija i praksa

Koristimo Atlassian, Bitbucket za izvornu pohranu i Bamboo za gradnje. Volimo pisati skripte za izradu u Cake-u jer je to isti C#. Gotovi paketi dolaze u Artifactory, a Ansible automatski dolazi do test servera, nakon čega se mogu odmah testirati.

Tranzicija sa monolita na mikroservise: istorija i praksa

Odvojeno evidentiranje


Nekada je jedna od ideja monolita bila da se obezbedi zajednička seča. Takođe smo morali da shvatimo šta da radimo sa pojedinačnim evidencijama koje se nalaze na diskovima. Dnevnici se pišu u tekstualne datoteke. Odlučili smo da koristimo standardni ELK stek. Nismo pisali direktno ELK-u preko provajdera, već smo odlučili da finaliziramo tekstualne dnevnike i upišemo ID praćenja u njih kao identifikator, dodajući naziv usluge kako bi se ovi zapisi mogli raščlaniti.

Tranzicija sa monolita na mikroservise: istorija i praksa

Uz pomoć Filebeat-a, dobijamo priliku da prikupimo naše logove sa servera, zatim ih konvertujemo, koristimo Kibanu za izgradnju zahteva u korisničkom interfejsu i vidimo kako je protekao poziv između servisa. ID praćenja puno pomaže u tome.

Testiranje i otklanjanje grešaka povezanih usluga


U početku nismo u potpunosti razumjeli kako možemo otkloniti greške u uslugama koje smo razvijali. Sve je bilo jednostavno sa monolitom, pokrenuli smo ga na lokalnoj mašini. Isprva su pokušali učiniti isto s mikroservisima, ali ponekad, da biste u potpunosti pokrenuli jedan mikroservis, morate pokrenuti nekoliko drugih, a to je nezgodno. Shvatili smo da je potrebno preći na model kada na lokalnoj mašini ostavimo samo servis ili servise koje želimo da otklonimo. Preostale usluge se koriste sa servera koji odgovaraju konfiguraciji sa prod. Nakon otklanjanja grešaka, tokom testiranja, za svaki zadatak test serveru se izdaju samo promijenjene usluge. Tako se rješenje testira u obliku u kojem će se u budućnosti naći u prodaji.

Postoje serveri na kojima su instalirane samo proizvodne verzije servisa. Ovi serveri su potrebni za incidente, za provjere isporuke prije implementacije i za internu obuku.

Dodali smo automatizirani proces testiranja koristeći popularnu biblioteku Specflow. NUnit automatski pokreće testove čim se implementiraju iz Ansiblea. Ako je pokrivenost zadatka potpuno automatska, onda nema potrebe za ručnim testiranjem. Iako je ponekad potrebno dodatno ručno testiranje. Da bismo odredili koje testove pokrenuti za određeni problem, koristimo oznake u Jira.

Osim toga, porasla je potreba za testiranjem opterećenja, koje se ranije provodilo samo u rijetkim slučajevima. Koristimo JMeter za pokretanje testova, InfluxDB da ih pohranimo, a Grafana za crtanje procesa.

Šta smo postigli?


Prvo, riješili smo se koncepta "oslobađanja". Dvomjesečna monstruozna izdanja nestala su kada je ovaj kolos postavljen u produkcijskom okruženju, prekidajući poslovne procese na neko vrijeme. Sada usluge raspoređujemo u prosjeku svakih 1,5 dan, grupišući ih, jer počinju u rad nakon odobrenja.

U našem sistemu nema fatalnih kvarova. Ako pustimo mikroservis s greškom, tada će funkcionalnost povezana s njom biti pokvarena, a sve ostale funkcionalnosti neće biti pogođene. Ovo uvelike poboljšava korisničko iskustvo.

Možemo kontrolisati šemu raspoređivanja. Možete odvojiti grupe usluga odvojeno od ostatka rješenja, ako je potrebno.

Osim toga, značajno smo smanjili problem velikim redom poboljšanja. Imamo zasebne timove za proizvode koji samostalno rade s nekim od usluga. Ovdje Scrum proces dobro dolazi. Određeni tim može imati zasebnog vlasnika proizvoda koji mu dodjeljuje zadatke.

Rezime

  • Mikroservise su veoma pogodne za razlaganje složenih sistema. U tom procesu počinjemo da shvatamo šta je u našem sistemu, koji su ograničeni konteksti, gde su njihove granice. Ovo vam omogućava da pravilno distribuirate poboljšanja na module i spriječite zamagljivanje koda.
  • Mikrousluge pružaju organizacione prednosti. Često se nazivaju samo arhitekturom, ali svaka arhitektura je potrebna za rješavanje poslovnih potreba, a ne sama po sebi. Stoga možemo reći da su mikroservis vrlo pogodan za rješavanje problema u malim timovima, s obzirom da je Scrum trenutno vrlo popularan.
  • Odvajanje je iterativni proces. Ne možete uzeti aplikaciju i jednostavno je podijeliti na mikroservise. Malo je vjerovatno da će rezultirajući proizvod biti izvodljiv. Kada izdvajamo mikroservise, korisno je prepisati postojeće naslijeđe, odnosno pretvoriti ga u kod koji nam se sviđa i koji funkcionalnošću i brzinom bolje zadovoljava potrebe poslovanja.

    Malo upozorenje: Troškovi prelaska na mikrousluge su prilično značajni. Trebalo je dosta vremena da se riješi problem infrastrukture. Stoga, ako imate malu aplikaciju koja ne zahtijeva posebno skaliranje, ako nema velikog broja kupaca koji se bore za pažnju i vrijeme vašeg tima, onda možda mikroservis nije ono što vam danas treba. Prilično je skupo. Ako pokrenete proces s mikrouslugama, tada će troškovi u početku biti veći nego ako isti projekat započne razvojem monolita.

    PS Emotivnija priča (i kao vama lično) - do link.
    Ovdje je puna verzija izvještaja.

izvor: www.habr.com

Dodajte komentar