Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Selles artiklis räägin, kuidas projekt, milles töötan, muutus suurest monoliidist mikroteenuste kogumiks.

Projekt alustas oma teekonda üsna ammu, 2000. aasta alguses. Esimesed versioonid olid kirjutatud Visual Basic 6-s. Aja jooksul sai selgeks, et selle keele arendamine tulevikus osutub keeruliseks, kuna IDE ja keel ise arenesid aeglaselt. 2000. aastate lõpus otsustati liikuda perspektiivikama C# keele juurde. Uus versioon kirjutati paralleelselt vana täiustamisega, kus järjest rohkem koodi viidi .NET-i. C#-il põhinev tagaplaan oli algselt suunatud teenuse arhitektuurile, kuid arendamisel kasutati tavalisi raamatukogusid koos loogikaga, ja teenuseid käivitati ühes protsessis. Tulemusena saime rakenduse, mida nimetasime "teenuse monoliidiks".

Üheks vähestest sellise ühenduse eelistest oli võimalus teenustel üksteist välise API kaudu kutsuda. Oli ilmseid eeltingimusi liikuda õige teenuse ja tulevikus ka mikroteenuse arhitektuuri poole.

Meie dekomposteerimise töö algas umbes 2015. aastal. Veel pole me saavutanud ideaalset seisundit - on alles jäänud osi suurest projektist, mida on raske nimeta monoliitideks, aga nad ei sarnane ka mikroteenustega. Sellegipoolest on edusamm märkimisväärne.
Seda ma ka artiklis kirjeldan.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Sisu

Olemasoleva lahenduse arhitektuur ja probleemid


Algne arhitektuur nägi välja järgmine: UI - eraldi rakendus, monoliitne osa oli kirjutatud Visual Basic 6-s, .NET rakendus oli koosne lahti seotud teenustest, mis töötasid üsna suure andmebaasiga.

Eelmise lahenduse puudused

Üksik rikkepunkt
Meil oli üks tõrke koht: .NET rakendus käitus ühes protsessis. Kui mõnes moodulis esines rike, kukkus kogu rakendus ära ja tuli see taaskäivitada. Kuna automatiseerime suurt hulka protsesse erinevatele kasutajatele, ei saanud me mõneks ajaks töötada kõik, kui üks neist ebaõnnestus. Ja programmitõrge korral ei aidanud ka varundamine.

Töötundide järjekord
See probleem on pigem organisatsiooniline. Meie rakenduses on palju kliente, ja kõik nad soovivad oma arendustöödega võimalikult kiiresti edasi minna. Varem polnud seda võimalik samaaegselt teha ja kõik kliendid said järjekorda. See protsess tekitas äri seas negatiivset suhtumist, kuna neil tuli tõestada, et nende ülesanne on väärtuslik. Arendusmeeskond kulutas aega, et seda järjekorda korraldada. See võttis palju aega ja energiat, ning toode ei suutnud lõpuks muutuda nii kiiresti, kui nad oleksid soovinud.

Ressursside ebapiisav kasutamine
Teenuste paigutamine ühte protsessi tähendas, et me kopeerisime alati konfiguratsiooni serverilt serverile täielikult. Soovisime paigutada kõige koormatud teenused eraldi, et mitte raisata ressursse ja saavutada oma kasutamise skeemi paindlikum juhtimine.

Moodsa tehnoloogia rakendamine on keeruline.
Tuttav probleem igale arendajale: on soov tuua projekti moodsaid tehnoloogiaid, kuid võimalused puuduvad. Suure monoliitse lahenduse puhul muutub iga olemasoleva teegi värskendamine, rääkimata uuele üleminekust, üsna keeruliseks ülesandeks. Tuleb pika aega tõestada tiimijuhile, et see toodab rohkem kasu kui kulutatud närve.

Muudatuste väljastamise keerukus.
See oli kõige tõsisem probleem - andsime välja versioone iga kahe kuu tagant.
Iga versioon muutus pangale tõeliseks katastroofiks, hoolimata testimisest ja arendajate pingutustest. Äri mõistis, et tal ei ole nädal alguses osa funktsionaalsusest kasutamiseks. Ja arendajad mõistsid, et neid ootab ees nädal tõsiste probleemidega.
Soov olukorda muuta oli kõigil.

