Testimi automatik i mikroservisëve në Docker për integrimin e vazhdueshëm

Në projektet e lidhura me zhvillimin e arkitekturës së mikroshërbimeve, CI/CD kalon nga një mundësi e këndshme në një nevojë të ngutshme. Testimi automatizuar është një pjesë thelbësore e integrimit të vazhdueshëm, një qasje e mençur ndaj së cilës mund t'i ofrojë skuadrës shumë mbrëmje të këndshme me familjen dhe miqtë. Në të kundërt, projekti rrezikon të mbetet kurrë i papërfunduar.

Mund të mbulohet tërë kodi i mikroshërbimit me teste njësie me objekte të simuluara, 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ë, problemet më të ndjeshme janë testimi i konsistencës së të dhënave në DB-relacionale, testimi i punës me shërbimet në re dhe supozimet e gabuara gjatë hartimit të objekteve të simuluara.

E gjitha kjo dhe pak më shumë zgjidhet përmes testimit të tërë mikroshërbimit në një kontejner Docker. Një avantazh i padiskutueshëm për të siguruar vlefshmërinë e testeve është se testet janë të ekspozuara ndaj po atyre imazheve Docker që shkojnë në prodhim.

Automatizimi i këtij qasjes paraqet disa probleme, zgjidhja e të cilave do të përshkruhet më poshtë:

  • konfliktet e detyrave paralelë në një docker host;
  • konfliktet e identifikuesve në databazë gjatë iteracioneve të testit;
  • pritja e gatishmërisë së mikroshërbimeve;
  • bashkimi dhe dërgimi i log-eve në sisteme të jashtme;
  • testimi i kërkesave HTTP dërguese;
  • testimi i websockets (me ndihmën e SignalR);
  • testimi i autentifikimit dhe autorizimit OAuth.

Ky është një artikull i frymëzuar nga prezantimit tim SECR 2019. Pra, për ata që nuk kanë dëshirë të lexojnë, këtu është regjistrimi i paraqitjes.

Testimi automatik i mikroservisëve në Docker për integrimin e vazhdueshëm

Në këtë artikull do të tregoj se si me ndihmën e një skripti të nis një shërbim në Docker, databazën dhe shërbimet Amazon AWS, pastaj testet në Postman dhe pasi të mbyllen, të ndaloj dhe fshij kontejnerët e krijuar. Testet kryhen me çdo ndryshim të kodit. Kështu, ne sigurohemi që çdo version funksionon në mënyrë korrekte me databazën dhe shërbimet AWS.

I njëjti skript niset si nga vetë zhvilluesit në desktopet e tyre Windows, ashtu edhe nga serveri Gitlab CI nën Linux.

Në mënyrë që implementimi i testeve të reja të jetë i arsyeshëm, ai nuk duhet të kërkojë instalimin e mjeteve shtesë as në kompjuterin e zhvilluesit, as në serverin ku testet ekzekutohen gjatë bashkimit. Docker zgjidh këtë problem.

Testi duhet të funksionojë në serverin lokal për arsyet në vazhdim:

  • Rrjeti nuk është kurrë plotësisht i besueshëm. Nga një mijë kërkesa, një mund të mos kalojë;
    Një test automatike në këtë rast nuk do të kalojë, puna do të ndalet dhe do të duhet të kërkosh shkakun në loga;
  • Kërkesat shumë të shpeshta nuk lejohen nga disa shërbime të jashtme.

Për më tepër, përdorimi i një testi nuk është i dëshirueshëm, sepse:

  • Një kod i keq që punon në të mund të prishë testin, por edhe të dhënat që kodi i saktë nuk mund t'i përpunojë;
  • Pavarësisht përpjekjeve tona për të kthyer të gjitha ndryshimet e bëra nga testi, gjatë vetë testit, diçka mund të shkojë keq (ndryshe, pse do ta bënim testin?).

Rreth projektit dhe organizimit të procesit

Kompania jonë ka zhvilluar një aplikacion web mikroshërbimesh që funksionon në Docker në cloud-in Amazon AWS. Në projekt tashmë ishin përdorur teste unitare, megjithatë shpesh ndodhnin gabime që testet unitare nuk i zbuluan. Ishte e nevojshme të testohej e gjithë mikroshërbimi së bashku me bazën e të dhënave dhe shërbimet e Amazon.

Projekti përdor një proces standard të integrimit të vazhdueshëm, i cili përfshin testimin e mikrosërvicit me çdo angazhim. Pas caktimit të detyrës, zhvilluesi bën ndryshimet në mikrosërvic, e teston manualisht dhe aktivizon të gjitha testet automatike të disponueshme. Nëse është e nevojshme, zhvilluesi ndryshon testet. Nëse nuk ka probleme, bëhet angazhi në degën e kësaj detyre. Pas çdo angazhimi, testet ekzekutohen automatikisht në server. Bashkimi në degën e përbashkët dhe aktivizimi i testeve automatike atje ndodh pas një rishikimi të suksesshëm. Nëse testet në degën e përbashkët kalojnë, shërbimi përditësohet automatikisht në ambientin e testimit në Amazon Elastic Container Service (standi). Stendi është i nevojshëm për të gjithë zhvilluesit dhe testuesit, dhe prishja e tij nuk rekomandohet. Testuesit në këtë ambient verifikojnë korrigjimin ose veçorinë e re duke kryer teste manuale.

Arkitektura e projektit

Testimi automatik i mikroservisëve në Docker për integrimin e vazhdueshëm

Aplikacioni përbëhet nga më shumë se dhjetë shërbime. Disa prej tyre janë shkruar në .NET Core, ndërsa disa të tjera në NodeJs. Çdo shërbim funksionon në një kontejner Docker në Amazon Elastic Container Service. Çdo shërbim ka bazën e vet të të dhënave Postgres, dhe disa gjithashtu përdorin Redis. Nuk ka baza të përbashkëta. Nëse disa shërbime kërkojnë të njëjtat të dhëna, këto të dhëna dërgohen në momentin e ndryshimit për çdo shërbim të tillë përmes SNS (Shërbimi i Njoftimit të Thjeshtë) dhe SQS (Shërbimi i Thjeshtë i Radhitjes), dhe shërbimet i ruajnë ato në bazat e tyre të ndara.

SQS dhe SNS

SQS lejon që përmes protokollit HTTPS të vendosen mesazhe në radhë dhe të lexohen mesazhet nga radha.

Nëse disa shërbime lexojnë një radhë, çdo mesazh i jepet vetëm një prej tyre. Kjo është e dobishme kur fillojmë disa instance të një shërbimi për të shpërndarë ngarkesën mes tyre.

Nëse është e nevojshme që secili mesazh të dorëzohet në disa shërbime, çdo marrës duhet të ketë radhen e vet, dhe për të dyfishuar mesazhet në disa radhë nevojitet SNS.

Në SNS krijoni një topic dhe e abononi atë, për shembull, me një radhë SQS. Mesazhet mund të dërgohen në topic. Në këtë mënyrë, mesazhi dërgohet në çdo radhë të abonuar në atë topic. SNS nuk ka një metodë për të lexuar mesazhet. Nëse është e nevojshme të zbuloni çfarë po dërgohet në SNS gjatë procesit të zgjidhjes ose testimit, mund të krijoni një radhë SQS, ta abononi atë në topic-un e nevojshëm dhe ta lexoni radhën.

Testimi automatik i mikroservisëve në Docker për integrimin e vazhdueshëm

API Gateway

Shumica e shërbimeve nuk janë të aksesueshme drejtpërdrejt nga interneti. Aksesimi bëhet përmes API Gateway, i cili kontrollon të drejtat e aksesit. Ky është gjithashtu shërbimi ynë, dhe për të ka gjithashtu teste.

