Automatizirano testiranje mikroservisa u Dockeru za kontinuiranu integraciju

U projektima vezanim uz razvoj mikroservisne arhitekture, CI/CD iz kategorije ugodne prilike prelazi u kategoriju hitne potrebe. Automatizirano testiranje sastavni je dio kontinuirane integracije čiji kompetentan pristup timu može pružiti mnogo ugodnih večeri s obitelji i prijateljima. U suprotnom postoji opasnost da projekt nikada neće biti dovršen.

Jediničnim testovima s mock objektima moguće je pokriti cijeli kod mikroservisa, no to samo djelomično rješava problem i ostavlja mnogo pitanja i poteškoća, posebice kod testiranja rada s podacima. Kao i uvijek, najhitniji su testiranje dosljednosti podataka u relacijskoj bazi podataka, testiranje rada s uslugama u oblaku i donošenje netočnih pretpostavki pri pisanju lažnih objekata.

Sve to i još malo više može se riješiti testiranjem cijelog mikroservisa u Docker kontejneru. Nedvojbena prednost za osiguranje valjanosti testova je da se testiraju iste Docker slike koje idu u proizvodnju.

Automatizacija ovog pristupa predstavlja brojne probleme čije će rješenje biti opisano u nastavku:

  • sukobi paralelnih zadataka u istom docker hostu;
  • sukobi identifikatora u bazi podataka tijekom testnih iteracija;
  • čekanje da mikroservisi budu spremni;
  • spajanje i ispisivanje dnevnika u vanjske sustave;
  • testiranje odlaznih HTTP zahtjeva;
  • testiranje web socketa (koristeći SignalR);
  • testiranje OAuth autentifikacije i autorizacije.

Ovaj se članak temelji na moj govor na SECR 2019. Dakle, za one koji su previše lijeni za čitanje, evo snimke govora.

Automatizirano testiranje mikroservisa u Dockeru za kontinuiranu integraciju

U ovom članku ću vam reći kako pomoću skripte pokrenuti servis koji se testira, bazu podataka i Amazon AWS servise u Dockeru, potom testove na Postmanu i nakon završetka zaustaviti i izbrisati stvorene spremnike. Testovi se izvode svaki put kada se kod promijeni. Na taj način osiguravamo da svaka verzija ispravno radi s AWS bazom podataka i uslugama.

Istu skriptu pokreću i sami programeri na svojim Windows stolnim računalima i Gitlab CI poslužitelj pod Linuxom.

Da bi bilo opravdano, uvođenje novih testova ne bi trebalo zahtijevati instalaciju dodatnih alata bilo na računalu programera ili na poslužitelju gdje se testovi izvode na commit. Docker rješava ovaj problem.

Test se mora izvoditi na lokalnom poslužitelju iz sljedećih razloga:

  • Mreža nikada nije potpuno pouzdana. Od tisuću zahtjeva, jedan može propasti;
    U tom slučaju automatski test neće raditi, rad će prestati, a razlog ćete morati potražiti u zapisima;
  • Neke usluge trećih strana ne dopuštaju prečeste zahtjeve.

Osim toga, nepoželjno je koristiti postolje jer:

  • Stalak se može pokvariti ne samo lošim kodom koji se na njemu izvodi, već i podacima koje ispravan kod ne može obraditi;
  • Bez obzira koliko se trudili vratiti sve promjene koje je test napravio tijekom samog testa, nešto može poći po zlu (inače, čemu testiranje?).

O projektu i organizaciji procesa

Naša je tvrtka razvila mikroservisnu web aplikaciju koja radi u Dockeru u oblaku Amazon AWS. Jedinični testovi već su korišteni na projektu, ali često su se događale pogreške koje jedinični testovi nisu otkrili. Trebalo je testirati cijeli mikroservis uz bazu podataka i Amazon servise.

