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 privada10.10.0.1(será el manager).node2, IP privada10.10.0.2(será el worker).
- Un usuario no root con privilegios
sudoy miembro del grupodockeren 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:
| Puerto | Protocolo | Uso |
|---|---|---|
| 2377 | TCP | Gestión del clúster (solo hacia los managers) |
| 7946 | TCP y UDP | Descubrimiento de nodos y estado de la red |
| 4789 | UDP | Trá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:
whoamiresuelve a una IP virtual (VIP) única. Docker reparte las conexiones que llegan a esa IP entre las réplicas.tasks.whoamiresuelve 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
Downendocker node ls: el puerto 7946 (TCP y UDP) está bloqueado entre ellos, o--advertise-addrapunta 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 foundal hacerdocker runen un worker: la red no se creó con--attachable. Bórrala condocker network rm app-neten 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-poolal crear el clúster, las overlays pueden recibir subredes de10.0.0.0/8que 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.
