Mikrosüsteemide automaatne testimine Dockeris pidevaks integreerimiseks

Mikroskoopiliste arhitektuuride arendamisega seotud projektides muutub CI/CD lihtsalt meeldivast võimalusest tulenevaks hädavajaduseks. Automaatne testimine on pideva integratsiooni lahutamatu osa, mis õigesti rakendatuna võimaldab meeskonnal nautida rohkem mõnusat aega peresõpradega. Vastasel juhul on projekt riskiga, et see jääb lõpuni viimata.

Saame mikrosüsteemi kogu koodi katta unit-teste ja mokkobjektega, kuid see lahendab ülesande vaid osaliselt, jättes palju küsimusi ja keerukusi, eriti andmetega töötamise testimisel. Nagu alati, on kõige teravamad probleemid – andmete järjepidevuse testimine relatsioonilises DB-s, töötamine pilveteenustega ja vale eeldused mokkobjektide kirjutamisel.

Kõik need probleemid ja veel rohkem lahendatakse terve mikrosüsteemi testimisega Docker-konteineris. Kindel eelis testide kehtivuse tagamisel on see, et testitakse samu Docker-pilte, mis lähevad tootmisse.

Selle lähenemise automatiseerimine toob kaasa mitmeid probleeme, mille lahendusi käsitletakse allpool:

  • paralleelsete ülesannete konfliktid ühes Docker-hostis;
  • konfliktid ID-de osas DB-s testimise iteratsioonide ajal;
  • mikrosüsteemide valmisoleku ootamine;
  • logide ühendamine ja edastamine välistesse süsteemidesse;
  • väljastavate HTTP-päringute testimine;
  • veebipõhiste soketite testimine (kasutades SignalR);
  • OAuth-i autentimise ja autoriseerimise testimine.

See artikkel on inspireeritud minu esitusel SECR 2019. Seega, neile, kellele ei meeldi lugeda, siin on ettekande salvestus.

Mikrosüsteemide automaatne testimine Dockeris pidevaks integreerimiseks

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

Sama skripti käivitavad nii arendajad oma Windowsi lauaarvutites kui ka Gitlab CI server Linuxis.

Uute testide rakendamine peab olema mõistlik, see ei tohi nõuda täiendavate tööriistade installimist ei arendaja arvutisse ega serverisse, kus testid käivitatakse commit'i ajal. Docker lahendab selle ülesande.

Test peab töötama kohalikul serveril järgmiste põhjuste tõttu:

  • Võrk ei ole kunagi täiesti usaldusväärne. Tuhandest päringust võib üks mitte läbida;
    Automaatne test sellisel juhul ei õnnestu, töö peatub ja tuleb otsida põhjust logidest;
  • Mõned kolmanda osapoole teenused ei luba liiga sagedasi päringuid.

Lisaks on seisu kasutamine soovimatu, kuna:

  • Seisu võib rikkuda mitte ainult halb kood, mis sellel töötab, vaid ka andmed, mida õige kood ei suuda töödelda;
  • Nii palju kui me püüdleme tagasi tuua kõik muudatused, mis testimise käigus on tehtud, võib testimise ajal midagi siiski valesti minna (muidu, miks me testime?).

Projektist ja protsessi korraldamisest

Meie ettevõte töötas välja mikroteenuste veebirakenduse, mis töötab Dockeris Amazon AWS-is. Projektis kasutati juba üksusteste, kuid sageli esines vigu, mida üksustestid ei suutnud tuvastada. Oli vajalik testida kogu mikroteenust koos andmebaasi ja Amazon teenustega.

Projektis kasutatakse standardset pideva integreerimise protsessi, mis hõlmab mikroteenuse testimist iga commit'i juures. Pärast ülesande määramist teeb arendaja mikroteenuses muudatusi, testib seda käsitsi ja käivitab kõik olemasolevad automaatsed testid. Vajadusel muudab arendaja teste. Kui probleeme ei leita, tehakse commit antud ülesande harusse. Iga commit'i järel käivitatakse serveris automaatselt testid. Üldharusse mergimine ja automaatsete testide käivitamine seal toimub pärast edukat ülevaatust. Kui üldharu testid on läbitud, uuendatakse teenust automaatselt testkeskkonnas Amazon Elastic Container Service'is (seisus). Seis on vajalik kõigile arendajatele ja testijatele, ning selle rikku mine on soovimatu. Testijad kontrollivad sellel keskkonnal fikseerimist või uut funktsiooni, tehes käsitsi teste.

Projekti arhitektuur

Mikrosüsteemide automaatne testimine Dockeris pidevaks integreerimiseks

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

SQS ja SNS

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

Kui mitu teenust loevad ühte järjekorda, siis jõuab iga sõnum vaid ühele neist. See on kasulik, kui käivitate mitu eksemplari ühest teenusest, et koormust nende vahel jagada.

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

SNS-is loote teema (topic) ja liidate selle näiteks SQS-i järjekorraga. Teemasse saab saata sõnumeid. Selle käigus saadetakse sõnum igasse järjekorda, mis on selle teema juurde liidetud. SNS-is ei ole meetodit sõnumite lugemiseks. Kui silumis- või testimisprotsessi käigus on vaja teada, mis SNS-i saadetakse, siis saab luua SQS-i järjekorra, liita selle soovitud teema juurde ja lugeda järjekorda.

Mikrosüsteemide automaatne testimine Dockeris pidevaks integreerimiseks

API Gateway

Enamik teenuseid ei ole otseselt internetist kätte saadavad. Juurdepääs toimub API Gateway kaudu, mis kontrollib juurdepääsuõigusi. See on samuti meie teenus ja selle jaoks on samuti olemas testid.

Reaalaja teavitused

Rakendus kasutab SignalR, et näidata kasutajale reaalaja teavitusi. See on rakendatud teavitusteenuses. See on otse internetist kätte saadav ja töötab ise OAuth'iga, kuna Web-sokettide toe integreerimine Gateway'sse osutus vähem otstarbekaks võrreldes OAuth'i ja teavitusteenuse integreerimisega.

Tuntud lähenemine testimisele

Ühiktestid asendavad sellised asjad nagu andmebaas, kasutades valeobjekte. Kui mikroteenus, näiteks, üritab luua kirjet tabelis, kus on väline võti, ja vastavat kirjet ei eksisteeri, ei saa päringut täita. Ühiktestid ei saa seda tuvastada.

Uues artiklis Microsoftilt soovitatakse kasutada in-memory andmebaasi ja sisse viia valeobjekte.

In-memory andmebaas on üks neist andmebaasidest, mida Entity Framework toetab. See on loodud spetsiaalselt testide jaoks. Andmed selles andmebaasis salvestatakse ainult kuni protsessi lõpetamiseni, mis seda kasutab. Sellest ei pea tabelite loomist ning andmete terviklikkust ei kontrollita.

Mokki mudelid simuleerivad asendatavat klassi vaid seni, kuni testimise arendaja mõistab selle toimimist.

Kuidas saavutada Postgresi automaatne käivitamine ja migratsiooni teostamine testi käivitamisel, ei ole Microsofti artiklis märgitud. Minu lahendus teeb seda ja peale selle ei lisata tarkvaramoodulisse mingit koodi, mis on mõeldud ainult testide jaoks.

Liigume lahenduse juurde

Arendusprotsessi käigus selgus, et ühiktestid ei ole piisavad, et kõik probleemid õigeaegselt tuvastada, seega otsustati läheneda küsimusele teisiti.

Testkeskkonna seadistamine

