Automatizované testování mikroslužeb v Dockeru pro nepřetržitou integraci

V projektech souvisejících s rozvojem architektury mikroslužeb se CI/CD posouvá z kategorie příjemné příležitosti do kategorie naléhavé nutnosti. Automatizované testování je nedílnou součástí neustálé integrace, kompetentní přístup může týmu poskytnout mnoho příjemných večerů s rodinou a přáteli. V opačném případě hrozí, že projekt nebude nikdy dokončen.

Je možné pokrýt celý kód mikroslužby unit testy s mock objekty, ale to problém řeší jen částečně a zanechává mnoho otázek a potíží, zejména při testování práce s daty. Jako vždy nejpalčivější je testování konzistence dat v relační databázi, testování práce s cloudovými službami a vytváření nesprávných předpokladů při psaní falešných objektů.

To vše a ještě něco navíc lze vyřešit testováním celé mikroslužby v kontejneru Docker. Nepochybnou výhodou pro zajištění platnosti testů je, že se testují stejné obrazy Dockeru, které jdou do výroby.

Automatizace tohoto přístupu představuje řadu problémů, jejichž řešení bude popsáno níže:

  • konflikty paralelních úloh ve stejném hostiteli dockeru;
  • konflikty identifikátorů v databázi během testovacích iterací;
  • čekání na připravenost mikroslužeb;
  • slučování a odesílání protokolů do externích systémů;
  • testování odchozích požadavků HTTP;
  • testování webových zásuvek (pomocí SignalR);
  • testování autentizace a autorizace OAuth.

Tento článek je založen na můj projev na SECR 2019. Takže pro ty, kteří jsou příliš líní číst, zde je záznam projevu.

Automatizované testování mikroslužeb v Dockeru pro nepřetržitou integraci

V tomto článku vám řeknu, jak pomocí skriptu spustit testovanou službu, databázi a služby Amazon AWS v Dockeru, poté testy na Postman a po jejich dokončení zastavit a odstranit vytvořené kontejnery. Testy se provádějí při každé změně kódu. Tímto způsobem zajistíme, aby každá verze správně fungovala s databází a službami AWS.

Stejný skript spouští jak samotní vývojáři na svých Windows desktopech, tak server Gitlab CI pod Linuxem.

Abychom byli oprávněni, zavedení nových testů by nemělo vyžadovat instalaci dalších nástrojů ani na počítač vývojáře, ani na server, kde se testy spouštějí v rámci commitu.Docker tento problém řeší.

Test musí běžet na lokálním serveru z následujících důvodů:

  • Síť není nikdy zcela spolehlivá. Z tisíce žádostí může jeden selhat;
    V tomto případě automatický test nebude fungovat, práce se zastaví a budete muset hledat důvod v protokolech;
  • Některé služby třetích stran nepovolují příliš časté požadavky.

Kromě toho je nežádoucí používat stojan, protože:

  • Stojan může být rozbit nejen špatným kódem, který na něm běží, ale také daty, která správný kód nedokáže zpracovat;
  • Bez ohledu na to, jak moc se snažíme vrátit zpět všechny změny provedené testem během samotného testu, něco se může pokazit (jinak proč testovat?).

O projektu a organizaci procesu

Naše společnost vyvinula mikroservisní webovou aplikaci běžící v Dockeru v cloudu Amazon AWS. Unit testy byly na projektu již použity, ale často se vyskytovaly chyby, které unit testy neodhalily. Bylo nutné otestovat celou mikroslužbu spolu s databází a službami Amazonu.

Projekt využívá standardní kontinuální integrační proces, který zahrnuje testování mikroslužby s každým potvrzením. Po přiřazení úlohy vývojář provede změny v mikroslužbě, ručně ji otestuje a spustí všechny dostupné automatizované testy. V případě potřeby vývojář testy změní. Pokud nejsou nalezeny žádné problémy, provede se potvrzení do větve tohoto problému. Po každém potvrzení se na serveru automaticky spustí testy. Ke sloučení do společné větve a spuštění automatických testů na ní dochází po úspěšné kontrole. Pokud testy na sdílené pobočce projdou, služba se automaticky aktualizuje v testovacím prostředí na Amazon Elastic Container Service (bench). Stojan je nezbytný pro všechny vývojáře a testery a není radno jej rozbíjet. Testeři v tomto prostředí zkontrolují opravu nebo novou funkci provedením ručních testů.

Architektura projektu

Automatizované testování mikroslužeb v Dockeru pro nepřetržitou integraci

Aplikace se skládá z více než deseti služeb. Některé z nich jsou napsány v .NET Core a některé v NodeJs. Každá služba běží v kontejneru Docker ve službě Amazon Elastic Container Service. Každý má svou vlastní databázi Postgres a některé mají také Redis. Neexistují žádné společné databáze. Pokud několik služeb potřebuje stejná data, jsou tato data, když se změní, přenášena do každé z těchto služeb prostřednictvím SNS (Simple Notification Service) a SQS (Amazon Simple Queue Service) a služby je ukládají do svých vlastních samostatných databází.

SQS a SNS

SQS umožňuje zařadit zprávy do fronty a číst zprávy z fronty pomocí protokolu HTTPS.

Pokud několik služeb čte jednu frontu, pak každá zpráva dorazí pouze do jedné z nich. To je užitečné při spuštění několika instancí stejné služby, aby se mezi ně rozložilo zatížení.

Pokud chcete, aby byla každá zpráva doručena více službám, musí mít každý příjemce svou vlastní frontu a pro duplikování zpráv do více front je potřeba SNS.

V SNS vytvoříte téma a přihlásíte se k jeho odběru, například fronta SQS. Můžete posílat zprávy k tématu. V tomto případě je zpráva odeslána do každé fronty přihlášené k tomuto tématu. SNS nemá metodu pro čtení zpráv. Pokud během ladění nebo testování potřebujete zjistit, co se odesílá do SNS, můžete vytvořit frontu SQS, přihlásit se k odběru požadovaného tématu a přečíst si frontu.

Automatizované testování mikroslužeb v Dockeru pro nepřetržitou integraci

Brána API

Většina služeb není přímo dostupná z internetu. Přístup je přes API Gateway, která kontroluje přístupová práva. To je také naše služba a existují pro ni testy.

Oznámení v reálném čase

Aplikace používá SignálRk zobrazení oznámení v reálném čase uživateli. Toto je implementováno v oznamovací službě. Je přístupná přímo z internetu a sama o sobě funguje s OAuth, protože se ukázalo jako nepraktické zabudovat podporu pro webové sokety do Gateway ve srovnání s integrací OAuth a notifikační služby.

Dobře známý testovací přístup

Unit testy nahrazují věci jako databáze falešnými objekty. Pokud se například mikroslužba pokusí vytvořit záznam v tabulce s cizím klíčem a záznam, na který tento klíč odkazuje, neexistuje, nelze požadavek provést. Unit testy to nemohou zjistit.

В článek od Microsoftu Navrhuje se použít databázi v paměti a implementovat falešné objekty.

In-memory databáze je jedním z DBMS podporovaných Entity Framework. Byl vytvořen speciálně pro testování. Data v takové databázi jsou uložena pouze do ukončení procesu, který ji využívá. Nevyžaduje vytváření tabulek a nekontroluje integritu dat.

Mock objekty modelují třídu, kterou nahrazují, pouze do té míry, do jaké vývojář testu rozumí, jak to funguje.

Jak přimět Postgres, aby se automaticky spustil a provedl migraci při spuštění testu, není uvedeno v článku Microsoftu. Moje řešení to dělá a navíc do samotné mikroslužby nepřidává žádný kód speciálně pro testy.

Přejděme k řešení

Během vývojového procesu se ukázalo, že testy jednotek nestačí k včasnému nalezení všech problémů, a proto bylo rozhodnuto přistoupit k této problematice z jiného úhlu.

Nastavení testovacího prostředí

Prvním úkolem je nasazení testovacího prostředí. Kroky potřebné ke spuštění mikroslužby:

  • Nakonfigurujte testovanou službu pro místní prostředí, v proměnných prostředí zadejte podrobnosti pro připojení k databázi a AWS;
  • Spusťte Postgres a proveďte migraci spuštěním Liquibase.
    V relačních DBMS musíte před zápisem dat do databáze vytvořit datové schéma, jinými slovy tabulky. Při aktualizaci aplikace musí být tabulky převedeny do podoby používané v nové verzi a pokud možno bez ztráty dat. Tomu se říká migrace. Vytváření tabulek v původně prázdné databázi je zvláštní případ migrace. Migraci lze zabudovat do samotné aplikace. .NET i NodeJS mají migrační rámce. V našem případě jsou z bezpečnostních důvodů mikroslužby zbaveny práva na změnu datového schématu a migrace se provádí pomocí Liquibase.
  • Spusťte Amazon LocalStack. Jedná se o implementaci služeb AWS pro domácí provoz. Na Docker Hubu je hotový obrázek pro LocalStack.
  • Spusťte skript a vytvořte potřebné entity v LocalStack. Shell skripty používají AWS CLI.

Používá se pro testování na projektu Listonoš. Existoval již dříve, ale byl spuštěn ručně a testoval aplikaci již nasazenou na stánku. Tento nástroj vám umožňuje provádět libovolné HTTP(S) požadavky a kontrolovat, zda odpovědi odpovídají očekávání. Dotazy jsou sloučeny do kolekce a lze spustit celou kolekci.

Automatizované testování mikroslužeb v Dockeru pro nepřetržitou integraci

Jak funguje automatický test?

Během testu v Dockeru vše funguje: testovaná služba, Postgres, migrační nástroj i Postman, respektive jeho konzolová verze – Newman.

Docker řeší řadu problémů:

  • Nezávislost na konfiguraci hostitele;
  • Instalace závislostí: Docker stahuje obrázky z Docker Hub;
  • Vrácení systému do původního stavu: jednoduché vyjmutí nádob.

docker-compose sjednocuje kontejnery do virtuální sítě izolované od internetu, ve které se kontejnery navzájem nalézají podle doménových jmen.

Test je řízen skriptem shellu. Pro spuštění testu na Windows používáme git-bash. Pro Windows i Linux tedy stačí jeden skript. Git a Docker instalují všichni vývojáři projektu. Při instalaci Gitu na Windows se nainstaluje git-bash, takže to má taky každý.

Skript provádí následující kroky:

  • Vytváření obrázků dokovacích stanic
    docker-compose build
  • Spuštění databáze a LocalStack
    docker-compose up -d <контейнер>
  • Migrace databáze a příprava LocalStacku
    docker-compose run <контейнер>
  • Spuštění testované služby
    docker-compose up -d <сервис>
  • Spuštění testu (Newman)
  • Zastavení všech kontejnerů
    docker-compose down
  • Odesílání výsledků do Slack
    Máme chat, kam chodí zprávy se zeleným zaškrtnutím nebo červeným křížkem a odkazem na log.

Následující obrázky Docker se týkají těchto kroků:

  • Testovaná služba je stejný obrázek jako pro produkci. Konfigurace pro test je prostřednictvím proměnných prostředí.
  • Pro Postgres, Redis a LocalStack se používají hotové obrázky z Docker Hub. K dispozici jsou také hotové obrázky pro Liquibase a Newman. Stavíme naše na jejich kostře a přidáváme tam naše soubory.
  • K přípravě LocalStack použijete hotový AWS CLI image a vytvoříte image obsahující skript na jeho základě.

Použití objemy, nemusíte vytvářet obraz Dockeru jen pro přidání souborů do kontejneru. Svazky však nejsou vhodné pro naše prostředí, protože samotné úlohy Gitlab CI běží v kontejnerech. Docker můžete ovládat z takového kontejneru, ale svazky připojují pouze složky z hostitelského systému, nikoli z jiného kontejneru.

Problémy, kterým je třeba čelit

Čekání na připravenost

Když běží kontejner se službou, neznamená to, že je připraven přijímat připojení. Musíte počkat na pokračování připojení.

Tento problém se někdy řeší pomocí skriptu wait-for-it.sh, který čeká na příležitost navázat TCP spojení. LocalStack však může vyvolat chybu 502 Bad Gateway. Navíc se skládá z mnoha služeb, a pokud je jedna z nich připravena, o ostatních to nic nevypovídá.

rozhodnutí: Zřizovací skripty LocalStack, které čekají na odpověď 200 od SQS i SNS.

Konflikty paralelních úloh

Na stejném hostiteli Dockeru může současně běžet více testů, takže názvy kontejnerů a sítí musí být jedinečné. Testy z různých poboček téže služby mohou navíc běžet současně, takže nestačí zapsat jejich jména do každého nového souboru.

rozhodnutí: Skript nastaví proměnnou COMPOSE_PROJECT_NAME na jedinečnou hodnotu.

Funkce Windows

Při používání Dockeru v systému Windows chci upozornit na řadu věcí, protože tyto zkušenosti jsou důležité pro pochopení toho, proč dochází k chybám.

  1. Skripty shellu v kontejneru musí mít konce řádků pro Linux.
    Symbol shellu CR je syntaktická chyba. Z chybové hlášky je těžké poznat, že tomu tak je. Při úpravě takových skriptů ve Windows potřebujete správný textový editor. Kromě toho musí být správně nakonfigurován systém správy verzí.

Takto je git nakonfigurován:

git config core.autocrlf input

  1. Git-bash emuluje standardní linuxové složky a při volání exe souboru (včetně docker.exe) nahradí absolutní linuxové cesty cestami Windows. To však nedává smysl pro cesty, které nejsou na místním počítači (nebo cesty v kontejneru). Toto chování nelze zakázat.

rozhodnutí: přidat další lomítko na začátek cesty: //bin místo /bin. Linux takovým cestám rozumí, pro něj je několik lomítek stejných jako jedna. Ale git-bash takové cesty nerozpozná a nepokouší se je převést.

Výstup protokolu

Při spouštění testů bych rád viděl protokoly z Newmana i testované služby. Vzhledem k tomu, že události těchto protokolů jsou vzájemně propojeny, je jejich sloučení v jedné konzoli mnohem pohodlnější než dva samostatné soubory. Newman se spustí přes docker-compose run, a tak jeho výstup skončí v konzoli. Nezbývá než zajistit, aby tam šel i výstup služby.

Původní řešení bylo udělat docker-compose up žádná vlajka -d, ale pomocí schopností shellu odešlete tento proces na pozadí:

docker-compose up <service> &

To fungovalo, dokud nebylo nutné odeslat protokoly z Dockeru do služby třetí strany. docker-compose up přestalo odesílat protokoly do konzole. Tým však fungoval připevněte docker.

rozhodnutí:

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

Konflikt identifikátorů během testovacích iterací

Testy probíhají v několika iteracích. Databáze není vyčištěna. Záznamy v databázi mají unikátní ID. Pokud zapíšeme konkrétní ID do požadavků, dostaneme konflikt při druhé iteraci.

Aby se tomu zabránilo, musí být ID buď jedinečné, nebo musí být odstraněny všechny objekty vytvořené testem. Některé objekty nelze smazat z důvodu požadavků.

rozhodnutí: generování GUID pomocí skriptů Postman.

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

Poté použijte symbol v dotazu {{myUUID}}, která bude nahrazena hodnotou proměnné.

Spolupráce přes LocalStack

Pokud testovaná služba čte nebo zapisuje do fronty SQS, pak pro ověření musí samotný test pracovat s touto frontou.

rozhodnutí: požadavky od Postmana na LocalStack.

Rozhraní API služeb AWS je zdokumentováno, což umožňuje provádět dotazy bez sady SDK.

Pokud služba zapisuje do fronty, pak ji přečteme a zkontrolujeme obsah zprávy.

Pokud služba odesílá zprávy SNS, ve fázi přípravy LocalStack také vytvoří frontu a přihlásí se k odběru tohoto tématu SNS. Pak se vše srovná s tím, co bylo popsáno výše.

Pokud služba potřebuje přečíst zprávu z fronty, pak v předchozím testovacím kroku zapíšeme tuto zprávu do fronty.

Testování požadavků HTTP pocházejících z testované mikroslužby

Některé služby fungují přes HTTP s něčím jiným než AWS a některé funkce AWS nejsou implementovány v LocalStack.

rozhodnutí: v těchto případech může pomoci MockServer, která má v sobě hotový obrázek Docker hub. Očekávané požadavky a odpovědi na ně jsou konfigurovány HTTP požadavkem. API je zdokumentováno, takže požadavky posíláme od Postmana.

Testování OAuth ověřování a autorizace

Používáme OAuth a Webové tokeny JSON (JWT). Test vyžaduje poskytovatele OAuth, kterého můžeme spustit lokálně.

Veškerá interakce mezi službou a poskytovatelem OAuth sestává ze dvou požadavků: zaprvé je požadována konfigurace /.známá/openid-konfiguracea poté je požadován veřejný klíč (JWKS) na adrese z konfigurace. To vše je statický obsah.

rozhodnutí: Náš testovací poskytovatel OAuth je server se statickým obsahem a dva soubory na něm. Token se vygeneruje jednou a odevzdá Gitu.

Vlastnosti testování SignalR

Pošťák nepracuje s websockety. Pro testování SignalR byl vytvořen speciální nástroj.

Klient SignalR může být více než jen prohlížeč. Existuje pro něj klientská knihovna pod .NET Core. Klient napsaný v .NET Core naváže spojení, je ověřen a čeká na konkrétní sekvenci zpráv. Pokud je přijata neočekávaná zpráva nebo je ztraceno spojení, klient skončí s kódem 1. Pokud je přijata poslední očekávaná zpráva, klient skončí s kódem 0.

Newman pracuje současně s klientem. Spustí se několik klientů, aby zkontrolovali, zda jsou zprávy doručovány každému, kdo je potřebuje.

Automatizované testování mikroslužeb v Dockeru pro nepřetržitou integraci

Chcete-li spustit více klientů, použijte volbu --měřítko na příkazovém řádku docker-compose.

Před spuštěním skript Postman čeká, až všichni klienti navážou spojení.
S problémem čekání na spoj jsme již narazili. Ale byly tam servery a tady je klient. Je potřeba jiný přístup.

rozhodnutí: klient v kontejneru používá mechanismus Zdravotní prohlídkainformovat skript na hostiteli o jeho stavu. Klient vytvoří soubor na určité cestě, řekněme /healthcheck, jakmile je navázáno spojení. Skript HealthCheck v souboru dockeru vypadá takto:

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

Tým docker zkontrolovat Zobrazuje normální stav, zdravotní stav a výstupní kód pro kontejner.

Po dokončení Newman skript zkontroluje, že všechny kontejnery s klientem byly ukončeny s kódem 0.

Happinnes existuje

Poté, co jsme překonali výše popsané potíže, jsme měli sadu testů stabilního běhu. V testech každá služba funguje jako jedna jednotka, interaguje s databází a Amazon LocalStack.

Tyto testy chrání tým 30+ vývojářů před chybami v aplikaci se složitou interakcí 10+ mikroslužeb s častým nasazením.

Zdroj: www.habr.com

Přidat komentář