Hemos visto en entradas anteriores cómo podíamos levantar un contenedor MySQL, añadiendo volúmenes para persistir los datos y cómo crear una base de datos en el momento de creación del contenedor. Sin embargo, a la hora de desplegar una aplicación, lo ideal es levantar todos los servicios a la vez mediante docker-compose, tal y como lo hacíamos con nuestra aplicación y la base de datos en AWS.
Vamos ahora a realizar lo mismo, pero utilizando nuestra base de datos MySQL«dockerizada«. Podemos hacerlo de dos formas, o bien utilizando la imagen que habíamos creado anteriormente, o bien parametrizar todos esos datos en el docker-compose.yml.
Veamos la primera opción:
version: '3'
services:
mysql:
image: mysql-db/basic:latest
expose:
- 3306
app:
image: secdevoops/spring-boot-docker:0.0.1-SNAPSHOT
expose:
- 8080
ports:
- "8080:8080"
depends_on:
- mysql
En este caso, como vemos, estamos usando ambas imágenes que habíamos creado para generar nuestro fichero docker-compose.yml. Debemos recordar que en nuestra aplicación debemos poner las propiedades de conexión a nuestra base de datos de forma adecuada en el application.properties y generar la imagen de nuevo si es necesario. En este caso, quedaría así:
spring.datasource.url=jdbc:mysql://mysql:3306/myapp
spring.datasource.username=root
spring.datasource.password=password
Ya simplemente tenemos que levantar nuestros servicios con el comando:
$ docker-compose up --build
La otra opción de que disponemos es poner todas las propiedades de nuestra base de datos MySQL en el docker-compose.yml usando como imagen base la imagen oficial de MySQL en lugar de la nuestra e indicar todos los parámetros en el fichero de configuración.
version: '3'
services:
mysql:
image: mysql:latest
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=myapp
volumes:
- mysql_volume:/var/lib/mysql
- mysql_entrypoint:/docker-entrypoint-initdb.d
expose:
- 3306
app:
image: secdevoops/spring-boot-docker:0.0.1-SNAPSHOT
expose:
- 8080
ports:
- "8080:8080"
depends_on:
- "mysql"
volumes:
mysql_volume:
mysql_entrypoint:
En este caso, hemos creado dos volúmenes para el contenedor MySQL. El primero será para la persistencia de datos y el segundo para poder copiar el script que usaremos para crear la base de datos.
Vamos a crear lo primero los contenedores y los volúmenes mediante el siguiente comando con la opción –no-start para que se creen, pero no se levanten:
$ docker-compose up --no-start
Ahora copiaremos nuestro script al volumen docker_mysql_entrypoint. Debemos copiarlo como usuario con permisos de root.
$ sudo cp init_script.sql /var/lib/docker/volumes/docker_mysql_entrypoint/_data/
Al levantar nuestros servicios con docker-compose por primera vez, vamos a tener un problema, y es que la base de datos aún no ha sido creada y, por tanto, nuestra aplicación no se puede conectar. Si lo paramos y lo volvemos a ejecutar, no debería darnos problemas, ya que ahora la base de datos sí estará creada.
Este problema surge porque nuestra aplicación no está esperando a que el contenedor de MySQL termine de levantarse. Incluso, aunque se haya creado, podría darse el caso de que tarde más que la aplicación en levantarse y sigamos teniendo el mismo problema. ¿Cómo podemos solucionar esto? Lo ideal es que el contenedor de nuestra aplicación espere a que el servicio de MySQL esté levantado por completo.
Una opción es modificar el fichero de propiedades de la aplicación para indicarle a Spring que pruebe hasta que la base de datos esté disponible.
spring.datasource.testOnBorrow = true
spring.datasource.testWhileIdle = true
spring.datasource.timeBetweenEvictionRunsMillis = 60000
spring.datasource.minEvictableIdleTimeMillis = 30000
spring.datasource.validationQuery = SELECT 1
spring.datasource.max-active = 15
spring.datasource.max-idle = 10
spring.datasource.max-wait = 8000
Ahora ya podremos levantar nuestra aplicación junto con la base de datos ejecutando:
$ docker-compose up
El proyecto entero está disponible en GitHub https://github.com/secdevoops/spring-boot-docker