Автоматизирано тестване на микроуслуги в Docker за непрекъсната интеграция

В проектите за микросервизна архитектура CI/CD се превръща от добра възможност в задължителна. Автоматизираното тестване е неразделна част от непрекъснатата интеграция, компетентният подход към който може да даде на екипа много приятни вечери със семейството и приятелите. В противен случай има риск проектът никога да не бъде завършен.

Възможно е да се покрие целия код на микроуслугата с модулни тестове с фалшиви обекти, но това само частично решава проблема и оставя много въпроси и трудности, особено при тестване на работа с данни. Както винаги, най-острите са тестването на съгласуваността на данните в релационна база данни, тестване на работа с облачни услуги и неправилни предположения при писане на фалшиви обекти.

Всичко това и още малко се решава чрез тестване на цялата микроуслуга в Docker контейнер. Безспорното предимство за гарантиране на валидността на тестовете е, че се тестват същите изображения на Docker, които влизат в производство.

Автоматизирането на този подход създава редица проблеми, чието решение ще бъде описано по-долу:

  • конфликти на паралелни задачи в един докер хост;
  • конфликти на идентификатори в базата данни по време на тестови итерации;
  • изчакване готовността на микроуслугите;
  • консолидиране и извеждане на логове към външни системи;
  • тестване на изходящи HTTP заявки;
  • тестване на уеб сокет (с помощта на SignalR);
  • тестване на OAuth удостоверяване и оторизация.

Тази статия се основава на моята реч на SECR 2019. Така че за тези, които са твърде мързеливи да четат, ето и записа на речта.

Автоматизирано тестване на микроуслуги в Docker за непрекъсната интеграция

В тази статия ще ви разкажа как да използвате скрипт, за да стартирате тестовата услуга, базата данни и услугите на Amazon AWS в Docker, след това тестовете на Postman и след като приключат, спрете и изтрийте създадените контейнери. Тестовете се изпълняват при всяка промяна на кода. По този начин се уверяваме, че всяка версия работи правилно с базата данни и услугите на AWS.

Същият скрипт се изпълнява както от самите разработчици на техните настолни компютри с Windows, така и от Gitlab CI сървъра под Linux.

За да бъде оправдано въвеждането на нови тестове, то не трябва да изисква инсталирането на допълнителни инструменти нито на машината за разработка, нито на сървъра, където тестовете се изпълняват при комит.Docker решава този проблем.

Тестът трябва да се изпълнява на локален сървър поради следните причини:

  • Мрежата не е напълно надеждна. От хиляда молби една може да се провали;
    В този случай автоматичният тест няма да премине, работата ще спре, ще трябва да потърсите причината в регистрационните файлове;
  • Твърде честите заявки не са разрешени от някои услуги на трети страни.

Освен това е нежелателно да използвате стойката, защото:

  • Кабината може да бъде повредена не само от лош код, работещ върху нея, но и от данни, които правилният код не може да обработи;
  • Без значение колко упорито се опитваме да върнем всички промени, направени от теста по време на самия тест, нещо може да се обърка (в противен случай защо е тестът?).

За проекта и организацията на процеса

Нашата компания разработваше уеб приложение за микросервизи, работещо на Docker в облака на Amazon AWS. Проектът вече използва модулни тестове, но често имаше грешки, които модулните тестове не откриваха. Беше необходимо да се тества цялата микроуслуга заедно с базата данни и услугите на Amazon.

Проектът използва стандартен непрекъснат процес на интеграция, който включва тестване на микроуслугата с всеки ангажимент. След възлагане на задача, разработчикът прави промени в микроуслугата, тества я ръчно и изпълнява всички налични автоматизирани тестове. Ако е необходимо, разработчикът променя тестовете. Ако не бъдат намерени проблеми, се прави ангажимент към клона на тази задача. След всеки комит, тестовете се изпълняват автоматично на сървъра. Обединяването в общ клон и стартирането на автоматични тестове върху него става след успешен преглед. Ако тестовете на общия клон преминат успешно, услугата се актуализира автоматично в тестовата среда на Amazon Elastic Container Service (bench). Стойката е необходима за всички разработчици и тестери и е нежелателно да се счупи. Тестващите в тази среда тестват корекция или нова функция, като изпълняват ръчни тестове.

Архитектура на проекта

Автоматизирано тестване на микроуслуги в Docker за непрекъсната интеграция

Приложението се състои от повече от десет услуги. Някои от тях са написани на .NET Core, а други на NodeJs. Всяка услуга работи в Docker контейнер в Amazon Elastic Container Service. Всеки има собствена база данни на Postgres, а някои имат и Redis. Няма споделени бази. Ако няколко услуги се нуждаят от едни и същи данни, тогава тези данни се предават на всяка от тези услуги чрез SNS (Simple Notification Service) и SQS (Amazon Simple Queue Service) в момента на тяхната промяна и услугите ги записват в отделните си бази данни.

SQS и SNS

SQS ви позволява да поставяте съобщения в опашка през HTTPS и да четете съобщения от опашката.

Ако няколко услуги четат една и съща опашка, тогава всяко съобщение идва само до една от тях. Това е полезно, когато изпълнявате множество копия на една и съща услуга, за да разпределите натоварването между тях.

Ако искате всяко съобщение да бъде доставено до множество услуги, всеки получател трябва да има своя собствена опашка и SNS е необходим за дублиране на съобщения в множество опашки.

В SNS създавате тема и се абонирате за нея, например SQS опашка. Можете да изпращате съобщения до темата. В този случай съобщението се изпраща до всяка опашка, абонирана за тази тема. SNS няма метод за четене на съобщения. Ако по време на отстраняване на грешки или тестване трябва да знаете какво се изпраща на SNS, тогава можете да създадете SQS опашка, да се абонирате за желаната тема и да прочетете опашката.

Автоматизирано тестване на микроуслуги в Docker за непрекъсната интеграция

API шлюз

Повечето услуги не са директно достъпни от интернет. Достъпът е през API Gateway, който проверява правата за достъп. Това също е наша услуга и за нея има тестове.

Известия в реално време

Приложение използва SignalRза показване на известия в реално време на потребителя. Това е реализирано в услугата за уведомяване. Той е достъпен директно от Интернет и работи със самия OAuth, тъй като интегрирането на поддръжката на уеб сокет в Gateway се оказа непрактично в сравнение с интегрирането на OAuth и услугата за уведомяване.

Известен подход за тестване

Единичните тестове заместват макетни обекти за неща като база данни. Ако микроуслуга, например, се опита да създаде запис в таблица с външен ключ и записът, към който се отнася този ключ, не съществува, тогава заявката не може да бъде изпълнена. Единичните тестове не могат да открият това.

В статия от Microsoft предлага се да се използва база в паметта и да се внедряват фалшиви обекти.

База в паметта - това е една от СУБД, които Entity Framework поддържа. Той е създаден специално за тестване. Данните в такава база данни се съхраняват само до края на процеса, който я използва. Не е необходимо да създава таблици и целостта на данните не се проверява.

Фалшивите обекти моделират замествания клас само доколкото разработчикът на теста разбира как работи.

Как да накарате Postgres автоматично да стартира и стартира миграцията, когато стартирате теста, не е разгледано в статията от Microsoft. Моето решение прави това и в допълнение към самия микросклад не се добавя код специално за тестове.

Да преминем към решението

По време на процеса на разработка стана ясно, че модулните тестове не са достатъчни, за да открият всички проблеми навреме, така че беше решено да се подходи към този въпрос от различен ъгъл.

Настройка на тестова среда

Първата задача е да се внедри тестовата среда. Стъпки, които са необходими за стартиране на микроуслугата:

  • Настройте тестваната услуга за локална среда, променливите на средата уточняват подробностите за свързване към базата данни и AWS;
  • Стартирайте Postgres и мигрирайте, като стартирате Liquibase.
    В релационните СУБД, преди да запишете данни в базата данни, трябва да създадете схема на данни, с други думи, таблици. Когато актуализирате приложение, таблиците трябва да бъдат преобразувани във формата, използвана от новата версия, и за предпочитане без загуба на данни. Това се нарича миграция. Създаването на таблици в първоначално празна база данни е специален случай на миграция. Миграцията може да бъде вградена в самото приложение. Както .NET, така и NodeJS имат рамки за миграция. В нашия случай, от съображения за сигурност, микроуслугите са лишени от правото да променят схемата на данните и миграцията се извършва с помощта на Liquibase.
  • Стартирайте Amazon LocalStack. Това е внедряване на AWS услуги за работа у дома. Има готово изображение за LocalStack в Docker Hub.
  • Стартирайте скрипт, за да създадете необходимите обекти в LocalStack. Shell скриптовете използват AWS CLI.

За тестване на проекта използвайте пощаджия. Беше и преди, но се стартира ръчно и се тества приложението, което вече е разположено на щанда. Този инструмент ви позволява да правите произволни HTTP(S) заявки и да проверявате дали отговорите отговарят на вашите очаквания. Заявките се комбинират в колекция и цялата колекция може да се изпълнява.

Автоматизирано тестване на микроуслуги в Docker за непрекъсната интеграция

Как работи автоматизираният тест

По време на теста всичко работи в Docker: тестваната услуга и Postgres, и инструментът за миграция, и Postman, или по-скоро неговата конзолна версия - Newman.

Docker решава редица проблеми:

  • Независимост от конфигурацията на хоста;
  • Инсталиране на зависимости: docker изтегля изображения от Docker Hub;
  • Връщане на системата в първоначалното й състояние: просто премахнете контейнерите.

докер-съставяне обединява контейнери във виртуална мрежа, изолирана от Интернет, в която контейнерите се намират един друг чрез имена на домейни.

Тестът се контролира от shell скрипт. Използваме git-bash, за да изпълним теста на Windows. Така един скрипт е достатъчен както за Windows, така и за Linux. Git и Docker се инсталират от всички разработчици на проекта. Инсталирането на Git на Windows инсталира git-bash, така че всеки има и това.

Скриптът изпълнява следните стъпки:

  • Изграждане на докер изображения
    docker-compose build
  • Стартиране на DB и LocalStack
    docker-compose up -d <контейнер>
  • Миграция на база данни и подготовка на LocalStack
    docker-compose run <контейнер>
  • Изпълнение на тестовата услуга
    docker-compose up -d <сервис>
  • Изпълнете тест (Newman)
  • Спрете всички контейнери
    docker-compose down
  • Публикуване на резултати в Slack
    Имаме чат, където отиват съобщения със зелена отметка или червен кръст и връзка към дневника.

Тези стъпки включват следните изображения на Docker:

  • Услугата, която се тества, е същото изображение като за производството. Конфигурацията за теста е чрез променливи на средата.
  • За Postgres, Redis и LocalStack се използват предварително изградени изображения от Docker Hub. Има и готови изображения за Liquibase и Newman. Ние изграждаме нашия върху техния скелет, добавяйки нашите файлове там.
  • Предварително компилирано AWS CLI изображение се използва за осигуряване на LocalStack и изображение, съдържащо скрипта, се изгражда от него.

Използването на обеми, не е нужно да създавате Docker изображение само за да добавите файлове към контейнер. Въпреки това обемите не са подходящи за нашата среда, тъй като самите задачи на Gitlab CI се изпълняват в контейнери. Docker може да се управлява от такъв контейнер, но обемите само монтират папки от хост системата, а не от друг контейнер.

Проблеми, които може да срещнете

В очакване на готовност

Когато контейнерът с услугата работи, това не означава, че е готов да приема връзки. Трябва да изчакате връзката да продължи.

Този проблем понякога се решава с помощта на скрипт. изчакайте го.ш, който чака да бъде установена TCP връзка. LocalStack обаче може да изведе грешка 502 Bad Gateway. Освен това се състои от много услуги и ако една от тях е готова, не казва нищо за останалите.

Решение: Скриптове за осигуряване на LocalStack, които чакат отговор 200 от SQS и SNS.

Конфликти на паралелни задачи

Множество тестове могат да се изпълняват едновременно на един и същ хост Docker, така че имената на контейнерите и мрежите трябва да са уникални. Освен това, тестове от различни клонове на една и съща услуга могат да се изпълняват едновременно, така че не е достатъчно да напишете собствените си имена във всеки файл за композиране.

Решение: Скриптът задава уникална стойност за променливата COMPOSE_PROJECT_NAME.

Функции на Windows

Когато използвате Docker в Windows, има няколко неща, които искам да обърна на вниманието ви, защото този опит е важен за разбирането на причините за грешките.

  1. Шелскриптовете в контейнер трябва да имат Linux окончания на редове.
    Символът CR за обвивка е синтактична грешка. От съобщението за грешка е трудно да се разбере какъв е проблемът. Когато редактирате такива скриптове в Windows, имате нужда от подходящ текстов редактор. Освен това системата за контрол на версиите трябва да бъде правилно конфигурирана.

Ето как е конфигуриран git:

git config core.autocrlf input

  1. Git-bash емулира стандартни Linux папки и при извикване на exe файл (включително docker.exe) заменя абсолютните Linux пътища с Windows пътища. Това обаче няма смисъл за пътища извън локалната машина (или пътища в контейнер). Това поведение не е деактивирано.

Решение: добавете допълнителна наклонена черта в началото на пътя: //bin вместо /bin. Linux разбира такива пътеки, за него няколко наклонени черти са еднакви. Но git-bash не разпознава такива пътища и не се опитва да ги конвертира.

Извеждане на лог

Когато изпълнявам тестове, бих искал да видя регистрационни файлове както от Newman, така и от услугата, която се тества. Тъй като събитията от тези журнали са взаимосвързани, комбинирането им в една конзола е много по-удобно от два отделни файла. Нюман пробяга docker-compose изпълнение, и така изходът му завършва в конзолата. Остава да се уверим, че резултатът от услугата също стига до там.

Първоначалното решение беше да се направи докер-композиране нагоре без знаме -d, но използвайки възможностите на обвивката, изпратете този процес на заден план:

docker-compose up <service> &

Това работеше, докато не трябваше да изпратя регистрационни файлове от докер към услуга на трета страна. докер-композиране нагоре Спря извеждането на регистрационни файлове към конзолата. Екипът обаче проработи прикрепете докера.

Решение:

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

Конфликт на идентификатор по време на итерации на теста

Тестовете се провеждат в множество итерации. Базата данни не е изчистена. Записите в базата данни имат уникални идентификатори. Ако запишем конкретни идентификатори в заявките, ще получим конфликт при втората итерация.

За да го избегнете, или идентификаторите трябва да са уникални, или всички обекти, създадени от теста, трябва да бъдат изтрити. Някои обекти не могат да бъдат изтрити, както се изисква.

Решение: генерирайте GUID със скриптове в Postman.

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

След това в заявката използвайте символа {{myUUID}}, която ще бъде заменена от стойността на променливата.

Взаимодействие чрез LocalStack

Ако тестваната услуга чете или пише в SQS опашката, тогава самият тест трябва също да работи с тази опашка, за да провери това.

Решение: заявки от Postman към LocalStack.

API за услуги на AWS е документиран, което ви позволява да правите заявки без SDK.

Ако услугата пише в опашката, тогава я четем и проверяваме съдържанието на съобщението.

Ако услугата изпраща съобщения до SNS, на етапа на подготовка, LocalStack също създава опашка и се абонира за тази SNS тема. Тогава всичко се свежда до горното.

Ако услугата трябва да прочете съобщение от опашката, тогава в предишната тестова стъпка записваме това съобщение в опашката.

Тестване на HTTP заявки, идващи от тестваната микроуслуга

Някои услуги работят през HTTP с нещо различно от AWS, а някои функции на AWS не са внедрени в LocalStack.

Решение: в тези случаи може да помогне макет сървър, който има готово изображение в Докер център. Очакваните заявки и техните отговори се конфигурират от HTTP заявката. API е документиран, така че правим заявки от Postman.

Тестване на OAuth удостоверяване и оторизация

Използваме OAuth и JSON уеб токени (JWT). Тестът изисква OAuth доставчик, който можем да изпълним локално.

Цялото взаимодействие на услугата с доставчика на OAuth се свежда до две заявки: първо се изисква конфигурацията /.добре позната/openid-конфигурация, след което публичният ключ (JWKS) се иска на адреса от конфигурацията. Всичко това е статично съдържание.

Решение: Нашият тестов OAuth доставчик е сървър за статично съдържание и два файла на него. Токенът се генерира веднъж и се предава на Git.

Характеристики на теста на SignalR

Пощальонът не работи с уеб сокети. Създаден е специален инструмент за тестване на SignalR.

Клиентът на SignalR може да бъде нещо повече от браузър. Има клиентска библиотека за него под .NET Core. Клиент, написан на .NET Core, установява връзка, удостоверява и изчаква определена последователност от съобщения. Ако се получи неочаквано съобщение или връзката се загуби, клиентът излиза с код 1. Ако се получи последното очаквано съобщение, излиза с код 0.

Нюман работи едновременно с клиента. Стартират се няколко клиента, за да се провери дали съобщенията се доставят до всеки, който има нужда от тях.

Автоматизирано тестване на микроуслуги в Docker за непрекъсната интеграция

За да стартирате няколко клиента, използвайте опцията --мащаб на командния ред на docker-compose.

Преди да стартира Postman, скриптът изчаква всички клиенти да установят връзка.
Вече се сблъскахме с проблема с изчакването на връзка. Но имаше сървъри, а тук е клиентът. Имаме нужда от различен подход.

Решение: клиентът в контейнера използва механизма Преглед на здраветоза да информира скрипта на хоста за неговия статус. Клиентът създава файл по определен път, да речем /healthcheck, веднага щом се установи връзката. Скриптът HealthCheck в докер файла изглежда така:

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

Отбор докер инспектира показва нормалното състояние на контейнера, здравословното състояние и кода за изход.

След като Newman завърши, скриптът проверява дали всички контейнери с клиента са прекратени, освен това с код 0.

Щастието съществува

След като преодоляхме трудностите, описани по-горе, имахме набор от стабилни работещи тестове. При тестовете всяка услуга работи като един обект, взаимодействащ с базата данни и с Amazon LocalStack.

Тези тестове защитават екип от 30+ разработчици от грешки в приложение със сложни взаимодействия на 10+ микроуслуги с чести внедрявания.

Източник: www.habr.com

Добавяне на нов коментар