Mikroteenuste automatiseeritud testimine Dockeris pideva integratsiooni jaoks.

Mikroteenuste arhitektuuriga seotud projektides muutub CI/CD meeldivast võimalusest äärmiselt vajalikuks. Automatiseeritud testimine on pideva integreerimise lahutamatu osa, mille õigesti rakendamine võib tuua meeskonnale palju meeldivaid õhtuid koos pere ja sõpradega. Vastasel juhul on projekt ohus jääda kunagi lõpetamata.

Kogu mikroteenuse koodi saab katta üksustestide ja muude testidega, kuid see lahendab ülesande vaid osaliselt ja tekitab palju küsimusi ja keerukusi, eriti andmetega töötamise testimisel. Nagu tavaliselt, on kõige pakilisemad teemad andmete järjepidevuse testimine suhelistes andmebaasides, töötamine pilveteenustega ja vale eeldused mokk-objektide kirjutamisel.

Kõik see ja veelgi enam saab lahendatud, testides kogu mikroteenust Docker-konteineris. Kindel eelis testide kehtivuse tagamisel on see, et testitakse samu Docker-pilte, mis lähevad tootmisse.

Selle lähenemise automatiseerimine toob endaga kaasas mitmeid probleeme, mille lahendused on allpool kirjeldatud:

  • paralleelsete ülesannete konfliktid ühes Docker-hostis;
  • identifikaatorite konfliktid andmebaasis testitsükli käigus;
  • mikroteenuste valmiduse ootamine;
  • logide ühendamine ja edastamine välistesse süsteemidesse;
  • väljuvate HTTP-päringute testimine;
  • veebipikkade testimine (SignalR-i abil);
  • OAuth autentimise ja autoriseerimise testimine.

See artikkel põhineb minu ettekande põhjal SECR 2019-l. Nii et neile, kellele ei meeldi lugeda, siin on esitluse salvestus..

Mikroteenuste automatiseeritud testimine Dockeris pideva integratsiooni jaoks.

Artiklis räägin, kuidas skripti abil käivitada Dockeris testitav teenus, andmebaas ja Amazon AWS teenused, seejärel testida Postmanis ning pärast nende lõpetamist peatada ja kustutada loodud konteinerid. Testid käivitatakse iga koodimuutuse korral. Nii veendume, et iga versioon töötab õigesti koos andmebaasi ja AWS teenustega.

Sama skripti käivitavad nii arendajad oma Windows-i tööt Desktopides kui ka Gitlab CI server Linuxis.

Uute testide rakendamine peab olema põhjendatud, see ei tohiks nõuda täiendavate tööriistade installimist ei arendaja arvutisse, ega serverisse, kus testid käivitatakse commit'i käigus. Docker lahendab selle probleemi.

Test peab töötama kohalikes serverites järgmiste põhjuste tõttu:

  • Võrk ei ole kunagi täiesti usaldusväärne. Tuhande päringu seast võib üks mitte läbida;
    Automaatne test sellisel juhul ei õnnestu, töö seiskub ning tuleb otsida põhjus logidest;
  • Liiga sageli tehtud päringud ei ole lubatud teatavate kolmandate osapoolte teenuste poolt.

Lisaks ei ole soovitatav kasutada stendi, kuna:

  • Stendi võib rikkuda mitte ainult halb kood, mis sellel töötab, vaid ka andmed, mida korrektne kood ei oska töödelda;
  • Kuigi me püüame testimise käigus tagasi tuua kõik muudatused, mis on tehtud testiga, võib midagi siiski valesti minna (vastasel juhul, miks testida?).

Projektist ja protsessi organiseerimisest

Meie ettevõte töötas välja mikroteenusele tugineva veebirakenduse, mis töötab Dockeris Amazon AWSis. Projektis kasutati juba üheotstarbelisi teste, kuid tihti ilmnesid vead, mida üheotstarbelised testid ei tuvastanud. Oli vajalik testida tervet mikroteenust koos andmebaasi ja Amazoniteenustega.

Projektis kasutatakse standardset pideva integreerimise protsessi, mis hõlmab mikroteenuste testimist iga commit'i järel. Pärast ülesande määramist teeb arendaja muudatused mikroteenuses, testib seda käsitsi ja käivitab kõik olemasolevad automaatsed testid. Vajadusel muudab arendaja teste. Kui probleeme ei leita, tehakse commit selle ülesande haru. Iga commit'i järel käivituvad serveris automaatselt testid. Ühisesse harusse merge'imine ja automaatsete testide käivitamine seal toimub pärast edukat ülevaatust. Kui testid ühises harus on läbitud, uuendatakse teenust automaatselt testkeskkonnas Amazon Elastic Container Service'is (testkeskkonnas). Testkeskkond on vajalik kõigile arendajatele ja testijatele ning selle riknemine pole soovitatav. Tesitijad kontrollivad selles keskkonnas parandust või uut funktsiooni, tehes käsitsi teste.

Projekti arhitektuur

Mikroteenuste automatiseeritud testimine Dockeris pideva integratsiooni jaoks.

Rakendus koosneb enam kui kümnest teenusest. Mõned neist on kirjutatud .NET Core'is, teised NodeJs'is. Iga teenus töötab Docker-konteineris Amazon Elastic Container Service'is. Igal teenusel on oma Postgres andmebaas ja mõnedel on ka Redis. Ühiseid andmebaase ei ole. Kui mitmed teenused vajavad samu andmeid, edastatakse need andmed nende muutmisel igaühele neist SNS (Simple Notification Service) ja SQS (Amazon Simple Queue Service) kaudu ning teenused salvestavad need oma isoleeritud andmebaasidesse.

SQS ja SNS

SQS võimaldab HTTPS protokolli kaudu sõnumeid järjekorda paigutada ja järjekorrast sõnumeid lugeda.

Kui mitu teenust loevad ühte järjekorda, jõuab iga sõnum ainult ühele neist. See on kasulik, kui käivitate mitmeid üksusi samast teenusest, et koormust nende vahel jaotada.

Kui on vajalik, et iga sõnum toimetataks mitmele teenusele, peab igal saajal olema oma järjekord ja sõnumite dubleerimiseks mitmesse järjekorda on vajalik SNS.

SNS-is loote teema, millele saate tellida näiteks SQS-järjekorra. Teema kaudu saab saatma sõnumeid. Iga sõnum saadetakse igasse järjekorda, mis on selle teema jaoks registreeritud. SNS-is ei ole meetodit sõnumite lugemiseks. Kui silumise või testimise käigus on vaja teada, mida SNS-i saadetakse, on võimalik luua SQS-järjekord, registreerida see soovitud teema jaoks ja lugeda järjekorda.

Mikroteenuste automatiseeritud testimine Dockeris pideva integratsiooni jaoks.

API Gateway

Enamik teenuseid ei ole internetist otseselt ligipääsetavad. Ligipääs toimub API Gateway kaudu, mis kontrollib juurdepääsuõigusi. See on samuti meie teenus ja selle jaoks on samuti testid.

Reaalajas teavitused

Rakendus kasutab SignalR, et näidata kasutajale reaalajas teavitusi. See on rakendatud teavitusteenuses. See on otseselt internetist ligipääsetav ja töötab OAuth-iga, kuna Web-socketi toe integreerimine Gateway-sse osutus ebasoodsaks võrreldes OAuth ja teavitusteenuse integreerimisega.

Tuntud lähenemisviis testimisele

Üksustestid asendavad sellised asjad nagu andmebaas, tehes seda n-ö valeobjektidega. Kui mikroteenus püüab näiteks luua kirjet tabelis, millel on väline võti, ja kirjet, millele see võti osutab, ei eksisteeri, siis ei saa päringut täita. Üksustestid ei suuda seda tuvastada.

V artiklis Microsoftilt soovitame kasutada in-memory andmebaasi ja sisse tuua valeobjekte.

In-memory andmebaas on üks andmebaasihaldussüsteemidest, mida Entity Framework toetab. See on loodud spetsiaalselt testide jaoks. Andmed sellises andmebaasis salvestatakse ainult kuni protsessi lõpuni, mis seda kasutab. Tabeleid ei pea looma ja andmete terviklikkust ei kontrollita.

Valeobjektid modelleerivad asendatavat klassi ainult niivõrd, kuivõrd testi arendaja mõistab selle tööd.

Kuidas saavutada Postgresi automaatne käivitamine ja migratsiooni täitmine testi käivitamisel, ei ole artiklis Microsoftilt märgitud. Minu lahendus seda teeb ning lisaks ei lisata mikroteenusesse mingit koodi spetsiaalselt testide jaoks.

Liigume lahenduse juurde

