Una red overlay de Docker une contenedores que se ejecutan en servidores distintos como si estuvieran en la misma red local: cada contenedor tiene una IP de esa red y puede llegar a los demás por su nombre, sin publicar puertos en el host. Por debajo, Docker encapsula el tráfico en VXLAN y, si lo pides, lo cifra con IPsec. En este tutorial crearás un clúster de Docker Swarm con dos servidores Ubuntu 24.04, una red overlay cifrada y un servicio repartido entre ambos, y comprobarás la comunicación y el DNS entre hosts.

Requisitos previos

Para seguir esta guía necesitas:

  • Dos servidores con Ubuntu 24.04 LTS, por ejemplo dos VPS de CubePath, conectados por una red privada. En este tutorial se usan estos nombres y direcciones; sustitúyelos por los tuyos:
    • node1, IP privada 10.10.0.1 (será el manager).
    • node2, IP privada 10.10.0.2 (será el worker).
  • Un usuario no root con privilegios sudo y miembro del grupo docker en ambos servidores.
  • Docker Engine instalado desde el repositorio oficial de Docker en ambos servidores, a ser posible en la misma versión.

Cada paso indica en qué servidor se ejecuta.

Cómo funciona una red overlay

Las redes overlay necesitan un plano de control que sepa qué contenedor está en qué host. Docker lo obtiene de Swarm, así que para usarlas hay que crear un clúster Swarm aunque no vayas a usar sus servicios. Swarm usa tres puertos entre los nodos:

PuertoProtocoloUso
2377TCPGestión del clúster (solo hacia los managers)
7946TCP y UDPDescubrimiento de nodos y estado de la red
4789UDPTráfico de datos encapsulado en VXLAN
ESP (protocolo IP 50)-Tráfico cifrado, solo si la red es encrypted

Todo este tráfico debe ir por la red privada, nunca por Internet sin cifrar.

Paso 1: Abrir los puertos en el cortafuegos

Ejecuta estos comandos en ambos servidores. Permiten el tráfico de Swarm únicamente desde la red privada 10.10.0.0/24:

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

Comprueba las reglas:

sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
2377/tcp                   ALLOW       10.10.0.0/24
7946/tcp                   ALLOW       10.10.0.0/24
7946/udp                   ALLOW       10.10.0.0/24
4789/udp                   ALLOW       10.10.0.0/24
Anywhere/esp               ALLOW       10.10.0.0/24

Comprueba también que los nodos se ven por la red privada. Desde node2:

ping -c 3 10.10.0.1

Paso 2: Crear el clúster Swarm

En node1, inicializa Swarm anunciando la IP privada, para que los nodos se comuniquen por ella y no por la pública:

docker swarm init \
  --advertise-addr 10.10.0.1 \
  --default-addr-pool 10.200.0.0/16 \
  --default-addr-pool-mask-length 24

Por defecto, Swarm asigna las subredes de las redes overlay dentro de 10.0.0.0/8, que puede solaparse con tu red privada. --default-addr-pool reserva para ello un rango que no uses en ningún otro sitio, en este caso 10.200.0.0/16, dividido en subredes /24.

La salida incluye el comando para unir workers:

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

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

    docker swarm join --token SWMTKN-1-3abc...xyz 10.10.0.1:2377

Copia ese comando y ejecútalo en node2:

docker swarm join --token SWMTKN-1-3abc...xyz 10.10.0.1:2377
This node joined a swarm as a worker.

Si pierdes el comando, recupéralo en node1 con docker swarm join-token worker.

En node1, comprueba que ambos nodos están activos:

docker node ls
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
k2x8c1v9m3n4b5q6w7e8r9t0y *   node1      Ready     Active         Leader           28.4.0
p9o8i7u6y5t4r3e2w1q0a9s8d     node2      Ready     Active                          28.4.0

Paso 3: Crear una red overlay cifrada

En node1 (las redes overlay se crean siempre desde un manager), crea la red app-net:

docker network create \
  --driver overlay \
  --attachable \
  --opt encrypted \
  app-net
  • --driver overlay: crea una red que abarca todos los nodos del clúster.
  • --attachable: permite conectar a la red contenedores normales (docker run, docker compose), además de servicios de Swarm.
  • --opt encrypted: cifra con IPsec (AES-GCM) el tráfico de datos entre nodos. El tráfico de gestión de Swarm ya va cifrado siempre; esta opción cifra también el de tus aplicaciones, con un pequeño coste de CPU.

Comprueba la red y sus opciones:

docker network ls --filter driver=overlay
docker network inspect app-net --format '{{.Scope}} {{.Options}}'
NETWORK ID     NAME      DRIVER    SCOPE
7h3k9m2n1b4v   app-net   overlay   swarm
x1c2v3b4n5m6   ingress   overlay   swarm
swarm map[com.docker.network.driver.overlay.vxlanid_list:4097 encrypted:]

La red ingress la crea Swarm automáticamente para publicar puertos de servicios en todos los nodos.

Paso 4: Desplegar un servicio en ambos nodos

