Cluster Docker en Swarm mode sobre Docker-on-Docker

Este experimento surge como prueba de concepto para tratar de ver qué posibilidades ofrece docker-on-docker para un uso en laboratorios.

Hemos usado muchas veces docker dentro de un contenedor como cliente para desplegar entornos más complejos en los que por ejemplo no tenemos docker-compose o bien queremos aislar la ejecución del despliegue del sistema subyacente (por ejemplo conteniendo los ficheros de despliegue en la propia imagen); pero también puede ser útil levantar un contenedor con el propio demonio de docker.

En este laboratorio, usaremos una imagen docker-on-docker preparada usando alpine como base junto con todos los paquetes y sus dependencias necesarias (git iptables curl xz python py-pip procps). Añadimos además pip para instalar docker-compose como módulo python (no recuerdo ahora mismo por qué, pero tuve algún problema en este caso con la instalación).

En la imagen, expondremos al mundo el puerto estándar 2375 y el rango desde el 23768 al 65000 para permitir la publicación al exterior de contenedores creados sobre los contenedores docker-on-docker.

Comenzaremos creando un demonio docker “dockerizado” que publique la API en el puerto 2375 y el socket estandar.

Descargamos la imagen y crearemos un entorno de cluster usando el modo swarm, compuesto por 3 contenedores manager y un worker.

Para aislar el entorno de cluster, creamos una red llamada “dond” (el tipo dependerá del entorno, pero podemos usar perfectamente tipo bridge para usar localmente)

$ docker pull frjaraur/docker-on-docker

zero@betelgeuse:~$ docker pull frjaraur/docker-on-docker

Using default tag: latest

latest: Pulling from frjaraur/docker-on-docker

3690ec4760f9: Already exists

1d665f10bf06: Already exists

a0f6fbfda2f2: Already exists

2cc1b5ecb2e5: Already exists

6762ce100ad2: Already exists

Digest: sha256:5b52c7a250c014cc3478f2b03e8a53b14de54870627f69a70802944a7eabbcc9

Status: Downloaded newer image for frjaraur/docker-on-docker:latest

$ docker network create dond

zero@betelgeuse:~$ docker network create dond

1d2fc56781ac66bc6badf53c4309ee8225d3c5b72f5819f741783587737ad775

Una vez creada la red, creamos el primer contenedor

$ docker run –privileged -v /etc/timezone:/etc/timezone:ro \

-v /etc/localtime:/etc/localtime:ro \

-v /lib/modules:/lib/modules:ro \

-p 8080:8080 –net=dond \

–name dond1 -d frjaraur/docker-on-docker

zero@betelgeuse:~$ docker run –privileged -v /etc/timezone:/etc/timezone:ro \

> -v /etc/localtime:/etc/localtime:ro \

> -v /lib/modules:/lib/modules:ro \

> -p 8080:8080 –net=dond \

> –name dond1 -d frjaraur/docker-on-docker

1ec4b6565a9afc4b9a601022225d21396fb1d0761a7f296150d54b938e49f059

Hemos usado el modo privilegiado para no tener que añadir recursos de uno en uno.

Añadimos los ficheros localtime y timezone del host local en lectura para sincronizar los eventos en los logs. Además, al usar alpine necesitamos incluir el directorio de módulos, también en modo lectura.

El puerto 8080 lo exponemos y publicamos para poder probar posteriormente la publicación de una aplicación en el interior del cluster. En el resto de contenedores que formarán parte del cluster no es necesario realizar la publicación gracias a “routing mesh”.

Para facilitar la gestión, nombraremos los contenedores. Por último, todos estarán disponibles en al red “dond” creada anteriormente.

El resto de contenedores del cluster se crearán de la siguiente forma

$ docker run –privileged -v /etc/timezone:/etc/timezone:ro \

-v /etc/localtime:/etc/localtime:ro \

-v /lib/modules:/lib/modules:ro \

–net=dond –name dond2 -d frjaraur/docker-on-docker

$ docker run –privileged -v /etc/timezone:/etc/timezone:ro \

-v /etc/localtime:/etc/localtime:ro \

-v /lib/modules:/lib/modules:ro \

-v /tmp:/tmp \

–net=dond –name dond3 -d frjaraur/docker-on-docker

$ docker run –privileged -v /etc/timezone:/etc/timezone:ro \

-v /etc/localtime:/etc/localtime:ro \

-v /lib/modules:/lib/modules:ro \

-v /tmp:/tmp \

–net=dond –name dond4 -d frjaraur/docker-on-docker

zero@betelgeuse:~$ docker run –privileged -v /etc/timezone:/etc/timezone:ro \

> -v /etc/localtime:/etc/localtime:ro \

> -v /lib/modules:/lib/modules:ro \