Njoftime në kohë reale

Aplikacioni përdor SignalR, për të treguar përdoruesve njoftime në kohë reale. Kjo realizohet në shërbimin e njoftimeve. Ai është i aksesueshëm drejtpërdrejt nga interneti dhe punon vetë me OAuth, sepse inkorporimi i mbështetjes për Web-sockets në Gateway u tregua i papërshtatshëm, krahasuar me integrimin e OAuth dhe shërbimit të njoftimeve.

Qasja e njohur në testim

Testet njësi zëvendësojnë objekte mok me gjëra si baza e të dhënave. Nëse një mikroshërbim, për shembull, përpiqet të krijojë një regjistrim në një tabelë me çelës të jashtëm, dhe regjistrimi të cilit i referohet ky çelës nuk ekziston, atëherë kërkesa nuk mund të përfundojë. Testet njësi nuk mund ta zbulojnë këtë.

artikulli nga Microsoft propozohet përdorimi i një baze të të dhënave in-memory dhe injektimi i objekteve mok.

Baza in-memory – është një nga DBMS që mbështet Entity Framework. Ajo është krijuar posaçërisht për teste. Të dhënat në një bazë të tillë ruajnë vetëm deri në përfundimin e procesit që e përdor. Nuk nevojitet të krijohen tavolina, dhe integriteti i të dhënave nuk kontrollohet.

Objektet mok modelojnë klasën e zëvendësueshme vetëm aq sa zhvilluesi i testit e kupton funksionimin e saj.

Si të arrijmë nisjen automatike të Postgres dhe ekzekutimin e migrimit kur testi fillon, në artikullin nga Microsoft nuk është përmendur. Zgjidhja ime e bën këtë dhe, për më tepër, nuk shtohet asnjë kod në mikroshërbim posaçërisht për teste.

Të kalojmë në zgjidhje

Gjatë procesit të zhvillimit u kuptua se testet unike nuk ishin të mjaftueshme për të gjetur të gjitha problemet në kohë, prandaj u vendos të qasemi ndaj këtij problemi nga një këndvështrim tjetër.

Konfigurimi i mjedisit të testimit

Detyra e parë – të krijojmë mjedisin e testimit. Hapat e nevojshëm për të nisur mikroshërbimin:

  • Konfiguroni shërbimin që do të testoni në mjedisin lokal, në variablat e mjedisit duhet të jepen akreditimet për lidhjen me bazën e të dhënave dhe AWS;
  • Nisni Postgres dhe kryeni migrimin duke nisur Liquibase.
    Në DBMS-të relacionale, përpara se të regjistroni të dhëna në bazë, duhet të krijoni skemën e të dhënave, thjesht, tavolatë. Kur përditësohet aplikacioni, tabelat duhet të përshtaten me formatin që përdor versioni i ri, dhe, preferohet, pa humbje të të dhënave. Kjo quhet migrim. Krijimi i tabelave në një bazë fillestare të zbrazët – është një rast i veçantë i migrimit. Migrimi mund të integorohet në aplikacionin vetë. Në .NET dhe NodeJS ekzistojnë framework-e për migrim. Në rastin tonë, për qëndrueshmëri, mikroshërbimet nuk kanë të drejtën të ndryshojnë skemën e të dhënave, dhe migrimi bëhet përmes Liquibase.
  • Aktivizo Amazon LocalStack. Kjo është një implementim i shërbimeve AWS për ta ekzekutuar lokalisht. Për LocalStack ka një imazh të gatshëm në Docker Hub.
  • Ekzekuto skriptin për krijimin e entiteteve të nevojshme në LocalStack. Skriptet Shell përdorin AWS CLI.