Ootused mikroteenustele


Komponendid antakse välja nende valmiduse järgi. Komponendid antakse välja valmiduse järgi lahenduse dekompositsiooni ja erinevate protsesside eraldamise tõttu.

Väikesed tootemeeskonnad. See on oluline, kuna suure meeskonna, kes töötas vana monoliidi kallal, haldamine oli keeruline. Selline meeskond pidi töötama rangete protsesside järgi, kuid sooviti rohkem loovust ja iseseisvust. Selleks saaksid endale lubada ainult väiksed meeskonnad.

Teenuste isoleerimine eraldi protsessides. Ideaalis oleks soovinud isoleerida konteineritesse, kuid suur hulk .NET Frameworki põhiseid teenuseid töötab ainult Windowsi all. Praegu on küll hakanud ilmuma teenuseid .NET Core'i peal, kuid neid on veel vähe.

Paigaldamise paindlikkus. Sooviksime kombineerida teenuseid nii, nagu meile vajalik, mitte nii, nagu kood sunnib.

Uute tehnoloogiate kasutamine. See huvitab iga programmeerijat.

Ülemineku probleemid


Muidugi, kui monoliidi jagamine mikroteenusteks oleks lihtne, ei räägiks me sellest konverentsidel ja ei kirjutaks artikleid. Sellel protsessil on palju undercurrents'e, kirjeldan peamisi, mis takistasid meid.

Esimene probleem tüüpiline enamikule monoliitidele: äriloogika sidusus. Kui me kirjutame monoliiti, tahame taaskasutada meie klasse, et mitte kirjutada liigset koodi. Kuid mikroteenustele üleminek toob selle probleemina esile: kogu kood on piisavalt rangelt seotud ja teenuste eraldamine on keeruline.

Tööde alustamise hetkeks oli hoidlates üle 500 projekti ja rohkem kui 700 tuhat koodirea. See on piisavalt suur lahendus ja teine probleem. Lihtsalt võtta ja jagada see mikroteenusteks ei olnud võimalik.

Kolmas probleem – vajaliku infrastruktuuri puudumine. Tegelikult tegelesime algkoodi käsitsi kopeerimisega serveritesse.

Kuidas liikuda monoliidilt mikroteenustele


Mikroteenuste eraldamine

Esiteks, me määratlesime endale kohe, et mikroteenuste eraldamine on iteratiivne protsess. Meilt nõuti alati samaaegselt äriülesannete arendamist. Kuidas me seda tehniliselt teostame – see on juba meie probleem. Seetõttu valmistusime iteratiivseks protsessiks. Teistmoodi ei ole võimalik, kui teil on suur rakendus, ja see ei ole algselt valmis selleks, et seda uuesti kirjutada.

Milliseid meetodeid me mikroteenuste eraldamiseks kasutame?

Esimene meetod — tuua olemasolevad moodulid teenustena välja. Meil on selles osas vedanud: juba oli olemas teenuseid, mis töötasid WCF protokolli kaudu. Need olid jaotatud eraldi kogumitesse. Me edastasime need eraldi, lisades igale kogumile väikese käivitamismooduli. See oli kirjutatud suurepärase Topshelf raamatukogu abil, mis võimaldab rakendust käivitada nii teenusena kui ka konsoolidena. See on mugav silumise jaoks, kuna ei ole vaja lisa projekte lahendusse.

Teenused olid seotud äriloogikaga, kuna nad kasutasid ühiseid kogumeid ja töötasid ühise andmebaasiga. Neid oli raske pidada puhtaks mikroteenuseks. Kuid me saime neid teenuseid välja anda eraldi erinevates protsessides. Isegi see võimaldas vähendada nendevahelist mõju, vähendades paralleelse arenduse probleemi ja ühte rikkepunkti.

Hosti kogumik on Program klassis vaid üks koodirida. Tööd Topshelfiga oleme peitnud abiklassi.

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

       }
    }
}

