Mikro-servisų architektūra: privalumai ir trūkumai

Kas iš tikrųų yra mikro-servisų architektūra?

Prieš kelerius metus programuotojų bendruomenėje prasidėjo tikras hype’as apie mikro-servisus. Visi kalbėjo, kaip tai yra ateitis, kaip monolitinės aplikacijos yra praeitis, ir kaip bet koks naujas projektas turėtų būti kuriamas būtent mikro-servisų principu. Bet kas iš tikrųjų slypi už šio termino?

Paprasčiausiai tariant, mikro-servisų architektūra – tai būdas kurti programinę įrangą kaip mažų, nepriklausomų servisų rinkinį. Kiekvienas toks servisas atlieka konkrečią verslo funkciją ir gali būti kuriamas, diegiamas bei plečiamas atskirai nuo kitų. Įsivaizduokite, kad jūsų aplikacija nėra vienas didelis monolitinis blokas, o veikiau kaip LEGO konstruktorius – kiekviena detalė gali būti keičiama ar tobulinima neliečiant kitų.

Pavyzdžiui, e-komercijos platformoje galite turėti atskirą servisą naudotojų autentifikacijai, kitą – produktų katalogui, dar vieną – mokėjimų apdorojimui, ir t.t. Kiekvienas iš šių servisų veikia savo aplinkoje, turi savo duomenų bazę ir bendrauja su kitais per gerai apibrėžtas sąsajas (dažniausiai REST API arba message queues).

Kodėl visi taip susidomėjo mikro-servisais?

Atsakymas gana paprastas – didžiosios technologijų kompanijos pradėjo viešai kalbėti apie savo sėkmės istorijas. Netflix, Amazon, Uber – visos šios kompanijos perėjo nuo monolitinių sistemų prie mikro-servisų ir pasidalino savo patirtimi. Kai Netflix pasakoja, kaip jie gali diegti naujus funkcionalumus tūkstančius kartų per dieną nesulaužydami visos sistemos, tai skamba įspūdingai.

Bet čia svarbu suprasti kontekstą. Šios kompanijos susidūrė su konkrečiomis problemomis, kurias mikro-servisai padėjo išspręsti. Jos turėjo šimtus ar tūkstančius programuotojų, milijonus vartotojų ir sudėtingas verslo procesus. Jūsų dešimties žmonių startupo situacija gali būti visiškai kitokia.

Mikro-servisų populiarumą taip pat paskatino konteinerizacijos technologijos, ypač Docker ir Kubernetes. Staiga tapo daug paprasčiau valdyti daugybę mažų servisų – juos galima lengvai pakuoti, diegti ir orkestruoti. Cloud platformos kaip AWS, Google Cloud ar Azure taip pat pridėjo savo indėlį, pasiūlydamos įrankius, kurie supaprastina mikro-servisų infrastruktūros valdymą.

Tikrasis mikro-servisų pranašumas

Pirmas ir, mano nuomone, svarbiausias privalumas – komandų nepriklausomumas. Kai turite didelę organizaciją su daugeliu komandų, mikro-servisai leidžia kiekvienai komandai dirbti savo tempu. Frontend komanda gali atnaujinti vartotojo sąsają, mokėjimų komanda – integruoti naują mokėjimo metodą, o produktų komanda – tobulinti paieškos algoritmą. Visi dirba lygiagrečiai, niekas neblokuoja kitų.

Technologijų įvairovė – dar vienas didelis pliusas. Nebereikia visą sistemą rašyti viena programavimo kalba ar naudoti vieną framework’ą. Jei turite komandą, kuri puikiai moka Python ir machine learning, jie gali kurti rekomendacijų servisą Python’u. Kita komanda, kuri specializuojasi Java, gali kurti mokėjimų apdorojimo servisą. Kiekvienas servisas gali naudoti tinkamiausią technologiją konkrečiai problemai spręsti.