Për testimin në projekt përdoret Postman. Ai ka ekzistuar edhe më parë, por ishte ekzekutuar manualisht dhe testonte aplikacionin që ishte tashmë i vendosur. Ky mjet lejon dërgimin e kërkesave HTTP(S) të rastësishme dhe kontrollimin e përputhshmërisë së përgjigjeve me pritshmëritë. Kërkesat bashkohen në një koleksion, dhe është e mundur të ekzekutohet i tërë koleksioni një herë.

Testimi automatik i mikroservisëve në Docker për integrimin e vazhdueshëm

Si funksionon testi automatik

Gjatë testit në Docker funksionon gjithçka: dhe shërbimi që testohet, dhe Postgres, dhe mjeti për migrim, dhe Postman, apo më saktëm, versioni i tij në konsolë – Newman.

Docker zgjidh një mori problemi:

  • Pavarësia nga konfigurimi i host-it;
  • Instalimi i varësive: Docker shkarkon imazhe nga Docker Hub;
  • Kthimi i sistemit në gjendjen fillestare: thjesht fshijmë kontejnerët.

Docker-compose bashkon kontejnerët në një rrjet virtual, të izoluara nga interneti, ku kontejnerët gjenden me njëri-tjetrin përmes emrave të domain-eve.

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 tek të gjithë zhvilluesit në projekt. Kur instalohet Git në Windows, instalon gjithashtu git-bash, kështu që të gjithë e kanë atë.

Skripti kryen hapat e mëposhtëm:

  • Ndërtimi i imazheve docker
    docker-compose build
  • Çelësi i DB dhe LocalStack
    docker-compose up -d
  • Migruar DB dhe përgatitura LocalStack
    docker-compose run
  • Çelësi i shërbimit të testuar
    docker-compose up -d
  • Ekzekutimi i testit (Newman)
  • Ndalo të gjithë kontejnerët
    docker-compose down
  • Postimi i rezultateve në Slack
    Kemi një bisedë ku shkojnë mesazhet me shenjë të gjelbër ose të kuqe dhe lidhje me logun.

Në këta hapa janë përfshirë imazhet e mëposhtme Docker:

  • Shërbimi i testuar është e njëjta imazh si për prodhim. Konfigurimi për testin bëhet përmes variablave të mjedisit.
  • Për Postgres, Redis dhe LocalStack përdoren imazhe të gatshme nga Docker Hub. Për Liquibase dhe Newman gjithashtu ka imazhe të gatshme. Ne i ndërtojmë tonat mbi bazën e tyre, duke shtuar aty skedarët tanë.
  • Përgatitja e LocalStack përdor një imazh të gatshëm AWS CLI, dhe mbi të krijohet një imazh që përmban skriptin.

Duke përdorur volumes, nuk mund të krijohet imazhi 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 vetë punojnë në konteinerë. Nga një kontejner të tillë, mund të menaxhohet Docker, por volumes monton dosje vetëm nga sistemi host, dhe jo nga një kontejner tjetër.

Problemet me të cilat mund të përballeni

Prerja e gatishmërisë

Kur kontejneri me shërbimin është aktivizuar, kjo nuk do të thotë ende se është i gatshëm të pranojë lidhje. Duhet të prisni lidhjen për të vazhduar.

Ky problem ndonjëherë zgjidhet me një skenar wait-for-it.sh, i cili pret mundësinë për të vendosur një lidhje TCP. Megjithatë, LocalStack mund të japë një gabim 502 Bad Gateway. Për më tepër, ai përbëhet nga shumë shërbime dhe, nëse njëra prej tyre është e gatshme, kjo nuk thotë asgjë për të tjerat.

Zgjidhja: skenarët e përgatitjes së LocalStack, të cilët presin përgjigje 200 si nga SQS ashtu edhe nga SNS.

Konkurrencat e detyrave paralele

Disa teste mund të funksionojnë njëherësh në një host Docker, kështu që emrat e kontejnerëve dhe rrjeteve duhet të jenë unikë. Për më tepër, testet nga degë të ndryshme të një shërbimi gjithashtu mund të funksionojnë njëherësh, prandaj nuk mjafton të caktohen emra të veçantë 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ë dua t'i drejtoj vëmendjen tuaj, pasi kjo përvojë është e rëndësishme për të kuptuar arsyet e gabimeve.

  1. Skriptet e shell në kontejner duhet të kenë përfundime linjash të Linux-it.
    Simboli CR për shell është një gabim sintaksor. Nga mesazhi i gabimit është e vështirë të kuptohet se çfarë është problemi. Kur redaktoni skripte të tilla në Windows, nevojitet një redaktor i duhur teksti. Për më tepër, sistemi i kontrollit të versioneve duhet të jetë i konfiguruar siç duhet.

Ja se si konfigurohet git:

git config core.autocrlf input

  1. Git-bash emulon dosjet standarde të Linux-it dhe kur thirret një skedari exe (përfshirë docker.exe), zëvendëson rrugët absolute të Linux-it me ato të Windows-it. Megjithatë, kjo nuk ka kuptim për rrugët që nuk janë në makinë lokale (ose rrugët në kontejner). Ky sjellje nuk mund të çaktivizohet.

Zgjidhja: shtoni një slash shtesë në fillim të rrugës: \/\bin në vend të \/bin. Linux e kupton këtë lloj rrugësh, për të është e njëjta gjë si një. Por git-bash nuk i njeh këto rrugë dhe nuk përpiqet t'i transformojë.

Këputja e logeve

Gjatë ekzekutimit të testeve do të doja të shihja loget nga Newman dhe nga shërbimi që po testohet. Duke qenë se ngjarjet e këtyre logeve 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 aktivizohet përmes docker-compose run, dhe kështu dalja e tij shkon në konsolë. Mbetej të bëhej që aty të sillte dhe daljen e shërbimit.

Zgjidhja fillestare ishte të bëhej docker-compose up pa flamurin -d, por, duke përdorur mundësitë e shellet, të dërgonte këtë proces në prapa:

docker-compose up <shërbimi> &

Kjo funksionoi deri sa u kërkua të dërgoheshin loget nga docker në një shërbim të jashtëm. docker-compose up ndaloi të sillte loget në konsolë. Megjithatë, komandat docker attach.

Zgjidhja:

docker attach --no-stdin ${COMPOSE_PROJECT_NAME}_<shërbimi>_1 &

Konflikti i identifikuesve gjatë iteracioneve të testit

Testet ekzekutohen në disa iteracione. Të dhënat në këtë rast nuk pastrihen. Shënimet në bazë kanë ID unike. Nëse shkruani ID specifike në kërkesa, në iteracionin e dytë do të kemi një konflikt.

Për të evituar këtë, ose ID-të duhet të jenë unike, ose duhet të fshijmë të gjitha objektet e krijuara nga testi. Nuk lejohet të fshihen disa objekte, sipas kërkesave.

Zgjidhja: gjeneroni GUID me skriptet në Postman.

var uuid = require('uuid');
var myid = uuid.v4();
pm.environment.set('myUUID', myid);

Pastaj në kërkesë përdorni simbolin {{myUUID}}, i cili do të zëvendësohet me vlerën e ndryshores.

Ndërveprimi përmes LocalStack

Nëse shërbimi që po testohet lexon nga SQS-u ose shkruan në të, atëherë për ta verifikuar këtë, testi gjithashtu duhet të punojë me këtë radhë.

Zgjidhja: kërkesat nga Postman për LocalStack.

API e shërbimeve AWS është dokumentuar, çka lejon të bëhen kërkesa pa SDK.

Nëse shërbimi shkruan në radhë, ne e lexojmë dhe kontrollojmë përmbajtjen e mesazhit.

Nëse shërbimi dërgon mesazhe në SNS, në fazën e përgatitjes LocalStack krijohet gjithashtu një radhë dhe abonohet në këtë temë SNS. Më pas, gjithçka reduktohet në atë që u përmend më sipër.

Nëse shërbimi duhet të lexojë një mesazh nga radhët, në hapin e mëparshëm të testit ne e shkruajmë këtë mesazh në radhën.

Testimi i kërkesave HTTP që dalin nga mikroshërbimi që po testohet

Disa shërbime punojnë me HTTP me diçka tjetër përveç AWS, dhe disa funksionalitete të AWS nuk janë realizuar në LocalStack.

Zgjidhja: në këto raste mund të ndihmojë MockServer, i cili ka një imazh të gatshëm në Docker Hub. Kërkesat dhe përgjigjet e pritura vendosen me një kërkesë HTTP. API është dokumentuar, kështu që bëjmë kërkesa nga Postman.

Testimi i autentikimit dhe autorizimit OAuth

Ne përdorim OAuth dhe JSON Web Tokens (JWT). Për testimin ne na nevojitet një ofrues OAuth, të cilin mund ta zhvillojmë lokalisht.

Të gjitha ndërveprimet e shërbimit me ofruesin OAuth përfshinë dy kërkesa: fillimisht kërkohet konfigurimi /.well-known/openid-configuration, dhe më pas kërkohet çelësi publik (JWKS) nga adresa që vjen nga konfigurimi. Të gjitha këto janë përmbajtje statike.

Zgjidhja: ofruesi ynë testues OAuth është një server përmbajtjeje statike me dy skedarë mbi të. Tokeni është gjeneruar një herë dhe është komituar në Git.

Veçoritë e testimit të SignalR

Postman nuk punon me websockets. Për testimin e SignalR është krijuar një mjet i veçantë.

Klienti i SignalR nuk mund të jetë vetëm shfletuesi. 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 ndërpritet, klienti mbyllet me kodin 1. Kur merr mesazhin e fundit të pritur, mbyllet me kodin 0.

Në të njëjtën kohë me klientin punon Newman. Aktivizohen disa klientë për të verifikuar se mesazhet shpërndahen te të gjithë ata që kanë nevojë.

Testimi automatik i mikroservisëve në Docker për integrimin e vazhdueshëm

Për të aktivizuar disa klientë përdoret opsioni —scale në komandën docker-compose.

Para se të ekzekutohet skenari Postman, ai pret që të gjithë klientët të krijojnë lidhjen.
Problemi i pritjes së lidhjes na ka ndodhur më parë. Por atje ishin serverët, e këtu është klienti. Duhet një qasje tjetër.

Zgjidhja: klienti në kontejner përdor mekanizmin HealthCheck, për të raportuar skenarit në host për statusin e tij. Klienti krijon një skedar në një rrugë të caktuar, për shembull, /healthcheck, sa herë që lidhja është krijuar. Skripti HealthCheck në dockerfile duket kështu:

HEALTHCHECK --interval=3s CMD if [ ! -e /healthcheck ]; then false; fi

Ekipa docker inspect tregon statusin e zakonshëm për kontejnerin, statusin e shëndetit dhe kodin e përfundimit.

Pas përfundimit të Newman, skripti kontrollon se të gjithë kontejnerët me klientin përfunduan, dhe për këtë, me kodin 0.

Lumturia është

Pasi të kaluam vështirësitë e përshkruara më sipër, morëm një grup testesh që funksionojnë në mënyrë të qëndrueshme. Në testet, çdo shërbim punon si një tërësi, duke bashkëvepruar me bazën e të dhënave dhe me Amazon LocalStack.

Këto teste mbrojnë ekipin nga 30+ zhvillues nga gabimet në aplikacionin me ndërveprime të ndërlikuara midis 10+ mikrosërvicesh gjatë lançimeve të shpeshta.

Burimi: habr.com

Bli një hosting të besueshëm për faqet me mbrojtje DDoS, VPS VDS serverë 🔥 Bli një hosting të besueshëm për faqet me mbrojtje DDoS, VPS VDS serverë | ProHoster