Teine mikroteenuste eraldamise viis: luua neid uute probleemide lahendamiseks. Kui samas monoliit ei kasva, on see juba hea, see tähendab, et liikume õiges suunas. Uute probleemide lahendamiseks oleme püüdnud teha eraldi teenuseid. Kui see oli võimalik, siis oleme loonud rohkem „kanonilisi“ teenuseid, mis haldavad täielikult oma andmemudelit ja eraldi andmebaasi.

Meie, nagu paljud teised, alustasime autentimis- ja autoriseerimisteenustega. Need sobivad selleks ideaalselt. Nad on iseseisvad, nende andmemudel on tavaliselt eraldi. Nad ei suhtu monoliiti, vaid ainult monoliit pöördub nende poole teatud probleemide lahendamiseks. Nendel teenustel on võimalik alustada üleminekut uuele arhitektuurile, testida nende peal infrastruktuuri, katsetada erinevaid lähenemisviise, mis on seotud võrgurakendustega jne. Meie organisatsioonis ei ole tiime, kellel ei oleks õnnestunud luua autentimisteenust.

Kolmas viis mikroteenuste eristamiseks, mida me kasutame, on meie jaoks veidi spetsiifiline. See on äriloogika eraldamine UI-kihist. Meie peamine UI-rakendus on desktop-versioon, mis on nagu ka backend kirjutatud C#-s. Arendajad eksisid perioodiliselt ja eraldasid UI-sse loogika osi, mis pidanuks eksisteerima backend-is ja mida oleks võinud taaskasutada.

Kui vaadata tõelist näidet UI-osa koodist, siis on selge, et suur osa sellest lahendusest sisaldab tõeliselt äriloogikat, mis on kasulik muudes protsessides, mitte ainult UI-vormi koostamisel.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Tegelikku UI loogikat on seal ainult viimasel paaril real. Me viisime selle serverisse, et oleks võimalik taaskasutada, vähendades seeläbi UI-d ja saavutades õige arhitektuuri.

Neljas, kõige olulisem viis mikroteenuste eraldamiseks, mis võimaldab monoliiti vähendada, on olemasolevate teenuste välja viimine koos ümbertöötamisega. Kui me viime olemasolevad moodulid sellisena välja, ei pruugi tulemus alati arendajatele meeldida, ja äri protsess ajast, mil funktsionaalsus loodi, võib olla vananenud. Refaktooringu abil saame toetada uut äriprotsessi, kuna äri nõuded muutuvad pidevalt. Saame parandada algkoodi, eemaldada teadaolevaid vigu, luua kvaliteetsem andmemudel. Saame palju eeliseid.

Teenuste eraldamine, millel on piiratud kontekst, on tihedalt seotud spetsiifilise disainiga. See mõisted on seotud domeenimudeli osaga, kus kõik termini on üheselt määratletud. Vaadakem näiteks kindlustuste ja arvete konteksti. Meil on monoliitne rakendus, kus on vaja kindlustustes töötada arvega. Me eeldame, et arendaja leiab teisest komplektist olemasoleva klassi „Arve“, seob selle klassiga „Kindlustus“ ning me saame töötava koodi. DRY printsiip on järgitud, ülesanne läheb olemasoleva koodi kasutamise tõttu kiiremini täidetud.

Tulemuseks selgub, et arvete ja kindlustuste kontekstid on omavahel seotud. Kui tulevad uued nõudmised, siis see seos takistab arendust, suurendades juba niigi keerulise äriloogika keerukust. Selle probleemi lahendamiseks tuleb koodis leida piirid kontekstide vahel ja eemaldada nende rikkumised. Näiteks võib kindlustuste konteksti jaoks olla piisav 20-kohaline arve number ja arve avamise kuupäev.

Ette eristada neid piiratud kontekste üksteisest ja alustada mikroteenuste eraldamise protsessi monoliitsest lahendusest, kasutasime lähenemist, mis hõlmas välist API loomist rakenduse sees. Kui me teadsime, et mõni moodul peab muutuma mikroteenuseks, muudetakse seda protsessi raames, tegime me kohe väljakutseid sellele loogikale, mis kuulub teise piiratud konteksti, läbi väliste kõnede. Näiteks REST-i või WCF kaudu.