Skalabilumas tampa daug lankstesnis. Vietoj to, kad skaluotumėte visą aplikaciją, galite skaluoti tik tuos servisus, kuriems to reikia. Jei jūsų produktų paieška gauna 10 kartų daugiau užklausų nei kiti servisai, paprasčiausiai paleiskite daugiau paieškos serviso instancijų. Tai ne tik efektyviau, bet ir pigiau.

Atsparumas gedimams teoriškai turėtų būti geresnis. Jei vienas servisas sugenda, kiti gali toliau veikti. Žinoma, tai veikia tik tada, kai tinkamai suprojektuojate sistemą su fallback mechanizmais ir circuit breakers. Bet principas teisingas – vieno komponento gedimas nesugriaus visos sistemos.

Dar vienas neakivaizdus privalumas – lengvesnis kodas ir greičiau suprantama logika. Kai servisas atlieka vieną konkrečią funkciją, naujas komandos narys gali greičiau suprasti, kaip jis veikia. Nebereikia kelias savaites tyrinėti milžinišką monolitinį codebase’ą, bandant suprasti, kur kas vyksta.

Realybė ne visada tokia rožinė

Dabar pereikime prie dalykų, apie kuriuos ne visi mėgsta kalbėti. Mikro-servisai atneša sudėtingumo, ir to sudėtingumo yra tikrai daug. Vietoj vienos aplikacijos, kurią galite paleisti savo kompiuteryje, dabar turite dešimtis ar šimtus servisų, kurie turi tarpusavyje bendrauti. Kaip visa tai paleisti lokalioje aplinkoje testavimui? Kaip debuginti, kai problema slypi ne viename servise, o jų sąveikoje?

Tinklo latencija tampa jūsų problema. Monolite funkcijos kviečia viena kitą tiesiogiai – tai greita. Mikro-servsuose kiekvienas išorinis kvietimas vyksta per tinklą. Tai reiškia latenciją, galimus tinklo gedimus, timeout’us. Operacija, kuri monolite užtruktų mikrosekundes, dabar gali užtrukti dešimtis milisekundžių ar net ilgiau.

Duomenų valdymas tampa tikra galvos skausmo priežastimi. Mikro-servisų filosofija sako, kad kiekvienas servisas turėtų turėti savo duomenų bazę. Bet kaip tada užtikrinti duomenų konsistenciją tarp servisų? Kaip atlikti transakcijas, kurios apima kelis servisus? Distributed transactions yra sudėtinga tema, ir dažnai tenka naudoti eventual consistency modelį, kuris ne visada yra intuityvus.

Testavimas tampa kur kas sudėtingesnis. Unit testai lieka paprasti, bet integration testai ir end-to-end testai? Kaip testuoti scenarijas, kurie apima penkis skirtingus servisus? Reikia mock’inti kitus servisus? O gal kurti atskirą testavimo aplinką su visais servisais? Abi galimybės turi savo problemų.

Deployment ir DevOps sudėtingumas išauga eksponentiškai. Vietoj vieno deployment’o dabar turite dešimtis. Reikia versijų valdymo strategijos, rollback planų, monitoring’o kiekvienam servisui. Jums tikrai reikės CI/CD pipeline’ų, konteinerizacijos, orkestravimo platformos. Tai reiškia, kad jums reikia DevOps ekspertizės, kuri gali būti brangi ir sunku rasti.

Monitoring ir debugging košmaras

Kai kas nors neveikia produkcinėje aplinkoje, kaip sužinote, kur problema? Monolite galite pažiūrėti log’us, pridėti breakpoint’ų, ir gana greitai rasti problemą. Mikro-servisuose užklausa gali keliauti per dešimt skirtingų servisų. Kuriame iš jų įvyko klaida? Ar problema yra viename servise, ar jų sąveikoje?

Jums tikrai reikės distributed tracing sprendimų kaip Jaeger ar Zipkin. Reikės centralizuoto logging’o su įrankiais kaip ELK stack (Elasticsearch, Logstash, Kibana) ar panašiais. Reikės metrics ir monitoring sistemų kaip Prometheus ir Grafana. Visa tai reikia sukonfigūruoti, palaikyti ir mokėti naudotis.

Viena iš didžiausių problemų – cascade failures. Kai vienas servisas pradeda lėtai veikti ar krenta, tai gali sukelti domino efektą. Kiti servisai, kurie nuo jo priklauso, pradeda timeout’inti, jų queue’ai pildosi, jie pradeda vartoti daugiau resursų, ir galiausiai visa sistema gali sugriūti. Tam išvengti reikia circuit breakers, rate limiting, proper timeout’ų ir retry logikos – visa tai prideda sudėtingumo.

Kada mikro-servisai tikrai turi prasmę?

Atsakymas nėra paprastas „visada” ar „niekada”. Mikro-servisai turi prasmę, kai:

Jūsų organizacija yra pakankamai didelė – kalbame apie kelias komandas, kurios dirba su ta pačia sistema. Jei turite 3-5 programuotojus, monolitas greičiausiai bus geresnis pasirinkimas. Mikro-servisai sprendžia organizacines problemas, ne tik technines.

Jūsų sistema turi aiškiai atskirtas verslo sritis. E-komercijos platforma su produktų katalogu, užsakymų valdymu, mokėjimais, pristatymu – tai puikus kandidatas. Bet jei jūsų aplikacija yra gana paprasta CRUD sistema, mikro-servisai gali būti overkill.

Jums reikia skirtingo skalabilumo skirtingoms dalims. Jei viena jūsų sistemos dalis gauna 100 kartų daugiau apkrovos nei kitos, mikro-servisai leidžia skaluoti tik ją. Bet jei visa jūsų sistema skaluojasi vienodai, šis privalumas neakivaizdus.

Turite DevOps ekspertizę ir infrastruktūrą. Jei jūsų komandoje nėra žmonių, kurie gali sukonfigūruoti ir palaikyti Kubernetes cluster’į, mikro-servisai bus sunkus kelias. Arba turite būti pasirengę investuoti į mokymąsi ir įrankius.

Praktiniai patarimai norintiems pradėti

Jei vis dar manote, kad mikro-servisai yra jums, štai keletas patarimų, kurie gali padėti išvengti įprastų klaidų:

Pradėkite nuo monolit’o. Taip, girdėjote teisingai. Net Martin Fowler, vienas mikro-servisų evangelistų, rekomenduoja pradėti su monolitu. Pirmiausia sukurkite veikiančią sistemą, supraskite savo verslo domeną, ir tik tada, kai tikrai matote poreikį, pradėkite skaidyti į mikro-servisus. Tai vadinama „monolith first” approach.

Investuokite į automation. Be tinkamo CI/CD, mikro-servisai yra košmaras. Jums reikia automatizuoto testing’o, automatizuoto deployment’o, automatizuoto monitoring’o. Jei darai dalykus rankiniu būdu, greitai paskęsi.

API design yra kritinis. Kai servisai bendrauja per API, šių API dizainas tampa labai svarbus. Naudokite versioning’ą nuo pat pradžių. Dokumentuokite savo API (OpenAPI/Swagger yra jūsų draugas). Galvokite apie backward compatibility.

Pradėkite su keliais servisais, ne šimtu. Nereikia iškart skaidyti viską į smulkiausius galimus servisus. Pradėkite su keliais stambesiais servisais ir skaidykite toliau tik tada, kai matote aiškų poreikį. Per smulkūs servisai (kartais vadinami „nano-services”) sukuria daugiau problemų nei sprendžia.

Service mesh gali padėti, bet ne iš karto. Įrankiai kaip Istio ar Linkerd gali išspręsti daug mikro-servisų problemų (service discovery, load balancing, security, observability), bet jie patys yra sudėtingi. Nepradėkite nuo jų – pirmiausia supraskite problemas, kurias jie sprendžia.

Alternatyvos ir hibridiniai sprendimai

Nebūtina rinktis tik vieną kelią. Yra įvairių hibridinių architektūrų, kurios gali būti optimalesnės:

Modular monolith – tai monolitinė aplikacija, bet su aiškia moduline struktūra. Kiekvienas modulis turi aiškias ribas ir sąsajas, panašiai kaip mikro-servisai, bet visi veikia tame pačiame procese. Tai suteikia kai kuriuos mikro-servisų privalumus (aiškus atskyrimas, galimybė dirbti nepriklausomai) be daugelio jų trūkumų (tinklo latencija, deployment sudėtingumas).

Macroserivces arba miniservices – vietoj dešimčių labai mažų servisų, turite kelis stambesnius. Pavyzdžiui, vieną servisą visam backend’ui, kitą – admin panelei, dar vieną – API. Tai sumažina sudėtingumą, bet vis tiek leidžia skaluoti ir diegti atskirai.

Strangler pattern – jei turite esamą monolitą ir norite pereiti prie mikro-servisų, nebandykite visko perrašyti iš karto. Vietoj to, pamažu „nusmaugkite” monolitą, iškeliant funkcionalumą į naujus servisus po vieną. Tai mažina riziką ir leidžia mokytis pakeliui.

Ką daryti su legacy sistemomis?

Daugelis komandų susiduria su klausimu: turime seną monolitinę sistemą, ar turėtume ją migruoti į mikro-servisus? Atsakymas priklauso nuo daugelio faktorių.

Jei jūsų monolitas veikia gerai, nėra didelių performance problemų, ir komanda gali produktyviai dirbti – galbūt nereikia nieko keisti. „If it ain’t broke, don’t fix it” yra geras patarimas. Migracija į mikro-servisus yra didelis projektas, kuris gali užtrukti metus ir sunaudoti daug resursų.

Bet jei matote tikras problemas – deployment’ai trunka per ilgai, sistema sunkiai skaluojasi, komandos blokuoja viena kitą, kodas tapo nesuprantamas – tada migracija gali turėti prasmę. Tik darykite tai pamažu, naudodami strangler pattern ar panašias strategijas.

Svarbu įvertinti ROI (return on investment). Migracija į mikro-servisus kainuos – laiko, pinigų, komandos pastangų. Ar tie privalumai, kuriuos tikitės gauti, yra verti šių investicijų? Kartais atsakymas yra „taip”, bet dažnai – „ne dabar” arba „tik iš dalies”.

Kai viskas susidėlioja į savo vietas

Mikro-servisų architektūra nėra nei stebuklingas sprendimas, nei katastrofa. Tai įrankis, kuris tam tikrose situacijose veikia puikiai, o kitose sukuria daugiau problemų nei sprendžia. Esmė yra suprasti, kada jis tinka jūsų kontekstui.

Jei kuriate naują projektą mažoje komandoje, greičiausiai pradėkite nuo monolit’o. Nesijaudinkite – tai nereiškia, kad užsidarote duris ateityje. Gerai suprojektuotas monolitas gali būti suskaidytas į mikro-servisus, kai ateis laikas. O gal tas laikas niekada neateis, ir tai visiškai gerai.

Jei jau turite didelę sistemą ir komandą, kuri kovoja su monolito apribojimais, mikro-servisai gali būti atsakymas. Bet būkite pasirengę investuoti į infrastruktūrą, įrankius ir mokymąsi. Ir nepamirškite, kad technologija yra tik dalis sprendimo – organizacinė struktūra ir procesai yra ne mažiau svarbūs.

Galiausiai, nesvarbu, ką pasirinksite, svarbiausias dalykas yra suprasti savo sistemos poreikius, komandos galimybes ir verslo tikslus. Architektūra turėtų tarnauti jūsų tikslams, o ne atvirkščiai. Ir atminkite – nėra vieno teisingo atsakymo visiems. Tai, kas veikia Netflix ar Amazon, nebūtinai veiks jūsų projektui, ir tai visiškai normalu.

Daugiau

Vault secrets valdymas: slaptažodžių saugojimas