Практика Docker: знакомимся с docker-maven-plugin
Олег Накрайников рассказал, как запускать внешние зависимости в Docker для интеграционных тестов в spring-boot-проекте.


Фото: Bloomberg / Getty Images

Запускать интеграционные тесты можно по-разному, но в работе я ориентируюсь на следующие требования:
- способ должен позволять запускать тесты и локально, и на CI;
- развёртывание зависимостей не должно чрезмерно увеличивать время сборки;
- не должно возникать проблем с поиском сборок зависимостей (баз данных, брокеров и так далее).
Поэтому я использую maven — это фреймворк для автоматизации сборки проектов от Apache, в котором структура проекта описывается на подмножестве XML, языке POM. Далее расскажу, как запускать интеграционные тесты с его помощью, и поделюсь конфигурациями плагинов.
Как запустить интеграционный тест в maven
Интеграционные тесты в maven запускаются одной строкой:
mvn verify -Docker_host = localhost
Теперь настроим maven. Если прописать свойство docker_host, то сборщик активирует профиль IT:
<profile>
<id>it</id>
<activation>
<property>
<name>docker_host</name>
</property>
</activation>
Профиль включает плагины maven-failsafe-plugin и docker-maven-plugin.
Первый необходим для запуска самих тестов:
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goals>
<goal>verify</goals>
</goals>
</execution>
</executions>
А второй — как раз для запуска зависимостей в контейнерах Docker:
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.38.0</version>
В конфигурации docker-maven-plugin необходимо указать образы нужных нам зависимостей:
<configuration>
<dockerHost>tcp://${docker_host}:${docker_port}</dockerHost>
<images>
<image>
<name>postgres:11</name>
<alias>postgresql</alias>
<run>
<env>
<POSTGRES_USER>postgres</POSTGRES_USER>
<POSTGRES_PASSWORD>postgres</POSTGRES_PASSWORD>
<POSTGRES_DB>postgres</POSTGRES_DB>
</env>
<ports>
<port>${postgres_port}:5432</port>
</ports>
<wait>
<log>(?s)database system is ready to accept connections.*database system is ready to accept connections</log>
<time>10000</time>
</wait>
</run>
</image>
</images>
</configuration>
Также укажем, что перед проведением тестов контейнеры нужно собрать и запустить, а по завершении — остановить и удалить:
<executions>
<execution>
<id>start</id>
<phase>pre-integration-test</phase>
<goals>
<goal>build</goals>
<goal>start</goals>
</goals>
</execution>
<execution>
<id>stop</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goals>
</goals>
</execution>
<executions>
Зачем нужно свойство docker_host
<configuration>
<dockerHost>tcp://${docker_host}:${docker_port}</dockerHost>
<images>
<image>
В docker_host указывается адрес, по которому расположен docker engine — на нём плагин и будет работать с контейнерами. Данное свойство позволит запускать интеграционные тесты как локально на машине разработчика, так и на CI.
Статья написана на основе треда Олега в Twitter.
Всё чаще в современных CI воркеры разворачиваются в виде docker-контейнеров. Поэтому для сборки нашего проекта необходимо развернуть два контейнера: maven и Docker in Docker (dind). В maven собирается проект и запускаются интеграционные тесты, а в dind — контейнеры с зависимостями. Адрес dind-контейнера как раз указывается в свойстве docker_host.
Что сделать, чтобы тесты добрались до зависимостей?
Через плагин maven-failsafe-plugin устанавливаем переменные окружения и прописываем настройки в application.yml:
<configuration>
<environmentVariables>
<spring.profiles.active>tests-remote</spring.profiles.active>
<postgres_host>${docker_host}</postgres_host>
<postgres_port>${docker_por}</postgres_port>
</environmentVariables>
</configuration>
spring:
profiles:
active: tests-local
external.url: http://localhost:8001
db:
username: 'postgres'
password: 'postgres'
driverClassName: 'org.postgresql.Driver'
minimumIdle: 5
maximumPoolSize: 10
connectionTimeout: 10000
validationTimeout: 5000
connectionsTestQuery: 'SELECT 1'
___
spring:
config:
activate:
on-profile: tests-local
db:
jdbcUrl: 'jdbc:postgresql://localhost:5432/postgres'
___
spring:
config:
activate:
on-profile: test-remote
db:
jdbc:postgresql://${postgres.host}:${postgres.port}/postgres'
Здесь on-profile: tests-local нужен для запуска тестов из IDE, а on-profile: tests-remote — через maven.
Чтобы писать и запускать тесты из IDE, придётся поднять зависимости вручную двумя командами:
mvn docker:start -Docker_host=localhost
mvn docker:stop -Docker_host=localhost
Изложенный подход обладает следующими преимуществами:
- Вы сможете запускать тесты и локально, и на CI.
- Контейнеры будут создаваться только перед тестами и удаляться сразу после их окончания.
- Через docker-maven-plugin можно строить достаточно сложное окружение с множеством контейнеров и сетью между ними.
Пример проекта, собранного с помощью docker-maven-plugin, можно посмотреть в моём GitHub-репозитории, а почитать документацию к плагину — здесь.