Oleme endale kindlalt otsustanud, et ei väldi koodi, mis nõuab jaotatud tehingute tegemist. Meie puhul osutus selle reegli järgimine piisavalt lihtsaks. Meie jaoks ei ole kunagi tekkinud olukordi, kus ranged jaotatud tehingud oleksid tõeliselt vajalikud - moodulite vahelise lõppkooskõla tagamine on täiesti piisav.

Vaatame ühte konkreetset näidet. Meil on orkestrite mõisted — konveier, mis töötleb "taotluse" objekti. See loob järjestikku kliendi, konto ja pangakaardi. Kui klient ja konto on edukalt loodud, kuid kaardi loomine ebaõnnestub, ei muutu taotlus staatuseks "edukalt" ja jääb staatuseks "kaart ei ole loodud". Tulevikus lõpusüsteem haarab selle ja lõpetab selle. Süsteem on mõnda aega ebakõlas, kuid see meid üldiselt rahuldab.

Kui siiski tekib olukord, kus tuleb koosolekuks osa andmeid salvestada, peame tõenäoliselt minema teenuse suuremaks koondamiseks, et seda ühes protsessis töödelda.

Vaatame mikroteenuse eraldamise näidet. Kuidas saaksime seda suhteliselt ohutult tootmisse viia? Selles näites on meil süsteemi eraldi osa — palgahalduse moodul, mille koodist soovime osa muuta mikroteenuseks.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Esiteks loome mikroteenuse, kirjutades koodi ümber. Parandame mõned detailid, mis meid mitte rahuldatud. Rakendame uusi ärianalüüse kliendi poolt. Lisame ühendusse UI ja tagaplaani vahel API Gateway, mis tagab kõnede edastamise.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Seejärel juurutame selle konfiguratsiooni tootmisse, kuid pilootseisundis. Enamik meie kasutajatest jätkab endiselt töötlust vanade äriprotsesside kaudu. Uute kasutajate jaoks arendame välja uue versiooni monoliitset rakendust, mis seda protsessi enam ei sisalda. Tegelikult toimib meil pilootina monoliidi ja mikroteenuse kombinatsioon.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Eduka piloodi korral mõistame, et uus konfiguratsioon on tõeliselt toimiv ning saame vanade monoliite tasakaalust eemaldada ja jätta uue konfiguratsiooni vana lahenduse kohale.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Kokkuvõttes kasutame praktiliselt kõiki olemasolevaid meetodeid monoliitse lähtekoodi jagamiseks. Kõik need võimaldavad meil vähendada rakenduse osi ja viia need uutele raamatukogudele, tagades kvaliteetsemat lähtekoodi.

Töö andmebaasiga


Andmed ei jagune nii hästi nagu lähtekood, kuna need sisaldavad mitte ainult praegust skeemi, vaid ka kogunenud ajaloolisi andmeid.

Meie andmebaasil, nagu paljudel teistel, oli veel üks oluline puudus — tohutu suurus. Seda andmebaasi kavandati segase monoliidi äri loogika jäägiga, ja erinevate piiratud kontekstide tabelite vahel tekkisid seosed.

Meie puhul, lisaks kõigile muredele (suur andmebaas, hulk seoseid, mõnikord arusaamatud piirid tabelite vahel), tekkis probleem, millega paljudes suurtes projektides kokku puututakse: jagatud andmebaasi mustri kasutamine. Andmed võeti tabelitest vaate kaudu, replikatsiooniga ja saadetud teistesse süsteemidesse, kus seda replikatsiooni vajati. Tulemuseks oli see, et me ei saanud tabeleid eraldi skeemi viia, sest neid kasutati aktiivselt.

Jagamisel aitab meid just see piiramine, mis koodis olemas on. See annab tavaliselt meile piisavalt hea ülevaate sellest, kuidas me jagame andmeid andmebaasi tasandil. Me mõistame, millised tabelid kuuluvad ühte piiratud konteksti ja millised teise.

