Utilizar la malla de enrutamiento del modo Swarm
El modo Swarm de Docker Engine facilita la publicación de puertos para servicios con el fin de ponerlos a disposición de recursos externos al swarm. Todos los nodos participan en una malla de enrutamiento ingress. La malla de enrutamiento permite que cada nodo del swarm acepte conexiones en puertos publicados para cualquier servicio que se ejecute en él, incluso si no hay ninguna tarea ejecutándose en ese nodo. La malla de enrutamiento redirige todas las solicitudes entrantes a los puertos publicados en los nodos disponibles hacia un contenedor activo.
Para utilizar la red ingress en el swarm, debes tener los siguientes puertos abiertos entre los nodos del swarm antes de habilitar el modo Swarm:
- Puerto
7946TCP/UDP para el descubrimiento de redes de contenedores. - Puerto
4789UDP (configurable) para la red ingress de contenedores.
Al configurar las redes en un Swarm, se debe tener especial cuidado. Consulta el tutorial para obtener una descripción general.
También debes abrir el puerto publicado entre los nodos del swarm y cualquier recurso externo, como un balanceador de carga externo, que requiera acceso al puerto.
También puedes omitir la malla de enrutamiento para un servicio determinado.
Publicar un puerto para un servicio
Utiliza la opción --publish para publicar un puerto cuando creas un servicio. target se utiliza para especificar el puerto dentro del contenedor, y published se utiliza para especificar el puerto a vincular en la malla de enrutamiento. Si omites el puerto published, se vinculará un puerto aleatorio de número alto para cada tarea del servicio. Necesitarás inspeccionar la tarea para determinar el puerto.
$ docker service create \
--name <SERVICE-NAME> \
--publish published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
<IMAGE>
NoteEl formato anterior de esta sintaxis es una cadena separada por dos puntos, donde el puerto publicado va primero y el puerto de destino va segundo, como
-p 8080:80. Se prefiere la nueva sintaxis porque es más legible y ofrece mayor flexibilidad.
El <PUBLISHED-PORT> es el puerto donde el swarm hace que el servicio esté disponible. Si lo omites, se vinculará un puerto aleatorio de número alto.
El <CONTAINER-PORT> es el puerto donde el contenedor escucha. Este parámetro es obligatorio.
Por ejemplo, el siguiente comando publica el puerto 80 del contenedor nginx en el puerto 8080 para cualquier nodo del swarm:
$ docker service create \
--name my-web \
--publish published=8080,target=80 \
--replicas 2 \
nginx
Cuando accedes al puerto 8080 en cualquier nodo, Docker redirige tu solicitud a un contenedor activo. En los propios nodos del swarm, es posible que el puerto 8080 no esté realmente vinculado, pero la malla de enrutamiento sabe cómo redirigir el tráfico y evita que ocurran conflictos de puertos.
La malla de enrutamiento escucha en el puerto publicado para cualquier dirección IP asignada al nodo. Para direcciones IP enrutables externamente, el puerto está disponible desde fuera del host. Para todas las demás direcciones IP, el acceso solo está disponible desde dentro del host.

Puedes publicar un puerto para un servicio existente utilizando el siguiente comando:
$ docker service update \
--publish-add published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
<SERVICE>
Puedes utilizar docker service inspect para ver el puerto publicado del servicio. Por ejemplo:
$ docker service inspect --format="{{json .Endpoint.Spec.Ports}}" my-web
[{"Protocol":"tcp","TargetPort":80,"PublishedPort":8080}]
La salida muestra el <CONTAINER-PORT> (etiquetado como TargetPort) de los contenedores y el <PUBLISHED-PORT> (etiquetado como PublishedPort) donde los nodos escuchan las solicitudes para el servicio.
Publicar un puerto solo para TCP o solo para UDP
Por defecto, cuando publicas un puerto, este es un puerto TCP. Puedes publicar específicamente un puerto UDP en su lugar o además de un puerto TCP. Al publicar puertos tanto TCP como UDP, si omites el especificador de protocolo, el puerto se publica como un puerto TCP. Si utilizas la sintaxis larga (recomendada), establece la clave protocol en tcp o udp.
Solo TCP
Sintaxis larga:
$ docker service create --name dns-cache \
--publish published=53,target=53 \
dns-cache
Sintaxis corta:
$ docker service create --name dns-cache \
-p 53:53 \
dns-cache
TCP y UDP
Sintaxis larga:
$ docker service create --name dns-cache \
--publish published=53,target=53 \
--publish published=53,target=53,protocol=udp \
dns-cache
Sintaxis corta:
$ docker service create --name dns-cache \
-p 53:53 \
-p 53:53/udp \
dns-cache
Solo UDP
Sintaxis larga:
$ docker service create --name dns-cache \
--publish published=53,target=53,protocol=udp \
dns-cache
Sintaxis corta:
$ docker service create --name dns-cache \
-p 53:53/udp \
dns-cache
Omitir la malla de enrutamiento
Por defecto, los servicios de swarm que publican puertos lo hacen utilizando la malla de enrutamiento. Cuando te conectas a un puerto publicado en cualquier nodo de swarm (independientemente de si está ejecutando un servicio determinado o no), eres redirigido de forma transparente a un nodo trabajador que ejecuta ese servicio. En la práctica, Docker actúa como un balanceador de carga para tus servicios de swarm.
Puedes omitir la malla de enrutamiento, de modo que cuando accedas al puerto vinculado en un nodo determinado, accedas siempre a la instancia del servicio que se ejecuta en ese nodo. Esto se conoce como modo host. Hay algunas consideraciones a tener en cuenta:
Si accedes a un nodo que no está ejecutando una tarea de servicio, el servicio no escuchará en ese puerto. Es posible que nada esté escuchando, o que una aplicación completamente diferente esté escuchando.
Si esperas ejecutar múltiples tareas de servicio en cada nodo (como cuando tienes 5 nodos pero ejecutas 10 réplicas), no puedes especificar un puerto de destino estático. Permite que Docker asigne un puerto aleatorio de número alto (omitiendo el parámetro
published), o asegúrate de que solo se ejecute una única instancia del servicio en un nodo determinado, utilizando un servicio global en lugar de uno replicado, o mediante restricciones de ubicación (placement constraints).
Para omitir la malla de enrutamiento, debes utilizar la opción larga de --publish y establecer mode en host. Si omites la clave mode o la estableces en ingress, se utilizará la malla de enrutamiento. El siguiente comando crea un servicio global utilizando el modo host y omitiendo la malla de enrutamiento.
$ docker service create --name dns-cache \
--publish published=53,target=53,protocol=udp,mode=host \
--mode global \
dns-cache
Configurar un balanceador de carga externo
Puedes configurar un balanceador de carga externo para los servicios de swarm, ya sea en combinación con la malla de enrutamiento o sin utilizarla en absoluto.
Uso de la malla de enrutamiento
Puedes configurar un balanceador de carga externo para redirigir las solicitudes a un servicio de swarm. Por ejemplo, podrías configurar HAProxy para balancear las solicitudes a un servicio nginx publicado en el puerto 8080.

En este caso, el puerto 8080 debe estar abierto entre el balanceador de carga y los nodos del swarm. Los nodos del swarm pueden residir en una red privada que sea accesible para el servidor proxy, pero que no sea accesible públicamente.
Puedes configurar el balanceador de carga para equilibrar las solicitudes entre todos los nodos del swarm, incluso si no hay tareas programadas en ese nodo. Por ejemplo, podrías tener la siguiente configuración de HAProxy en /etc/haproxy/haproxy.cfg:
global
log /dev/log local0
log /dev/log local1 notice
...snip...
# Configure HAProxy to listen on port 80
frontend http_front
bind *:80
stats uri /haproxy?stats
default_backend http_back
# Configure HAProxy to route requests to swarm nodes on port 8080
backend http_back
balance roundrobin
server node1 192.168.99.100:8080 check
server node2 192.168.99.101:8080 check
server node3 192.168.99.102:8080 checkAl acceder al balanceador de carga HAProxy en el puerto 80, este reenvía las solicitudes a los nodos del swarm. La malla de enrutamiento de Swarm redirige la solicitud a una tarea activa. Si, por cualquier motivo, el planificador de Swarm asigna tareas a nodos diferentes, no necesitas volver a configurar el balanceador de carga.
Puedes configurar cualquier tipo de balanceador de carga para redirigir solicitudes a los nodos de swarm. Para obtener más información sobre HAProxy, consulta la documentación de HAProxy.
Sin la malla de enrutamiento
Para utilizar un balanceador de carga externo sin la malla de enrutamiento, establece --endpoint-mode en dnsrr en lugar del valor predeterminado de vip. En este caso, no hay una única IP virtual. En su lugar, Docker configura entradas DNS para el servicio de modo que una consulta DNS para el nombre del servicio devuelve una lista de direcciones IP y el cliente se conecta directamente a una de ellas.
No puedes usar --endpoint-mode dnsrr junto con --publish mode=ingress. Debes ejecutar tu propio balanceador de carga frente al servicio. Una consulta DNS para el nombre del servicio en el host de Docker devuelve una lista de direcciones IP de los nodos que ejecutan el servicio. Configura tu balanceador de carga para consumir esta lista y equilibrar el tráfico entre los nodos.
Consulta Configurar el descubrimiento de servicios.