Në projektet që lidhen me zhvillimin e arkitekturës mikroshërbimesh, CI/CD kalon nga një mundësi e këndshme në një nevojë të domosdoshme. Testimi automatik është një pjesë e pandashme e integrimit të vazhdueshëm, dhe një qasje e mençur ndaj tij mund t'i ofrojë ekipit shumë mesnate të këndshme me familjen dhe miqtë. Në të kundërt, projekti rrezikon të mbetet asnjëherë i pa tamam.
Është e mundur të mbulohet i gjithë kodi i mikroshërbimit me teste njësie me objekte të simuluar, por kjo zgjidh vetëm pjesërisht problemin dhe lë shumë pyetje dhe vështirësi, veçanërisht gjatë testimit të punës me të dhënat. Si gjithmonë, më kritiket janë testimi i konsistencës së të dhënave në një DB relacional, testimi i punës me shërbimet në cloud dhe supozimet e gabuara gjatë hartimit të objekteve të simuluara.
Të gjitha këto dhe pak më shumë zgjidheshin përmes testimit të një mikroshërbimi të tërë në një kontejner Docker. Një avantazh i padiskutueshëm për të garantuar vlefshmërinë e testeve është se testet i nënshtrohen të njëjtave imazhe Docker që shkojnë në prodhim.
Automatizimi i këtij qasjeje paraqet një sërë problemesh, zgjidhja e të cilave do të përshkruhet më poshtë:
- konfliktet e detyrave paralele në një host docker;
- konfliktet e identifikuesve në DB gjatë iteracioneve të testit;
- pritja e gatishmërisë së mikroshërbimeve;
- bashkimi dhenxjerrja e logeve në sisteme të jashtme;
- testimi i kërkesave HTTP që dalin;
- testimi i websockets (me ndihmën e SignalR);
- testimi i autentifikimit dhe autorizimit OAuth.
Ky është një artikull mbi motivet e në SECR 2019. Pra, për ata që mërziten nga leximi, .

Në këtë artikull do të flas për mënyrën si të funksiononi me një skript për të nisur shërbimin që testoni në Docker, bazën e të dhënave dhe shërbimet Amazon AWS, pastaj testet në Postman dhe, pas përfundimit të tyre, të ndaloni dhe hiqni kontejnerët e krijuar. Testet ekzekutohen me çdo ndryshim të kodit. Në këtë mënyrë, ne e kemi të sigurt që çdo version punon siç duhet me bazën e të dhënave dhe shërbimet AWS.
I njëjti skript ekzekutohet nga zhvilluesit si në kompjuterat e tyre Windows, ashtu edhe nga serveri Gitlab CI në Linux.
Që introduktimi i testeve të reja të jetë i justifikuar, ai nuk duhet të kërkojë instalimin e mjeteve të tjera as në kompjuterin e zhvilluesit, as në serverin ku testet ekzekutohen gjatë komitit. Docker e zgjidh këtë problem.
Testi duhet të funksionojë në serverin lokal për arsyet e mëposhtme:
- Rrjeti nuk është plotësisht i besueshëm. Nga mijëra kërkesa, një mund të mos kalojë;
Testi automatizuar në këtë rast nuk do të kalojë, operimi do të ndalet, do të duhet të kërkoni shkakun në log-et; - Kërkesat shumë të shpeshta nuk pranohet nga disa shërbime të palëve të treta.
Përveç kësaj, angazhimi i një standi nuk është i dëshirueshëm, sepse:
- Standin mund ta prishë jo vetëm kodi i keq që funksionon mbi të, por edhe të dhënat që kodi i saktë nuk mund t'i përpunojë;
- Pavarësisht se si përpiqemi të kthejmë pas të gjitha ndryshimet e bërë nga testi, gjatë vetë testit, diçka mund të shkojë gabim (përndryshe, për çfarë testi?).
Rreth projektit dhe organizimit të procesit
Kompania jonë zhvilloi një aplikacion web me mikrosërvices, që funksionon në Docker në Amazon AWS. Në projekt tashmë ishin përdorur teste unike, megjithatë shpesh ndodhnin gabime që testet unike nuk i zbulojnë. Duhej të testohej një mikrosërvic i tërë së bashku me bazën e të dhënave dhe shërbimet e Amazon.
Në projekt aplikohet procesi standard i integrimit të vazhdueshëm, duke përfshirë testimin e mikroservicit me çdo commit. Pas caktimit të detyrës, zhvilluesi bën ndërrime në mikrosërvic, e teston vetë manualisht dhe shkarkon të gjitha testet automatike që ka. Nëse është e nevojshme, zhvilluesi modifikon testet. Nëse nuk zbulohen probleme, bëhet commit në degën e këtij detyrei. Pas çdo commit, testet automatike aktivizohen automatikisht në server. Merge në degën e zakonshme dhe ekzekutimi i testeve automatike mbi të ndodh pas një rishikimi të suksesshëm. Nëse testet në degën e zakonshme kalojnë, shërbimi përditësohet automatikisht në mjedisin e testimit në Amazon Elastic Container Service (stand). Standi është i nevojshëm për të gjithë zhvilluesit dhe testuesit, dhe është e padëshirueshme ta prishim. Testuesit në këtë mjedis kontrollojnë një fix ose një tipar të ri, duke kryer teste manuale.
Arkitektura e projektit

