Docker Swarm es el modo de orquestación integrado en Docker Engine: agrupa varios servidores en un clúster y reparte entre ellos contenedores replicados, con balanceo de carga, actualizaciones progresivas y recuperación automática si un nodo cae. Es mucho más sencillo de operar que Kubernetes y usa el mismo formato de ficheros Compose que ya conoces. En este tutorial crearás un clúster de tres nodos en Ubuntu 24.04 (un manager y dos workers), desplegarás un stack replicado y practicarás las operaciones del día a día.

Requisitos previos

Para seguir esta guía necesitas:

  • Tres servidores con Ubuntu 24.04 LTS, por ejemplo VPS de CubePath, con al menos 1 GB de RAM cada uno.
  • Docker Engine instalado desde el repositorio oficial de Docker en los tres, y tu usuario añadido al grupo docker para ejecutar docker sin sudo.
  • Una red privada entre los tres servidores. En esta guía se usan estas direcciones de ejemplo, sustitúyelas por las tuyas:
NodoRolIP privada
manager1Manager10.0.0.11
worker1Worker10.0.0.12
worker2Worker10.0.0.13
  • Un nombre de host distinto en cada servidor (hostnamectl muestra el actual). Swarm identifica los nodos por su hostname en docker node ls.

Paso 1: Abrir los puertos de Swarm en la red privada

Los nodos de Swarm se comunican por estos puertos, que deben estar abiertos solo entre los nodos y nunca hacia Internet:

PuertoProtocoloUso
2377TCPGestión del clúster (solo hacia los managers)
7946TCP y UDPDescubrimiento y comunicación entre nodos
4789UDPTráfico de las redes overlay (VXLAN)
ESP (protocolo IP 50)-Redes overlay cifradas

Ejecuta en los tres servidores, ajustando 10.0.0.0/24 a tu red privada:

sudo ufw allow OpenSSH
sudo ufw allow from 10.0.0.0/24 to any port 2377 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 7946
sudo ufw allow from 10.0.0.0/24 to any port 4789 proto udp
sudo ufw allow from 10.0.0.0/24 proto esp
sudo ufw enable

Comprueba las reglas:

sudo ufw status

Paso 2: Inicializar el clúster en el manager

En manager1, inicializa Swarm indicando la IP privada con --advertise-addr. Es la dirección que los demás nodos usarán para conectarse; si no la indicas y el servidor tiene varias interfaces, Docker puede elegir la pública:

docker swarm init --advertise-addr 10.0.0.11
Swarm initialized: current node (k3v9q2x8m1n7b5c4z6a0d2f1g) is now a manager.

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

    docker swarm join --token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx 10.0.0.11:2377

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

Copia el comando docker swarm join completo. Si lo pierdes, puedes volver a mostrarlo en cualquier momento desde el manager:

docker swarm join-token worker

Paso 3: Unir los workers al clúster

Ejecuta el comando docker swarm join que copiaste en worker1 y en worker2:

docker swarm join --token SWMTKN-1-your_worker_token 10.0.0.11:2377
This node joined a swarm as a worker.

Vuelve a manager1 y lista los nodos. Todos los comandos de gestión del clúster (docker node, docker service, docker stack) se ejecutan en un manager:

docker node ls
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
k3v9q2x8m1n7b5c4z6a0d2f1g *   manager1   Ready     Active         Leader           28.4.0
p8w2e5r7t1y3u6i9o0a4s2d5f     worker1    Ready     Active                          28.4.0
h6j3k9l1z4x7c2v8b5n0m3q6w     worker2    Ready     Active                          28.4.0

Paso 4: Desplegar un stack en una red overlay cifrada

Un stack es un conjunto de servicios definidos en un fichero Compose que Swarm despliega y mantiene en el clúster. Crea el fichero en manager1:

mkdir -p ~/stacks
nano ~/stacks/demo.yml

El servicio usa la imagen traefik/whoami, un servidor HTTP mínimo que responde con el nombre del contenedor que atiende la petición. Así podrás ver el balanceo entre réplicas:

services:
  whoami:
    image: traefik/whoami
    ports:
      - "8080:80"
    networks:
      - app
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
        order: start-first
      restart_policy:
        condition: on-failure
      resources:
        limits:
          cpus: "0.25"
          memory: 64M

networks:
  app:
    driver: overlay
    driver_opts:
      encrypted: "true"

Los bloques importantes son:

  • replicas: 3: Swarm mantiene siempre tres contenedores y los reparte entre los nodos disponibles.
  • update_config: las actualizaciones sustituyen los contenedores de uno en uno, esperan 10 segundos entre cada uno, arrancan el nuevo antes de parar el antiguo y vuelven atrás solas si algo falla.
  • encrypted: "true": el tráfico entre contenedores de distintos nodos por la red app va cifrado con IPsec. Por eso abriste ESP en el paso 1.

Despliega el stack con el nombre demo:

docker stack deploy -c ~/stacks/demo.yml demo
Creating network demo_app
Creating service demo_whoami

Comprueba en qué nodos se han colocado las réplicas:

docker service ps demo_whoami
ID             NAME            IMAGE                   NODE       DESIRED STATE   CURRENT STATE           ERROR   PORTS
x1c4v7b0n3m6   demo_whoami.1   traefik/whoami:latest   worker1    Running         Running 20 seconds ago
q9w2e5r8t1y4   demo_whoami.2   traefik/whoami:latest   worker2    Running         Running 20 seconds ago
a3s6d9f2g5h8   demo_whoami.3   traefik/whoami:latest   manager1   Running         Running 20 seconds ago

El puerto publicado usa la malla de enrutado (routing mesh) de Swarm: el puerto 8080 responde en todos los nodos y reparte las peticiones entre las réplicas, estén donde estén. Haz varias peticiones contra un mismo nodo:

for i in 1 2 3; do curl -s http://10.0.0.12:8080 | grep Hostname; done
Hostname: 5e1f0a9b2c7d
Hostname: 8d3c6b1a4f2e
Hostname: c7a2e9d4b1f6

Cada respuesta llega de un contenedor distinto aunque siempre preguntas al mismo nodo.

Paso 5: Escalar y actualizar servicios

Para absorber más carga, aumenta el número de réplicas. Swarm crea las nuevas y las reparte entre los nodos:

docker service scale demo_whoami=6
demo_whoami scaled to 6
overall progress: 6 out of 6 tasks
verify: Service demo_whoami converged

Para que el cambio sea permanente, actualiza también replicas en demo.yml: si vuelves a ejecutar docker stack deploy, el fichero manda.

Cualquier cambio en la definición del contenedor (imagen, variables de entorno, límites) se aplica como actualización progresiva según update_config. Por ejemplo, añade una variable de entorno:

docker service update --env-add APP_VERSION=2 demo_whoami

Swarm sustituye las réplicas de una en una y muestra el progreso hasta que el servicio converge. En tu flujo habitual cambiarás la etiqueta de imagen en demo.yml y volverás a ejecutar docker stack deploy, que hace lo mismo. Si la nueva versión no se comporta como esperas, vuelve a la anterior:

docker service rollback demo_whoami

Revisa el historial de tareas para ver las réplicas sustituidas:

docker service ps demo_whoami

Las tareas antiguas aparecen con DESIRED STATE en Shutdown, y las activas en Running.

Paso 6: Mantenimiento de nodos

Antes de reiniciar o actualizar un servidor, drénalo: Swarm deja de asignarle tareas y mueve las que tenía a otros nodos:

docker node update --availability drain worker1

Comprueba que no quedan contenedores en ejecución en worker1:

docker node ps worker1 --filter desired-state=running

La lista debe salir vacía. Haz el mantenimiento y, al terminar, vuelve a activar el nodo:

docker node update --availability active worker1

Para fijar un servicio a ciertos nodos, etiqueta el nodo y usa una restricción de colocación. Por ejemplo, para una base de datos que debe correr donde está su disco:

docker node update --label-add storage=ssd worker2

Y en el servicio del fichero de stack:

    deploy:
      placement:
        constraints:
          - node.labels.storage == ssd

Paso 7: Hacer copia de seguridad del estado del clúster

El estado de Swarm (servicios, redes, secretos, configuración) vive en la base de datos Raft de los managers, en /var/lib/docker/swarm. Para copiarlo de forma consistente hay que parar Docker en el manager. Con un solo manager, mientras Docker esté parado no podrás gestionar el clúster, pero los contenedores de los workers siguen funcionando:

sudo systemctl stop docker docker.socket
sudo tar -czf /root/swarm-backup-$(date +%F).tar.gz -C /var/lib/docker swarm
sudo systemctl start docker

Comprueba que el manager vuelve a estar operativo:

docker node ls

Guarda la copia fuera del servidor. Para restaurar en un manager nuevo, se extrae el archivo en /var/lib/docker/ con Docker parado y se ejecuta docker swarm init --force-new-cluster para que el nodo arranque el clúster con ese estado. Con 3 managers rara vez necesitarás restaurar, porque los otros dos mantienen el estado.

Solución de problemas

  • docker swarm join se queda esperando o da timeout: el worker no llega al puerto 2377 del manager. Comprueba la regla de UFW en el manager y que usas la IP privada con nc -zv 10.0.0.11 2377 desde el worker.
  • El servicio responde en un nodo pero no en otros: falla el tráfico overlay entre nodos. Revisa que 4789/udp, 7946 y ESP están permitidos entre todos los nodos.
  • Una tarea se queda en Pending con no suitable node: ningún nodo cumple las restricciones de colocación o los recursos pedidos. Consulta el motivo con docker service ps --no-trunc demo_whoami.
  • La imagen no se descarga en los workers: si viene de un registro privado, despliega con docker stack deploy --with-registry-auth para pasar las credenciales a los nodos.

Conclusión

Tienes un clúster Docker Swarm de tres nodos con un stack replicado en una red overlay cifrada, y sabes escalar, actualizar, revertir y drenar nodos sin cortar el servicio. Como siguientes pasos, promociona dos nodos más a manager para tener alta disponibilidad, guarda contraseñas con docker secret en lugar de variables de entorno, y pon un proxy inverso como Traefik o Nginx delante de los servicios publicados para servirlos con HTTPS.