Docker Swarm
Bien, ahora tenemos una herramienta muy interesante que nos permite construir clusters de containers de forma nativa y con extrema facilidad, como ya es costumbre con los productos creados por el equipo de Docker. ;)
Con Docker Swarm puedes construir clusters de containers con características importantes como balanceador de cargas y failover.
Para crear un cluster con Docker Swarm, basta indicar cuáles hosts supervisará y el resto lo hace él.
Por ejemplo, cuando vayas a crear un nuevo container, lo creará en el host que tenga la menor carga, es decir, se encargará del balanceo de carga y garantizará siempre que el container sea creado en el mejor host disponible en ese momento.
La estructura de cluster de Docker Swarm es bastante simple y se resume a un manager y varios workers. El manager es el responsable de orquestar los containers y distribuirlos entre los hosts workers. Los workers son los que cargan el piano, que hospedan los containers.
13.1. Creando nuestro cluster
Una cosa importante que comenzó después de la versión 1.12 fue la inclusión de Docker Swarm dentro de Docker, es decir, hoy cuando realizas la instalación de Docker, automáticamente estás instalando Docker Swarm, que no es más que una forma de orquestar tus containers mediante la creación de un cluster con alta disponibilidad, balanceo de carga y comunicación cifrada, todo esto nativo, sin ningún esfuerzo o dificultad.
Para nuestro escenario, vamos a utilizar tres máquinas Ubuntu. La idea es hacer que tengamos dos managers y 1 worker.
Necesitamos tener siempre más de un node representando el manager, porque, si nos quedamos sin manager, nuestro cluster estará totalmente indisponible.
Con esto tenemos el siguiente escenario:
-
LINUXtips-01 -- Manager activo.
-
LINUXtips-02 -- Manager.
-
LINUXtips-03 -- Worker.
No hace falta decir que necesitamos tener Docker instalado en todas estas máquinas, ¿verdad, amiguito? :D
Para iniciar, vamos a ejecutar el siguiente comando en "LINUXtips-01":
root@linuxtips-01:~# docker swarm init
Swarm initialized: current node (2qacv429fvnret8v09fqmjm16) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-18wccykydxte59gch2pix 172.31.58.90:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
root@linuxtips-01:~#
Con el comando anterior, ¡iniciamos nuestro cluster!
Observa el siguiente fragmento de la salida del último comando:
# docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-18wccykydxte59gch2pix 172.31.58.90:2377
Esta línea no es más que toda la información que necesitas para agregar workers a tu cluster! ¿Cómo así?
Simple: lo que necesitas ahora es ejecutar exactamente este comando en la próxima máquina que deseas incluir en el cluster como worker! ¡Simple como volar, no?
De acuerdo con nuestro plan, la única máquina que sería worker es la máquina "LINUXtips-03", ¿correcto? Entonces vamos a acceder a ella y ejecutar exactamente la línea de comando recomendada en la salida del "docker swarm init".
root@linuxtips-03:~# docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-18wccykydxte59gch2pix 172.31.58.90:2377
This node joined a swarm as a worker.
root@linuxtips-03:~#
¡Maravilloso! ¡Un node más agregado al cluster!
Para que puedas ver cuáles nodes existen en el cluster, basta ejecutar el siguiente comando en el manager activo:
root@linuxtips-01:~# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
2qac LINUXtips-01 Ready Active Leader 18.03.1-ce
nmxl LINUXtips-03 Ready Active 18.03.1-ce
root@linuxtips-01:~#
Como podemos notar en la salida del comando, tenemos dos nodes en nuestro cluster, uno como manager y otro como worker. La columna "MANAGER STATUS" trae la información de quién es el "Leader", es decir, quién es nuestro manager.
En nuestro plan tendríamos dos managers, ¿correcto?
Ahora la pregunta es: ¿cómo sé cuál es el token que necesito utilizar para agregar un node más a mi cluster, pero esta vez como otro manager?
Recuerda que, cuando ejecutamos el comando para agregar el worker al cluster, teníamos en el comando un token? Pues bien, ese token es quien define si el node será un worker o un manager, y en aquella salida nos trajo solamente el token para agregar workers.
Para que podamos visualizar el comando y el token referente a los managers, necesitamos ejecutar el siguiente comando en el manager activo:
root@linuxtips-01:~# docker swarm join-token manager
To add a manager to this swarm, run the following command:
docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-3i4jsv4i70odu1mes0ebe1l1e 172.31.58.90:2377
root@linuxtips-01:~#
Para visualizar el comando y el token referente a los workers:
root@linuxtips-01:~# docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-18wccykydxte59gch2pixq9av 172.31.58.90:2377
root@linuxtips-01:~#
¿Fácil, no?
Ahora lo que necesitamos es ejecutar en "LINUXtips-02" el comando para inclusión de un node más como manager. Por lo tanto, ejecuta:
root@linuxtips-02:~# docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-3i4jsv4i70odu1mes0ebe1l1e 172.31.58.90:2377
This node joined a swarm as a manager.
root@linuxtips-02:~#
¡Listo! ¡Ahora tenemos nuestro cluster completo con dos managers y un worker!
Vamos a visualizar los nodes que forman parte de nuestro cluster. Recuerda: cualquier comando para administración del cluster o creación de servicios deberá obligatoriamente ser ejecutado en el manager activo, ¡siempre!
root@linuxtips-01:~# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
2qac LINUXtips-01 Ready Active Leader 18.03.1-ce
j6lm LINUXtips-02 Ready Active Reachable 18.03.1-ce
nmxl LINUXtips-03 Ready Active 18.03.1-ce
root@linuxtips-01:~#
Observa que el "MANAGER STATUS" de "LINUXtips-02" es "Reachable". Esto indica que es un manager, pero no es el manager activo, que siempre tiene el "MANAGER STATUS" como "Leader".
Si queremos saber detalles sobre determinado node, podemos usar el subcomando "inspect":
root@linuxtips-01:~# docker node inspect LINUXtips-02
[
{
"ID": "x3fuo6tdaqjyjl549r3lu0vbj",
"Version": {
"Index": 27
},
"CreatedAt": "2017-06-09T18:09:48.925847118Z",
"UpdatedAt": "2017-06-09T18:09:49.053416781Z",
"Spec": {
"Labels": {},
"Role": "worker",
"Availability": "active"
},
"Description": {
"Hostname": "LINUXtips-02",
"Platform": {
"Architecture": "x86_64",
"OS": "linux"
},
"Resources": {
"NanoCPUs": 1000000000,
"MemoryBytes": 1038807040
},
"Engine": {
"EngineVersion": "17.05.0-ce",
"Plugins": [
{
"Type": "Network",
"Name": "bridge"
},
{
"Type": "Network",
"Name": "host"
},
{
"Type": "Network",
"Name": "null"
},
{
"Type": "Network",
"Name": "overlay"
},
{
"Type": "Volume",
"Name": "local"
}
]
}
},
"Status": {
"State": "ready",
"Addr": "172.31.53.23"
}
}
]
root@linuxtips-01:~#
Y si queremos promover un node worker a manager, ¿cómo debemos hacer? Simple como volar, comprueba a continuación:
root@linuxtips-01:~# docker node promote LINUXtips-03
Node LINUXtips-03 promoted to a manager in the swarm.
root@linuxtips-01:~# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
2qac LINUXtips-01 Ready Active Leader 18.03.1-ce
j6lm LINUXtips-02 Ready Active Reachable 18.03.1-ce
nmxl LINUXtips-03 Ready Active Reachable 18.03.1-ce
root@linuxtips-01:~#
Si quieres convertir un node manager en worker, haz:
root@linuxtips-01:~# docker node demote LINUXtips-03
Node LINUXtips-03 demoted to a manager in the swarm.
Vamos a verificar:
root@linuxtips-01:~# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
2qac LINUXtips-01 Ready Active Leader 18.03.1-ce
j6lm LINUXtips-02 Ready Active Reachable 18.03.1-ce
nmxl LINUXtips-03 Ready Active 18.03.1-ce
root@linuxtips-01:~#
Ahora, si quieres remover un node del cluster, basta ejecutar el siguiente comando en el node deseado:
root@linuxtips-03:~# docker swarm leave
Node left the swarm.
root@linuxtips-03:~#
Y necesitamos aún ejecutar el comando de remoción de ese node también en nuestro manager activo de la siguiente forma:
root@linuxtips-01:~# docker node rm LINUXtips-03
LINUXtips-03
root@linuxtips-01:~#
Con esto, podemos ejecutar el "docker node ls" y constatar que el node fue realmente removido del cluster. Si quieres agregarlo nuevamente, basta repetir el proceso que fue utilizado para agregarlo, ¿te acuerdas? :D
Para remover un node manager de nuestro cluster, necesitamos agregar la flag "--force" al comando "docker swarm leave", como se muestra a continuación:
root@linuxtips-02:~# docker swarm leave --force
Node left the swarm.
root@linuxtips-02:~#
Ahora, basta removerlo también en nuestro node manager:
root@linuxtips-01:~# docker node rm LINUXtips-02
LINUXtips-02
root@linuxtips-01:~#
13.2. El sensacional services
Una de las mejores cosas que Docker Swarm nos ofrece es justamente la posibilidad de hacer uso de los services.
El services no es más que un VIP o DNS que realizará el balanceo de peticiones entre los containers. Podemos establecer un número x de containers respondiendo por un service y esos containers estarán distribuidos por nuestro cluster, entre nuestros nodes, garantizando alta disponibilidad y balanceo de carga, ¡todo esto nativamente!
El services es una forma, ya utilizada en Kubernetes, de conseguir gestionar mejor tus containers, enfocándose en el servicio que esos containers están ofreciendo y garantizando alta disponibilidad y balanceo de carga. Es una manera muy simple y efectiva para escalar tu entorno, aumentando o disminuyendo la cantidad de containers que responderá para un determinado service.
¿Un poco confuso? Sí, lo sé, pero se volverá fácil. :)
Imagina que necesitamos disponibilizar el servicio de Nginx para ser el nuevo web server. Antes de crear ese service, necesitamos algunas informaciones:
-
Nombre del service que deseo crear
webserver.
-
Cantidad de containers que deseo debajo del service
5.
-
Puertos que vamos a "bindear", entre el service y el node
8080:80.
-
Imagen de los containers que voy a utilizar
nginx.
Ahora que ya tenemos esas informaciones, vamos a crear nuestro primer service. :)
root@linuxtips-01:~# docker service create --name webserver --replicas 5 -p 8080:80 nginx
0azz4psgfpkf0i5i3mbfdiptk
root@linuxtips-01:~#
Ahora ya tenemos nuestro service creado. Para probarlo, basta ejecutar:
root@linuxtips-01:~# curl CUALQUIER_IP_NODES_CLUSTER:8080
El resultado del comando anterior te traerá la página de bienvenida de Nginx.
Como estamos utilizando los services, cada conexión caerá en un container diferente, haciendo así el balanceo de cargas "automágicamente"!
Para visualizar el service creado, ejecuta:
root@linuxtips-01:~# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0azz4p webserver replicated 5/5 nginx:lates *:8080->80/tcp
Como podemos notar, tenemos el service creado y con él cinco réplicas en ejecución, es decir, cinco containers en ejecución.
Si queremos saber dónde están ejecutándose nuestros containers, en cuáles nodes están siendo ejecutados, basta ejecutar el siguiente comando:
root@linuxtips-01:~# docker service ps webserver
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
zbt1j webserver.1 nginx:latest LINUXtips-01 Running Running 8 minutes ago
iqm9p webserver.2 nginx:latest LINUXtips-02 Running Running 8 minutes ago
jliht webserver.3 nginx:latest LINUXtips-01 Running Running 8 minutes ago
qcfth webserver.4 nginx:latest LINUXtips-03 Running Running 8 minutes ago
e17um webserver.5 nginx:latest LINUXtips-02 Running Running 8 minutes ago
root@linuxtips-01:~#
Así conseguimos saber dónde está ejecutándose cada container y aún su status.
Si necesito saber más detalles sobre mi service, basta utilizar el subcomando "inspect".
root@linuxtips-01:~# docker service inspect webserver
[
{
"ID": "0azz4psgfpkf0i5i3mbfdiptk",
"Version": {
"Index": 29
},
"CreatedAt": "2017-06-09T19:35:58.180235688Z",
"UpdatedAt": "2017-06-09T19:35:58.18899891Z",
"Spec": {
"Name": "webserver",
"Labels": {},
"TaskTemplate": {
"ContainerSpec": {
"Image": "nginx:latest@sha256:41ad9967ea448d7c2b203c699b429abe1ed5af331cd92533900c6d77490e0268",
"StopGracePeriod": 10000000000,
"DNSConfig": {}
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "any",
"Delay": 5000000000,
"MaxAttempts": 0
},
"Placement": {},
"ForceUpdate": 0
},
"Mode": {
"Replicated": {
"Replicas": 5
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
},
"Endpoint": {
"Spec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
},
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
],
"VirtualIPs": [
{
"NetworkID": "89t2aobeik8j7jcre8lxhj04l",
"Addr": "10.255.0.5/16"
}
]
}
}
]
root@linuxtips-01:~#
En la salida del "inspect" conseguiremos obtener informaciones importantes sobre nuestro service, como puertos expuestos, volúmenes, containers, limitaciones, entre otras cosas.
Una información muy importante es la dirección del VIP del service:
"VirtualIPs": [
{
"NetworkID": "89t2aobeik8j7jcre8lxhj04l",
"Addr": "10.255.0.5/16"
}
]
Esta es la dirección IP del "balanceador" de ese service, es decir, siempre que accedan vía ese IP, él distribuirá la conexión entre los containers. ¿Simple, no?
Ahora, si queremos aumentar el número de containers debajo de ese service, es muy simple. Basta ejecutar el siguiente comando:
root@linuxtips-01:~# docker service scale webserver=10
webserver scaled to 10
root@linuxtips-01:~#
¡Listo, así de simple!
¡Ahora ya tenemos diez containers respondiendo peticiones debajo de nuestro service webserver! ¡Simple como volar!
Para visualizar, basta ejecutar:
root@linuxtips-01:~# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0azz webserver replicated 10/10 nginx:latest *:8080->80/tcp
root@linuxtips-01:~#
Para saber en cuáles nodes están en ejecución, recuerda el "docker service ls webserver".
Para acceder a los logs de ese service, basta ejecutar:
root@linuxtips-01:~# docker service logs -f webserver
webserver.5.e17umj6u6bix@LINUXtips-02 | 10.255.0.2 - - [09/Jun/2017:19:36:12 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
Así, tendrás acceso a los logs de todos los containers de ese service. ¡Muy práctico!
"¡Me cansé de jugar! ¡Quiero remover ese service mío!" Es tan simple como crearlo. Ejecuta:
root@linuxtips-01:~# docker service rm webserver
webserver
root@linuxtips-01:~#
¡Listo! Tu service fue eliminado y puedes verificarlo en la salida del siguiente comando:
root@linuxtips-01:~# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
root@linuxtips-01:~#
Crear un service con un volumen conectado es bastante simple. Haz:
root@linuxtips-01:~# docker service create --name webserver --replicas 5 -p 8080:80 --mount type=volume,src=teste,dst=/app nginx
yfheu3k7b8u4d92jemglnteqa
root@linuxtips-01:~#
Cuando creo un service con un volumen conectado a él, esto indica que ese volumen estará disponible en todos mis containers de ese service, es decir, el volumen con el nombre de "teste" estará montado en todos los containers en el directorio "/app".