Aplikacioni përbëhet nga më shumë se dhjetë shërbime. Disa prej tyre janë shkruar në .NET Core, dhe disa në NodeJs. Çdo shërbim funksionon në një konteiner Docker në Amazon Elastic Container Service. Secili ka bazën e vet të Postgres-it, dhe disa madje kanë edhe Redis. Nuk ka baza të përbashkëta. Nëse disa shërbime kanë nevojë për të njëjtat të dhëna, këto të dhëna, më momentin e ndryshimit të tyre, dërgohen çdo shërbimi përmes SNS (Shërbimi i Njoftimeve të Thjeshta) dhe SQS (Shërbimi i Thjeshtë i Radhëve të Amazon), dhe shërbimet i ruajnë ato në bazat e tyre të veçanta.
SQS dhe SNS
SQS lejon që përmes protokollit HTTPS të vendosen mesazhe në radhë dhe të lexohen mesazhe nga radhë.
Nëse disa shërbime lexojnë një radhë, çdo mesazh arrin vetëm tek një prej tyre. Kjo është e dobishme kur lançoni disa ekzemplarë të një shërbimi për të shpërndarë ngarkesën midis tyre.
Nëse nevojitet që çdo mesazh të dërgohet në disa shërbime, secili marrës duhet të ketë radhën e vet, dhe për dyfishimin e mesazheve në disa radhë nevojitet SNS.
Me SNS krijoni një temë dhe abononi, për shembull, një radhë SQS. Në temë mund të dërgoni mesazhe. Në këtë mënyrë, mesazhi dërgohet në çdo radhë që është abonuar në këtë temë. Në SNS nuk ka metodë për të lexuar mesazhe. Nëse gjatë procesit të debugimit ose testimit është e nevojshme të dihet çfarë dërgohet në SNS, mund të krijoni një radhë SQS, ta abononi në temën e duhur dhe të lexoni radhen.

API Gateway
Shumica e shërbimeve nuk janë të aksesueshme direkt nga interneti. Qasja bëhet nëpërmjet API Gateway, i cili kontrollon të drejtat e qasjes. Kjo është gjithashtu shërbimi ynë, dhe për të ka gjithashtu teste.
Njoftime në kohë reale
Aplikacioni përdor , për të treguar përdoruesit njoftime në kohë reale. Kjo është realizuar në shërbimin e njoftimeve. Ai është i aksesueshëm direkt nga interneti dhe vetë punon me OAuth, sepse integrazimi i mbështetjes për Web-socket në Gateway u duk si një zgjidhje e paarsyeshme, krahasuar me integrimin e OAuth dhe shërbimit të njoftimeve.
Qasja e njohur ndaj testimit
Testet njësike zëvendësojnë objekte të falsifikuara siç janë baza e të dhënave. Nëse mikroshërbimi, për shembull, përpiqet të krijojë një regjistrim në një tabelë me çelës të jashtëm, dhe regjistrimi në të cilin ky çelës referohet nuk ekziston, atëherë kërkesa nuk mund të përfundojë. Testet njësike nuk mund ta zbulojnë këtë.
Në është propozuar të përdoret një bazë in-memory dhe të inkorporohen objekte të falsifikuara.
Baza në memorie – kjo është një nga bazat e të dhënave që mbështet Entity Framework. Ajo është krijuar posaçërisht për teste. Të dhënat në një të tillë bazë ruhen vetëm deri në përfundimin e procesit që e përdor atë. Nuk është e nevojshme të krijoni tabela në të, dhe integriteti i të dhënave nuk verifikohet.
Objektet e imituara modelojnë klasën e zëvendësimit vetëm aq sa zhvilluesi i testit e kupton funksionimin e saj.
Si të arrijmë që Postgres të startojë automatikisht dhe të realizojë migrimin gjatë nisjes së testit, nuk përmendet në artikullin nga Microsoft. Zgjidhja ime e bën këtë dhe, për më tepër, nuk shton asnjë kod të veçantë për teste në mikroshërbimin vetë.
Të kalojmë në zgjidhje
Gjatë procesit të zhvillimit, u kuptua se testet njësi nuk ishin të mjaftueshme për të gjetur të gjitha problemet në kohë, prandaj u vendos të qaset për këtë çështje nga një këndvështrim tjetër.
Konfigurimi i mjedisit të testimit
Detyra e parë – të vendosim mjedisin e testimit. Hapat e nevojshëm për të nisur mikroshërbimin:
- Konfiguroni shërbimin që do të testoni për mjedisin lokal, në variablat e mjedisit specifikoni akreditivët për lidhjen me bazën dhe AWS;
- Nisni Postgres dhe realizoni migrimin, duke nisur Liquibase.
Në bazat e të dhënave relacionale, para se të regjistroni të dhënat në bazë, duhet të krijoni skemën e të dhënave, thënë ndryshe, tabelat. Gjatë përditësimit të aplikacionit, duhen sjellë tabelat në formën e përdorur nga versioni i ri, preferohet pa humbur të dhëna. Kjo quhet migrim. Krijimi i tabelave në një bazë fillimisht të zbrazët është një rast i veçantë migrimi. Migrimi mund të integrohet brenda aplikacionit vetë. Si në .NET ashtu edhe në NodeJS ka korniza për migrim. Në rastin tonë, për arsye sigurie, mikroshërbimet nuk kanë të drejtë të ndryshojnë skemën e të dhënave dhe migrimi realizohet me ndihmën e Liquibase. - Nisni Amazon LocalStack. Ky është një implementim i shërbimeve AWS për ta nisur në përvetësinë tuaj. Për LocalStack ekziston një imazh i gatshëm në Docker Hub.
- Nisni skriptin për të krijuar entitetet e nevojshme në LocalStack. Skriptet Shell përdorin AWS CLI.
Për testimin në projekt përdoret . Ai ka qenë edhe më përpara, por ishte nisur manualisht dhe ishte testuar aplikacioni, tashmë i vendosur në platformë. Ky mjet lejon të bëni kërkesa HTTP(S) të rastit dhe të kontrolloni që përgjigjet përputhen me pritshmëritë. Kërkesat bashkohen në një koleksion, dhe mund të nisni të gjithë koleksionin njëherësh.

