Un host, dos cargas de trabajo y un requisito que las separa: un cliente de descargas que debe salir por una VPN comercial y una interfaz de administración que debe seguir usando la conexión del host. Instalar el cliente VPN en el host incumple el requisito de inmediato, porque entonces ambas cargas comparten una sola salida. El patrón que sí funciona es trasladar el túnel a su propio contenedor y permitir que solo las cargas que lo necesitan se unan al espacio de nombres de red de ese contenedor. Todo lo demás conserva la ruta habitual de Docker, y los detalles que siguen son consecuencias de quién posee el espacio de nombres.

Por qué un espacio de nombres compartido cambia las reglas

Un contenedor que se une al espacio de nombres de otro no obtiene su propia pila de red. Ve las mismas interfaces, la misma tabla de enrutamiento y el mismo loopback que el contenedor al que se unió, y precisamente por eso el patrón es útil: la carga resuelve y se conecta con normalidad, y el túnel de debajo captura el tráfico sin que la aplicación lo sepa. El coste es que cada ajuste de red tiene ahora un solo propietario, y el contenedor que se une no lo es.

Esa regla de propiedad responde a la mayoría de las preguntas de configuración antes de que se planteen. Si un ajuste vive en la pila de red, pertenece al contenedor lateral. Si describe el proceso, se queda con la carga.

El loopback es la parte que sorprende a la gente. Dentro del contenedor que se une, localhost se resuelve al loopback del contenedor lateral, no a uno privado propio, así que un servicio que se enlaza a 127.0.0.1 esperando aislamiento ahora comparte esa dirección con el contenedor del túnel.

Publica los puertos en el contenedor que posee el espacio de nombres

La asignación de puertos pertenece al propietario del espacio de nombres incluso cuando el proceso que escucha se ejecuta en el otro contenedor. Declara la asignación en el contenedor lateral y deja que la carga conserve su propia configuración de proceso intacta. Una asignación en el servicio que se une no puede surtir efecto, ya sea porque el motor la rechaza o porque la ignora en silencio según cómo se inicie el contenedor, y por eso una interfaz que era accesible hace un momento desaparece en cuanto se aplica el patrón.

services:
  gluetun:
    image: qmcgaw/gluetun
    cap_add: [NET_ADMIN]
    ports: ["8080:8080"]
  client-vpn:
    image: example/client
    network_mode: "service:gluetun"
  client-direct:
    image: example/client
    ports: ["8081:8080"]

Lee ese bloque como tres decisiones en lugar de tres servicios. El contenedor lateral posee el túnel y el puerto publicado; el cliente que se une no declara ninguna red; el cliente directo conserva una asignación propia en la red predeterminada. La capacidad NET_ADMIN es lo que permite al contenedor lateral crear interfaces y alterar rutas, y es la razón por la que este contenedor necesita más privilegios que las cargas a las que sirve.

El servicio que se queda directo conserva su propia red

La carga que no debe pasar por el túnel conserva el puente predeterminado o una red definida por el usuario propia, con su propio puerto publicado y su propia salida. Es tentador conectarla a la red del contenedor lateral para que los dos contenedores puedan hablar por nombre, pero ese único paso traslada su tráfico al túnel sin avisar. Si necesitan alcanzarse, construye una red definida por el usuario aparte, conecta ambos a ella y deja el contenedor lateral como la única vía de salida.

Mantén las dos preguntas separadas: qué contenedores pueden alcanzarse y cuál sale por dónde. Fusionarlas es la forma más habitual de que este diseño vuelva a convertirse en una VPN para todo el host.

Una matriz de fallos para la primera hora

La mayoría de los primeros intentos fallan de una de cuatro maneras, y cada una tiene una causa distinta. Compruébalas en este orden, porque las dos primeras explican casi todos los informes.

SymptomLikely cause
Web UI unreachable after the changePort mapping declared on the joining container instead of the sidecar
Both containers show the provider's exit addressThe direct service was attached to the sidecar network
Internal names stop resolvingResolver settings live on the host, not in the shared namespace
Health check fails but the app worksThe check probes localhost, which now means the sidecar

La cuarta fila es la que la gente diagnostica mal como un fallo de la aplicación. Una sonda escrita contra el loopback propio de la carga ahora está probando el contenedor del túnel, así que o falla o informa de un servicio completamente equivocado.

Cuando un solo nombre de host necesita el túnel

A veces un túnel completo es demasiado: un nombre interno debe pasar por una VPN corporativa mientras un socket local o una conexión de pantalla sigue funcionando. Un túnel completo dentro del contenedor rompe esa ruta y todo lo dirigido al host, así que enruta solo el prefijo que lo necesita. Un envoltorio de enrutamiento dividido alrededor del cliente lo hace, enviando el nombre de host indicado por el túnel y dejando la ruta anterior intacta para todo lo demás.

Elige esto cuando solo necesitas alcanzar un servicio y no intentas mover todo el tráfico del contenedor. Es una herramienta distinta del contenedor lateral y responde a una pregunta distinta.

Verifica y luego haz que sobreviva a un reinicio

Pregunta a cada contenedor de qué dirección parece venir. El del túnel debería imprimir la dirección del proveedor y el directo la salida del host. Respuestas idénticas significan que el servicio directo heredó el espacio de nombres equivocado o que una VPN a nivel de host sigue capturándolo todo, y esas dos correcciones son distintas. Vuelve a ejecutar la comprobación después de cada edición de compose, porque un solo bloque de servicio copiado basta para reintroducir el problema.

Tres cosas suelen romperse más adelante. El DNS sigue al propietario del espacio de nombres, así que los ajustes de resolutor y de dominio de búsqueda pertenecen al contenedor lateral y no al host. Una VPN con reenvío de puertos cambia su puerto de escucha al reconectar, así que el puerto reenviado del proveedor y el puerto de escucha del lado del contenedor deben volver a escribirse en la configuración del cliente o en la asignación de puertos, normalmente mediante un pequeño actualizador que lee la API del proveedor. Y cualquier sonda escrita contra localhost ahora pregunta al contenedor del túnel y no a la carga.

Escribe el patrón, incluido qué contenedor posee el espacio de nombres y qué puertos le pertenecen. La siguiente persona añadirá un servicio, copiará el bloque equivocado y reproducirá el primer fallo de este artículo.

Artículos relacionados