> –net=dond –name dond2 -d frjaraur/docker-on-docker

26a144e826e4c95f2f6a6842cd35637e5f9683a255686e078f70bd97aa2c586e

zero@betelgeuse:~$ docker run –privileged -v /etc/timezone:/etc/timezone:ro \

> -v /etc/localtime:/etc/localtime:ro \

> -v /lib/modules:/lib/modules:ro \

> -v /tmp:/tmp \

> –net=dond –name dond3 -d frjaraur/docker-on-docker

c955c1bbd1b709514ed5071229de0c66e587641e335e5e483295aea4612b6a03

zero@betelgeuse:~$ docker run –privileged -v /etc/timezone:/etc/timezone:ro \

> -v /etc/localtime:/etc/localtime:ro \

> -v /lib/modules:/lib/modules:ro \

> -v /tmp:/tmp \

> –net=dond –name dond4 -d frjaraur/docker-on-docker

f936c04d484811235dba35bd74c84b20f415b8052e978c3e656d2681426fe01e

Ahora que tenemos 4 contenedores con el demonio de docker en ejecución, vamos a crear el cluster con ellos.

Iniciamos el cluster en el contenedor “dond1”

$ docker exec dond1 docker swarm init

zero@betelgeuse:~$ docker exec dond1 docker swarm init

Swarm initialized: current node (1591odwhb59njsnyg9iqkcjjg) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join \

–token SWMTKN-1-3ju07qz2cdr2w7fw3jzlrgdjo5iy41skoo9ljb18z07172xwex-3tdqjygqohionz2b9q9ax89tu \

172.20.0.2:2377

To add a manager to this swarm, run ‘docker swarm join-token manager’ and follow the instructions.

Obtenemos los token necesarios tanto para los nodos de cluster con rol de manager como para los de tipo worker.

$ docker exec dond1 docker swarm join-token -q manager

zero@betelgeuse:~$ docker exec dond1 docker swarm join-token -q manager

SWMTKN-1-3ju07qz2cdr2w7fw3jzlrgdjo5iy41skoo9ljb18z07172xwex-cc4vnqri0r7myhoplbxgx75ms

$ docker exec dond1 docker swarm join-token -q worker

zero@betelgeuse:~$ docker exec dond1 docker swarm join-token -q worker

SWMTKN-1-3ju07qz2cdr2w7fw3jzlrgdjo5iy41skoo9ljb18z07172xwex-3tdqjygqohionz2b9q9ax89tu

Posteriormente usaremos los token mostrados.

Para los contendores con rol de tipo manager ejecutaremos

$ docker exec dond2 docker swarm join –token <MANAGERS_TOKEN>

<DOCKER_CONTAINER_dond1_IP>:2377

$ docker exec dond2 docker swarm join –token <MANAGERS_TOKEN>

<DOCKER_CONTAINER_dond1_IP>:2377

zero@betelgeuse:~$ docker exec dond2 docker swarm join \

> –token SWMTKN-1-3ju07qz2cdr2w7fw3jzlrgdjo5iy41skoo9ljb18z07172xwex-cc4vnqri0r7myhoplbxgx75ms \

> 172.20.0.2:2377

This node joined a swarm as a manager.

zero@betelgeuse:~$ docker exec dond3 docker swarm join \

> –token SWMTKN-1-3ju07qz2cdr2w7fw3jzlrgdjo5iy41skoo9ljb18z07172xwex-cc4vnqri0r7myhoplbxgx75ms \

> 172.20.0.2:2377

This node joined a swarm as a manager.

Y para el worker, ejecutamos

$ docker exec dond4 docker swarm join –token <WORKERS_TOKEN>

<DOCKER_CONTAINER_dond1_IP>:2377

zero@betelgeuse:~$ docker exec dond4 docker swarm join \

> –token SWMTKN-1-3ju07qz2cdr2w7fw3jzlrgdjo5iy41skoo9ljb18z07172xwex-3tdqjygqohionz2b9q9ax89tu \

> 172.20.0.2:2377

This node joined a swarm as a worker.

Y es así de simple. Ahora mismo tenemos un cluster de contenedores ejecutando el demonio de docker (docker-on-docker) usando el modo swarm.

Verificamos el entorno ejecutando

$ docker exec dond1 docker node ls

zero@betelgeuse:~$ docker exec dond1 docker node ls

ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS

1591odwhb59njsnyg9iqkcjjg * 1ec4b6565a9a Ready Active Leader

2igbtaqj7tw1q5rczneov57ie f936c04d4848 Ready Active

49oooglxzhf22nvgba859wk7r 26a144e826e4 Ready Active Reachable

4n0jkimrkxzj0rkrucjoooe59 c955c1bbd1b7 Ready Active Reachable