Projekt koristi standardni proces kontinuirane integracije, koji uključuje testiranje mikroservisa sa svakim commitom. Nakon dodjele zadatka, programer mijenja mikroservis, testira ga ručno i pokreće sve dostupne automatizirane testove. Ako je potrebno, programer mijenja testove. Ako se ne pronađu nikakvi problemi, izvršava se obveza na granu ovog izdanja. Nakon svakog izvršenja, testovi se automatski pokreću na poslužitelju. Spajanje u zajedničku granu i pokretanje automatskih testova na njoj događa se nakon uspješnog pregleda. Ako testovi na zajedničkoj grani prođu, usluga se automatski ažurira u testnom okruženju na Amazon Elastic Container Service (klupa). Stalak je neophodan svim programerima i testerima i nije ga poželjno razbijati. Testeri u ovom okruženju provjeravaju popravak ili novu značajku izvođenjem ručnih testova.

Arhitektura projekta

Automatizirano testiranje mikroservisa u Dockeru za kontinuiranu integraciju

Aplikacija se sastoji od više od deset usluga. Neki od njih su napisani u .NET Core, a neki u NodeJs. Svaka usluga radi u Docker spremniku u Amazon Elastic Container Service. Svaki ima svoju Postgres bazu, a neki imaju i Redis. Ne postoje zajedničke baze podataka. Ako više servisa treba iste podatke, tada se ti podaci, kada se promijene, prenose svakom od tih servisa putem SNS (Simple Notification Service) i SQS (Amazon Simple Queue Service), a servisi ih spremaju u svoje zasebne baze podataka.

SQS i SNS

SQS vam omogućuje stavljanje poruka u red i čitanje poruka iz reda pomoću HTTPS protokola.

Ako više servisa čita jedan red čekanja, tada svaka poruka stiže samo jednom od njih. Ovo je korisno kada se pokreće nekoliko instanci iste usluge kako bi se opterećenje rasporedilo između njih.

Ako želite da svaka poruka bude isporučena na više servisa, svaki primatelj mora imati vlastiti red čekanja, a SNS je potreban za dupliciranje poruka u više redova čekanja.

U SNS-u kreirate temu i pretplatite se na nju, na primjer, SQS red. Možete slati poruke na temu. U ovom slučaju, poruka se šalje svakom redu čekanja pretplaćenom na ovu temu. SNS nema metodu za čitanje poruka. Ako tijekom otklanjanja pogrešaka ili testiranja trebate saznati što se šalje SNS-u, možete stvoriti SQS red čekanja, pretplatiti ga na željenu temu i čitati red čekanja.

Automatizirano testiranje mikroservisa u Dockeru za kontinuiranu integraciju

API pristupnik

Većina usluga nije izravno dostupna s interneta. Pristup je putem API Gatewaya, koji provjerava prava pristupa. Ovo je također naša usluga, a za nju postoje i testovi.

Obavijesti u stvarnom vremenu

Aplikacija koristi SignalRza prikaz obavijesti u stvarnom vremenu korisniku. Ovo je implementirano u servisu obavijesti. Dostupan je izravno s interneta i sam radi s OAuthom, jer se pokazalo nepraktičnim ugraditi podršku za web utičnice u Gateway, u usporedbi s integracijom OAutha i usluge obavijesti.

Dobro poznati pristup testiranju

Jedinični testovi zamjenjuju stvari poput baze podataka lažnim objektima. Ako mikroservis, na primjer, pokuša stvoriti zapis u tablici sa stranim ključem, a zapis na koji se taj ključ poziva ne postoji, tada se zahtjev ne može izvršiti. Jedinični testovi to ne mogu otkriti.

В članak iz Microsofta Predlaže se korištenje baze podataka u memoriji i implementacija lažnih objekata.

Baza podataka u memoriji jedan je od DBMS-ova koje podržava Entity Framework. Napravljen je posebno za testiranje. Podaci u takvoj bazi podataka pohranjuju se samo dok proces koji ih koristi ne završi. Ne zahtijeva izradu tablica i ne provjerava integritet podataka.

Lažni objekti modeliraju klasu koju zamjenjuju samo u onoj mjeri u kojoj programer testa razumije kako to radi.

Kako postići da se Postgres automatski pokrene i izvrši migracije kada pokrenete test nije navedeno u Microsoftovom članku. Moje rješenje to čini i, osim toga, samoj mikroservisi ne dodaje nikakav kôd posebno za testove.

Prijeđimo na rješenje

Tijekom procesa razvoja postalo je jasno da jedinični testovi nisu dovoljni da se na vrijeme pronađu svi problemi, pa je odlučeno ovom pitanju pristupiti iz drugog kuta.

Postavljanje testnog okruženja

Prvi zadatak je postaviti testno okruženje. Koraci potrebni za pokretanje mikroservisa:

  • Konfigurirajte uslugu koja se testira za lokalno okruženje, navedite detalje za povezivanje s bazom podataka i AWS-om u varijablama okruženja;
  • Pokrenite Postgres i izvedite migraciju pokretanjem Liquibase.
    U relacijskim DBMS-ovima, prije upisa podataka u bazu podataka, morate kreirati shemu podataka, drugim riječima, tablice. Prilikom ažuriranja aplikacije, tablice se moraju dovesti u oblik koji koristi nova verzija i, po mogućnosti, bez gubitka podataka. To se zove migracija. Stvaranje tablica u inicijalno praznoj bazi podataka poseban je slučaj migracije. Migracija se može ugraditi u samu aplikaciju. I .NET i NodeJS imaju okvire za migraciju. U našem slučaju, iz sigurnosnih razloga, mikroservisi su lišeni prava promjene podatkovne sheme, a migracija se izvodi pomoću Liquibase.
  • Pokrenite Amazon LocalStack. Ovo je implementacija AWS usluga za rad kod kuće. Postoji gotova slika za LocalStack na Docker Hubu.
  • Pokrenite skriptu za stvaranje potrebnih entiteta u LocalStacku. Shell skripte koriste AWS CLI.

Koristi se za testiranje na projektu Poštar. Postojao je i prije, ali je pokretan ručno i testirao aplikaciju koja je već bila postavljena na štandu. Ovaj alat omogućuje vam postavljanje proizvoljnih HTTP(S) zahtjeva i provjeru odgovaraju li odgovori očekivanjima. Upiti se kombiniraju u zbirku i može se pokrenuti cijela zbirka.

Automatizirano testiranje mikroservisa u Dockeru za kontinuiranu integraciju

Kako funkcionira automatski test?

Tijekom testa u Dockeru radi sve: servis koji se testira, Postgres, alat za migraciju i Postman, odnosno njegova konzolna verzija - Newman.

Docker rješava niz problema:

  • Neovisnost o konfiguraciji glavnog računala;
  • Instaliranje ovisnosti: Docker preuzima slike s Docker Huba;
  • Vraćanje sustava u prvobitno stanje: jednostavno uklanjanje spremnika.

docker-compose ujedinjuje kontejnere u virtualnu mrežu, izoliranu od interneta, u kojoj se kontejneri međusobno pronalaze po nazivima domena.

Testom upravlja skripta ljuske. Za pokretanje testa u sustavu Windows koristimo git-bash. Dakle, jedna skripta je dovoljna i za Windows i za Linux. Git i Docker instaliraju svi programeri na projektu. Prilikom instaliranja Gita na Windows, instalira se git-bash, tako da svi imaju i njega.

Skripta izvodi sljedeće korake:

  • Izrada docker slika
    docker-compose build
  • Pokretanje baze podataka i LocalStacka
    docker-compose up -d <контейнер>
  • Migracija baze podataka i priprema LocalStack-a
    docker-compose run <контейнер>
  • Pokretanje usluge koja se testira
    docker-compose up -d <сервис>
  • Izvođenje testa (Newman)
  • Zaustavljanje svih kontejnera
    docker-compose down
  • Objavljivanje rezultata u Slacku
    Imamo chat gdje idu poruke sa zelenom kvačicom ili crvenim križićem i vezom na dnevnik.

Sljedeće Docker slike uključene su u ove korake:

  • Usluga koja se testira je ista slika kao i za proizvodnju. Konfiguracija za test je kroz varijable okoline.
  • Za Postgres, Redis i LocalStack koriste se gotove slike iz Docker Huba. Također postoje gotove slike za Liquibase i Newman. Mi svoje gradimo na njihovom kosturu, dodajući tamo svoje datoteke.
  • Da biste pripremili LocalStack, koristite gotovu AWS CLI sliku i izradite sliku koja sadrži skriptu koja se temelji na njoj.