Esimene ülesanne on seadistada testkeskkond. Praktilised sammud mikrosüsteemi käivitamiseks:

  • Seadista testitav teenus kohalikuks keskkonnaks, määrates keskkonnamuutujates andmebaasi ja AWS-i ühenduse õigused;
  • Käivita Postgres ja teosta migratsioon, käivitades Liquibase.
    Relaatsioonilistes andmebaasides tuleb andmete talletamiseks kõigepealt andmemudelite skeem luua, teisisõnu tabelid. Rakenduse uuendamisel tuleb tabelid viia vastavusse uue versiooniga, eelistatult andmeid kaotamata. Seda nimetatakse migratsiooniks. Tabelite loomine esialgu tühjas andmebaasis on migratsiooni üksikjuhtum. Migratsiooni saab integreerida ise rakendusse. N tanto .NET-is, kui ka NodeJS-is on migratsiooniks raamistikud. Meie juhul ei ole mikrosüsteemidel andmemudelite skeemi muutmiseks õigusi ja migratsioon toimub Liquibase'i abil.
  • Käivita Amazon LocalStack. See on AWS-i teenuste rakendus, mille saab enda serveris käivitada. LocalStackile on olemas valmis pilt Docker Hubis.
  • Käivita skript vajalikud üksuste loomise jaoks LocalStackis. Shell-skriptid kasutavad AWS CLI-d.

Projekti testimiseks kasutatakse Postman. See oli varem olemas, kuid seda käidi käsitsi ja testiti rakendust, mis oli juba stendis käivitatud. See tööriist võimaldab teha suvalisi HTTP(S)-päringuid ja kontrollida, kas vastused vastavad ootustele. Päringud ühendatakse kollektsiooniks ja kogu kollektsiooni saab käivitada korraga.

Mikrosüsteemide automaatne testimine Dockeris pidevaks integreerimiseks

Kuidas töötab automaatne test

Testi ajal töötab Dockeris kõik: nii testitav teenus, Postgres, migreerimise tööriist kui ka Postman, täpsemalt, selle konsooliversioon – Newman.

Docker lahendab mitmeid probleeme:

  • Sõltumatus hosti konfiguratsioonist;
  • Sõltuvuste paigaldamine: Docker laadib pildid Docker Hubist;
  • Süsteemi algsesse seisundisse taastamine: lihtsalt eemaldame konteinerid.

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

Testi haldab shell-skript. Windowsi all testimiseks kasutame git-bashi. Niimoodi piisab ühest skriptist nii Windowsile kui ka Linuxile. Git ja Docker on kõigil projekti arendajatel paigaldatud. Git'i paigalduse käigus Windowsis paigaldatakse ka git-bash, seega on see ka kõigil olemas.

Skript täidab järgmisi samme:

  • Docker-piltide koostamine
    docker-compose build
  • Andmebaasi ja LocalStacki käivitamine
    docker-compose up -d <konteiner>
  • Andmebaasi migratsioon ja LocalStacki ettevalmistamine
    docker-compose run <konteiner>
  • Testitava teenuse käivitamine
    docker-compose up -d <teenus>
  • Testi käivitamine (Newman)
  • Kõigi konteinerite peatamine
    docker-compose down
  • Tulemuste postitamine Slacki
    Meil on vestlus, kuhu satuvad sõnumid rohelise linnukese või punase risti ja logi lingiga.

Selles etapis kasutatakse järgmisi Docker-pilte:

  • Testitav teenus on sama pilt, mis tootmises. Testimisconfiguratsioon - keskkonnamuutujate kaudu.
  • PostgreSQL, Redis ja LocalStacki jaoks kasutatakse valmis pilte Docker Hubist. Ka Liquibase ja Newman jaoks on valmis pildid. Me ehitame oma nende peal, lisades sinna meie failid.
  • LocalStacki ettevalmistamiseks kasutatakse valmis pildi AWS CLI, millest luuakse pilt, mis sisaldab skripti.

Kasutades volumes, Docker-pildi ehitamine ainult failide konteinerisse lisamiseks ei ole vajalik. Siiski ei sobi volumes meie keskkonda, sest Gitlab CI ülesanded töötavad ise konteinerites. Sellest konteinerist on võimalik Dockerit hallata, kuid volumes monteerivad kaustad ainult host-süsteemist, mitte teisest konteinerist.

Probleemid, millega võidakse silmitsi seista

Valmiduse ootus

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

Seda ülesannet lahendatakse mõnikord skripti abil wait-for-it.sh, mis ootab võimalust TCP-ühenduse loomist. Siiski võib LocalStack anda 502 Bad Gateway eksituse. Lisaks koosneb see paljusid teenuseid, ja kui üks neist on valmis, ei tähenda see, et ülejäänud oleksid samuti.

Lahendus: LocalStacki ettevalmistusskripte, mis ootavad 200 vastust nii SQSilt kui ka SNSilt.

Paralleelsete ülesannete konfliktid

Kuidas mitu testi saavad samal Docker-hostil samaaegselt töötada, peavad konteinerite ja võrkude nimed olema unikaalsed. Veelgi enam, erinevatelt harudelt sama teenuse testid võivad samuti samaaegselt töötada, seega ei piisa, kui iga compose-failis määrata oma nimed.

Lahendus: skript seab COMPOSE_PROJECT_NAME muutujale unikaalse väärtuse.

Windowsi eripärad

Dockerit Windowsis kasutades on mitmeid asju, millele soovin tähelepanu juhtida, kuna see kogemus on oluline arusaamade jaoks, mis aitavad vigu mõista.

  1. Konteineris olevatel shell-skriptidel peavad olema Linuxi lõpp-punktid.
    CR-sümbol shellis on süntaksiviga. Eksitusteate põhjal on raske mõista, et probleem on selles. Selliste skriptide redigeerimiseks Windowsis on vajalik õige tekstiredaktor. Lisaks peab versioonikontrollisüsteem olema õigesti seadistatud.

Nii seadistatakse git:

git config core.autocrlf input

  1. Git-bash emuleerib Linuxi standardkaustu ja käivitades exe-faili (sealhulgas docker.exe) asendab see absoluutseid Linuxi teid Windowsi tee-dega. Kuid see ei kehti mitte lokaalmachines asuvate teede (või konteineris asuvate teede) puhul. Sellist käitumist ei saa välja lülitada.

Lahendus: lisada tee algusesse täiendav kaldkriips: \/\/bin asemel \/bin. Linux mõistab selliseid teid, kuna tema jaoks on mitu kaldkriipsu sama, mis üks. Kuid git-bash ei tunne selliseid teid ära ega püüa neid konverteerida.

Logide väljund

Testide käivitamisel tahaksin näha logisid nii Newmanilt kui ka testitavalt teenuselt. Kuna nende logide sündmused on omavahel seotud, on nende kombineerimine ühes konsoolis oluliselt mugavam kui kahes eraldi failis. Newmanit käivitatakse läbi docker-compose run, seega jõuab selle väljund konsooli. On jäänud teha nii, et teenuse väljund jõuaks sinna samuti.

Esialgne lahendus oli teha docker-compose up ilma liputa -d, kuid kasutades shelli võimalusi, suunata see protsess tausta:

docker-compose up <service> &

See toimis seni, kuni oli vaja saata logid Dockerist kolmandale teenusele. docker-compose up lõpetas logide kuvamise konsoolis. Kuid käsk töötas docker attach.

Lahendus:

docker attach --no-stdin ${COMPOSE_PROJECT_NAME}__1 &

Tuhandete identifikaatorite konflikt testide iteratsioonide ajal

Testid käivitatakse mitme iteratsiooni jooksul. Andmebaas ei ole sel juhul tühi. Andmebaasi kirjed on unikaalsete ID-dega. Kui määrata kindlad ID-d päringutesse, saame teisel iteratsioonil konflikti.

Selle vältimiseks peavad kas ID-d olema unikaalsed või tuleb kõik testiga loodud objektid kustutada. Teatud objekte ei saa kustutada 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 muutujaväärtusega.

Koostöö LocalStackiga

Kui testitav teenus loeb SQS-rea või kirjutab sellesse, peab ka test ise töötama selle reaga.

Lahendus: päringud Postmanist LocalStacki.

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

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

Kui teenus saadab sõnumeid SNS-i, luuakse LocalStacki käivitamise etapil ka järjekord ja liitutakse selle SNS-teemaga. Edasi läheb kõik eeltooduga kokku.

Kui teenus peab järjekorrast sõnumit lugema, kirjutame selle sõnumi järjekorda eelneva testi sammus.

HTTP päringute testimine, mida saadab testitav mikroteenus

Mõned teenused töötavad HTTP kaudu millegi muuga, välja arvatud AWS, ja mõned AWS-i funktsioonid ei ole LocalStackis rakendatud.

Lahendus: sellistel juhtudel võib aidata MockServer, millel on valmis pilt Docker Hub. Oodatavad päringud ja vastused on seadistatud HTTP päringuga. API on dokumenteeritud, seega teeme päringud Postmanis.

OAuth-i autentimise ja autoriseerimise testimine

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

Kogu teenuse koostöö OAuth-i pakkujaga seisneb kahes päringus: najult taotletakse konfigureerimist /.well-known/openid-configuration, seejärel taotletakse avalikku võtit (JWKS) aadressilt, mis tuleneb konfigureerimisest. Kõik see on staatiline sisu.

Lahendus: meie testiv OAuth-i pakkuja on staatilise sisu server ja kaks faili sellel. Token genereeritakse üks kord ja kommitakse Gitisse.

SignalR-i testimise eripära

Postman ei tööta veebipartneritega. SignalR-i testimiseks loodi spetsiaalne tööriist.

SignalR kliendiks ei saa olla mitte ainult brauser. Selle jaoks on olemas .NET Core'i klienditeek. .NET Core'is kirjutatud klient loob ühenduse, läbib autentimise ja ootab teatud sõnumite järjestust. Kui saabub ootamatu sõnum või ühendus katkeb, lõpeb klient koodiga 1. Viimase oodatud sõnumi saamisel lõpeb see koodiga 0.

Samaaegselt kliendiga töötab Newman. Kliente käivitatakse mitu, et kontrollida, et sõnumid toimetatakse kõigile, kellele nad mõeldud on.

Mikrosüsteemide automaatne testimine Dockeris pidevaks integreerimiseks

Mitme kliendi käivitamiseks kasutatakse valikut —scale docker-compose käsureas.

Enne Postmani käivitamist ootab skript, kuni kõik kliendid on ühenduses.
Ootamise probleem on meile juba varem ette tulnud. Kuid seal olid serverid, siin on klient. Vajame teistsugust lähenemist.

Lahendus: konteineris oleva kliendi HealthCheck, et teatada hostis olevale skriptile oma olekust. Klient loob faili teatud teele, näiteks /healthcheck, kohe kui ühendus on loodud. HealthCheck skript dockeri failis näeb välja nii:

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

Meeskond docker inspect näitab konteineri normaalse oleku, health-oleku ja lõpetamise koodi.

Pärast Newman'i lõpetamist kontrollib skript, et kõik kliendi konteinerid on lõpetatud, ja koodiga 0.

Õnn on olemas

Pärast seda, kui oleme ületanud eespool kirjeldatud raskused, on meil olemas komplekt stabiilselt töötavaid teste. Testides töötab iga teenus tervikuna, suheldes andmebaasi ja Amazon LocalStackiga.

Need testid kaitsevad 30+ arendaja meeskonda rakenduse vigade eest, millel on keeruline koostöö 10+ mikros teenustega sagedaste juurutamiste ajal.

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster