Docker-compose con Gradle
Tags: java, gradle
Published: 2022-04-26 05:06:45

---
title: Docker-compose con Gradle
published: true
date: 2022-04-13 00:00:00 UTC
tags: java,gradle
canonical_url: https://jorge.aguilera.soy/blog/2022/gradle-docker-compose.html
---

Probablemente este post a los puristas del TDD les resulte una aberración porque lo que deberías es fomentar el uso de los test para guiar el desarrollo de tu aplicación.

En esta idea una de las batallas que tienes que lidiar suele ser el tema de cómo preparar una infraestructura para estos test. Por ejemplo, tu aplicación requiere una base de datos y te toca usar un H2, pero puede ser más complicado y requerir además un Redis, un RabbitMQ etc por lo que terminas usando `test-containers` (todo un acierto)

Todo eso está muy bien pero a veces el desarrollo no puede, o no quieres, que sea guiado por los test. A veces tienes que montar todo el entorno y tirar de depurador para reproducir un comportamiento extraño, por ejemplo. Y casi siempre esto lo resuelves montando un docker-compose donde configuras toda la infra y/o servicios requeridos por tu aplicación, donde tienes que lidiar con puertos, persistencia , levantar los servicios etc

En este post vamos a ver cómo podemos "integrar" este docker-compose con nuestro `build.gradle` de tal forma que podamos levantar esos servicios e integrarlos en nuestra app de forma cómoda integrandola en nuestras task. Para ello vamos a usar el plugin `com.avast.gradle.docker-compose`

## Docker-compose

Supongamos que nuestra arquitectura requiere de una base de datos MySQL, un Redis y un servicio propio tuyo (distinto del que estás trabajando). Probablemente nuestro docker-compose de desarrollo sería algo como:

```
version: "3.5"
services:

  mysql:
    image: mysql:5.6
    ports:
      - 3306
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"

  cache:
    image: redis:6.2-alpine
    restart: always
    ports:
      - 6379
    command: redis-server --save 20 1 --loglevel warning --requirepass tupwd

  business:
    image: pvidasoftware/greatapp
    ports:
      - 8080
```

Probablmente tu docker-compose tenga una configuracion distinta para los puertos y mapees un puerto del contenedor con un puerto fijo de tu maquina para poder así acceder al servicio. No es mala idea pero al final es un infierno de configuración cuando el proyecto es compartido entre varios miembros y hay que ponerse de acuerdo en qué puertos usamos para qué cosas

## Gradle

En primer lugar vamos a incluir el plugin de `avast` en nuestro build:

build.gradle

```
plugins {
    id "java" (1)
    ....
    id "com.avast.gradle.docker-compose" version "0.15.2" (2)
}
```

| **1** | Si es un proyecto java sueles tener este plugin, pero no es necesario para este post |
| **2** | Añadimos el pluign de Avast, la ultima version a día de hoy |

Esto nos va añadir una serie de `tasks` en el grupo `docker` como por ejemplo `composeUp`y `composeDown` que obviamente sirven para gestionar un docker-compose que le indiquemos

En segundo lugar vamos a indicarle al plugin donde reside nuestro docker-compose

build.gradle

```
dockerCompose {
    useComposeFiles.add("infra/docker-compose.yml")
    isRequiredBy(tasks.run)
}
```

En este ejemplo le estamos diciendo que tenemos un docker-compose en el directorio `infra` y que la tarea que está interesada en controlar el estado del docker-compose es `run` (tipica tarea para correr el java)

Por último vamos a "enlazar" los servicios del docker con nuestra tarea que ejecuta la aplicación mediante variables de entorno:

build.gradle

```
run.doFirst {
    dockerCompose.exposeAsEnvironment(run)

    def mysqlInfo = dockerCompose.servicesInfos.mysql.firstContainer (1)
    def redisInfo = dockerCompose.servicesInfos.cache.firstContainer (2)
    def businessInfo = dockerCompose.servicesInfos.business.firstContainer (3)

    environment "JDBC_URL", "mysql://${mysqlInfo.host}:${mysqlInfo.ports[3306]}/"

    environment "REDIS_HOST", redisInfo.host
    environment "REDIS_PORT", redisInfo.ports[6379]

    environment "SERVICE_API_URL", "http://${businessInfo.host}:${businessInfo.ports[8080]}/api/"

}
```

| **1** | servicesInfos.mysql obtiene info del servicio llamado mysql en el docker-compose |
| **2** | servicesInfos.cache obtiene info del servicio llamado cache en el docker-compose |
| **3** | servicesInfos.business obtiene info del servicio llamado business en el docker-compose |

El plugin se encarga de levantar los servicios y proporcionarnos información sobre los puertos donde está escuchando cada uno, información que usamos para configurar nuestra tarea. En este ejemplo configuramos un jdbc, un host y un puerto y una url a un api que son las variables que se supone requiere nuestra aplicacion para poder ejecutarse correctamente.

Cuando ejecutemos la tarea `run`, los servicios se levantan, se ejecuta nuestra aplicación y cuando paramos la tarea, el plugin se encarga de parar el docker-compose (alguna vez me ha fallado y me ha tocado pararlos a mano usando la tarea `composeDown`)

<dl>
<dt>NOTE</dt>
<dd>
<p>Este plugin tiene unas cuantas configuraciones interesantes adicionales que iré investigando</p>
</dd>
</dl>