Korištenje sveska, ne morate izraditi Docker sliku samo da biste dodali datoteke u spremnik. Međutim, volumeni nisu prikladni za naše okruženje jer se sami Gitlab CI zadaci izvode u spremnicima. Možete kontrolirati Docker iz takvog spremnika, ali volumeni samo montiraju mape iz glavnog sustava, a ne iz drugog spremnika.

Problemi na koje možete naići

Čeka se spremnost

Kada spremnik s uslugom radi, to ne znači da je spreman prihvatiti veze. Morate pričekati da se veza nastavi.

Ovaj se problem ponekad rješava pomoću skripte čekaj.š, koji čeka priliku za uspostavljanje TCP veze. Međutim, LocalStack može izbaciti pogrešku 502 Bad Gateway. Osim toga, sastoji se od mnogo usluga, a ako je jedna od njih spremna, to ne govori ništa o ostalima.

odluka: LocalStack skripte za dodjelu koje čekaju odgovor 200 i od SQS-a i od SNS-a.

Sukobi paralelnih zadataka

Više testova može se izvoditi istovremeno na istom Docker hostu, tako da nazivi spremnika i mreže moraju biti jedinstveni. Štoviše, testovi iz različitih ogranaka iste usluge također se mogu izvoditi istovremeno, tako da nije dovoljno napisati njihova imena u svakoj datoteci za sastavljanje.

odluka: Skripta postavlja varijablu COMPOSE_PROJECT_NAME na jedinstvenu vrijednost.

Značajke sustava Windows

Postoje brojne stvari koje želim istaknuti kada koristite Docker u sustavu Windows, jer su ta iskustva važna za razumijevanje zašto dolazi do pogrešaka.

  1. Shell skripte u spremniku moraju imati Linux završetke redaka.
    Simbol ljuske CR sintaktička je pogreška. Iz poruke o pogrešci teško je zaključiti da je to slučaj. Kada uređujete takve skripte u sustavu Windows, potreban vam je odgovarajući uređivač teksta. Osim toga, sustav kontrole verzija mora biti ispravno konfiguriran.

Ovako je git konfiguriran:

git config core.autocrlf input

  1. Git-bash emulira standardne Linux mape i, kada poziva exe datoteku (uključujući docker.exe), zamjenjuje apsolutne Linux staze s Windows stazama. Međutim, to nema smisla za staze koje nisu na lokalnom stroju (ili staze u spremniku). Ovo se ponašanje ne može onemogućiti.

odluka: dodajte dodatnu kosu crtu na početak staze: //bin umjesto /bin. Linux razumije takve staze; za njega je nekoliko kosih crta isto što i jedna. Ali git-bash ne prepoznaje takve staze i ne pokušava ih pretvoriti.

Log izlaz

Prilikom izvođenja testova, želio bih vidjeti zapise i Newmana i usluge koja se testira. Budući da su događaji ovih zapisa međusobno povezani, njihovo kombiniranje u jednoj konzoli mnogo je praktičnije nego dvije odvojene datoteke. Newman lansira putem docker-compose run, i tako njegov izlaz završava u konzoli. Sve što preostaje je osigurati da izlaz usluge također ide tamo.

Prvotno rješenje bilo je učiniti sastavljač bez zastave -d, ali koristeći mogućnosti ljuske, pošaljite ovaj proces u pozadinu:

docker-compose up <service> &

Ovo je funkcioniralo sve dok nije bilo potrebno poslati zapise iz Dockera u uslugu treće strane. sastavljač prestao ispisivati ​​zapisnike na konzolu. Međutim, tim je radio pričvršćivač.

odluka:

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

Sukob identifikatora tijekom ponavljanja testa

Testovi se izvode u nekoliko iteracija. Baza podataka nije izbrisana. Zapisi u bazi podataka imaju jedinstvene ID-ove. Ako zapišemo određene ID-ove u zahtjevima, dobit ćemo sukob u drugoj iteraciji.

Da biste to izbjegli, ID-ovi moraju biti jedinstveni ili se moraju izbrisati svi objekti stvoreni testom. Neki se objekti ne mogu izbrisati zbog zahtjeva.

odluka: generirajte GUID-ove koristeći Postman skripte.

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

Zatim upotrijebite simbol u upitu {{myUUID}}, koja će biti zamijenjena vrijednošću varijable.

Suradnja putem LocalStacka

Ako usluga koja se testira čita ili piše u SQS red čekanja, tada da bi se to potvrdilo, sam test također mora raditi s ovim redom čekanja.

odluka: zahtjevi od poštara do LocalStacka.

API za AWS usluge je dokumentiran, što omogućuje postavljanje upita bez SDK-a.

Ako servis piše u red čekanja, mi to čitamo i provjeravamo sadržaj poruke.

Ako usluga šalje poruke SNS-u, u fazi pripreme LocalStack također stvara red i pretplaćuje se na ovu SNS temu. Onda se sve svodi na gore opisano.

Ako servis treba pročitati poruku iz reda čekanja, tada u prethodnom testnom koraku upisujemo tu poruku u red čekanja.

Testiranje HTTP zahtjeva koji potječu iz mikroservisa koji se testira

Neke usluge rade preko HTTP-a s nečim što nije AWS, a neke značajke AWS-a nisu implementirane u LocalStack.

odluka: u ovim slučajevima može pomoći MockServer, koji ima gotovu sliku u Docker čvorište. Očekivani zahtjevi i odgovori na njih konfigurirani su HTTP zahtjevom. API je dokumentiran, tako da postavljamo zahtjeve od Postmana.

Testiranje OAuth autentifikacije i autorizacije

Koristimo OAuth i JSON web tokeni (JWT). Test zahtijeva pružatelja OAuth kojeg možemo pokrenuti lokalno.

Sva interakcija između usluge i OAuth davatelja svodi se na dva zahtjeva: prvo se traži konfiguracija /.well-known/openid-configuration, a zatim se na adresi iz konfiguracije traži javni ključ (JWKS). Sve je ovo statični sadržaj.

odluka: Naš testni OAuth pružatelj usluga je statički poslužitelj sadržaja i dvije datoteke na njemu. Token se generira jednom i predaje Gitu.

Značajke SignalR testiranja

Poštar ne radi s websocketima. Napravljen je poseban alat za testiranje SignalR-a.

SignalR klijent može biti više od običnog preglednika. Za njega postoji klijentska biblioteka pod .NET Core. Klijent, napisan u .NET Core, uspostavlja vezu, autentificiran je i čeka određeni niz poruka. Ako se primi neočekivana poruka ili se veza izgubi, klijent izlazi s kodom 1. Ako se primi zadnja očekivana poruka, klijent izlazi s kodom 0.

Newman radi istovremeno s klijentom. Pokreće se nekoliko klijenata kako bi se provjerilo jesu li poruke isporučene svima kojima su potrebne.

Automatizirano testiranje mikroservisa u Dockeru za kontinuiranu integraciju

Za pokretanje više klijenata koristite opciju --ljestvica na docker-compose naredbenom retku.

Prije pokretanja skripta Postman čeka da svi klijenti uspostave veze.
Već smo se susreli s problemom čekanja na vezu. Ali postojali su poslužitelji, a ovdje je klijent. Potreban je drugačiji pristup.

odluka: klijent u spremniku koristi mehanizam Provjera zdravljada obavijesti skriptu na hostu o svom statusu. Klijent stvara datoteku na određenoj stazi, recimo /healthcheck, čim se veza uspostavi. Skripta HealthCheck u docker datoteci izgleda ovako:

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

Momčad doker pregledati Prikazuje normalno stanje, zdravstveno stanje i izlazni kod za spremnik.

Nakon što Newman završi, skripta provjerava jesu li svi spremnici s klijentom zatvoreni, s kodom 0.

Sreća postoji

Nakon što smo prevladali gore opisane poteškoće, imali smo niz testova stabilnog rada. U testovima, svaka usluga radi kao jedna jedinica, u interakciji s bazom podataka i Amazon LocalStack.

Ovi testovi štite tim od 30+ programera od pogrešaka u aplikaciji sa složenom interakcijom 10+ mikroservisa s čestim implementacijama.

Izvor: www.habr.com

Dodajte komentar