Oleme rakendanud kahte globaalselt meetodit andmebaasi eraldamiseks: olemasolevate tabelite eraldamine ja eraldamine ümberehitamisega.

Olemasolevate tabelite eraldamine on meetod, mida on hea rakendada, kui andmestruktuur on kvaliteetne, rahuldab ärianalüüse ning kõik on sellega rahul. Sel juhul saame olemasolevad tabelid eraldada eraldi skeemiks.

Eraldamine ümberehitamisega on vajalik, kui äri mudel on oluliselt muutunud ja tabelid ei rahulda meid enam üldse.

Olemasolevate tabelite eraldamine. Peame määrama, mida me eraldame. Ilma selle teadmisega ei saa midagi saavutada, ja siin aitab meid koodi piiratud konteksti eraldamine. Reeglina, kui suudame mõista konteksti piire algkoodis, saame aru, millised tabelid peaksid eraldamist saama.

Kujutage ette lahendust, kus kaks monoliidi moodulit suhtlevad ühe andmebaasiga. Me peame tagama, et eraldatavate tabelite osaga suudleb ainult üks moodul, samas kui teine hakkab sellega suhtlema API kaudu. Esialgu piisab, kui API kaudu toimub ainult kirjutamine. See on hädavajalik tingimus, et saaksime rääkida mikroteenuste sõltumatusest. Lugemise seosed võivad jääda, kuni see ei tekita suurt probleemi.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Järgmise sammuna saame juba koodiosa, mis töötab eraldatavate tabelitega, koos või ilma ümber töötamiseta, eraldi mikroteenuseks eraldada ja käivitada eraldi protsessis, konteineris. See on eraldi teenus, millel on seos monoliidi andmebaasiga ja nende tabelitega, mis ei kuulu otseselt tema alla. Monoliit suhtleb endiselt lugemise kaudu eraldatud osaga.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Hiljem eemaldame selle seose, mille puhul ka monoliidi rakenduse andmete lugemine eraldatavatest tabelitest viiakse API peale.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Eemaldame esmalt peamisest andmebaasist tabelid, millega töötab ainult uus mikrosüsteem. Me saame tabelid viia eraldi skeemi või isegi eraldi füüsilisse andmebaasi. Ühendus lugemiseks mikrosüsteemi ja monoliidi andmebaasi vahel jääb, kuid selles pole midagi hullu; sellises konfiguratsioonis võib see piisavalt kaua töötada.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Viimane samm on kõik sidemed täielikult eemaldada. Sel juhul võib meil olla vajalik andmete migreerimine peamisest andmebaasist. Mõnikord tahame taaskasutada mõningaid välisosistest süsteemidest replikatsioone, või katalooge mitmes andmebaasis. See juhtub meil perioodiliselt.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Eraldamine koos töötlemisega. See meetod on väga sarnane esimesele, ainult et see toimub vastupidises järjestuses. Meil выделяется kohe uus andmebaas ja uus mikrosüsteem, mis suhtleb monoliidiga API kaudu. Kuid selle juurde jääb andmebaasi tabelite kogum, mille tahame tulevikus eemaldada. Me ei vaja seda enam, uues mudelis asendasime selle.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Selle skeemi toimimiseks vajame ilmselt üleminekuperioodi.

Edasi on kaks võimalikku lähenemist.

Esimene: me dublime kõik andmed uutes ja vanades andmebaasides. Sellisel juhul tekib meil andmete ülepakkumine, mis võib põhjustada sünkroonimisprobleeme. Kuid saame siiski võtta kaks erinevat klienti. Üks töötab uue versiooniga, teine vanaga.

Teine: jagame andmeid mingite äri tunnuste järgi. Näiteks, meil oli süsteemis 5 toodet, mis on salvestatud vanasse andmebaasi. Kuues toode uue äriprojekti raames paigutatakse uude andmebaasi. Kuid meil on vaja API Gateway't, mis sünkroonib need andmed ja näitab kliendile, kust ja mida võtta.

Mõlemad lähenemised on töötavad, valige vastavalt olukorrale.

