Clúster de Docker con Swarm Mode

Publicado por Ignacio Sánchez Ginés el

DockerDevOpsVagrantDocker Swarm

A partir de la versión 1.12 de Docker es muy sencillo hacer un clúster formado por varios Docker Hosts mediante lo que se conoce como Swarm Mode.

Swarm es ahora parte integral del engine y no tendremos que instalar elementos adicionales.

Además, se han incluido nuevas funcionalidades como escalado, balanceo de carga y rolling updates, todo ello completamente integrado en la instalación de Docker.

Vamos a ver en primer lugar los conceptos básicos dentro de Swarm y después, gracias a Vagrant, veremos cómo crear un clúster funcional.

Si lo que quieres es ver directamente el Vagrantfile aquí tienes el repo.

Nodos

En un clúster de Swarm existen dos tipos de nodo, Manager y Worker.

Los nodos Manager son los encargados de gestionar el clúster. Entre todos los Manager se elige automáticamente un líder y éste es el encargado de mantener el estado del clúster.

Los Manager son también los encargados de distribuir las tareas o tasks (unidades básicas de trabajo) entre todos los nodos Worker, los cuales reciben estas tareas y las ejecutan.

Los nodos Manager por defecto también actúan como nodos Worker aunque se puede cambiar su configuración para que sólo asuman tareas de Manager.

Servicios y tareas

Un servicio define las tareas que serán ejecutadas dentro del clúster.

Cuando creamos un servicio le indicamos a Swarm qué imagen y qué parametrización se utilizará para crear los contenedores que se ejecutarán después como tareas dentro del clúster.

Existen dos tipos de servicios, replicados y globales:

  • En un servicio replicado, Swarm creará una tarea por cada réplica que le indiquemos para después distribuirlas en el clúster. Por ejemplo, si creamos un servicio con 4 réplicas, Swarm creará 4 tareas.
  • En un servicio global, Swarm ejecutará una tarea en cada uno de los nodos del clúster.

Como hemos dicho antes, las tareas son la unidad de trabajo dentro de Swarm. Realmente son la suma de un contenedor más el comando que ejecutaremos dentro de ese contenedor.

Los Manager asignan tareas a los nodos Worker de acuerdo al número de réplicas definidas por el servicio. Una vez que la tarea es asignada a un nodo ya no se puede mover a otro, tan sólo puede ejecutarse o morir.

Ante la caída de una tarea, Swarm es capaz de crear otra similar en ese u otro nodo para cumplir con el número de réplicas definido.

Balanceo

Swarm tiene un sistema de balanceo interno para exponer servicios hacia el exterior del clúster.

Un Manger es capaz de publicar automáticamente un puerto generado al azar en el rango 30000-32767 para cada servicio, o bien, nosotros podemos publicar uno específico.

Cualquier sistema externo al clúster podrá acceder al servicio en este puerto publicado a través de cualquier nodo del clúster, independientemente de que ese nodo esté ejecutando una tarea del servicio o no.

Todos los nodos del clúster enrutarán a una tarea que esté ejecutando el servicio solicitado.

Además, Swarm cuenta con un DNS interno que asigna automáticamente una entrada a cada uno de los servicios desplegados en el clúster.

Creación del clúster

Para crear un clúster con Swarm Mode tenemos que partir de un nodo destinado a ser Manager. Este nodo debe tener Docker 1.12 o superior ya instalado.

Suponiendo que la IP del nodo es 192.168.3.80, ejecutamos el siguiente comando:

$ docker swarm init --advertise-addr 192.168.3.80
Swarm initialized: current node (cf2mywzpa3pd77z23u5jciqwi) is now a manager.

To add a worker to this swarm, run the following command:  
    docker swarm join \
    --token SWMTKN-1-50edlk935u9qgvrs8alhpzf1awgdil2dmfs4zgpd8ue2ltkmww-3y9tb0pjnieqao9ahkutpvpxe \
    192.168.3.80:2377

To add a manager to this swarm, run the following command:  
    docker swarm join \
    --token SWMTKN-1-50edlk935u9qgvrs8alhpzf1awgdil2dmfs4zgpd8ue2ltkmww-06m3vta11n4ihjyr1ytycwqvf \
    192.168.3.80:2377

Al ejecutar el comando hemos inicializado este nodo como Manager.

Con el parámetro obligatorio --advertise-addr le indicamos la IP del Manager que se utilizará internamente para las conexiones de Swarm. Si omitimos el puerto tomará el 2377 por defecto.

La salida del comando nos muestra dos tokens. Cada uno de ellos sirve para unir nodos Manager y Worker adicionales.

Añadiremos ahora un nodo Worker al clúster. Para ello y desde la consola del Worker ejecutamos:

$ docker swarm join --token  SWMTKN-1-50edlk935u9qgvrs8alhpzf1awgdil2dmfs4zgpd8ue2ltkmww-3y9tb0pjnieqao9ahkutpvpxe 192.168.3.80:2377
This node joined a swarm as a worker.  

Como puedes ver hemos utilizado el token para nodos Worker. Le indicamos también la IP y puerto de uno de los nodos Manager del clúster existente.

Si queremos añadir otro Manager el comando sería el mismo, pero usaríamos el otro token.

Gestión de servicios

Una vez tengamos el clúster preparado, podemos empezar a correr servicios sobre él.

Para dar de alta un nuevo servicio nos vamos a la consola de un Manager y ejecutamos lo siguiente:

$ docker service create --replicas 1 --name helloworld alpine ping enmilocalfunciona.io

Donde --name es el nombre del servicio y --replicas es el número de tareas de este servicio que queremos crear.

Ahora podemos ver un listado de todos los servicios en el clúster:

$ docker service ls

ID            NAME        SCALE  IMAGE   COMMAND  
9uk4639qpg7n  helloworld  1/1    alpine  ping enmilocalfunciona.io  

Y podemos ver información sobre el servicio:

$ docker service inspect --pretty helloworld

ID:     9uk4639qpg7npwf3fn2aasksr  
Name:       helloworld  
Mode:       REPLICATED  
 Replicas:      1
Placement:  
UpdateConfig:  
 Parallelism:   1
ContainerSpec:  
 Image:     alpine
 Args:  ping docker.com

Podemos ver todas las tareas que se están ejecutando para este servicio:

$ docker service ps helloworld

ID                         NAME          SERVICE     IMAGE   LAST STATE         DESIRED STATE  NODE  
8p1vev3fq5zm0mi8g0as41w35  helloworld.1  helloworld  alpine  Running 3 minutes  Running        worker2  

Si queremos aumentar o disminuir el número de réplicas ejecutamos:

$ docker service scale helloworld=5

helloworld scaled to 5  

Y después comprobamos en qué nodos están corriendo las nuevas réplicas:

$ docker service ps helloworld

ID                         NAME          SERVICE     IMAGE   LAST STATE          DESIRED STATE  NODE  
8p1vev3fq5zm0mi8g0as41w35  helloworld.1  helloworld  alpine  Running 7 minutes   Running        worker2  
c7a7tcdq5s0uk3qr88mf8xco6  helloworld.2  helloworld  alpine  Running 24 seconds  Running        worker1  
6crl09vdcalvtfehfh69ogfb1  helloworld.3  helloworld  alpine  Running 24 seconds  Running        worker1  
auky6trawmdlcne8ad8phb0f1  helloworld.4  helloworld  alpine  Running 24 seconds  Accepted       manager1  
ba19kca06l18zujfwxyc5lkyn  helloworld.5  helloworld  alpine  Running 24 seconds  Running        worker2  

Rolling Updates

Otra funcionalidad de Swarm Mode son los Rolling Updates, es decir, la capacidad de gestionar actualizaciones en los servicios.

Si la imagen de un servicio se ha actualizado o cambia, podemos decirle a Swarm que actualice el servicio y, a su vez, todas las tareas asociadas al servicio.

La actualización de las tareas se puede hacer secuencialmente o en paralelo.

Por defecto se hace secuencialmente. Si queremos paralelizar las actualizaciones debemos indicarlo en el momento de la creación del servicio:

$ docker service create --replicas 4 --name redis --update-delay 10s --update-parallelism 2 redis:3.0.6

Donde --update-delay es el tiempo que esperará hasta empezar con la actualización de la siguiente tarea, y donde --update-parallelism será el número de tareas que actualizará en paralelo.

Para realizar una actualización le indicamos la nueva imagen:

$ docker service update --image redis:3.0.7 redis

Ejemplo con Vagrant

He preparado un Vagrantfile que define la creación de 4 máquinas: 1 Manager y 3 Workers.

Puedes descargarlo desde este repositorio en GitHub.

Para ejecutar el ejemplo necesitas Vagrant y VirtualBox instalados en tu equipo.

Abre un terminal, ubícate en la carpeta donde está el Vagrantfile y ejecuta lo siguiente:

$ vagrant up

Tardará un rato en crear y provisionar las 4 máquinas. Una vez terminado podrás ver las máquinas corriendo en la consola de VirtualBox.

El proceso de provisión dentro del Vagrantfile inicializa el clúster, por lo que la máquina manager será un nodo Manager y las máquinas worker01, worker02 y worker03 serán nodos Worker.

Vuelve a la línea de comandos y ejecuta lo siguiente:

$ vagrant ssh manager

Con este comando establecemos una conexión SSH con el nodo Manager. Desde aquí podemos lanzar comandos contra el clúster.

Por ejemplo, podemos ver todos los nodos:

ubuntu@manager:~$ docker node ls  
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS  
00wdlxkq8z3vclk4qhnvbwcte *  manager   Ready   Active        Leader  
0hb5gtdgyz12ur6tc0w19i1ms    worker01  Ready   Active  
27kfxl9tt8cp8tlocsixzadhs    worker03  Ready   Active  
eym3j1kpehhnpi3kn6y3t8tgr    worker02  Ready   Active  

Crear un servicio nuevo:

ubuntu@manager:~$ docker service create --replicas 10 --name helloworld -p 8080:8080 drhelius/helloworld-node-microservice  
ewa6g6c3vg2kbqzl5uqvi3pli

Y ver sus tareas:

ubuntu@manager:~$ docker service ps helloworld  
ID                         NAME           IMAGE                                  NODE      DESIRED STATE  CURRENT STATE           ERROR  
dzzx4h9hvrtw3epmevcu7yy2l  helloworld.1   drhelius/helloworld-node-microservice  worker01  Running        Running 21 seconds ago  
bno0v0qbwg0w0s6f1g6x1alf8  helloworld.2   drhelius/helloworld-node-microservice  worker01  Running        Running 19 seconds ago  
dsr04ea3elew33o322rgc7hrf  helloworld.3   drhelius/helloworld-node-microservice  worker02  Running        Running 19 seconds ago  
dbdx3h4i2wu9vv9blbummxjbf  helloworld.4   drhelius/helloworld-node-microservice  manager   Running        Running 18 seconds ago  
dzvx5ytxesyep19w3um0cjydt  helloworld.5   drhelius/helloworld-node-microservice  manager   Running        Running 18 seconds ago  
7ok2a8f38v8vvowte2296l5si  helloworld.6   drhelius/helloworld-node-microservice  manager   Running        Running 18 seconds ago  
99cu0ng1gwrld9qn9zob71lo6  helloworld.7   drhelius/helloworld-node-microservice  worker02  Running        Running 19 seconds ago  
52ocauexh8pxkn2sl6xqtr3he  helloworld.8   drhelius/helloworld-node-microservice  worker03  Running        Running 20 seconds ago  
1p1rxo3cjku5wbddseig86d9c  helloworld.9   drhelius/helloworld-node-microservice  worker03  Running        Running 19 seconds ago  
cj7q61uifz0aha4axzotko868  helloworld.10  drhelius/helloworld-node-microservice  worker03  Running        Running 19 seconds ago  

Para consumir este servicio podemos realizar una petición sobre cualquier nodo del clúster.

Swarm enrutará nuestra petición a un nodo que tenga una tarea de ese servicio corriendo.

Por ejemplo, podemos probarlo apuntando al Manager y utilizando el puerto que expusimos en su creación, 8080:

ubuntu@manager:~$ curl localhost:8080  
Hello World!  

Aunque esta prueba funcionaría desde cualquier nodo o desde el exterior del clúster si apuntamos a la IP de un nodo.

En este momento podemos simular el fallo del nodo worker03 con 3 tareas corriendo.

Swarm tratará de mantener el estado dentro del clúster. Como se pierden 3 tareas, tendrá que ejecutar 3 nuevas en otros nodos.

Para probarlo, terminamos la sesión SSH con el Manager, entramos en el worker03 y lo apagamos:

ubuntu@manager:~$ exit

$ vagrant ssh worker03                                                                                

ubuntu@worker03:~$ sudo shutdown -h now  

Entramos de nuevo en el Manager y vemos qué ha pasado:

ubuntu@manager:~$ docker service ps helloworld  
ID                         NAME               IMAGE                                  NODE      DESIRED STATE  CURRENT STATE           ERROR  
dzzx4h9hvrtw3epmevcu7yy2l  helloworld.1       drhelius/helloworld-node-microservice  worker01  Running        Running 4 minutes ago  
bno0v0qbwg0w0s6f1g6x1alf8  helloworld.2       drhelius/helloworld-node-microservice  worker01  Running        Running 4 minutes ago  
dsr04ea3elew33o322rgc7hrf  helloworld.3       drhelius/helloworld-node-microservice  worker02  Running        Running 4 minutes ago  
dbdx3h4i2wu9vv9blbummxjbf  helloworld.4       drhelius/helloworld-node-microservice  manager   Running        Running 4 minutes ago  
dzvx5ytxesyep19w3um0cjydt  helloworld.5       drhelius/helloworld-node-microservice  manager   Running        Running 4 minutes ago  
7ok2a8f38v8vvowte2296l5si  helloworld.6       drhelius/helloworld-node-microservice  manager   Running        Running 4 minutes ago  
99cu0ng1gwrld9qn9zob71lo6  helloworld.7       drhelius/helloworld-node-microservice  worker02  Running        Running 4 minutes ago  
bi9hj1ewoudsvus0otqgy5851  helloworld.8       drhelius/helloworld-node-microservice  worker01  Running        Running 12 seconds ago  
52ocauexh8pxkn2sl6xqtr3he   \_ helloworld.8   drhelius/helloworld-node-microservice  worker03  Shutdown       Running 4 minutes ago  
3n0l2q8xk8ub6teh9n6fm014l  helloworld.9       drhelius/helloworld-node-microservice  worker01  Running        Running 13 seconds ago  
1p1rxo3cjku5wbddseig86d9c   \_ helloworld.9   drhelius/helloworld-node-microservice  worker03  Shutdown       Running 4 minutes ago  
93b9jwhx3kef40vp2yncu2jgr  helloworld.10      drhelius/helloworld-node-microservice  worker02  Running        Running 12 seconds ago  
cj7q61uifz0aha4axzotko868   \_ helloworld.10  drhelius/helloworld-node-microservice  worker03  Shutdown       Running 4 minutes ago

Si en este momento arrancamos de nuevo el worker03 veremos que las tareas se quedan como están, ya que ahora no es necesario ningún cambio para cumplir con las 10 réplicas.

Conclusión

Hemos visto los conceptos básicos y algunos comandos de Swarm Mode.

También hemos comprobado lo sencillo que resulta crear un clúster y hacerlo funcionar en Vagrant.

Toda la documentación sobre Swarm Mode se encuentra aquí.

Además, tenemos un listado con todos los comandos de Swarm.

A partir de aquí, puedes seguir haciendo cualquier tipo de prueba sobre el clúster en Vagrant.

Puedes consultar otros posts sobre Docker aquí.