Arenduse käigus sai selgeks, et ainult ühitesti testimisest ei piisa, et kõik probleemid õigeaegselt tuvastada, seetõttu otsustati sellele küsimusele läheneda teistmoodi.

Testkeskkonna seadistamine

Esimene ülesanne on käivitada testkeskkond. Sammud, mis on vajalikud mikroteenuse käivitamiseks:

  • Seadistada testitav teenus kohaliku keskkonna jaoks, keskkonnamuutujates tuleb määrata andmebaasi ja AWS-i ühenduse üksikasjad;
  • Käivitada Postgres ja teostada migratsioon, käivitades Liquibase.
    Relatsioonilistes andmebaasides tuleb andmete salvestamiseks andmebaasi esmalt luua andmeskeem, lihtsustatult öeldes tabelid. Rakenduse uuendamisel tuleb tabelid viia uue versiooniga kasutatavasse vormi, ja seda soovitavalt ilma andmete kadumiseta. Seda nimetatakse migratsiooniks. Tabelite loomine algses tühjas andmebaasis on migratsiooni erijuht. Migratsiooni saab integreerida rakendusse. N nii .NETis kui ka NodeJS-is on migratsiooni raamistikud. Meie juhtumil on mikroteenustes andmeskeemi muutmise õigus tagamise eesmärgil eemaldatud ning migratsioon toimub Liquibase'i abil.
  • Käivitage Amazon LocalStack. See on AWS teenuste rakendus, et jooksutada seda ise. LocalStack'i jaoks on Docker Hub'is olemas juba valmis pilt.
  • Käivitage skript, et luua LocalStack'is vajalikud üksused. Shell-skripte kasutatakse AWS CLI abiga.

Projektis testimiseks kasutatakse Postman. See oli juba varem olemas, kuid seda käidi käsitsi ja testiti rakendust, mis oli juba keskkonnas käivitunud. See tööriist võimaldab teha suvalisi HTTP(S)-päringuid ja kontrollida, kas vastused vastavad ootustele. Päringud koondatakse kogumikku ning terve kogumiku saab käivitada korraga.

Mikroteenuste automatiseeritud testimine Dockeris pideva integratsiooni jaoks.

Kuidas on korraldatud automaatne test

Testimise ajal töötab Dockeris kõik: nii testitav teenus, Postgres kui ka migratsioonitööriist ja Postman, või õigemini, selle konsooli versioon - Newman.

Docker lahendab mitmeid probleeme:

  • Sõltumatuse hosti konfiguratsioonist;
  • Sõltuvuste paigaldamine: docker laadib pilte Docker Hub'ist;
  • Süsteemi tagasi algsesse olekusse viimine: lihtsalt kustutame konteinerid.

Docker-compose ühendab konteinerid virtuaalsesse võrku, mis on isoleeritud internetist, kus konteinerid leiavad üksteist domeeninimede abil.

Testi haldavad shell-skriptid. Testi käivitamiseks Windowsis kasutame git-bashi. Seega piisab ühest skriptist nii Windowsi kui ka Linuxi jaoks. Git ja Docker on kõikidel projekti arendajatel juba installitud. Git'i installimisel Windowsis installitakse git-bash, seega on see kõigil olemas.

Skript täidab järgmisi samme:

  • Docker-piltide koostamine
    docker-compose build
  • Andmebaasi ja LocalStacki käivitamine
    docker-compose up -d
  • Andmebaasi migratsioon ja LocalStacki ettevalmistamine
    docker-compose run
  • Testitava teenuse käivitamine
    docker-compose up -d
  • Testi käivitamine (Newman)
  • Käivita kõik konteinerid
    docker-compose down
  • Tulemuste postitamine Slacki
    Meil on vestlus, kuhu tulevad sõnumid rohelise linnukese või punase ristiga ja lingiga logile.

Nendes sammudes on kaasatud järgmised Docker-pildid:

  • Testitav teenus on sama pilt, mis tootmiskeskkonnas. Testi konfiguratsioon - keskkonnamuutujate kaudu.
  • Postgres'i, Redis'i ja LocalStacki jaoks kasutatakse valmis pilte Docker Hub'ist. Liquibase ja Newman jaoks on samuti valmis kujundatud pildid. Me ehitame oma nende põhjal, lisades oma failid.
  • LocalStacki ettevalmistamiseks kasutatakse valmis pilti AWS CLI-st, ja selle alusel luuakse pilt, mis sisaldab skripti.

