Automatsko testiranje mikroservisa u Dockeru za kontinuiranu integraciju

U projektima koji se odnose na razvoj mikroservisne arhitekture, CI/CD prelazi iz kategorije ugodne prilike u kategoriju hitne potrebe. Automatsko testiranje je sastavni dio kontinuirane integracije, kompetentan pristup kojem timu može pružiti mnogo ugodnih večeri sa porodicom i prijateljima. U suprotnom postoji opasnost da projekat nikada neće biti završen.

Moguće je pokriti cijeli mikroservisni kod jediničnim testovima sa lažnim objektima, ali to samo djelomično rješava problem i ostavlja mnoga pitanja i poteškoće, posebno kod testiranja rada s podacima. Kao i uvijek, najhitnije su testiranje konzistentnosti podataka u relacijskoj bazi podataka, testiranje rada s uslugama u oblaku i stvaranje pogrešnih pretpostavki prilikom pisanja lažnih objekata.

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

Automatizacija ovog pristupa predstavlja niz problema, čije će rješenje biti opisano u nastavku:

  • sukobi paralelnih zadataka u istom docker hostu;
  • sukobi identifikatora u bazi podataka tokom iteracija testa;
  • čekanje da mikroservis bude spreman;
  • spajanje i izlaz dnevnika u vanjske sisteme;
  • testiranje odlaznih HTTP zahtjeva;
  • testiranje web socketa (koristeći SignalR);
  • testiranje OAuth autentifikacije i autorizacije.

Ovaj članak je zasnovan na moj govor na SECR 2019. Za one koji su previše lijeni da čitaju, evo snimka govora.

Automatsko testiranje mikroservisa u Dockeru za kontinuiranu integraciju

U ovom članku ću vam reći kako koristiti skriptu za pokretanje servisa koji se testira, baze podataka i Amazon AWS servisa u Dockeru, zatim testova na Postman-u i, nakon što se završe, zaustaviti i izbrisati kreirane kontejnere. Testovi se izvršavaju svaki put kada se kod promijeni. Na ovaj način osiguravamo da svaka verzija ispravno radi sa AWS bazom podataka i uslugama.

Istu skriptu pokreću i sami programeri na svojim Windows desktopima i Gitlab CI server pod Linuxom.

Da bi se opravdalo, uvođenje novih testova ne bi trebalo da zahteva instalaciju dodatnih alata ni na računar programera ni na server gde se testovi pokreću na urezivanje. Docker rešava ovaj problem.

Test se mora izvoditi na lokalnom serveru iz sljedećih razloga:

  • Mreža nikada nije potpuno pouzdana. Od hiljadu zahtjeva, jedan može propasti;
    U ovom slučaju, automatski test neće raditi, rad će prestati, a razlog ćete morati tražiti u zapisnicima;
  • Neki servisi trećih strana ne dozvoljavaju 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;
  • Koliko god se trudili da vratimo sve promjene koje je test napravio tokom samog testa, nešto može poći naopako (inače, zašto testirati?).

O projektu i organizaciji procesa

Naša kompanija je razvila mikroservisnu web aplikaciju koja radi u Dockeru u Amazon AWS oblaku. Jedinični testovi su već korišteni na projektu, ali su se često javljale greške koje jedinični testovi nisu otkrili. Bilo je potrebno testirati cijeli mikroservis zajedno s bazom podataka i Amazon servisima.

Projekat koristi standardni kontinuirani proces integracije, koji uključuje testiranje mikroservisa pri svakom urezivanju. Nakon dodjeljivanja zadatka, programer unosi izmjene u mikroservis, testira ga ručno i pokreće sve dostupne automatizirane testove. Ako je potrebno, programer mijenja testove. Ako se ne pronađu problemi, vrši se urezivanje na granu ovog izdanja. Nakon svakog urezivanja, testovi se automatski pokreću na serveru. Spajanje u zajedničku granu i pokretanje automatskih testova na njoj se dešava nakon uspješnog pregleda. Ako testovi na dijeljenoj grani prođu, usluga se automatski ažurira u testnom okruženju na Amazon Elastic Container Service (bench). Stalak je neophodan svim programerima i testerima i nije preporučljivo da ga razbijaju. Testeri u ovom okruženju provjeravaju ispravku ili novu funkciju izvodeći ručne testove.

Arhitektura projekta

Automatsko testiranje mikroservisa u Dockeru za kontinuiranu integraciju

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

SQS i SNS

SQS vam omogućava da stavite poruke u red čekanja i čitate poruke iz reda koristeći HTTPS protokol.

Ako nekoliko servisa čita jedan red čekanja, onda svaka poruka stiže samo jednom od njih. Ovo je korisno kada se pokreće nekoliko instanci iste usluge za raspodjelu opterećenja između njih.

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

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 metod za čitanje poruka. Ako tokom otklanjanja grešaka ili testiranja trebate saznati šta se šalje SNS-u, možete kreirati SQS red, pretplatiti ga na željenu temu i pročitati red.

Automatsko testiranje mikroservisa u Dockeru za kontinuiranu integraciju

API Gateways

Većina usluga nije direktno dostupna sa Interneta. Pristup je preko API Gateway-a, koji provjerava prava pristupa. Ovo je također naša usluga, a postoje i testovi za nju.

Obavještenja u realnom vremenu

Aplikacija koristi SignalRza prikaz obavještenja u realnom vremenu korisniku. Ovo je implementirano u servisu obavještavanja. Dostupan je direktno sa Interneta i sam radi sa OAuth-om, jer se pokazalo nepraktičnim izgraditi podršku za Web utičnice u Gateway, u poređenju sa integracijom OAutha i servisa obaveštenja.

Dobro poznati pristup testiranju

Jedinični testovi zamjenjuju stvari poput baze podataka lažnim objektima. Ako mikroservis, na primjer, pokuša kreirati zapis u tabeli sa stranim ključem, a zapis na koji taj ključ upućuje ne postoji, tada zahtjev ne može biti dovršen. 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 je jedan od DBMS-ova koje podržava Entity Framework. Napravljen je posebno za testiranje. Podaci u takvoj bazi podataka se pohranjuju samo dok se proces koji je koristi ne završi. Ne zahtijeva kreiranje tabela i ne provjerava integritet podataka.

Lažni objekti modeliraju klasu koju zamjenjuju samo u mjeri u kojoj programer testa razumije kako ona funkcionira.

Kako natjerati Postgres da se automatski pokrene i izvrši migracije kada pokrenete test nije navedeno u Microsoftovom članku. Moje rješenje to čini i, osim toga, ne dodaje nikakav kod posebno za testove u samu mikroservis.

Pređimo na rješenje

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

Postavljanje testnog okruženja

Prvi zadatak je implementacija testnog okruženja. Koraci potrebni za pokretanje mikroservisa:

  • Konfigurišite uslugu koja se testira za lokalno okruženje, navedite detalje za povezivanje na bazu podataka i AWS u varijablama okruženja;
  • Pokrenite Postgres i izvršite migraciju pokretanjem Liquibase.
    U relacionim DBMS-ovima, prije pisanja podataka u bazu podataka, potrebno je kreirati šemu podataka, drugim riječima, tabele. Prilikom ažuriranja aplikacije, tabele se moraju dovesti u formu koju koristi nova verzija, i po mogućnosti, bez gubljenja podataka. To se zove migracija. Kreiranje tabela u početno praznoj bazi podataka je poseban 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, mikroservisima je oduzeto pravo promjene šeme podataka, a migracija se vrši pomoću Liquibase-a.
  • Pokrenite Amazon LocalStack. Ovo je implementacija AWS usluga za rad kod kuće. Postoji gotova slika za LocalStack na Docker Hub-u.
  • Pokrenite skriptu da kreirate potrebne entitete u LocalStacku. Shell skripte koriste AWS CLI.

Koristi se za testiranje na projektu Poštar. Postojala je i ranije, ali je pokrenuta ručno i testirana aplikacija koja je već postavljena na štandu. Ovaj alat vam omogućava da napravite proizvoljne HTTP(S) zahtjeve i provjerite da li odgovori odgovaraju očekivanjima. Upiti se kombinuju u kolekciju i cijela kolekcija se može pokrenuti.

Automatsko testiranje mikroservisa u Dockeru za kontinuiranu integraciju

Kako funkcionira automatski test?

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

Docker rješava niz problema:

  • Nezavisnost od konfiguracije hosta;
  • Instaliranje zavisnosti: Docker preuzima slike sa Docker Hub-a;
  • Vraćanje sistema u prvobitno stanje: jednostavno uklanjanje kontejnera.

Docker-compose objedinjuje kontejnere u virtuelnu mrežu, izolovanu od Interneta, u kojoj se kontejneri nalaze po nazivima domena.

Test se kontroliše pomoću shell skripte. Za pokretanje testa na Windows-u koristimo git-bash. Dakle, jedna skripta je dovoljna i za Windows i za Linux. Git i Docker instaliraju svi programeri na projektu. Kada instalirate Git na Windows, instalira se git-bash, tako da svi imaju i to.

Skripta izvodi sljedeće korake:

  • Izrada Docker slika
    docker-compose build
  • Pokretanje baze podataka i LocalStack-a
    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 u koji idu poruke sa zelenom kvačicom ili crvenim krstom i vezom do dnevnika.

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

  • Usluga koja se testira je ista slika kao i za proizvodnju. Konfiguracija za test je preko varijabli okruženja.
  • Za Postgres, Redis i LocalStack koriste se gotove slike iz Docker Hub-a. Postoje i gotove slike za Liquibase i Newman. Mi gradimo naše na njihovom kosturu, dodajući tamo naše fajlove.
  • Da biste pripremili LocalStack, koristite gotovu AWS CLI sliku i kreirate sliku koja sadrži skriptu zasnovanu na njoj.

Koristeći volumena, ne morate da pravite Docker sliku samo da biste dodali datoteke u kontejner. Međutim, volumeni nisu prikladni za naše okruženje jer se sami Gitlab CI zadaci izvode u kontejnerima. Možete kontrolisati Docker iz takvog kontejnera, ali volumeni montiraju samo fascikle sa glavnog sistema, a ne iz drugog kontejnera.

Problemi na koje možete naići

Čeka se spremnost

Kada je kontejner sa uslugom pokrenut, to ne znači da je spreman da prihvati veze. Morate sačekati da se veza nastavi.

Ovaj problem se ponekad rješava korištenjem skripte čekaj-za-it.sh, koji čeka priliku da uspostavi TCP vezu. Međutim, LocalStack može baciti grešku 502 Bad Gateway. Osim toga, sastoji se od mnogih servisa, a ako je jedan od njih spreman, to ne govori ništa o ostalima.

odluka: LocalStack skripte za obezbjeđivanje koje čekaju 200 odgovora i od SQS-a i od SNS-a.

Konflikti paralelnih zadataka

Više testova može se izvoditi istovremeno na istom Docker hostu, tako da nazivi kontejnera i mreže moraju biti jedinstveni. Štaviše, testovi iz različitih grana istog servisa se takođe mogu izvoditi istovremeno, tako da nije dovoljno upisati njihova imena u svaku datoteku za sastavljanje.

odluka: Skripta postavlja varijablu COMPOSE_PROJECT_NAME na jedinstvenu vrijednost.

Windows funkcije

Postoji nekoliko stvari na koje želim da ukažem kada koristite Docker na Windows-u, jer su ova iskustva važna za razumevanje zašto dolazi do grešaka.

  1. Shell skripte u kontejneru moraju imati završetke Linux reda.
    Shell CR simbol je sintaktička greška. Iz poruke o grešci je teško zaključiti da je to slučaj. Kada uređujete takve skripte na Windows-u, potreban vam je odgovarajući uređivač teksta. Osim toga, sistem kontrole verzija mora biti pravilno konfiguriran.

Ovako je git konfigurisan:

git config core.autocrlf input

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

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

Izlaz dnevnika

Prilikom pokretanja testova, želio bih vidjeti dnevnike i Newmana i usluge koja se testira. Budući da su događaji ovih dnevnika međusobno povezani, njihovo kombiniranje u jednoj konzoli je mnogo zgodnije od dvije odvojene datoteke. Newman lansira preko docker-compose pokretanje, i tako njegov izlaz završava u konzoli. Sve što ostaje je osigurati da izlaz usluge također ide tamo.

Prvobitno rješenje je bilo učiniti docker-compose up 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 slati zapisnike iz Dockera na uslugu treće strane. docker-compose up prestao sa slanjem dnevnika na konzolu. Međutim, tim je radio Priključni priključak.

odluka:

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

Sukob identifikatora tokom iteracija testa

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

Da bi se to izbjeglo, ili ID-ovi moraju biti jedinstveni, ili svi objekti kreirani testom moraju biti obrisani. Neki objekti se 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 koristite simbol u upitu {{myUUID}}, koji će biti zamijenjen vrijednošću varijable.

Saradnja preko LocalStack-a

Ako servis koji se testira čita ili upisuje u SQS red, onda da bi se to potvrdilo, sam test mora također raditi s ovim redom.

odluka: zahtjevi od Poštara do LocalStack-a.

API za AWS usluge je dokumentovan, omogućavajući postavljanje upita bez SDK-a.

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

Ako usluga šalje poruke SNS-u, u fazi pripreme LocalStack takođe kreira red čekanja i prijavljuje se na ovu SNS temu. Onda se sve svodi na ono što je gore opisano.

Ako servis treba da pročita poruku iz reda, onda u prethodnom testnom koraku upisujemo ovu poruku u red.

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

Neke usluge rade preko HTTP-a sa nečim drugim osim AWS-a, a neke AWS funkcije nisu implementirane u LocalStack-u.

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 konfiguriraju se HTTP zahtjevom. API je dokumentiran, tako da šaljemo zahtjeve od Postmana.

Testiranje OAuth autentifikacije i autorizacije

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

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

odluka: Naš testni OAuth provajder je server statičkog sadržaja i dva fajla na njemu. Token se generira jednom i predaje Gitu.

Karakteristike SignalR testiranja

Poštar ne radi sa websockets. Stvoren je poseban alat za testiranje SignalR-a.

SignalR klijent može biti više od običnog pretraživača. Postoji klijentska biblioteka za to pod .NET Core. Klijent, napisan u .NET Core, uspostavlja vezu, provjerava autentičnost i čeka određeni niz poruka. Ako se primi neočekivana poruka ili se veza izgubi, klijent izlazi s kodom 1. Ako je primljena posljednja očekivana poruka, klijent izlazi s kodom 0.

Newman radi istovremeno sa klijentom. Pokreće se nekoliko klijenata kako bi se provjerilo da li se poruke dostavljaju svima kojima su potrebne.

Automatsko testiranje mikroservisa u Dockeru za kontinuiranu integraciju

Za pokretanje više klijenata koristite opciju --skala na komandnoj liniji docker-compose.

Prije pokretanja, Postman skripta čeka da svi klijenti uspostave veze.
Već smo se susreli sa problemom čekanja veze. Ali bilo je servera, a evo i klijenta. Potreban je drugačiji pristup.

odluka: klijent u kontejneru koristi mehanizam Provjera zdravljainformirati skriptu na hostu o njenom statusu. Klijent kreira datoteku na određenoj putanji, recimo /healthcheck, čim se veza uspostavi. HealthCheck skripta u docker datoteci izgleda ovako:

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

tim docker inspect Prikazuje normalan status, zdravstveni status i izlazni kod za kontejner.

Nakon što Newman završi, skripta provjerava da li su svi kontejneri s klijentom prekinuti, s kodom 0.

Happinnes postoji

Nakon što smo savladali gore opisane poteškoće, imali smo set testova stabilnog rada. U testovima, svaki servis radi kao jedna jedinica, u interakciji s bazom podataka i Amazon LocalStack-om.

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

izvor: www.habr.com

Dodajte komentar