En node1, crea un servicio con dos réplicas de traefik/whoami, un servidor web pequeño que responde con el nombre del contenedor. Swarm reparte las réplicas entre los nodos disponibles:

docker service create \
  --name whoami \
  --network app-net \
  --replicas 2 \
  traefik/whoami

Comprueba en qué nodo se ejecuta cada réplica:

docker service ps whoami
ID             NAME       IMAGE                   NODE    DESIRED STATE   CURRENT STATE
a1b2c3d4e5f6   whoami.1   traefik/whoami:latest   node1   Running         Running 20 seconds ago
f6e5d4c3b2a1   whoami.2   traefik/whoami:latest   node2   Running         Running 19 seconds ago

El servicio no publica ningún puerto: solo es accesible desde la red app-net.

Paso 5: Comprobar la comunicación entre hosts

En node2, arranca un contenedor temporal conectado a app-net y haz varias peticiones al servicio por su nombre:

docker run --rm --network app-net alpine \
  sh -c 'for i in 1 2 3 4; do wget -qO- http://whoami | grep Hostname; done'
Hostname: 3e7a1c9b5d2f
Hostname: 8b4d2f6a0c1e
Hostname: 3e7a1c9b5d2f
Hostname: 8b4d2f6a0c1e

Aparecen los dos nombres de contenedor, así que el contenedor de node2 llega también a la réplica que corre en node1 a través de la red overlay.

Cómo resuelve Docker los nombres

El DNS interno de Docker ofrece dos nombres por servicio:

  • whoami resuelve a una IP virtual (VIP) única. Docker reparte las conexiones que llegan a esa IP entre las réplicas.
  • tasks.whoami resuelve a la IP de cada réplica, útil para aplicaciones que gestionan su propio balanceo o descubrimiento (clústeres de bases de datos, por ejemplo).

Compruébalo desde node2:

docker run --rm --network app-net alpine sh -c 'nslookup whoami; nslookup tasks.whoami'
Name:	whoami
Address: 10.200.1.2

Name:	tasks.whoami
Address: 10.200.1.3
Name:	tasks.whoami
Address: 10.200.1.4

Las direcciones pertenecen al rango 10.200.0.0/16 que definiste al crear el clúster.

Comprobar que el tráfico va cifrado

Con la opción encrypted, Docker crea asociaciones de seguridad IPsec en el kernel entre los nodos que tienen contenedores en la red. En node2, mientras el servicio está en marcha:

sudo ip xfrm state | grep -c '^src'
4

Un número mayor que cero indica que hay túneles IPsec activos. Si devuelve 0, revisa que la regla de ESP del paso 1 está aplicada en ambos nodos.

Paso 6: Usar la red overlay desde Docker Compose

Como la red es attachable, también puedes conectarle contenedores de un proyecto de Docker Compose normal en cualquier nodo del clúster. Declárala como red externa. Por ejemplo, en node2:

mkdir -p ~/cliente && cd ~/cliente
nano compose.yaml
services:
  cliente:
    image: alpine
    command: sleep infinity
    networks:
      - app-net

networks:
  app-net:
    external: true

Arranca el proyecto y prueba el acceso al servicio de Swarm:

docker compose up -d
docker compose exec cliente wget -qO- http://whoami | grep Hostname
Hostname: 3e7a1c9b5d2f

Cuando termines las pruebas, elimina el proyecto con docker compose down y, en node1, el servicio con docker service rm whoami.

Solución de problemas

  • Los nodos aparecen como Down en docker node ls: el puerto 7946 (TCP y UDP) está bloqueado entre ellos, o --advertise-addr apunta a una IP que el otro nodo no alcanza.
  • El servicio resuelve por DNS pero las peticiones al otro nodo no responden: falta la regla de UDP 4789, o de ESP si la red es cifrada. Comprueba ambas en los dos nodos con sudo ufw status.
  • Las peticiones pequeñas funcionan y las grandes se quedan colgadas: problema de MTU. VXLAN añade 50 bytes de cabecera, y si la red privada tiene una MTU inferior a 1500 los paquetes grandes se descartan. Crea la red con una MTU menor, por ejemplo --opt com.docker.network.driver.mtu=1400.
  • network app-net not found al hacer docker run en un worker: la red no se creó con --attachable. Bórrala con docker network rm app-net en el manager (después de eliminar los servicios que la usan) y créala de nuevo con esa opción.
  • Conflictos de direcciones con tu red privada: si no usaste --default-addr-pool al crear el clúster, las overlays pueden recibir subredes de 10.0.0.0/8 que coinciden con otras redes. Puedes indicar una subred concreta al crear la red con --subnet 10.201.0.0/24.

Conclusión

Has creado un clúster Swarm de dos nodos, una red overlay cifrada y un servicio cuyas réplicas se comunican entre servidores por nombre, sin publicar puertos. Como siguientes pasos, añade un tercer manager para que el clúster tolere la caída de un nodo, despliega aplicaciones completas con docker stack deploy usando esta red, y publica solo el punto de entrada (por ejemplo, un proxy inverso) mientras el resto de servicios quedan dentro de la overlay.