Como podemos observar en la ejecución de “docker node ls” en el contenedor “dond1”, tenemos un cluster funcional formado por 4 nodos (3 managers y 1 worker). Y por supuesto podremos lanzar contenedores para proporcionar servicio dentro de nuestro recién creado cluster (por este motivo hemos mapeado el puerto 8080 de nuestro host al 8080 de uno de los contenedores del cluster, aunque podríamos permitir también la gestión dinámica de los mismos).

$ docker exec dond1 docker service create –name nginx -p 8080:80 nginx:alpine

La imagen nginx:alpine se descargará dentro del cluster para su ejecución y el servicio creado será accesible en el puerto 8080 de los contenedores del cluster (y en el puerto 8080 del host gracias a “routig mesh”).

zero@betelgeuse:~$ docker exec dond1 docker service create –name nginx -p 8080:80 nginx:alpine

c3502qxie1mg47zivvdc1yrfz

zero@betelgeuse:~$ docker exec dond1 docker service ls

ID NAME REPLICAS IMAGE COMMAND

c3502qxie1mg nginx 1/1 nginx:alpine

zero@betelgeuse:~$ docker exec dond1 docker service ps nginx

ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR

28t6medlyjrkrcz8g8autnky5 nginx.1 nginx:alpine 1ec4b6565a9a Running Running 32 seconds ago

Como vemos en el ejemplo de ejecución, el servicio llamado “nginx” se ha levantado en el nodo del cluster “1ec4b6565a9a”, que es el contenedor “dond1” en nuestro entorno de cluster docker-on-docker. Podríamos haber añadido el parámetro “–hostname” en el proceso de creación de los contenedores para poder tener control sobre sus hostname, pero para el laboratorio no es necesario.

zero@betelgeuse:~$ docker exec dond1 hostname

1ec4b6565a9a

Podemos acceder al servicio “nginx” desde los contenedores docker-on-docker que forman parte del cluster.

Por ejemplo, desde el contenedor en el que se ejecuta el servicio, “dond1”, ejecutamos

$ docker exec dond1 curl -sS http://0.0.0.0:8080

zero@betelgeuse:~$ docker exec dond1 curl -sS http://0.0.0.0:8080

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

<style>

body {

width: 35em;

margin: 0 auto;

font-family: Tahoma, Verdana, Arial, sans-serif;

}

</style>

</head>

<body>

<h1>Welcome to nginx!</h1>

<p>If you see this page, the nginx web server is successfully installed and

working. Further configuration is required.</p>

<p>For online documentation and support please refer to

<a href=”http://nginx.org/”>nginx.org</a>.<br/>

Commercial support is available at

<a href=”http://nginx.com/”>nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>

</body>

</html>

Por supuesto, en cualquier otro el resultado sería el mismo (routing mesh)

$ docker exec dond4 curl -sS http://0.0.0.0:8080

zero@betelgeuse:~$ docker exec dond4 curl -sS http://0.0.0.0:8080

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

<style>

body {

width: 35em;

margin: 0 auto;

font-family: Tahoma, Verdana, Arial, sans-serif;

}

</style>

</head>

<body>

<h1>Welcome to nginx!</h1>

<p>If you see this page, the nginx web server is successfully installed and

working. Further configuration is required.</p>

<p>For online documentation and support please refer to

<a href=”http://nginx.org/”>nginx.org</a>.<br/>

Commercial support is available at

<a href=”http://nginx.com/”>nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>

</body>

</html>

Y además, recordando que publicamos el puerto 8080, mapeado al puerto 8080 de nuestro host, en el proceso de creación del contenedor “dond1”, podremos acceder también desde fuera del entorno del cluster docker-on-docker creado, usando el host local.

$ curl -sS http://0.0.0.0:8080

zero@betelgeuse:~$ curl -sS http://0.0.0.0:8080

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

<style>

body {

width: 35em;

margin: 0 auto;

font-family: Tahoma, Verdana, Arial, sans-serif;

}

</style>

</head>

<body>

<h1>Welcome to nginx!</h1>

<p>If you see this page, the nginx web server is successfully installed and

working. Further configuration is required.</p>

<p>For online documentation and support please refer to

<a href=”http://nginx.org/”>nginx.org</a>.<br/>

Commercial support is available at

<a href=”http://nginx.com/”>nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>

</body>

</html>

En resumen, hemos visto de forma sencilla la creación de un entorno de cluster usando el modo swarm de docker 1.12, ejecutado sobre nuestro propio engine docker, gracias a una imagen docker-on-docker. Este entorno creado puede ser útil por ejemplo para probar pequeños despliegues de servicios o incluso para laboratorios en la formación relativa a la gestión de cluster.

¿Os animáis a probarlo?

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.