Esperando a Servicios en Docker-Compose

Ahorrando recursos con esta alternativa a depends_on

Alfredo Bautista Santos
DotTech
4 min readJun 16, 2020

--

¡Hola a todos! En este artículo vamos a ver un pequeño truco para evitar que los contenedores de nuestros despliegues estén constantemente reiniciándose debido a que algún servicio del que dependan no esté listo.

Qué son Docker y Docker-compose

Antes de nada, tenemos que conocer Docker y su orquestador, Docker-compose.

En este artículo no vamos a entrar en detalle sobre qué es Docker, os dejo la documentación por aquí; pero sí vamos a ver un pequeño resumen para entender las ventajas que nos ofrece a la hora de desplegar nuestras aplicaciones.

Docker es una plataforma que nos permite empaquetar software en unidades estandarizadas llamadas contenedores, que incluyen todo lo necesario para que el software se ejecute, incluyendo bibliotecas, herramientas de sistema, código y tiempo de ejecución.

Nos proporciona innumerables ventajas, entre las que se encuentra la posibilidad de crear un entorno con todas las dependencias necesarias en sus versiones correctas y poder desplegarlo en cualquier entorno sin los temidos cambios de versiones en producción.

Ahora bien, en la actualidad es raro tener un solo entorno que ejecute toda nuestra aplicación. Lo normal es tener diferentes máquinas para diferentes partes de la aplicación. El ejemplo más básico puede ser un servidor en PHP y un gestor de base de datos en diferentes máquinas. Aquí entra en juego Docker-compose.

Docker-compose es una herramienta que nos permite definir entornos multi-contenedor. En nuestro archivo docker-compose.yaml podemos indicar la configuración de nuestro despliegue y con un simple comando, levantamos toda la infraestructura.

Os dejo por aquí la documentación para que lo veáis en profundidad.

El problema

Ahora que ya tenemos nuestra infraestructura lista, nos disponemos a desplegarlo y… Un contenedor no deja de reiniciarse, ¿Por qué?

Es muy común que tengamos al inicio de nuestra aplicación las conexiones con otros servicios y puede que estos todavía no estén listos para escuchar conexiones, ya que los desplegamos e iniciamos a la vez y algunos son más lentos que otros.

En la API de Docker-compose 2.1 teníamos la propiedad depends_on, que nos permitía definir qué contenedor tiene que estar listo para que sea iniciado, y el parámetro healthcheck, que nos permitía definir un comando para saber si un servicio estaba o no preparado.

A partir de la versión 3.0, depends_on desaparece, y solo nos queda la opción de definir el restart: on-failure, que nos permite indicar que, en el caso de que ocurra algún error en el contenedor, este sea reiniciado. En nuestro caso, el contenedor fallaría sin cesar hasta que el contenedor del que depende esté listo.

Esta solución puede ser válida para muchos casos, pero debemos tener en cuenta el desperdicio de recursos de la creación de máquinas hasta que una esté lista; todo dependerá de la situación y lo pesado que sea el contenedor para iniciarse.

Wait-for-it al rescate

Wait-for-it es un pequeño script escrito en bash que nos permite esperar a nuestros servicios.

En este repositorio podéis encontrar el fichero y toda la documentación sobre su funcionamiento.

Supongamos que tenemos un despliegue parecido al anterior, una máquina de base de datos y un servidor que, al iniciar, se conectará a ella para comenzar a dar servicio mediante una API REST.

Podríamos tener un fichero docker-compose.yaml como el siguiente:

En este caso, el contenedor de la base de datos va a tardar mucho más en iniciarse que nuestro servidor, por lo que va a estar reiniciándose hasta que la base de datos acepte conexiones. Esto puede hacer que fácilmente sea reiniciado unas 2–3 veces.

Para arreglar este problema, únicamente tenemos que descargar el script y añadirlo a nuestro contenedor editando el Dockerfile:

Y en nuestro docker-compose.yaml vamos a sobreescribir el comando que ejecutará el contenedor al iniciarse, para que espere al servicio y luego inicie la aplicación:

Como vemos en la última línea, comenzamos ejecutando el wait-for-it.sh, indicándole por parámetros el host:puerto que debe comprobar para decidir si el servicio está funcionando o no.

También le podemos pasar opcionalmente el tiempo que debe esperar para hacer la comprobación o deshabilitarlo poniéndolo a 0. Por último, le añadimos el comando que debe ejecutar el contenedor para quedarse iniciado.

Resumen

Con este pequeño script nos podemos ahorrar muchos quebraderos de cabeza controlando el orden de inicio de los contenedores o el control en nuestra aplicación de la conexiones a los otros servicios.

También tenemos que tener en cuenta que el reinicio constante de contenedores malgasta muchos recursos.

Por ultimo dejo os mi twitter dónde me encontraréis hablando sobre tecnología en general pero sobre todo Flutter 💙 y contenedores 🐋

¡Un saludo y hasta la próxima!

--

--

Alfredo Bautista Santos
DotTech

Sysadmin and web developer. Co-organizer of GDGMarbella & FlutterConf in Marbella, Spain. Flutter enthusiastic.