Pärast seda, kui kinnitame, et kõik töötab, saab osa monoliidist, mis töötab vanade andmebaasi struktuuridega, välja lülitada.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Viimase sammuna on vana andmestruktuuri eemaldamine.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Kokkuvõtteks võib öelda, et meil on andmebaasidega probleeme: keeruline on nendega töötada võrreldes lähtekoodiga, jagamine on keerulisem, kuid seda saab ja tuleb teha. Oleme leidnud mõned viisid, mis võimaldavad seda teha piisavalt turvaliselt; siiski andmete osas on viga lihtsam teha kui lähtekoodiga.

Töö lähtekoodiga


Nii nägi algkoodi skeem välja, kui hakkasime monoliitset projekti analüüsima.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Seda saab tinglikult jagada kolmeks kihiks. See on käivitavate moodulite, pistikute, teenuste ja eraldi tegevuste kiht. Tegelikult olid need sissepääsupunktid monoliitses lahenduses. Kõik need olid tihedalt seotud Common kihiga. Selles oli äriloogika, mida teenused kasutasid ühiselt, ning palju seoseid. Iga teenus ja pistikupesa kasutas kuni 10 ja enam common-kogumit, olenevalt nende suurusest ja arendajate aususest.

Meil oli õnne, meil olid infrastruktuuri raamatukogud, mida sai kasutada eraldi.

Mõnikord juhtus, et mõned Common-objektid ei kuulunud tegelikult sellesse kihti, vaid olid infrastruktuuri raamatukogud. See lahendati ümbernimetamisega.

Kõige enam tekitas muret piiratud kontekstid. Mõnikord segunesid 3-4 konteksti ühes Common'i kogus ja kasutasid üksteist sama ärifunktsiooni raames. Oli vajalik mõista, kus on need võimalikud eraldada ja milliste piiride järgi, ning mida edasi teha selle eraldamise kaardistamisega lähtekoodi kogudele.

Me koostasime mõned reeglid koodi jagamise protsessiks.

Esimene: me enamorado de la idea de no querer compartir la lógica de negocios entre servicios, actividades y plugins. Queríamos hacer que la lógica de negocios fuera independiente dentro de los microservicios. Por otro lado, los microservicios, en un escenario ideal, se perciben como servicios que existen completamente de forma independiente. Creo que este enfoque es algo derrochador y difícil de alcanzar, ya que, por ejemplo, los servicios en C# estarán siempre conectados a la biblioteca estándar. Nuestro sistema está escrito en C#, otras tecnologías aún no se han utilizado. Por lo tanto, decidimos que podíamos permitirnos usar compilaciones técnicas comunes. Lo principal es que no contengan fragmentos de lógica de negocios. Si tienes un envoltorio conveniente sobre la ORM que utilizas, copiarlo de un servicio a otro es muy costoso.

Meie meeskond on asjatundjate erialase disaini fänn, seetõttu sobis meile suurepäraselt "sibulaarhitektuur". Meie teenuste aluseks ei ole data access layer, vaid domeeniloogikaga koostamine, mis sisaldab ainult äriloogikat ja on vabastatud infrastruktuuri seosest. Samuti saame me domeenikoostist iseseisvalt täiendada raamistikest tulenevate probleemide lahendamiseks.

Sel hetkel kohtasime esmakordselt tõsist probleemi. Teenus pidi viitama ühele domeenikogule, loogika soovisime teha sõltumatuks, ja DRY põhimõte segas meid tugevalt. Arendajad soovisid vältida dubleerimist, kasutades naaberkogude klasse, ja tulemuseks hakkasid domeenid jälle omavahel siduma. Analüüsisime tulemusi ja otsustasime, et probleem võib olla seotud ka lähtekoodi hoidlaga. Meil oli suur repoid, kus asusid kõik lähtekoodid. Projekti lahendust oli väga keeruline kohalikult masinal kokku panna. Seetõttu loodi projekti osade jaoks eraldi väiksed lahendused, ja keegi ei pidanud keelama neid lisada mõnda ühiskogusse või domeenikogusse ja seda uuesti kasutada. Ainsaks tööriistaks, mis ei lubanud meil seda teha, oli koodivaatlus. Kuid mõnikord andis ka see tõrkeid.

Seega hakkasime liikuma eraldi hoidlate mudeli suunas. Äri loogika ei voolanud enam teenusest teenusesse, domeenid muutusid tõeliselt sõltumatuks. Piiratud kontekste toetatakse selgemalt. Kuidas me samas infrastruktuuri raamatukogusid taaskasutame? Me eraldasime need eraldi hoidlasse, seejärel panime need Nuget-pakettideks, mille panime Artifactory'sse. Iga muudatusega toimub ehitamine ja avaldamine automaatselt.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Meie teenused hakkasid viitama sisemistele infrastruktuuri pakettidele täpselt nagu välimistele. Välimised raamatukogud laadime alla Nugetist. Artifactory'ga, kuhu me need paketid panime, töötamiseks kasutasime kahte paketihaldurit. Väikestes hoidlates kasutasime samuti Nugetit. Mitme teenusega hoidlates kasutasime Paket'i, mis tagab suurema versioonide ühtsuse moodulite vahel.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Nii et töötades lähtekoodiga, muutes veidi arhitektuuri ja eraldades hoidlaid, muudame oma teenused sõltumatumaks.

Infrastruktuuri probleemid


Enamik mikroteenustele ülemineku probleemidest on seotud infrastruktuuriga. Teil on vaja automatiseeritud juurutamist ning uusi teeke, et infrastruktuur töötaks.

Käsitsi installimine keskkondadesse

Esialgu paigaldasime lahendused keskkondadesse käsitsi. Selle protsessi automatiseerimiseks lõime CI/CD torustiku. Valisime pideva tarnimise (continuous delivery) protsessi, kuna pidev juurutamine (continuous deployment) ei sobi meie äriprotsesside jaoks veel. Seetõttu toimub juurutamine nupu vajutamisega, testimine aga automaatselt.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Kasutame Atlassianit, Bitbucketit lähtekoodi hoidmiseks ja Bamboo't ehitamiseks. Meile meeldib kirjutada ehitusskripte Cake'is, kuna see on sama, mis C#. Artifactory'sse tulevad juba valmis paketid, ning Ansible saadetakse automaatselt testserveritesse, mille järel saab neid kohe testida.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Erinev logimine


Oma ajal oli monoliidi ideedest üks ühise logimise tagamine. Me pidime ka aru saama, mida teha eraldi logidega, mis on kettale salvestatud. Logid meie juures kirjutatakse tekstifailidesse. Otsustasime kasutada standardset ELK-steki. Me ei hakanud logima otse ELK-sse teenusepakkujate kaudu, vaid otsustasime, et täiustame tekstiloge ja salvestame neisse jälgimise ID identifikaatorina, lisades teenuse nime, et neid logisid hiljem saaks analüüsida.

Üleminek monoliidilt mikroteenustele: ajalugu ja praktika

Filebeati abil saame võimaluse koguda meie logisid serverite, seejärel neid muuta, kasutada Kibanat päringute loomiseks kasutajaliideses ja vaadata, kuidas teenuste vahel kutsumine toimus. Selle juures aitab ID jälgimine suuresti.

Seotud teenuste testimine ja silumine


Alguses ei olnud meil täpselt arusaamist, kuidas arendatavaid teenuseid siluda. Monoliidiga oli kõik lihtne, käivitasime selle kohalikel masinatel. Alguses proovisime sama teha ka mikroteenustega, kuid mõnikord on ühe mikroteenuse täielikuks käivitamiseks vajalik käivitada ka mitu muud teenust, mis on ebamugav. Me mõistsime, et on vajalik liikuda mudelile, kus jätame kohalikele masinatele ainult teenuse või teenused, mida soovime siluda. Ülejäänud teenuseid kasutatakse serveritest, mis vastavad tootmise konfiguratsioonile. Pärast silumist, testimise käigus, väljastatakse testimise serverile ainult muudetud teenused iga ülesande jaoks. Nii testitakse lahendust sellises vormis, nagu see tulevikus tootmises olema hakkab.

