Introducción y ejemplo de cluster de contenedores con Docker Swarm

Escrito por el .
gnu-linux planeta-codigo
Enlace permanente Comentarios

Las funcionalidades de Docker Swarm están incorporadas en Docker para gestionar clusters de nodos con contenedores de los servicios que deseemos. En artículo comentaré algunas de las propiedades de networkning distribuido incorporado en Docker, como crear un cluster de nodos Docker usando VirtualBox con máquinas virtuales para simular múltiples máquinas junto con como lanzar un servicio en el cluster que en este caso consistirá en un servidor web nginx.

Docker

En artículos anteriores de la serie sobre Docker comentaba varias de las herramientas de Docker como Docker Compose, Dockerfile o Docker Machine con ejemplos de como usarlo en local. Una de las herramientas que me quedaba por investigar era Docker Swarm para crear clusters de nodos para contenedores Docker en un entorno de producción. A partir de la versión 1.12 de Docker se han incorporado varias características a Docker para usar contenedores de forma distribuida y que a pesar de la complejidad subyacente que debe haber es realmente simple usarlo.

Una de las características es el networking que hace transparente la comunicación en red distribuida que se hace entre los nodos y los contenedores de esos nodos. Además permite crear redes por software para que los contenedores conectados a esas redes se comuniquen de forma privada. Otra característica interesante de Docker Swarm es que se encarga de monitorizar el estado de los servicios recreando contenedores si alguno deja de funcionar. También a través del denominado routing mesh da igual al nodo del cluster por el que se acceda y da igual en que nodo esté el contenedor que Docker Swarm con esta propiedad se encargará de hacer llegar la petición al contenedor. Además, a lo que en Docker Swarm se denomina servicio se realiza balanceo de carga entre la instancias del mismo que haya en el cluster y al servicio se le asigna un DNS y dirección IP por el que puede ser accedido por otros servicios.

En el siguiente ejemplo para crear el cluster de nodos Docker usaré Docker Machine para crear las máquinas de los nodos en máquinas virtuales de VirtualBox aunque su funcionamiento es similar si usásemos la nube de Amazon EC2, Digital Ocean u otros.

El siguiente script crea primeramente varios nodos cada uno en una máquina virtual, luego establece el nodo 01 como manager y los nodos 02 y 03 como workers usando un token para unirlos al cluster según su rol. Los nodos manager se encargan de mantener el estado del cluster y los que a través de ellos los comandos de los servicios deben ser lanzados, en un entorno de producción posiblemente tendríamos 3 nodos manager para soportar tolerancia a fallos. Finalmente, se obtiene lista los nodos del cluster. El comando docker-machine env node-01 permite establecer el entorno contra el que el comando docker lanzará las operaciones como si de la máquina local se tratase.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env bash

#export MACHINE_STORAGE_PATH="/run/media/picodotdev/BMOVE ROJO/docker-machine/"

for i in "01" "02" "03"; do
	docker-machine create -d virtualbox node-$i
done

MANAGER_IP=$(docker-machine ip node-01)
eval $(docker-machine env node-01)

docker swarm init --advertise-addr $MANAGER_IP

MANAGER_TOKEN=$(docker swarm join-token -q manager)
WORKER_TOKEN=$(docker swarm join-token -q worker)

for i in "02" "03";do
	WORKER_IP=$(docker-machine ip node-$i)
	eval $(docker-machine env node-$i)

	docker swarm join --token $WORKER_TOKEN --advertise-addr $WORKER_IP $MANAGER_IP:2377
done

eval $(docker-machine env node-01)

docker-machine ls
docker node ls
01-cluster-create.sh

Una vez creado los nodos es cuando podemos empezar a crear servicios en el cluster. Los servicios son una definición de los contenedores de Docker que queremos que el cluster ejecute. En el ejemplo definiré el servicio de un servidor web nginx, primeramente crearé una red por software en el cluster a la que los servicios pueden conectarse que en el ejemplo (aunque para este no es necesario) utilizaré para hacer una consulta DNS con la herramienta drill para ver el nombre de dominio y dirección IP que asigna Docker Swarm al servicio del servidor web. Con docker service create se crean los servicios, algunos de los parámetros del comando son el nombre del servicio que queremos asignarle, los puertos que expone en este caso el 80 y 443 en el host para que sea accesible desde fuera del cluster, la redes a las que está conectado y finalmente la imagen del contenedor del servicio que en este caso será la versión de nginx con Alpine para Docker. Se pueden listar los servicios que contiene el cluster con docker service ls y los procesos de cada nodo donde podemos ver en que nodos se está ejecutando los contenedores con docker ps.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env bash

#export MACHINE_STORAGE_PATH="/run/media/picodotdev/BMOVE ROJO/docker-machine/"
eval $(docker-machine env node-01)

docker network create --driver overlay nginx
docker network ls -f "driver=overlay"

docker service create --name nginx -p 80:80 -p 443:443 --network nginx nginx:stable-alpine
sleep 1m

docker service create --name util --network nginx --mode global alpine sleep 1000000000
sleep 1m

docker service ls
docker service ps nginx
docker service ps util

UTIL_CONTAINER_ID=$(docker ps -q --filter label=com.docker.swarm.service.name=util)
docker exec -it $UTIL_CONTAINER_ID apk add --update drill
docker exec -it $UTIL_CONTAINER_ID drill nginx
docker service rm util

sleep 5s

for i in "01" "02" "03"; do
	eval $(docker-machine env node-$i)
	docker ps
done

eval $(docker-machine env node-01)

for i in "01" "02" "03"; do
    NODE_IP=$(docker-machine ip node-$i)
    curl http://$NODE_IP/
done
06-nginx-create.sh

Una de las propiedades interesantes del networking de Docker Swarm es que ofrece incorporado balanceo de carga, esto es, si el servicio de nginx del ejemplo estuviese formado por dos instancias las peticiones se distribuirían entre las instancias usando el método round-robin. Otra característica interesante si se observa el ejemplo con detalle es que da igual el nodo al que hagamos la petición que la respuesta se obtendrá igualmente, esto es, aunque la petición se haga al nodo 01 y realmente el contenedor del servidor nginx se esté ejecutando en el nodo 02 la petición se realizará correctamente gracias al routing mesh del networking de Docker Swarm, esto es gracias a que cada servicio tiene asignada una dirección IP, como se ha visto anteriormente en la salida del comando drill.

En este vídeo de asciinema se ve en funcionamiento todos los anteriores comandos. Y en la aplicación de VirtualBox estarán las máquinas virtuales de cada uno de los nodos que crea el ejemplo. En el vídeo se aprecia que el servicio de nginx se está ejecutando en el nodo 02 cuando se listan los procesos de Docker de cada nodo con docker ps, nótese sin embargo que al hacer un petición HTTP a cualquiera de los nodos se devuelve la página de inicio de nginx ya que gracias al routing mesh de Docker Swarm la petición se redirige de forma transparente para el cliente y el servicio al nodo donde realmente se está ejecutando el contenedor de nginx.

Máquinas virtuales de los nodos del _cluster_ de Docker Swarm

Máquinas virtuales de los nodos del cluster de Docker Swarm

Los comandos para eliminar un servicio del cluster y eliminar completamente el cluster son los siguientes.

1
2
3
4
5
6
7
#!/usr/bin/env bash

#export MACHINE_STORAGE_PATH="/run/media/picodotdev/BMOVE ROJO/docker-machine/"
eval $(docker-machine env node-01)

docker service rm nginx
docker network rm nginx
nginx-remove.sh
1
2
3
4
5
6
7
#!/usr/bin/env bash

#export MACHINE_STORAGE_PATH="/run/media/picodotdev/BMOVE ROJO/docker-machine/"

for i in "01" "02" "03"; do
	docker-machine rm -f node-$i
done
cluster-remove.sh

Un libro que me ha gustado mucho y que recomiendo leer sobre Docker Swarm es The Devops 2.1 Toolkit que lo explica detalladamente y todo el libro está orientado a como usarlo en un entorno de producción. Un libro más introductorio que también he leído y que está bastante bien es Docker in Action.

Finalmente, quizás si estás usando GNU/Linux y VirtualBox como yo al crear los nodos con el comando docker-machine te produzca el siguiente error (quizá se corrija en futuras versiones de Docker o VirtualBox).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Setting Docker configuration on the remote daemon...

This machine has been allocated an IP address, but Docker Machine could not
reach it successfully.

SSH for the machine should still work, but connecting to exposed ports, such as
the Docker daemon port (usually <ip>:2376), may not work properly.

You may need to add the route manually, or use another related workaround.

This could be due to a VPN, proxy, or host file configuration issue.

You also might want to clear any VirtualBox host only interfaces you are not using.
Checking connection to Docker...
Error creating machine: Error checking the host: Error checking and/or regenerating the certs: There was an error validating certificates for host "192.168.99.100:2376": dial tcp 192.168.99.100:2376: i/o timeout
You can attempt to regenerate them using 'docker-machine regenerate-certs [name]'.
Be advised that this will trigger a Docker daemon restart which might stop running containers.
vboxnet0-error.out

La solución que he encontrado para que funcione es asignar una dirección IP al adaptador puente solo-anfitrión y levantar la interfaz que usa Docker para comunicarse con las máquinas virtuales previamente a crear el nodo. En Arch Linux con los siguientes comandos.

1
2
3
4
#!/usr/bin/env bash

sudo ip addr add 192.168.99.1/24 dev vboxnet0
sudo ip link set dev vboxnet0 up
01-vboxnet0-configure.sh

Se puede definir un conjunto de servicios como una unidad en un archivo en stacks de forma similar a como es posible hacer con Docker Compose cosa que mostraré en otro artículo.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub.


Comparte el artículo: