Enrutamiento desde contenedores docker utilizando una interfaz de red física diferente y una puerta de enlace predeterminada


13

Información de contexto

Tengo un servidor con dos interfaces de red que ejecuta Docker. Docker, como algunas herramientas de virtualización, crea una interfaz de puente de Linux llamada docker0. Esta interfaz está configurada de manera predeterminada con una IP 172.17.42.1y todos los contenedores Docker se comunican con esta interfaz como su puerta de enlace y se les asignan direcciones IP en el mismo /16rango. Según tengo entendido, todo el tráfico de red hacia / desde los contenedores pasa a través de un NAT, por lo que parece que proviene de salida 172.17.42.1y de entrada se envía 172.17.42.1.

Mi configuración se ve así:

                                          +------------+        /
                                          |            |       |
                            +-------------+ Gateway 1  +-------
                            |             | 10.1.1.1   |     /
                     +------+-------+     +------------+    |
                     |     eth0     |                      /
                     |   10.1.1.2   |                      |
                     |              |                      |
                     | DOCKER HOST  |                      |
                     |              |                      | Internet
                     |   docker0    |                      |
                     |   (bridge)   |                      |
                     |  172.17.42.1 |                      |
                     |              |                      |
                     |     eth1     |                      |
                     |  192.168.1.2 |                      \
                     +------+-------+     +------------+    |
                            |             |            |     \
                            +-------------+ Gateway 2  +-------
                                          | 192.168.1.1|       |
                                          +------------+            

El problema

Quiero enrutar todo el tráfico desde / hacia cualquier contenedor Docker desde la segunda eth1 192.168.1.2interfaz a una puerta de enlace predeterminada 192.168.1.1, mientras que todo el tráfico desde / hacia la máquina host sale de la eth0 10.1.1.2interfaz a una puerta de enlace predeterminada 10.1.1.1. He intentado una variedad de cosas hasta ahora en vano, pero creo que lo más parecido a corregir es usar iproute2 así:

# Create a new routing table just for docker
echo "1 docker" >> /etc/iproute2/rt_tables

# Add a rule stating any traffic from the docker0 bridge interface should use 
# the newly added docker routing table
ip rule add from 172.17.42.1 table docker

# Add a route to the newly added docker routing table that dictates all traffic
# go out the 192.168.1.2 interface on eth1
ip route add default via 192.168.1.2 dev eth1 table docker

# Flush the route cache
ip route flush cache

# Restart the Docker daemon so it uses the correct network settings
# Note, I do this as I found Docker containers often won't be able
# to connect out if any changes to the network are made while it's     
# running
/etc/init.d/docker restart

Cuando saco un contenedor, no puedo hacer ping fuera de él después de hacer esto. No estoy seguro de si las interfaces de puente se manejan de la misma manera que las interfaces físicas para este tipo de enrutamiento, y solo quiero una verificación de la cordura, así como algún consejo sobre cómo podría llevar a cabo esta tarea aparentemente simple.


Tu comentario sobre el NAT no es correcto. Está configurado para enmascararse en la dirección de origen 172.17.0.0/16 donde la interfaz de salida no es docker0. es decir, -A POSTROUTING -s 172.17.0.0/16! -o docker0 -j MASQUERADA. Lo que significa que si el paquete que se origina en un contenedor acoplable sale a través de eth0 o eth1, tomará la dirección IP de esa interfaz, no la IP 172.17.xx.
Matt

Gracias por tu comentario. Entonces, eso cambiaría mi enfoque para el enrutamiento de una interfaz específica, ¿correcto? ¿Sabes cómo podría lograr esto? Tal como está, Docker parece usar la puerta de enlace predeterminada del sistema host, y este no es el comportamiento deseado. Quiero que todas las cosas Docker salgan de una interfaz específica.

No estoy seguro de por qué lo que sugerí no funciona. Pero otra opción podría ser utilizar tuberías. github.com/jpetazzo/pipework También puede ser útil leer la sección de redes avanzadas de la ayuda de Docker . docs.docker.com/articles/networking
Matt

@ Matt Le agradezco sus sugerencias sobre esto. Por lo que he leído, la tubería requiere comandos adicionales después de que se inicia un contenedor para obtener acceso a la puerta de enlace correcta, lo que parece una vulnerabilidad de seguridad. Fue creado originalmente para conectar contenedores entre varios hosts, lo que realmente no es mi objetivo aquí. En cuanto a los documentos de red avanzados de Docker, volveré a echar un vistazo, pero hasta ahora no ha sido útil en esta situación (lo había leído antes de publicar aquí). Voy a publicar un enlace a esta publicación en la página de problemas de Docker Github y veré cómo va desde allí.

Aquí hay un enlace al problema de Github: github.com/docker/docker/issues/13762

Respuestas:


1

Es posible que también tenga que buscar más en la configuración de iptables. Docker enmascara todo el tráfico que se origina en la subred del contenedor, digamos 172.17.0.0/16, a 0.0.0.0. Si ejecuta iptables -L -n -t nat, puede ver la cadena POSTROUTING en la tabla nat que hace esto:

POSTROUTING en cadena (política ACEPTAR)
destino de destino de origen de opt prot
MASQUERADE all - 172.17.0.0/16 0.0.0.0/0

Ahora, puede eliminar esta regla y reemplazarla con una regla que enmascara todo el tráfico que se origina desde la subred de los contenedores a la IP de su segunda interfaz - 192.168.1.2, ya que eso es lo que desea. La regla de eliminación será, suponiendo que es la primera regla en la cadena POSTROUTING:

iptables -t nat -D POSTROUTING 1

Luego agrega esta regla personalizada:

iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j SNAT --to-source 192.168.1.2

En realidad, lo adapta a la interfaz de salida. Esa parte de destino solo dice que cualquier tráfico que se origine desde la subred de origen a cualquier destino será enmascarado.
Matt

Desafortunadamente esto no funcionó. Mi proceso completo fue correr: ip rule add from 172.17.0.0/16 table docker ip route add default via 192.168.1.2 dev eth1 table docker ip route flush cache iptables -t nat -D POSTROUTING 1 iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j SNAT --to-source 192.168.1.2 /etc/init.d/docker restart. Luego intenté ejecutar un ping y un trazado de ruta desde un contenedor, y no pude alcanzar nada.

Tenía un requisito similar, el host tiene una configuración de IP secundaria para llegar a una IP pública. Y quería que el contenedor docker siguiera lo mismo. Esto funcionó en ese caso: "iptables -t nat -I POSTROUTING 1 -s 172.17.0.0/16 -d PUBIP / 32 -j SNAT --to-source SECONDARYIP"
Ram

1

Un amigo y yo nos encontramos con este problema exacto en el que queríamos que Docker admitiera múltiples solicitudes de servicio de interfaces de red. Estábamos trabajando específicamente con el servicio AWS EC2 donde también estábamos conectando / configurando / sacando a la luz las interfaces adicionales. En este proyecto , hay más de lo que necesita, por lo que trataré de incluir solo lo que necesita aquí.

Primero, lo que hicimos fue crear una tabla de ruta separada para eth1:

ip route add default via 192.168.1.2 dev eth1 table 1001

A continuación, configuramos la tabla de cambios para establecer algunas marcas de conexión que provienen de eth1:

iptables -t mangle -A PREROUTING -i eth1 -j MARK --set-xmark 0x1001/0xffffffff
iptables -t mangle -A PREROUTING -i eth1 -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff

Finalmente, agregamos esta regla para que todos los fwmarks utilicen la nueva tabla que creamos.

ip rule add from all fwmark 0x1001 lookup 1001

El siguiente iptablescomando restaurará la marca de conexión y luego permitirá que la regla de enrutamiento use la tabla de enrutamiento correcta.

iptables -w -t mangle -A PREROUTING -i docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff

Creo que esto es todo lo que se necesita de nuestro ejemplo más complejo donde (como dije) nuestro proyecto fue adjuntar / configurar / abrir la eth1interfaz en el momento del arranque.

Ahora, este ejemplo no impedirá que las conexiones eth0atiendan las solicitudes, docker0pero creo que podría agregar una regla de enrutamiento para evitar eso.


@stuntmachine solo tiene curiosidad si tuvo la oportunidad de probar esto. Si ya ha descubierto algo por su cuenta, ¿podría compartir su solución?
williamsbdev

Hola, @williamsbdev: publicación anterior y posibilidad remota, pero ¿crees que esta solución también funcionaría para mi problema en SO? stackoverflow.com/questions/51312310/…
Jolly Roger

2
Jolly Roger: creo que esto resolverá tu problema. Recientemente, otro equipo me preguntó sobre esta publicación de blog (esencialmente la misma solución que aquí) y me dijeron que funcionó muy bien. williamsbdev.com/posts/docker-connection-marking
williamsbdev

0

La mascarada no es de 172.17.42.1 sino más bien

 -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

Lo que significa que esta regla no funcionará bien.

ip rule add from 172.17.42.1 table docker

Intenta en su lugar

ip rule add from 172.17.0.0/16 table docker

Desafortunadamente esto no funcionó. Mis contenedores todavía estaban enrutando a la misma puerta de enlace predeterminada que el host. Esto es lo que ejecuté: ip rule add from 172.17.0.0/16 table docker ip route add default via 192.168.1.2 dev eth1 table docker ip route flush cache /etc/init.d/docker restart desde el contenedor ejecuté un traceroute y el primer salto fue 10.1.1.1 cuando debería haber sido 192.168.1.1
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.