Kasutades volumes, ei ole vajalik luua Docker-pilti ainult failide konteinerisse lisamiseks. Siiski ei sobi volumes meie keskkonda, kuna Gitlab CI ülesanded töötavad konteinerites. Sellisest konteinerist saab Dockerit hallata, kuid volumes mountivad kaustad ainult host-süsteemist, mitte teistest konteineritest.

Probleemid, millega võib kokku puutuda

Valmiduse ootamine

Kui teenuse konteiner on käivitatud, ei tähenda see, et see on valmis vastu võtma ühendusi. Tuleb oodata, kuni ühendus on loodud, et edasi minna.

Seda ülesannet lahendatakse vahel skripti wait-for-it.sh, abil, mis ootab TCP-ühenduse loomise võimalust. Siiski võib LocalStack anda 502 Bad Gateway vea. Lisaks koosneb see paljusid teenustest ja kui üks neist on valmis, ei tähenda see, et teised oleksid samuti valmis.

Lahendus: LocalStacki ettevalmistusskriptid, mis ootavad vastust 200 nii SQS kui ka SNS-i jaoks.

Paralleelsete ülesannete konfliktid

Mitmed testid võivad töötada samal Docker-hostil, seega peavad konteinerite ja võrkude nimed olema unikaalsed. Lisaks võivad sama teenuse erinevate harude testid samuti töötada samaaegselt, mistõttu ei piisa, kui iga compose-failis määrata oma nimesid.

Lahendus: skript määrab COMPOSE_PROJECT_NAME muutuja unikaalse väärtuse.

Windowsi omadused

Dockerit Windowsis kasutades on mitmeid asju, millele tahan tähelepanu juhtida, kuna see kogemus on oluline vigade mõistmiseks.

  1. Konteineris olevate shell-skriptide lõppudel peavad olema Linuxi lõppud.
    CR-sümbol shellis on süntaksite viga. Veateatest on raske aru saada, et probleem on selles. Selliste skriptide redigeerimisel Windowsis on vajalik õige tekstiredaktor. Lisaks peab versioonihaldussüsteem olema korralikult seadistatud.

Nii seadistatakse git:

git config core.autocrlf input

  1. Git-bash emuleerib Linuxi standardkaustade struktuuri ja kutsel exe-faili (sealhulgas docker.exe) asendatakse absoluutseid Linuxi teid Windowsi teedega. Kuid see ei ole mõistlik mitte-lokaalses masinas olevate teede (või konteineris olevate teede) puhul. Sellist käitumist ei saa välja lülitada.

Lahendus: lisa teed tuleks kirjutada täiendav kaldkriips tee algusesse: //bin asemel /bin. Linux mõistab selliseid teid, sest selle jaoks on mitu kaldkriipsu sama, mis üks. Kuid git-bash ei tuvasta neid teid ja ei ürita neid muuta.

Logide väljund

Testimise käigus sooviks näha logisid nii Newmanilt kui ka testitavalt teenuselt. Kuna nende logide sündmused on omavahel seotud, on nende ühendamine ühes konsolis palju mugavam kui kahe eraldi faili puhul. Newman käivitub läbi docker-compose run, ja seetõttu jõuab selle väljund konsooli. Jääb üle teha nii, et ka teenuse väljund sinna satuks.

Algne lahendus oli see, et teha docker-compose up ilma liputa -d, kuid kasutades shelli võimalusi, saata see protsess taustale:

docker-compose up <teenus> &

See töötas seni, kuni tuli vajadus saata logid dockersist kolmanda osapoole teenusesse. docker-compose up lakkas logide väljundi konsooli saatmine. Kuid toimis käsk docker attach.

Lahendus:

docker attach --no-stdin ${COMPOSE_PROJECT_NAME}_<teenus>_1 &

Identifikaatorite konflikt testi iteratsioonide ajal

Testid käivitatakse mitme iteratsiooniga. Samal ajal andmebaasi ei puhastata. Kirjed andmebaasis omavad unikaalseid ID-sid. Kui sisestada konkreetseid ID-sid päringutesse, saame teisel iteratsioonil konflikti.

Selle vältimiseks peavad kas ID-d olema unikaalsed või tuleb testiga loodud kõik objektid eemaldada. Muid kui teatud objekte eemaldada ei tohi vastavalt nõuetele.