Si është organizuar testi automatik
Gjatë testit në Docker, gjithçka funksionon: shërbimi në testim, Postgres, mjeti për migrim dhe Postman, apo më saktë, versioni i tij konsolë – Newman.
Docker zgjidh një sërë problemesh:
- Pavarësia nga konfigurimi i hostit;
- Instalimi i varësive: docker shkarkon imazhe nga Docker Hub;
- Kthimi i sistemit në gjendjen e mëparshme: thjesht fshijmë kontejnerët.
Docker-compose bashkëngjit kontejnerët në një rrjet virtual, të izoluar nga interneti, ku kontejnerët gjejnë njëri-tjetrin sipas emrave të domain-it.
Testi menaxhohet nga një skript shell. Për të ekzekutuar testin në Windows, përdorim git-bash. Kështu, mjafton një skript për Windows dhe Linux. Git dhe Docker janë të instaluar te të gjithë zhvilluesit në projekt. Kur instalohet Git në Windows, instalohet git-bash, kështu që edhe ai është i pranishëm te të gjithë.
Skripti ekzekuton hapat e mëposhtëm:
- Ndërtimi i imazheve docker
docker-compose build - Nisja e DB dhe LocalStack
docker-compose up -d - Migrimi i DB dhe përgatitja e LocalStack
docker-compose run - Nisja e shërbimit në testim
docker-compose up -d - Nisja e testit (Newman)
- Ndalesa e të gjithë kontejnerëve
docker-compose down - Postimi i rezultateve në Slack
Ne kemi një bisedë, ku arrijnë mesazhe me shenjat e gjelbërta ose të kuqe dhe me një lidhje për logun.
Në këto hapa janë të përfshira imazhe të ndryshme Docker:
- Shërbimi në testim – është i njëjti imazh si për prodhimin. Konfigurimi për testin – përmes variablave të mjedisit.
- Për Postgres, Redis dhe LocalStack përdoren imazhe të gatshme nga Docker Hub. Ka gjithashtu imazhe të gatshme për Liquibase dhe Newman. Ne i ndërtuam tonat mbi ato, duke i shtuar skedarët tanë.
- Për përgatitjen e LocalStack përdoret një imazh i gatshëm AWS CLI, dhe mbi atë krijohet një imazh që përmban skriptin.
Duke përdorur , nuk është e nevojshme të ndërtohet një imazh Docker vetëm për të shtuar skedarë në kontejner. Megjithatë, volumes nuk janë të përshtatshme për mjedisin tonë, sepse detyrat Gitlab CI funksionojnë brenda kontejnerëve. Nga një kontejner i tillë mund të menaxhohet docker, por volumes montojnë vetëm dosje nga sistemi host, dhe jo nga një kontejner tjetër.
Problemet me të cilat mund të përballeni
Pr等待准备就绪
Kur kontejneri me shërbim është nisur, kjo nuk do të thotë se është gati për të pranuar lidhje. Duhet të presim lidhjen për të vazhduar.
Kjo detyrë ndonjëherë zgjidhet me ndihmën e një skripti , i cili pret mundësinë për të vendosur një lidhje TCP. Megjithatë, LocalStack mund të japë një gabim 502 Bad Gateway. Përveç kësaj, ai përbëhet nga shumë shërbime, dhe nëse njëra prej tyre është gati, kjo nuk thotë asgjë për të tjerat.
Zgjidhja: skriptet e përgatitjes së LocalStack, të cilat presin një përgjigje 200 dhe nga SQS, dhe nga SNS.
Konfliktet e detyrave paralele
Disa teste mund të funksionojnë në të njëjtin Docker-host, prandaj emrat e kontejnerëve dhe rrjeteve duhet të jenë unike. Për më tepër, testet nga degë të ndryshme të një shërbimi gjithashtu mund të funksionojnë paralelisht, kështu që nuk mjafton të shkruash emrat e tyre në çdo skedar compose.
Zgjidhja: skripti vendos një vlerë unike për variablën COMPOSE_PROJECT_NAME.
Veçoritë e Windows
Kur përdorni Docker në Windows ka disa gjëra që do të doja t'i kushtoja vëmendje, pasi kjo përvojë është e rëndësishme për të kuptuar shkakun e gabimeve.
- Shell-skriptet brenda kontejnerit duhet të kenë fundet e rreshtave sipas Linux-it.
Simboli CR për shell-in është një gabim sintaksor. Nga mesazhi i gabimit është e vështirë të kuptohet se çfarë është problemi. Kur redaktoni skriptet e tilla në Windows, keni nevojë për një redaktues të tekstit të duhur. Për më tepër, sistemi i kontrollit të versioneve duhet të jetë konfiguruar siç duhet.
Kështu konfigurohet git:
git config core.autocrlf input- Git-bash emulon dosjet standarde të Linux-it dhe kur thirret një skedar exe (përfshirë docker.exe) zëvendëson rrugët absolute të Linux-it me rrugët e Windows-it. Megjithatë, kjo nuk ka kuptim për rrugët që nuk janë në makinën lokale (ose rrugët brenda kontejnerit). Ky sjellje nuk mund të çaktivizohet.
Zgjidhja: për të shtuar një slash shtesë në fillim të rrugës: \/bin në vend të \/bin. Linux-i i kupton këto rrugë, për të është e njëjta gjë disa slash-e si një. Por git-bash nuk i njeh këto rrugë dhe nuk përpiqet t'i konvertojë.
Dëshirimi i logjeve
Kur ekzekutohen testet, do të doja të shihja logjet nga Newman dhe nga shërbimi i testuar. Pasi ngjarjet e këtyre logjeve janë të lidhura me njëra-tjetrën, kombinimi i tyre në një konsolë është shumë më i përshtatshëm se dy skedarë të veçantë. Newman ekzekutohet përmes docker-compose run, dhe kështu dalja e tij përfundon në konsolë. Mbetej për t'u bërë që aty të përfshihej dhe dalja e shërbimit.
Zgjidhja fillestare ishte të bënte docker-compose up pa flagun -d, por duke përdorur mundësitë e shell-it, ta dërgonte këtë proces në sfond:
docker-compose up <service> &Kjo funksionoi derisa u kërkua të dërgoheshin logjet nga docker në një shërbim të jashtëm. docker-compose up ndaloi logët në konsolë. Megjithatë, komandat punuan docker attach.
Zgjidhja:
docker attach --no-stdin ${COMPOSE_PROJECT_NAME}__1 &Konflikti i identifikuesve gjatë iteracioneve të testit
Testet fillojnë me disa iteracione. Baza në këtë rast nuk pastrohet. Regjistrimet në bazë kanë ID unike. Nëse shkruajmë ID të caktuara në kërkesa, në iteracionin e dytë do të kemi një konflikt.
Për të mos e pasur atë, ose ID-të duhet të jenë unike, ose duhet të fshijmë të gjitha objektet e krijuara nga testi. Disa objekte nuk mund të fshihen, sipas kërkesave.
Zgjidhja: për të gjeneruar GUID-e me skripte në Postman.
var uuid = require('uuid');
var myid = uuid.v4();
pm.environment.set('myUUID', myid);Më pas në kërkesë përdorni simbolin {{myUUID}}, i cili do të zëvendësohet me vlerën e variablit.
Ndërveprimi përmes LocalStack
Nëse shërbimi i testuar lexon një radhë SQS ose shkruan në të, për të verifikuar këtë, testi duhet gjithashtu të punojë me këtë radhë.
Zgjidhja: kërkesat nga Postman në LocalStack.
API e shërbimeve AWS është dokumentuar, e cila lejon të bësh kërkesa pa SDK.
Nëse shërbimi shkruan në radhë, atëherë ne e lexojmë dhe verifikojmë përmbajtjen e mesazhit.
Nëse shërbimi dërgon mesazhe në SNS, në fazën e përgatitjes, LocalStack krijon gjithashtu një radhë dhe regjistrohet në këtë SNS-temë. Më tej, gjithçka reduktohet në atë që përmenda më lart.
Nëse shërbimi duhet të lexojë një mesazh nga radhë, atëherë në hapin e mëparshëm të testit ne e shkruajmë këtë mesazh në radhë.
Testimi i kërkesave HTTP që dalin nga mikrosërvori i testuar
Disa shërbime punojnë me HTTP me diçka tjetër përveç AWS-së dhe disa funksione të AWS-së nuk janë të realizuara në LocalStack.
Zgjidhja: në këto raste mund të ndihmojë , i cili ka një imazh të gatshëm në . Kërkesat dhe përgjigjet e pritura për to konfigurohen me një kërkesë HTTP. API është dokumentuar, prandaj bëjmë kërkesa nga Postman.
Testimi i autentifikimit dhe autorizimit OAuth
Ne përdorim OAuth dhe . Për testin na nevojitet një ofrues OAuth, që mund ta ekzekutojmë lokal.
Të gjitha ndërveprimet e shërbimit me ofruesin OAuth reduktohen në dy kërkesa: fillimisht kërkohet konfigurimi /.well-known/openid-configuration, pastaj kërkohet çelësi publik (JWKS) në adresën nga konfigurimi. Të gjitha këto janë përmbajtje statike.
Zgjidhja: ofruesi ynë testues OAuth është një server përmbajtjeje statike dhe dy skedare në të. Tokeni është gjeneruar një herë dhe është komituar në Git.
Karakteristikat e testimit të SignalR
Postman nuk punon me websockets. Një mjet i veçantë është krijuar për testimin e SignalR.
Klienti i SignalR nuk mund të jetë vetëm një shfletues. Ekziston një bibliotekë klienti për .NET Core. Klienti, i shkruar në .NET Core, krijon një lidhje, kalon autentikimin dhe pret një sekuencë të caktuar mesazhesh. Nëse merr një mesazh të papritur ose lidhja prishet, klienti përfundon me kodin 1. Kur merr mesazhin e fundit të pritshëm, përfundon me kodin 0.
Përveç klientit, Newman funksionon. Shkaktohen disa klientë për të verifikuar se mesazhet dërgohen të gjithëve që kanë nevojë.

Për të nisur disa klientë, përdoret opsioni —scale në komandën docker-compose.
Para se të nisë Postman, skripti pret që të vendosë lidhjen të gjithë klientët.
Problemi i pritjes së lidhjes na ka ndodhur tashmë. Por atje ishte një server, dhe këtu është klienti. Nevojitet një qasje tjetër.
Zgjidhja: klienti në kontejner përdor mekanizmin , për të njoftuar skriptin në host për statusin e tij. Klienti krijon një skedë në një rrugë të caktuar, le të themi, /healthcheck, sapo lidhja të vendoset. Skripti i HealthCheck në Dockerfile duket kështu:
HEALTHCHECK --interval=3s CMD if [ ! -e /healthcheck ]; then false; fiEkipa docker inspect tregon për kontejnerin statusin normal, statusin shëndetësor dhe kodin e përfundimit.
Pasi të përfundojë Newman, skripti kontrollon që të gjitha kontejnerët me klientin të kenë përfunduar, dhe për më tepër, me kodin 0.
Ka lumturi
Pas kalimit të sfidave të përmendura më sipër, kemi një grup testesh që punojnë stabilisht. Në teste, çdo shërbim funksionon si një njësi e vetme, interakton me bazën e të dhënave dhe me Amazon LocalStack.
Këto teste mbrojnë ekipin prej 30+ zhvilluesish nga gabime në aplikacion me ndërveprime të komplikuara të 10+ mikroshërbimeve gjatë implementimeve të shpeshta.
Burimi: habr.com
