El driver MAC-VLAN

En el articulo anterior revisamos las redes que son creadas por defecto por el servicio de Docker y como el driver bridge agrega un linux bridge entre el contenedor y la interfaz de red del servidor, además de utilizar un veth pair para conectar el contenedor con dicho bridge.

El siguiente bloque de código muestra la relación existente entre contenedor y el servidor donde se ejecuta, se puede observar que la interfaz de red del contenedor eth0@if23 hace referencia a la interfaz numero 23 del servidor que es vethe752504.

$ docker run --detach --name container ubuntu:18.04 sleep infinity
c719a954130f4769afeff28654800e8d7593b14442cfb36bcbd0ed0eade28b83
$ docker exec -ti container ip addr show eth0
22: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:50:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.80.0.2/24 brd 172.80.0.255 scope global eth0
       valid_lft forever preferred_lft forever
$ ip link show type veth
23: vethe752504@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether f6:5d:14:36:58:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242f6f9457f       no              vethe752504

Aunque utilizar un linux bridge resulta útil para la mayoría de los casos, existen aplicaciones las cuales requieren ser conectadas directamente a la red física del servidor donde son ejecutadas.

Existen dos tipos de drivers (macvlan y ipvlan) los cuales ofrecen una conexión más directa. Estos drivers reducen la latencia de red debido a que la ruta de tráfico de los paquetes de red es más corta. En este artículo revisamos a detalle el driver de MACVLAN o MAC-VLAN.

Definición

El driver MAC-VLAN opera en la capa de enlace de datos del modelo OSI y permite conectar múltiples subinterfaces de contenedores o máquinas virtuales a una sola interfaz física. Cada subinterfaz posee una dirección MAC (generada aleatoriamente) y por consiguiente una dirección IP.

Existen cuatro tipos de MAC-VLAN:

  • Privada: Donde todas las tramas de red son reenviadas por medio de la interfaz de red principal. No es posible una comunicación entre subinterfaces en el mismo servidor.
  • Virtual Ethernet Port Aggregator: Requiere un switch que tenga soporte IEEE 802.1Qbg el cual permite una comunicación entre subinterfaces en el mismo servidor. Las tramas de red son enviadas a través de la interfaz de red principal lo cual permite que puedan ser aplicadas políticas de red externas.
  • Bridge: Conecta todas las subinterfaces con un bridge lo cual permite que las tramas de red sean enviadas directamente sin salir del servidor.
  • Passthru: Permite conectar una sola subinterfaz a la interfaz de red principal.

Laboratorio

Para el caso de Docker, este servicio utiliza el tipo Bridge en el driver MAC-VLAN, lo que permite una comunicación externa e intercomunicación entre contenedores. El siguiente ejemplo muestra las instrucciones para crear una red de tipo MAC-VLAN en Docker y la creación de dos contenedores dentro de esa misma red.

$ ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:b0:b7:a2 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute eth0
       valid_lft 85773sec preferred_lft 85773sec
$ docker network create --driver macvlan --subnet=10.0.2.0/24  --gateway=10.0.2.2 --opt parent=eth0 macvlan0
d92d3cf366628e962a0bbd4b922d6ca7b2c50e9dc69c5219a2dbd398ae32d923
$ docker run --detach --name container --network macvlan0 busybox sleep infinity
aa915e2015629361306c80b2e508f94b6855857b14cfe6ea397c151250f12cb2
$ docker run --detach --name container2 --network macvlan0 busybox sleep infinity
dff856cb2457b27bfd2f8e5bef44eada3f63bdc4aa8b4e3e8e3dd2ed52cd53d4

Las siguientes instrucciones muestran las direcciones IP asignadas a los contenedores que fueron creados recientemente.

$ docker exec -ti container ip addr show eth0
13: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:0a:00:02:01 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.1/24 brd 10.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever
$ docker exec -ti container2 ip addr show eth0
14: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:0a:00:02:03 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.3/24 brd 10.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever

Una vez obtenidas las direcciones IP de los dos contenedores, es posible validar distintos tipos de comunicación.

Las siguientes instrucciones muestran la comunicación de este a oeste, donde se observa que los contenedores pueden comunicarse entre si.

$ docker exec -ti container ping -c 3 10.0.2.3
PING 10.0.2.3 (10.0.2.3): 56 data bytes
64 bytes from 10.0.2.3: seq=0 ttl=64 time=0.081 ms
64 bytes from 10.0.2.3: seq=1 ttl=64 time=0.083 ms
64 bytes from 10.0.2.3: seq=2 ttl=64 time=0.059 ms

--- 10.0.2.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.059/0.074/0.083 ms

La comunicación de sur a norte tiene algunas limitantes. Podemos observar que no es posible la comunicación directa con la dirección IP de la interfaz de red principal.

$ docker exec -ti container ping -c 3 10.0.2.15
PING 10.0.2.15 (10.0.2.15): 56 data bytes

--- 10.0.2.15 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

Sin embargo, es posible la comunicación directa con la dirección IP de la interfaz del Gateway.

$ docker exec -ti container ping -c 3 10.0.2.2
PING 10.0.2.2 (10.0.2.2): 56 data bytes
64 bytes from 10.0.2.2: seq=1 ttl=64 time=0.285 ms
64 bytes from 10.0.2.2: seq=2 ttl=64 time=0.233 ms

--- 10.0.2.2 ping statistics ---
3 packets transmitted, 2 packets received, 33% packet loss
round-trip min/avg/max = 0.233/0.259/0.285 ms