Cómo ejecutar contenedores de Docker con Maven exec-maven-plugin

Cuando estamos usando Maven y queremos levantar contenedores de Docker para, por ejemplo, ejecutar los tests de integración, es bastante típico usar alguno de los plugins específicos que existen. Donde a día de hoy posiblemente el más habitual es docker-maven-plugin de Fabric8.

Estos plugins suelen ser un recubrimiento sobre un cliente de Docker que al final se conecta con el Docker daemon para realizar las acciones necesarias. Si bien para casos sencillos son muy convenientes, y de hecho seguramente deberían ser la opción por defecto, el problema que tienen este tipo de plugins es que están más pensados para crear imágenes en vez de ejecutarlas, y sobre todo si queremos levantar distintos contenedores con dependencias entre ellos… la cosa se complica ?.

En este tutorial vamos a ver cómo podemos usar el simple plugin de Maven 

Text
exec-maven-plugin

 para quitarnos estos intermediarios y hacer lo que queramos directamente con el comando 

Text
docker

.

De hecho en este tutorial utilizaremos el comando 

Text
docker compose

 que nos permitirá más flexibilidad a la hora de levantar distintos contenedores de forma simultánea.

Podéis encontrar todo el código de este tutorial en: https://github.com/alejandropg/tutorial-maven-docker-exec. Incluido un ejemplo de cómo ejecutarlo integrado con los GitHub Actions.

Índice

  1. Entorno
  2. Maven 
    Text
    pom.xml

  3. El script para preparar el arranque de los tests de integración: 
    Text
    docker-compose-it-up.sh

  4. El script para limpiar el entorno después de los tests de integración: 
    Text
    docker-compose-it-down.sh

  5. La configuración de 
    Text
    docker compose

  6. Conclusiones
  7. Sobre el autor


1. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 16″ (Apple M1 Pro, 32GB LPDDR5, 1TB SSD).
  • Sistema Operativo: macOS Monterey 12.0.1.
  • Java version: 11.0.13, vendor: Azul Systems, Inc., arch: “aarch64”, family: “mac”.
  • Apache Maven 3.8.3.
  • Docker Desktop 4.1.1 (69879) y también probado en 4.2.0 (70708).


2. Maven 

Text
pom.xml

Para usar el plugin 

Text
exec-maven-plugin

 simplemente haremos la siguiente configuración en nuestro Maven 

Text
pom.xml

.


Text
...
<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>${exec-maven-plugin.version}</version>
            <executions>
                <execution>
                    <id>start</id>
                    <phase>pre-integration-test</phase>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                    <configuration>
                        <workingDirectory>${maven.multiModuleProjectDirectory}</workingDirectory>
                        <executable>docker-compose-it-up.sh</executable>
                    </configuration>
                </execution>
                <execution>
                    <id>stop</id>
                    <phase>post-integration-test</phase>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                    <configuration>
                        <workingDirectory>${maven.multiModuleProjectDirectory}</workingDirectory>
                        <executable>docker-compose-it-down.sh</executable>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
...

Aquí se ve como simplemente nos enganchamos a la fase de 

Text
pre-integration-test

 y 

Text
post-integration-test

 para ejecutar un script para arrancar “

Text
up

” y parar “

Text
down

” el Docker compose.

He elegido usar un script y no hacerlo directamente aquí porque eso nos puede dar mucha flexibilidad a la hora de ejecutar cualquier otro tipo de configuración de arranque que necesitemos para nuestros tests de integración. Lo veremos más adelante con un ejemplo.


3. El script para preparar el arranque de los tests de integración: 

Text
docker-compose-it-up.sh

Text
docker-compose-it-up.sh

 es un script sh normal y corriente, por lo que tendremos a nuestro alcance toda la potencia de la shell.


Text
#!/usr/bin/env sh

DOCKER_COMPOSE_FILE=docker-compose-it.yml

_docker_compose_get_image_name() {
    image_name=$(fgrep --no-filename --max-count=1 image "${DOCKER_COMPOSE_FILE}" | cut -d':' -f2-)
    # remove leading whitespace characters
    image_name="${image_name#"${image_name%%[![:space:]]*}"}"
    # remove trailing whitespace characters
    image_name="${image_name%"${image_name##*[![:space:]]}"}"  
    echo "${image_name}"
}

_docker_compose_pull_image() {    
    docker image inspect "${1}" 2>&1 1>/dev/null
    image_exist=$?
    if [ $image_exist != 0 ]; then
        echo '[INFO] Pull IT Docker images...'
        docker compose -f "${DOCKER_COMPOSE_FILE}" pull
    fi
}

# We don't want that the images download affect to the timeout,
# so pull images before up if they are not in the local image repository
image_name=$(_docker_compose_get_image_name)
_docker_compose_pull_image "${image_name}"

echo '[INFO] Up IT Docker containers...'
expect <<EOD
set timeout 5
spawn docker compose -f "${DOCKER_COMPOSE_FILE}" up
expect "database system is ready to accept connections"
EOD

Este script seguramente es lo más complicado de todo el tutorial. Al principio encontramos un par de funciones de ayuda donde el propósito de estas es una pequeña optimización para no hacer el 

Text
docker compose pull

constantemente y ahorrar unos preciosos segundos cuando lo estamos ejecutando en local. Es decir, si analizamos en detalle la función

Text
_docker_compose_pull_image

, podemos ver que lo que hace es comprobar si la imagen ya está en local o no. De no encontrarla intentará hacer un 

Text
pull

 de todo el Docker compose.

Luego la otra parte con “truco” es el uso del 

Text
expect

La intención del 

Text
expect

 es no terminar la ejecución del script hasta que se encuentre en la salida la línea especificada. De esta forma garantizamos que los tests de integración no empezarán a ejecutarse hasta que esté debidamente levantado el contenedor.


Text
expect "database system is ready to accept connections"

Esto sería el equivalente a, por ejemplo, cuando usando el 

Text
docker-maven-plugin

 de Fabric8 y hacemos:


Text
<wait>
    <log>database system is ready to accept connections</log>
</wait>

También se define un 

Text
timeout

 de 5 segundos, de forma que si en este tiempo no se encuentra la línea especificada el script terminará abruptamente. Precisamente por este timeout es por lo que separamos el 

Text
pull

 de las imágenes del 

Text
up

 del Docker compose, ya que si no lo separamos será muy fácil que salte el timeout la primera vez que lo ejecutemos, cuando intente descargar las imágenes.


4. El script para limpiar el entorno después de los tests de integración: 

Text
docker-compose-it-down.sh

De nuevo 

Text
docker-compose-it-down.sh

 es un script sh normal donde podremos ejecutar todo lo que necesitemos para limpiar el entorno tras ejecutar los tests de integración.


Text
#!/usr/bin/env sh

echo '[INFO] Down IT Docker containers...'
docker compose --file docker-compose-it.yml down

Se ve como en nuestro caso simplemente paramos el Docker compose. Además aquí son comandos normalitos y no necesitamos usar ningún “truco” como vimos con el

Text
expect

 en el punto anterior.


5. La configuración de 

Text
docker compose

Para configurar el Docker compose usamos un fichero de configuración normal y corriente: 

Text
docker-compose-it.yml

Text
services:
  it-ddbb:
    image: postgres:13.3-alpine
    ports:
      - 5432:5432
    volumes:
      - it-ddbb:/var/lib/postgresql/data:delegated
    environment:
      POSTGRES_PASSWORD: password

volumes:
  it-ddbb:

En el ejemplo solo levantamos un servicio, pero como es un fichero normal de configuración de Docker compose podríamos hacer cualquier cosa que este permita, como levantar n servicios, con dependencias entre ellos, montar volúmenes…


6. Conclusiones

Con este ejemplo estamos yendo un poco a “bajo nivel”, pero si os fijáis tampoco tanto, puesto que hay pocas lineas que no tuviéramos que poner de todas formas: nombre de la imagen, nombre de los volúmenes, en que fase del ciclo de Maven queremos engancharlo…

Y sin embargo conseguimos un aumento de flexibilidad enorme, ya que ponemos a nuestro alcance toda la potencia de la shell, de Docker y de Docker compose.

Así que desde luego esta puede ser una buena opción a tener en cuenta si vemos que se nos complica la cosa a la hora de levantar contenedores dentro del ciclo de vida de Maven.


7. Sobre el autor

Alejandro Pérez García (@alejandropgarci).

Ingeniero en Informática (especialidad de Ingeniería del Software) y Certified ScrumMaster.

Socio fundador de Autentia Real Business Solutions S.L. – “Soporte a Desarrollo”.

Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

He leído y acepto la política de privacidad

Información básica acerca de la protección de datos

  • Responsable: IZERTIS S.A.
  • Finalidad: Envío información de carácter administrativa, técnica, organizativa y/o comercial sobre los productos y servicios sobre los que se nos consulta.
  • Legitimación: Consentimiento del interesado
  • Destinatarios: Otras empresas del Grupo IZERTIS. Encargados del tratamiento.
  • Derechos: Acceso, rectificación, supresión, cancelación, limitación y portabilidad de los datos.
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad

Alejandro es socio fundador de Autentia y nuestro experto en Java EE, Linux y optimización de aplicaciones empresariales. Ingeniero en Informática y Certified ScrumMaster. Seguir @alejandropgarci Si te gusta lo que ves, puedes contratarle para darte ayuda con soporte experto, impartir cursos presenciales en tu empresa o para que realicemos tus proyectos como factoría (Madrid). Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación.

¿Quieres publicar en Adictos al trabajo?

Te puede interesar

22/06/2026

Juan Antonio Jiménez Torres

Vamos a instrumentalizar un SDD para un microservicio bancario. En este artículo sobre SDD - Spec Driven Development, instrumentalizado sobre OpenSpec vamos a construir una PoC de algo más serio que una tabla periódica. Un microservicio bancario muy simplificado que gestione cuentas y permita hacer transferencias

16/06/2026

Juan Antonio Jiménez Torres

Este es el segundo artículo sobre SDD, Spec Driven Development, instrumentalizado sobre OpenSpec. Pero esta vez nos acercaremos usando softwere gratuito. Por un lado OpenCode en lugar de ClaudeCode y usando el modelo gratuito Gemma4 de Google en lugar de otros de pago.

08/06/2026

Juan Antonio Jiménez Torres

Aprenderemos sobre SDD – Spec Driven Development instrumentalizado sobre OpenSpec. Haremos un primer acercamiento usando ClaudeCode con Sonnet, para luego, en un artículo posterior pasarnos a la versión gratuita de OpenCode con Gemma4