ProHoster > Блог > администрация > GitLab Shell Runner. Конкурентно стартирайте тествани услуги с Docker Compose
GitLab Shell Runner. Конкурентно стартирайте тествани услуги с Docker Compose
Тази статия ще бъде от интерес както за тестери, така и за разработчици, но е предназначена повече за автоматизатори, които са изправени пред проблема с настройването на GitLab CI / CD за интеграционно тестване в лицето на недостатъчни инфраструктурни ресурси и / или липсата на оркестрация на контейнери платформа. Ще ви кажа как да конфигурирате разгръщането на тестови среди с помощта на docker compose на един шел пълнеж на GitLab и така, че когато разгръщате няколко среди, стартираните услуги да не си пречат.
В моята практика често се случваше да „третирам“ интеграционното тестване на проекти. И често първият и най-важен проблем е CI тръбопроводът, в който интеграционното тестване развити услугата(ите) се хоства в среда за разработка/етап. Това доведе до доста проблеми:
Поради дефекти в една или друга услуга по време на интеграционното тестване, тестовата верига може да бъде повредена от повредени данни. Имаше случаи, когато изпращането на заявка със счупен JSON формат затвори услугата, което направи щанда напълно неработещ.
Забавянето на тестовата верига с нарастването на тестовите данни. Мисля, че няма смисъл да описвам пример с почистване / връщане на базата данни. В моята практика не съм срещал проект, при който тази процедура да върви гладко.
Рискът от нарушаване на работата на тестовата верига при тестване на общи системни настройки. Например политика за потребител/група/парола/приложение.
Тестовите данни от автоматичните тестове правят живота труден за ръчните тестери.
Някой ще каже, че добрите автотестове трябва да изчистят данните след себе си. Имам аргументи против:
Динамичните стойки са много удобни за използване.
Не всеки обект може да бъде премахнат от системата чрез API. Например, извикване за изтриване на обект не е реализирано, тъй като противоречи на бизнес логиката.
Когато създавате обект чрез API, може да се създаде огромно количество метаданни, което е проблематично за премахване.
Ако тестовете са зависими един от друг, тогава процесът на почистване на данните след изпълнението на тестовете се превръща в главоболие.
Допълнителни (и според мен неоправдани) извиквания към API.
И основният аргумент: когато тестовите данни започнат да се почистват директно от базата данни. Това се превръща в истински PK/FK цирк! Чуваме от разработчиците: „Току-що добавих/изтрих/преименувах знака, защо 100500 теста за интеграция се провалиха?“
Според мен най-оптималното решение е динамична среда.
Много хора използват docker-compose, за да стартират тестова среда, но малко хора използват docker-compose, когато правят интеграционно тестване в CI/CD. И тук не вземам предвид kubernetes, swarm и други платформи за оркестриране на контейнери. Не всяка фирма ги има. Би било хубаво, ако docker-compose.yml беше универсален.
Дори ако имаме собствен QA runner, как можем да сме сигурни, че услугите, стартирани чрез docker-compose, не си пречат?
Как да събирам регистрационни файлове на тествани услуги?
Как да почистите бегача?
Имам собствен GitLab runner за моите проекти и се натъкнах на тези проблеми, докато разработвах Java клиент за тестова релса. По-конкретно, когато изпълнявате интеграционни тестове. Тук ще продължим да решаваме тези проблеми с примери от този проект.
За бегач препоръчвам Linux виртуална машина с 4 vCPU, 4 GB RAM, 50 GB HDD.
Има много информация за настройка на gitlab-runner в интернет, така че накратко:
Отиваме до машината чрез SSH
Ако имате по-малко от 8 GB RAM, препоръчвам направи суап 10 GBза да не дойде убиецът на OOM и да ни убие задачите поради липса на RAM. Това може да се случи, когато повече от 5 задачи се изпълняват едновременно. Задачите ще бъдат по-бавни, но стабилни.
Пример с OOM killer
Ако в регистрационните файлове на задачите виждате bash: line 82: 26474 Killed, след това просто изпълнете на бегача sudo dmesg | grep 26474
[26474] 1002 26474 1061935 123806 339 0 0 java
Out of memory: Kill process 26474 (java) score 127 or sacrifice child
Killed process 26474 (java) total-vm:4247740kB, anon-rss:495224kB, file-rss:0kB, shmem-rss:0kB
И ако снимката изглежда нещо подобно, тогава или добавете суап, или добавете RAM.
Това ще ви позволи да изпълнявате паралелни задачи на един и същи бегач. Прочетете още тук.
Ако имате по-мощна машина, например 8 vCPU, 16 GB RAM, тогава тези числа могат да бъдат направени поне 2 пъти по-големи. Но всичко зависи от това какво точно ще бъде пуснато на този бегач и в какво количество.
Основната задача е универсален docker-compose.yml, който разработчиците/тестерите могат да използват както локално, така и в CI конвейера.
На първо място, ние създаваме уникални имена на услуги за CI. Една от уникалните променливи в GitLab CI е променливата CI_JOB_ID. Ако посочите container_name със стойност "service-${CI_JOB_ID:-local}", тогава в случая:
ако CI_JOB_ID не е дефиниран в променливите на средата,
тогава името на услугата ще бъде service-local
ако CI_JOB_ID дефинирани в променливи на средата (напр. 123),
тогава името на услугата ще бъде service-123
Второ, правим обща мрежа за изпълняване на услуги. Това ни дава изолация на ниво мрежа, когато изпълняваме множество тестови среди.
version: "3"
# Для корректной работы web (php) и fmt нужно,
# чтобы контейнеры имели общий исполняемый контент.
# В нашем случае, это директория /var/www/testrail
volumes:
static-content:
# Изолируем окружение на сетевом уровне
networks:
default:
external:
name: testrail-network-${CI_JOB_ID:-local}
services:
db:
image: mysql:5.7.22
# Каждый container_name содержит ${CI_JOB_ID:-local}
container_name: "testrail-mysql-${CI_JOB_ID:-local}"
environment:
MYSQL_HOST: db
MYSQL_DATABASE: mydb
MYSQL_ROOT_PASSWORD: 1234
SKIP_GRANT_TABLES: 1
SKIP_NETWORKING: 1
SERVICE_TAGS: dev
SERVICE_NAME: mysql
networks:
- default
migration:
image: registry.gitlab.com/touchbit/image/testrail/migration:latest
container_name: "testrail-migration-${CI_JOB_ID:-local}"
links:
- db
depends_on:
- db
networks:
- default
fpm:
image: registry.gitlab.com/touchbit/image/testrail/fpm:latest
container_name: "testrail-fpm-${CI_JOB_ID:-local}"
volumes:
- static-content:/var/www/testrail
links:
- db
networks:
- default
web:
image: registry.gitlab.com/touchbit/image/testrail/web:latest
container_name: "testrail-web-${CI_JOB_ID:-local}"
# Если переменные TR_HTTP_PORT или TR_HTTPS_PORTS не определены,
# то сервис поднимается на 80 и 443 порту соответственно.
ports:
- ${TR_HTTP_PORT:-80}:80
- ${TR_HTTPS_PORT:-443}:443
volumes:
- static-content:/var/www/testrail
links:
- db
- fpm
networks:
- default
Integration:
stage: test
tags:
- my-shell-runner
before_script:
# Аутентифицируемся в registry
- docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
# Генерируем псевдоуникальные TR_HTTP_PORT и TR_HTTPS_PORT
- export TR_HTTP_PORT=$(shuf -i10000-60000 -n1)
- export TR_HTTPS_PORT=$(shuf -i10000-60000 -n1)
# создаем директорию с идентификатором задачи
- mkdir ${CI_JOB_ID}
# копируем в созданную директорию наш docker-compose.yml
# чтобы контекст был разный для каждой задачи
- cp .indirect/docker-compose.yml ${CI_JOB_ID}/docker-compose.yml
script:
# поднимаем наше окружение
- make docker-up
# запускаем тесты исполняемым jar (у меня так)
- java -jar itest.jar --http-port ${TR_HTTP_PORT} --https-port ${TR_HTTPS_PORT}
# или в контейнере
- docker run --network=testrail-network-${CI_JOB_ID:-local} --rm itest
after_script:
# собираем логи
- make docker-logs
# останавливаем окружение
- make docker-kill
artifacts:
# сохраняем логи
when: always
paths:
- logs
expire_in: 30 days
В резултат на изпълнението на такава задача, директорията с регистрационни файлове в артефакти ще съдържа регистрационни файлове на услуги и тестове. Което е много удобно в случай на грешки. Всеки тест паралелно пише свой собствен журнал, но ще говоря за това отделно.
$ docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /home/gitlab-runner/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
$ export TR_HTTP_PORT=$(shuf -i10000-60000 -n1)
$ export TR_HTTPS_PORT=$(shuf -i10000-60000 -n1)
$ mkdir ${CI_JOB_ID}
$ cp .indirect/docker-compose.yml ${CI_JOB_ID}/docker-compose.yml
$ make docker-up
docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml kill
docker network rm testrail-network-${CI_JOB_ID:-local} || true
Error: No such network: testrail-network-204645172
docker network create testrail-network-${CI_JOB_ID:-local}
0a59552b4464b8ab484de6ae5054f3d5752902910bacb0a7b5eca698766d0331
docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml pull
Pulling web ... done
Pulling fpm ... done
Pulling migration ... done
Pulling db ... done
docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml up --force-recreate --renew-anon-volumes -d
Creating volume "204645172_static-content" with default driver
Creating testrail-mysql-204645172 ...
Creating testrail-mysql-204645172 ... done
Creating testrail-migration-204645172 ... done
Creating testrail-fpm-204645172 ... done
Creating testrail-web-204645172 ... done
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c6b76f9135ed registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 13 seconds ago Up 1 second 0.0.0.0:51148->80/tcp, 0.0.0.0:25426->443/tcp testrail-web-204645172
01d303262d8e registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 16 seconds ago Up 13 seconds 9000/tcp testrail-fpm-204645172
2cdab1edbf6a registry.gitlab.com/touchbit/image/testrail/migration:latest "docker-entrypoint.s…" 16 seconds ago Up 13 seconds 3306/tcp, 33060/tcp testrail-migration-204645172
826aaf7c0a29 mysql:5.7.22 "docker-entrypoint.s…" 18 seconds ago Up 16 seconds 3306/tcp testrail-mysql-204645172
6dbb3fae0322 registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 36 seconds ago Up 22 seconds 0.0.0.0:44202->80/tcp, 0.0.0.0:20151->443/tcp testrail-web-204645084
3540f8d448ce registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 38 seconds ago Up 35 seconds 9000/tcp testrail-fpm-204645084
70fea72aa10d mysql:5.7.22 "docker-entrypoint.s…" 40 seconds ago Up 37 seconds 3306/tcp testrail-mysql-204645084
d8aa24b2892d registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" About a minute ago Up 53 seconds 0.0.0.0:31103->80/tcp, 0.0.0.0:43872->443/tcp testrail-web-204644881
6d4ccd910fad registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" About a minute ago Up About a minute 9000/tcp testrail-fpm-204644881
685d8023a3ec mysql:5.7.22 "docker-entrypoint.s…" About a minute ago Up About a minute 3306/tcp testrail-mysql-204644881
1cdfc692003a registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" About a minute ago Up About a minute 0.0.0.0:44752->80/tcp, 0.0.0.0:23540->443/tcp testrail-web-204644793
6f26dfb2683e registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" About a minute ago Up About a minute 9000/tcp testrail-fpm-204644793
029e16b26201 mysql:5.7.22 "docker-entrypoint.s…" About a minute ago Up About a minute 3306/tcp testrail-mysql-204644793
c10443222ac6 registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 5 hours ago Up 5 hours 0.0.0.0:57123->80/tcp, 0.0.0.0:31657->443/tcp testrail-web-204567103
04339229397e registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 5 hours ago Up 5 hours 9000/tcp testrail-fpm-204567103
6ae0accab28d mysql:5.7.22 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp testrail-mysql-204567103
b66b60d79e43 registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 5 hours ago Up 5 hours 0.0.0.0:56321->80/tcp, 0.0.0.0:58749->443/tcp testrail-web-204553690
033b1f46afa9 registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 5 hours ago Up 5 hours 9000/tcp testrail-fpm-204553690
a8879c5ef941 mysql:5.7.22 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp testrail-mysql-204553690
069954ba6010 registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 5 hours ago Up 5 hours 0.0.0.0:32869->80/tcp, 0.0.0.0:16066->443/tcp testrail-web-204553539
ed6b17d911a5 registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 5 hours ago Up 5 hours 9000/tcp testrail-fpm-204553539
1a1eed057ea0 mysql:5.7.22 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp testrail-mysql-204553539
Всички задачи изпълнени успешно
Артефактите на задачите съдържат регистрационни файлове на услуги и тестове
Всичко изглежда красиво, но има нюанс. Конвейер може да бъде принудително анулиран, докато се изпълняват тестове за интеграция, в който случай работещите контейнери няма да бъдат спрени. От време на време трябва да почиствате бегача. За съжаление задачата за ревизия в GitLab CE все още е в статус отворено
Но ние добавихме планирано стартиране на задача и никой не ни забранява да го стартираме ръчно.
Отидете на нашия проект -> CI/CD -> Графици и стартирайте задачата Clean runner
Общо:
Имаме един бегач на черупки.
Няма конфликти между задачите и средата.
Имаме паралелно стартиране на задачи с интеграционни тестове.
Можете да изпълнявате интеграционни тестове както локално, така и в контейнер.
Сервизните и тестовите регистрационни файлове се събират и прикачват към задачата за конвейер.
Възможно е да почистите бегача от стари докер изображения.
Времето за настройка е ~2 часа.
Това всъщност е всичко. Ще се радвам на обратна връзка.