Lahendus: genereerida GUID-e skriptidega Postmanis.

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

Seejärel kasutada päringus sümbolit {{myUUID}}, mis asendatakse muudatuse väärtusega.

Koostöö LocalStackiga

Kui testitav teenus loeb SQS-järjekorrast või kirjutab sinna, peab ka test ise selle järjekorraga töötama.

Lahendus: päringud Postmanist LocalStackile.

AWS teenuste API-d on dokumenteeritud, mis võimaldab teha päringuid ilma SDK-ta.

Kui teenus kirjutab järjekorda, loeme selle ja kontrollime sõnumi sisu.

Kui teenus saadab sõnumeid SNS-ile, luuakse LocalStacki ettevalmistamise etapis veel üks järjekord ja liitub selle SNS-teemaga. Edasi läheb kõik eeltoodud järgi.

Kui teenus peab sõnumi järjekorrast lugema, siis kirjutame selle sõnumi järjekorda eelneval testimise sammul.

HTTP-päringute testimine, mis pärinevad testitavast mikroteenusest

Mõned teenused töötavad HTTP-s koos millegi muu kui AWS-iga ja mõned AWS-i funktsioonid ei ole LocalStackis rakendatud.

Lahendus: sellistel juhtudel aitab MockServer, millel on olemas valmis pilt Docker Hub. Eeldatavad päringud ja vastused konfigureeritakse HTTP-päringu kaudu. API on dokumenteeritud, seega teeme päringud Postmanist.

OAuthi autentimise ja autoriseerimise testimine

Kasutame OAuth-i ja JSON Web Tokens (JWT). Testimiseks on vajalik OAuth-i pakkuja, mille saame kohapeal käivitada.

Kogu teenuse suhtlus OAuth-i pakkujaga koosneb kahest päringust: esmalt küsitakse konfiguratsiooni /.well-known/openid-configuration, seejärel küsitakse avalikku võtit (JWKS) konfiguratsioonis toodud aadressilt. Kõik see on staatiline sisu.

Lahendus: meie testimise OAuth-i pakkuja on staatilise sisu server ja kaks faili sellel. Token genereeriti üks kord ja kommiti Git'i.

SignalR testimise eripärad

Postman ei toimi veebipesadega. SignalRi testimiseks loodi spetsiaalne tööriist.

SignalR'i klient ei ole ainult brauser. Selle jaoks on olemas .NET Core'i kliendi teek. .NET Core'i peale kirjutatud klient loob ühenduse, läbib autentimise ja ootab kindlat sõnumite järjekorda. Kui saadakse ootamatu sõnum või ühendus katke, lõpetab klient töö koodiga 1. Kui saadakse viimane oodatud sõnum, lõpetab see töö koodiga 0.

Samaaegselt kliendiga töötab Newman. Kliendid käivitatakse mitu, et kontrollida, et sõnumid jõuavad kõigile, kellele need on vajalikud.

Mikroteenuste automatiseeritud testimine Dockeris pideva integratsiooni jaoks.

Mitme kliendi käivitamiseks kasutatakse valikut —scale käskude rea kaudu docker-compose.

Enne Postmani skripti käivitamist ootab see, kuni kõik kliendid on ühenduse loonud.
Ootamise probleem on meile juba ette tulnud. Seal olid serverid, aga siin on klient. Vajalik on teine lähenemine.

Lahendus: konteineris asuv klient kasutab mehhanismi HealthCheck, et teavitada hostis olevat skripti oma olekust. Klient loob faili kindlas teel, näiteks /healthcheck, kohe, kui ühendus on loodud. HealthCheck-skript Docker'i failis näeb välja nii:

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

Meeskond docker inspect näitab konteineri tavalist olekut, terviseolekut ja väljumiskoodi.

Pärast Newman'i täitmist kontrollib skript, et kõik kliendiga seotud konteinerid on lõpetatud ja väljumiskood on 0.

Õnn on olemas

Pärast ülaltoodud raskuste ületamist saime stabiilsete testide komplekti. Testides töötab iga teenus terviklikuna, suhtleb andmebaasiga ja Amazon LocalStack'iga.

Need testid kaitsevad üle 30 arendaja meeskonda rakenduse vigade eest, mis on seotud 10+ mikroteenuse keerulise koostööga sagedaste juurutamiste ajal.

Allikas: habr.com

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