Outomatiese toetsing van mikrodienste in Docker vir deurlopende integrasie

In projekte wat verband hou met die ontwikkeling van mikrodiensargitektuur, beweeg CI/CD van die kategorie van 'n aangename geleentheid na die kategorie van 'n dringende noodsaaklikheid. Outomatiese toetsing is 'n integrale deel van deurlopende integrasie, 'n bekwame benadering tot wat die span baie aangename aande saam met familie en vriende kan gee. Andersins loop die projek die risiko om nooit voltooi te word nie.

Dit is moontlik om die hele mikrodienskode met eenheidstoetse met skynvoorwerpe te dek, maar dit los die probleem net gedeeltelik op en laat baie vrae en probleme, veral wanneer werk met data getoets word. Soos altyd is die dringendste om datakonsekwentheid in 'n relasionele databasis te toets, werk met wolkdienste te toets en verkeerde aannames te maak wanneer skynvoorwerpe geskryf word.

Dit alles en 'n bietjie meer kan opgelos word deur die hele mikrodiens in 'n Docker-houer te toets. ’n Ongetwyfelde voordeel om die geldigheid van toetse te verseker, is dat dieselfde Docker-beelde wat in produksie gaan, getoets word.

Outomatisering van hierdie benadering bied 'n aantal probleme, waarvan die oplossing hieronder beskryf sal word:

  • konflikte van parallelle take in dieselfde docker-gasheer;
  • identifiseerder konflikte in die databasis tydens toets iterasies;
  • wag vir mikrodienste om gereed te wees;
  • samevoeging en uitvoer van logs na eksterne stelsels;
  • toets uitgaande HTTP-versoeke;
  • websoktoetsing (met SignalR);
  • toets OAuth-verifikasie en magtiging.

Hierdie artikel is gebaseer op my toespraak by SECR 2019. So vir diegene wat te lui is om te lees, hier is 'n opname van die toespraak.

Outomatiese toetsing van mikrodienste in Docker vir deurlopende integrasie

In hierdie artikel sal ek jou vertel hoe om 'n skrip te gebruik om die diens wat getoets word, 'n databasis en Amazon AWS-dienste in Docker te gebruik, toets dan op Postman en, nadat dit voltooi is, stop en vee die geskepte houers uit. Toetse word uitgevoer elke keer as die kode verander. Op hierdie manier maak ons ​​seker dat elke weergawe korrek werk met die AWS databasis en dienste.

Dieselfde skrip word deur die ontwikkelaars self op hul Windows-rekenaars en deur die Gitlab CI-bediener onder Linux bestuur.

Om geregverdig te wees, moet die bekendstelling van nuwe toetse nie die installering van bykomende gereedskap vereis op die ontwikkelaar se rekenaar of op die bediener waar die toetse op 'n commit uitgevoer word nie. Docker los hierdie probleem op.

Die toets moet om die volgende redes op 'n plaaslike bediener loop:

  • Die netwerk is nooit heeltemal betroubaar nie. Uit 'n duisend versoeke kan een misluk;
    In hierdie geval sal die outomatiese toets nie werk nie, die werk sal stop, en jy sal die rede in die logs moet soek;
  • Te gereelde versoeke word nie deur sommige derdepartydienste toegelaat nie.

Daarbenewens is dit ongewens om die staander te gebruik omdat:

  • 'n Stand kan nie net gebreek word deur slegte kode wat daarop loop nie, maar ook deur data wat die korrekte kode nie kan verwerk nie;
  • Maak nie saak hoe hard ons probeer om al die veranderinge wat deur die toets tydens die toets self gemaak is, terug te keer nie, iets kan verkeerd gaan (anders, hoekom toets?).

Oor die projek en prosesorganisasie

Ons maatskappy het 'n mikrodiens-webtoepassing ontwikkel wat in Docker in die Amazon AWS-wolk loop. Eenheidtoetse is reeds op die projek gebruik, maar foute het dikwels voorgekom wat die eenheidstoetse nie opgespoor het nie. Dit was nodig om 'n hele mikrodiens saam met die databasis en Amazon-dienste te toets.

Die projek gebruik 'n standaard deurlopende integrasieproses, wat die toetsing van die mikrodiens met elke commit insluit. Nadat 'n taak toegewys is, maak die ontwikkelaar veranderinge aan die mikrodiens, toets dit met die hand en voer alle beskikbare outomatiese toetse uit. Indien nodig, verander die ontwikkelaar die toetse. As geen probleme gevind word nie, word 'n verbintenis aan die tak van hierdie uitgawe gemaak. Na elke commit word toetse outomaties op die bediener uitgevoer. Om in 'n gemeenskaplike tak saam te smelt en outomatiese toetse daarop te begin, vind plaas na 'n suksesvolle hersiening. As die toetse op die gedeelde tak slaag, word die diens outomaties opgedateer in die toetsomgewing op Amazon Elastic Container Service (bank). Die staander is nodig vir alle ontwikkelaars en toetsers, en dit is nie raadsaam om dit te breek nie. Toetsers in hierdie omgewing kontroleer 'n oplossing of 'n nuwe kenmerk deur handtoetse uit te voer.

Projek argitektuur

Outomatiese toetsing van mikrodienste in Docker vir deurlopende integrasie

Die toepassing bestaan ​​uit meer as tien dienste. Sommige van hulle is in .NET Core geskryf en sommige in NodeJs. Elke diens loop in 'n Docker-houer in die Amazon Elastic Container Service. Elkeen het sy eie Postgres-databasis, en sommige het ook Redis. Daar is geen algemene databasisse nie. As verskeie dienste dieselfde data benodig, word hierdie data, wanneer dit verander, na elk van hierdie dienste oorgedra via SNS (Simple Notification Service) en SQS (Amazon Simple Queue Service), en die dienste stoor dit in hul eie aparte databasisse.

SQS en SNS

SQS laat jou toe om boodskappe in 'n tou te plaas en boodskappe uit die tou te lees met behulp van die HTTPS-protokol.

As verskeie dienste een tou lees, kom elke boodskap slegs na een van hulle. Dit is nuttig wanneer verskeie gevalle van dieselfde diens uitgevoer word om die las tussen hulle te versprei.

As jy wil hê dat elke boodskap aan verskeie dienste afgelewer moet word, moet elke ontvanger sy eie tou hê, en SNS is nodig om boodskappe in veelvuldige toue te dupliseer.

In SNS skep jy 'n onderwerp en teken daarop in, byvoorbeeld 'n SQS-tou. Jy kan boodskappe na onderwerp stuur. In hierdie geval word die boodskap gestuur na elke tou wat op hierdie onderwerp ingeteken is. SNS het nie 'n metode om boodskappe te lees nie. As jy tydens ontfouting of toetsing moet uitvind wat na SNS gestuur word, kan jy 'n SQS-tou skep, dit op die verlangde onderwerp inteken en die tou lees.

Outomatiese toetsing van mikrodienste in Docker vir deurlopende integrasie

API-poort

Die meeste dienste is nie direk vanaf die internet toeganklik nie. Toegang is via API Gateway, wat toegangsregte kontroleer. Dit is ook ons ​​diens, en daar is ook toetse daarvoor.

Intydse kennisgewings

Die toepassing gebruik SeinRom intydse kennisgewings aan die gebruiker te wys. Dit word in die kennisgewingdiens geïmplementeer. Dit is direk vanaf die internet toeganklik en self werk met OAuth, want dit blyk onprakties te wees om ondersteuning vir websockets in Gateway in te bou, in vergelyking met die integrasie van OAuth en die kennisgewingdiens.

Bekende toetsbenadering

Eenheidtoetse vervang dinge soos die databasis met skynvoorwerpe. As 'n mikrodiens, byvoorbeeld, probeer om 'n rekord in 'n tabel met 'n vreemde sleutel te skep, en die rekord waarna daardie sleutel verwys, bestaan ​​nie, dan kan die versoek nie voltooi word nie. Eenheidtoetse kan dit nie opspoor nie.

В artikel van Microsoft Daar word voorgestel om 'n in-geheue databasis te gebruik en skynvoorwerpe te implementeer.

In-geheue databasis is een van die DBBS'e wat deur die Entiteitsraamwerk ondersteun word. Dit is spesifiek vir toetsing geskep. Data in so 'n databasis word slegs gestoor totdat die proses wat dit gebruik, beëindig word. Dit vereis nie die skep van tabelle nie en kontroleer nie data-integriteit nie.

Bespotlike voorwerpe modelleer die klas wat hulle vervang slegs in die mate dat die toetsontwikkelaar verstaan ​​hoe dit werk.

Hoe om Postgres te kry om outomaties migrasies te begin en uit te voer wanneer jy 'n toets uitvoer, word nie in die Microsoft-artikel gespesifiseer nie. My oplossing doen dit en voeg boonop geen kode spesifiek vir toetse by die mikrodiens self nie.

Kom ons gaan aan na die oplossing

Tydens die ontwikkelingsproses het dit duidelik geword dat eenheidstoetse nie genoeg was om alle probleme betyds op te spoor nie, daarom is besluit om hierdie kwessie vanuit 'n ander hoek te benader.

Die opstel van 'n toetsomgewing

Die eerste taak is om 'n toetsomgewing te ontplooi. Stappe wat nodig is om 'n mikrodiens te bestuur:

  • Konfigureer die diens wat getoets word vir die plaaslike omgewing, spesifiseer die besonderhede vir koppeling aan die databasis en AWS in die omgewingsveranderlikes;
  • Begin Postgres en voer die migrasie uit deur Liquibase uit te voer.
    In relasionele DBBS'e, voordat u data in die databasis skryf, moet u 'n dataskema skep, met ander woorde, tabelle. Wanneer 'n toepassing opgedateer word, moet tabelle gebring word na die vorm wat deur die nuwe weergawe gebruik word, en verkieslik sonder om data te verloor. Dit word migrasie genoem. Die skep van tabelle in 'n aanvanklik leë databasis is 'n spesiale geval van migrasie. Migrasie kan in die toepassing self ingebou word. Beide .NET en NodeJS het migrasieraamwerke. In ons geval word mikrodienste om sekuriteitsredes die reg ontneem om die dataskema te verander, en die migrasie word met Liquibase uitgevoer.
  • Begin Amazon LocalStack. Dit is 'n implementering van AWS-dienste om tuis uit te voer. Daar is 'n klaargemaakte prent vir LocalStack op Docker Hub.
  • Begin die skrip om die nodige entiteite in LocalStack te skep. Shell-skrifte gebruik die AWS CLI.

Word gebruik om op die projek te toets Postman. Dit het voorheen bestaan, maar dit is met die hand van stapel gestuur en het 'n toepassing getoets wat reeds by die staanplek ontplooi is. Hierdie instrument laat jou toe om arbitrêre HTTP(S)-versoeke te maak en te kyk of die antwoorde met verwagtinge ooreenstem. Navrae word in 'n versameling gekombineer, en die hele versameling kan uitgevoer word.

Outomatiese toetsing van mikrodienste in Docker vir deurlopende integrasie

Hoe werk die outomatiese toets?

Tydens die toets werk alles in Docker: die diens wat getoets word, Postgres, die migrasie-instrument, en Postman, of eerder sy konsole-weergawe - Newman.

Docker los 'n aantal probleme op:

  • Onafhanklikheid van gasheerkonfigurasie;
  • Installeer afhanklikhede: Docker laai beelde van Docker Hub af;
  • Om die stelsel terug te keer na sy oorspronklike toestand: verwyder eenvoudig die houers.

Docker-komponeer verenig houers in 'n virtuele netwerk, geïsoleer van die internet, waarin houers mekaar deur domeinname vind.

Die toets word beheer deur 'n dopskrif. Om die toets op Windows uit te voer, gebruik ons ​​git-bash. Dus, een skrif is genoeg vir beide Windows en Linux. Git en Docker word deur alle ontwikkelaars op die projek geïnstalleer. Wanneer Git op Windows geïnstalleer word, is git-bash geïnstalleer, so almal het dit ook.

Die skrip voer die volgende stappe uit:

  • Bou docker-beelde
    docker-compose build
  • Begin die databasis en LocalStack
    docker-compose up -d <контейнер>
  • Databasismigrasie en voorbereiding van LocalStack
    docker-compose run <контейнер>
  • Die bekendstelling van die diens wat getoets word
    docker-compose up -d <сервис>
  • Hardloop die toets (Newman)
  • Stop alle houers
    docker-compose down
  • Plaas resultate in Slack
    Ons het 'n geselsie waar boodskappe met 'n groen regmerkie of 'n rooi kruis en 'n skakel na die log gaan.

Die volgende Docker-beelde is by hierdie stappe betrokke:

  • Die diens wat getoets word, is dieselfde beeld as vir produksie. Die konfigurasie vir die toets is deur omgewingsveranderlikes.
  • Vir Postgres, Redis en LocalStack word klaargemaakte beelde van Docker Hub gebruik. Daar is ook klaargemaakte beelde vir Liquibase en Newman. Ons bou ons s'n op hul skelet en voeg ons lêers daar by.
  • Om LocalStack voor te berei, gebruik jy 'n klaargemaakte AWS CLI-beeld en skep 'n prent wat 'n skrif bevat wat daarop gebaseer is.

Die gebruik van volumes, hoef jy nie 'n Docker-beeld te bou net om lêers by die houer te voeg nie. Volumes is egter nie geskik vir ons omgewing nie omdat Gitlab CI-take self in houers loop. U kan Docker vanaf so 'n houer beheer, maar volumes monteer slegs vouers vanaf die gasheerstelsel, en nie vanaf 'n ander houer nie.

Probleme wat jy mag teëkom

Wag vir gereedheid

Wanneer 'n houer met 'n diens aan die gang is, beteken dit nie dat dit gereed is om verbindings te aanvaar nie. Jy moet wag vir die verbinding om voort te gaan.

Hierdie probleem word soms opgelos deur 'n skrip te gebruik wag-vir-dit.sh, wat wag vir 'n geleentheid om 'n TCP-verbinding te vestig. LocalStack kan egter 'n 502 Bad Gateway-fout gooi. Boonop bestaan ​​dit uit baie dienste, en as een van hulle gereed is, sê dit niks van die ander nie.

besluit: LocalStack-voorsiening-skrifte wat wag vir 'n 200-antwoord van beide SQS en SNS.

Parallelle taakkonflikte

Veelvuldige toetse kan gelyktydig op dieselfde Docker-gasheer uitgevoer word, so houer- en netwerkname moet uniek wees. Boonop kan toetse van verskillende takke van dieselfde diens ook gelyktydig loop, so dit is nie genoeg om hul name in elke opstellêer te skryf nie.

besluit: Die skrip stel die COMPOSE_PROJECT_NAME veranderlike op 'n unieke waarde.

Windows-kenmerke

Daar is 'n aantal dinge wat ek wil uitwys wanneer u Docker op Windows gebruik, aangesien hierdie ervarings belangrik is om te verstaan ​​waarom foute voorkom.

  1. Shell-skrifte in 'n houer moet Linux-reëleindes hê.
    Die dop CR simbool is 'n sintaksis fout. Dit is moeilik om uit die foutboodskap te sê dat dit die geval is. Wanneer u sulke skrifte op Windows redigeer, benodig u 'n behoorlike teksredigeerder. Daarbenewens moet die weergawebeheerstelsel behoorlik gekonfigureer word.

Dit is hoe git opgestel is:

git config core.autocrlf input

  1. Git-bash emuleer standaard Linux-vouers en, wanneer 'n exe-lêer (insluitend docker.exe) geroep word, vervang absolute Linux-paaie met Windows-paaie. Dit maak egter nie sin vir paaie wat nie op die plaaslike masjien (of paaie in 'n houer) is nie. Hierdie gedrag kan nie gedeaktiveer word nie.

besluit: voeg 'n bykomende skuinsstreep by die begin van die pad: //bin in plaas van /bin. Linux verstaan ​​sulke paaie; daarvoor is verskeie skuinsstreepies dieselfde as een. Maar git-bash herken nie sulke paaie nie en probeer dit nie omskakel nie.

Log uitset

Wanneer ek toetse uitvoer, wil ek graag sien dat logs van beide Newman en die diens getoets word. Aangesien die gebeure van hierdie logboeke met mekaar verbind is, is dit baie geriefliker om dit in een konsole te kombineer as twee afsonderlike lêers. Newman loods via docker-compose run, en so beland sy uitset in die konsole. Al wat oorbly is om seker te maak dat die uitset van die diens ook daarheen gaan.

Die oorspronklike oplossing was om te doen docker-opstel geen vlag nie -d, maar gebruik die dop-vermoëns, stuur hierdie proses na die agtergrond:

docker-compose up <service> &

Dit het gewerk totdat dit nodig was om logs van Docker na 'n derdeparty-diens te stuur. docker-opstel opgehou om logs na die konsole uit te voer. Die span het egter gewerk docker heg.

besluit:

docker attach --no-stdin ${COMPOSE_PROJECT_NAME}_<сервис>_1 &

Identifiseerderkonflik tydens toetsiterasies

Toetse word in verskeie iterasies uitgevoer. Die databasis is nie skoongemaak nie. Rekords in die databasis het unieke ID's. As ons spesifieke ID's in versoeke neerskryf, sal ons 'n konflik kry by die tweede herhaling.

Om dit te vermy, moet óf die ID's uniek wees, óf alle voorwerpe wat deur die toets geskep is, moet uitgevee word. Sommige voorwerpe kan weens vereistes nie uitgevee word nie.

besluit: genereer GUID's met behulp van Postman-skrifte.

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

Gebruik dan die simbool in die navraag {{myUUID}}, wat vervang sal word met die waarde van die veranderlike.

Samewerking via LocalStack

As die diens wat getoets word, 'n SQS-waglys lees of skryf, moet die toets self ook met hierdie tou werk om dit te verifieer.

besluit: versoeke van Postman na LocalStack.

Die AWS-dienste-API is gedokumenteer, sodat navrae sonder 'n SDK gemaak kan word.

As 'n diens na 'n tou skryf, lees ons dit en kontroleer die inhoud van die boodskap.

As die diens boodskappe na SNS stuur, skep LocalStack in die voorbereidingstadium ook 'n tou en teken in op hierdie SNS-onderwerp. Dan kom dit alles neer op wat hierbo beskryf is.

As die diens 'n boodskap uit die tou moet lees, skryf ons in die vorige toetsstap hierdie boodskap na die tou.

Toets HTTP-versoeke wat afkomstig is van die mikrodiens wat getoets word

Sommige dienste werk oor HTTP met iets anders as AWS, en sommige AWS-kenmerke word nie in LocalStack geïmplementeer nie.

besluit: in hierdie gevalle kan dit help MockServer, wat 'n klaargemaakte beeld in het Docker-spilpunt. Verwagte versoeke en antwoorde daarop word gekonfigureer deur 'n HTTP-versoek. Die API is gedokumenteer, so ons rig versoeke van Postman.

Toets tans OAuth-verifikasie en magtiging

Ons gebruik OAuth en JSON Web Tokens (JWT's). Die toets vereis 'n OAuth-verskaffer wat ons plaaslik kan laat loop.

Alle interaksie tussen die diens en die OAuth-verskaffer kom neer op twee versoeke: eerstens word die konfigurasie versoek /.welbekende/openid-konfigurasie, en dan word die publieke sleutel (JWKS) by die adres van die konfigurasie aangevra. Dit alles is statiese inhoud.

besluit: Ons toets OAuth-verskaffer is 'n statiese inhoudbediener en twee lêers daarop. Die teken word een keer gegenereer en aan Git verbind.

Kenmerke van SignalR-toetsing

Posman werk nie met websockets nie. 'n Spesiale hulpmiddel is geskep om SignalR te toets.

'n SignalR-kliënt kan meer as net 'n blaaier wees. Daar is 'n kliëntbiblioteek daarvoor onder .NET Core. Die kliënt, geskryf in .NET Core, vestig 'n verbinding, word geverifieer en wag vir 'n spesifieke volgorde van boodskappe. As 'n onverwagte boodskap ontvang word of die verbinding word verloor, gaan die kliënt uit met 'n kode van 1. As die laaste verwagte boodskap ontvang word, gaan die kliënt uit met 'n kode van 0.

Newman werk gelyktydig met die kliënt. Verskeie kliënte word geloods om seker te maak dat boodskappe afgelewer word aan almal wat dit nodig het.

Outomatiese toetsing van mikrodienste in Docker vir deurlopende integrasie

Gebruik die opsie om verskeie kliënte te laat loop --skaal op die docker-compose-opdragreël.

Voordat dit uitgevoer word, wag die Postman-skrip vir alle kliënte om verbindings te bewerkstellig.
Ons het reeds die probleem ondervind om vir 'n verbinding te wag. Maar daar was bedieners, en hier is die kliënt. ’n Ander benadering is nodig.

besluit: die kliënt in die houer gebruik die meganisme Gesondheids ondersoekom die draaiboek op die gasheer oor sy status in te lig. Die kliënt skep 'n lêer op 'n spesifieke pad, sê /healthcheck, sodra die verbinding tot stand gebring is. Die HealthCheck-skrip in die docker-lêer lyk soos volg:

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

Span docker inspekteer Toon die normale status, gesondheidstatus en uitgangkode vir die houer.

Nadat Newman voltooi het, kontroleer die skrip dat alle houers by die kliënt beëindig is, met kode 0.

Happinnes bestaan

Nadat ons die probleme hierbo beskryf het oorkom, het ons 'n stel stabiele hardlooptoetse gehad. In toetse werk elke diens as 'n enkele eenheid, in wisselwerking met die databasis en Amazon LocalStack.

Hierdie toetse beskerm 'n span van 30+ ontwikkelaars teen foute in 'n toepassing met komplekse interaksie van 10+ mikrodienste met gereelde ontplooiings.

Bron: will.com

Voeg 'n opmerking