On serverid, millel on ainult teenuste tootmisversioonid. Need serverid on vajalikud intsidentide puhul, tarnimise kontrollimise jaoks enne juurutamist ja sisemiste koolituste jaoks.

Meil on lisandunud automaatse testimise protsess, kasutades populaarset Specflow teeki. Testid käivitatakse automaatselt NUnit'i abil kohe pärast juurutamist Ansible'ist. Kui ülesande katmine on täielikult automaatne, pole käsitsi testimist vaja. Kuigi mõnikord on siiski vajalik täiendav käsitsi testimine. Teatud ülesande jaoks käivitatavate testide määratlemiseks kasutame Jira sildiaid.

Täiendavalt on suurenenud vajadus koormustestimise järele, mida varem viidi läbi vaid harvadel juhtudel. Testide käivitamiseks kasutame JMeter'i, nende säilitamiseks InfluxDB-d ning protsessi graafikute koostamiseks Grafana't.

Mida me saavutasime?


Esiteks oleme vabanenud mõisted 'vabastamine'. Kaks kuud kestnud monstruehivad vabastamised, mil see hiiglane juurutati tootmisotsustesse, katkestades ajutiselt äri protsessid, on kadunud. Nüüd juurutame teenuseid keskmiselt iga 1,5 päeva tagant, koondades neid, kuna nad lähevad kasutusele pärast kooskõlastamist.

Meie süsteemis ei esine kriitilisi rikkeid. Kui me väljastame mikroteenuse veaga, siis seonduv funktsionaalsus lõpetab töö, kuid ülejäänud funktsionaalsus ei kahjustu. See parandab tunduvalt kasutajakogemust.

Me saame hallata juurutamise skeemi. Saame eraldada teenuste grupid eraldi ülejäänud lahendusest, kui see on vajalik.

Lisaks oleme oluliselt vähendanud suurt muudatusprotsesside järjekorda. Meil on tekkinud eraldi tootmismeeskonnad, mis töötavad osa teenustega sõltumatult. Siin sobib juba hästi Scrum-protsess. Konkreetse meeskonna võib omada eraldi tooteomanik, kes seab neile ülesandeid.

Kokkuvõte

  • Mikroteenused sobivad hästi keeruliste süsteemide dekomponeerimiseks. Protsessi käigus hakkame aru saama, mis meie süsteemis on, millised piiratud kontekstid esinevad ja kus nende piirid asuvad. See võimaldab õigesti jaotada muudatused moodulite lõikes, vältides samas koodi segadusseminemist.
  • Mikroteenused pakuvad organisatsioonilisi eeliseid. Nendest räägitakse sageli ainult arhitektuurina, kuid iga arhitektuur on vajalik äri vajaduste rahuldamiseks, mitte iseenesest. Seetõttu võime öelda, et mikroteenused sobivad hästi väikeste meeskondade ülesannete lahendamiseks, arvestades, et Scrum on praegu väga populaarne.
  • Jagamine on iteratiivne protsess. Ei saa võtta rakendust ja lihtsalt jagada seda mikroteenusteks. Tootest, mis nii saadakse, on väga vähe tõenäolist, et see töötab. Mikroteenuste eraldamisel on kasulik olemasolev pärand ümber kirjutada, muutes selle koodiks, mis meile meeldib ja mis paremini rahuldab äri vajadusi funktsionaalsuse ja kiirusel.

    Väike ettevaatus: Mikroteenuste kasutuselevõtmise kulud on piisavalt suured. Ainult infrastruktuuriprobleemide lahendamiseks kulus palju aega. Seetõttu, kui teil on väike rakendus, mis ei vaja spetsiifilist skaleerimist, ega ole palju kliente, kes konkureerivad teie meeskonna tähelepanu ja aja nimel, siis võib-olla ei ole mikroteenused täna teie jaoks vajalikud. See on üsna kulukas. Kui alustada protsessi mikroteenustega, on algkulud suuremad kui sama projekti alustamine monoliidi arendamisega.

    P.S. Veel emotsionaalsem jutustus (nagu isiklikult teile) – lingi kaudu.
    Siin on ettekande täisversioon.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster