El objetivo de este laboratorio es crear una stack de servicios, es decir, definiciones en un archivo de texto en formato YAML de múltiples servicios además de volúmenes y redes orquestados por docker-compose. Para ello, aprovecharemos las imágenes Docker que hemos generado en los laboratorios anteriores. Como resultado, obtendremos un fichero YAML con toda la configuración necesaria para levantar nuestro stack con un solo comando.
Se trata de una herramienta ideal para ejecutar stacks de aplicaciones contenerizadas. Por ejemplo, un conjunto de servicios que requieren ser orquestados de forma conjunta. Un caso típico es una aplicación compuesta de un front-end, un back-end y una capa de persistencia. Mediante una definición YAML, se configuran todos los servicios implicados en el stack y, mediante un único comando, permite desplegar el stack completo de servicios.
Se trata de un tipo de orquestación de stacks de contenedores. Cuando queremos gestionar distintos servicios que tienen que interactuar entre sí, compose se encarga de mantener los procesos y configurar la red subyacente para que estos estén constantemente up and running. Si alguno de los servicios falla, compose se encarga de volver a arrancarlo manteniendo la integridad del stack.
En un fichero docker-compose, es posible definir tres tipos de recursos básicos:
- Servicios: la sección de servicios hace referencia a la configuración de los contenedores. Teniendo en cuenta nuestro objetivo final, nuestra sección se servicios estará compuesta por tres servicios contenerizados, un servicio de front, un back y una capa de persistencia.
- Volúmenes: hace referencia a un espacio de disco físico que será reservado en el host para persistir la información de uno o varios servicios contenerizados.
- Redes: hace referencia a la capa de comunicación que se va a definir para el stack. Aquí se definirán las reglas de comunicación entre los contenedores e incluso entre los contenedores y el host.
services:
frontend:
image: my-front
...
backend:
image: my-back
...
db:
image: persistence
...
volumes:
my-volume:
networks:
my-stack-network:
Antes de empezar este laboratorio, debemos asegurarnos que los contenedores no estan corriendo ya que al levantar los servicios del stack, podemos encontrarnos con un conflicto de puertos. Para ello, podemos ejecutar el siguiente comando:
# Este comando nos devuelve una lista de los contenedores independientemente de su estado
docker ps -a
Si el comando anterior nos devuelve contenedores, para asegurarnos que no existe ningun conflicto, ejecutamos el siguiente comando:
# Este comando elimina todos los contenedores independientemente de su estado
docker rm -f $(docker ps -aq)
Podemos observar que eliminar los contenedores no implica eliminar las imágenes o los volumenes que hemos creado anteriormente
# Con este comando podemos ver la lista de volúmenes
docker volume ls
# Con este comando podemos ver la lista de imágenes
docker images
No es necesario si vamos a trabajar con Play With Docker
Descarga el paquete
curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
Aplica permisos de ejecución al fichero binario
chmod +x /usr/local/bin/docker-compose
Añádelo al PATH mediante un enlace simbólico
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
Recomendamos usar la versión estable Tras la instalación, ejecuta el siguiente comando:
docker-compose --version
Si todo ha ido bien, se mostrará la versión instalada de docker-compose.
Cramos una carpeta en el directorio de root
mkdir docker-compose
Creamos un fichero docker-compose.yaml usando tu editor de texto favorito.
cd docker-compose
vi docker-compose.yaml
Para definir la configuración del servicio de frontend, debemos tener en cuenta los siguientes puntos:
- Nombre del contenedor
- Hostname
- Nombre de la imagen
- Puerto que expone
frontend:
container_name: frontend
hostname: frontend
image: helloworld:latest
ports:
- 80:80
Para definir la configuración del servicio de backend, debemos tener en cuenta los siguientes puntos:
- Nombre del contenedor
- Hostname
- Nombre de la imagen
- Variables de entorno
- Puerto que expone
backend:
container_name: backend
hostname: backend
image: spring_boot_app
environment:
- SPRING_PROFILES_ACTIVE=db
- DB_URL=db
ports:
- 8080:8080
Para definir la configuración del servicio de persistencia, debemos tener en cuenta los siguientes puntos:
- Nombre del contenedor
- Hostname
- Nombre de la imagen
- Puerto que expone
- Mapeo del volumen de persistencia de datos indicando el tipo de volumen, el nombre y el target.
db:
container_name: db
hostname: db
image: postgres:latest
ports:
- 5432:5432
volumes:
- type: volume
source: postgres-data
target: /var/lib/postgresql/data
Ahora que tenemos la configuración de los tres servicios, ya podemos definir el docker-compose completo. Guarda fichero como "docker-compose.yaml". Hay que tener en cuenta, que en los ficheros YAML es necesario respetar la identación y no usar tabulaciones.
version: '3.2'
services:
my-frontend:
container_name: frontend
hostname: frontend
image: helloworld:latest
ports:
- 80:80
my-backend:
container_name: backend
hostname: backend
image: spring_boot_app
environment:
- SPRING_PROFILES_ACTIVE=db
- DB_URL=db
ports:
- 8080:8080
db:
container_name: db
hostname: db
image: postgres:latest
ports:
- 5432:5432
volumes:
- type: volume
source: postgres-data
target: /var/lib/postgresql/data
volumes:
postgres-data:
external: true
Recuerda definir el recurso de volúmenes
Para levantar el stack de recursos definido anteriormente, ejecuta el siguiente comando en la ruta donde tengas el fichero docker-compsoe:
docker-compose up -d
Utilizamos el flag "-d" para levantar el stack en modo detached. Si falla, lo mejor será quitar el flag para ver los logs de arranque.
docker-compose irá informando a medida que los diferentes servicios esten levantados.
[node1] (local) root@192.168.0.33 ~/docker-compose
$ docker-compose up -d
Creating network "docker-compose_default" with the default driver
Creating db ... done
Creating backend ... done
Creating frontend ... done
Como puedes ver en el log, docker-compose se ha encargado de crear una nueva red para levantar nuestros servicios (docker-compose_default). Es por esto, que todos los servicios que estén en esa red podrán descubrirse mediante hostname. Al igual que en los casos anteriores, podemos conocer el estado de los servicios mediante los siguientes comandos:
[node1] (local) root@192.168.0.33 ~/docker-compose
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------
backend java -jar demo.jar Up 0.0.0.0:8080->8080/tcp
db docker-entrypoint.sh postgres Up 0.0.0.0:5432->5432/tcp
frontend nginx -g daemon off; Up 0.0.0.0:80->80/tcp
[node1] (local) root@192.168.0.33 ~/docker-compose
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7d1f619c3b09 spring_boot_app "java -jar demo.jar" 8 minutes ago Up 8 minutes 0.0.0.0:8080->8080/tcp backend
d5cf55d4bd10 helloworld:latest "nginx -g 'daemon of…" 8 minutes ago Up 8 minutes 0.0.0.0:80->80/tcp frontend
b13347584877 postgres:latest "docker-entrypoint.s…" 8 minutes ago Up 8 minutes 0.0.0.0:5432->5432/tcp db
Si utilizamos el comando "docker inspect" podemos observar como nuestra base de datos tiene un volumen mapeado al directorio del volumen persistente que preparamos en el lab04:
docker inspect db
"Mounts": [
{
"Type": "volume",
"Name": "postgres-data",
"Source": "/var/lib/docker/volumes/postgres-data/_data",
"Destination": "/var/lib/postgresql/data",
"Driver": "local",
"Mode": "rw",
"RW": true,
"Propagation": ""
}
]
Hemos comprobado que los servicios se han levantado correctamente y que la BBDD tiene un volumen mapeado, ¿Serías capaz de comprobar si la aplicación funciona correctamente?
En este laboratorio hemos levantado un stack de servicios dockerizados mediante docker-compose. Esta herramienta ha gestionado de manera automática la generación de los contenedores, la creación de una red común (y aislada) para nuestros servicios, el mapeo de puertos y de volúmenes, mediante la definición de un fichero YAML y la utilización de un solo comando. Si ejecutamos el siguiente comando, podemos ver como docker-compose elimina los recursos generados:
[node1] (local) root@192.168.0.33 ~/docker-compose
$ docker-compose down
Stopping db ... done
Stopping backend ... done
Stopping frontend ... done
Removing db ... done
Removing backend ... done
Removing frontend ... done
Removing network docker-compose_default
En este log, docker-compose nos está indicando que está parando cada uno de los servicios, eliminando cada contenedor y eliminando la red que había creado para levantarlos. De esta forma, docker-compose se vuelve una herramienta sumamente útil para aquellas aplicaciones con interdependencias y que necesitan de más servicios para funcionar correctamente. Como podemos ver, los servicios interactúan entre sí de forma conjunta dentro de una red aislada. Es por eso que, por ejemplo, el backend puede comunicarse con la base de datos mediante nombre del servicio y no es necesario indicar una URL o una IP.
< Lab 04 - PostgreSQL y volúmenes persistentes con docker | Lab 06 - Escalado y orquestración de microservicios >