Contenedores LXD en un puente con la interfaz de red del anfitrión

per Victor Carceler darrera modificació 2021-05-15T11:30:37+01:00

Con la configuración por defecto los contenedores LXD tienen sus interfaces de red en un puente de la máquina anfitrión, pero  normalmente la interfaz de red del anfitrión no forma parte de este puente. Esto permite que los contenedores se vean entre sí y dispongan de acceso a la red, pero no permite acceder desde la red a los contenedores sin aplicar redirección de puertos en el anfitrión.

Una configuración muy típica consiste en incorporar la interfaz de red del anfitrión al mismo puente que utilizan los contenedores para permitir que estos tengan su propia IP en la LAN del anfitrión.

Veamos cómo hacer esto en una MV Ubuntu 20.04 de escritorio.

Configuración de red en el host anfitrión

Con finalidades didácticas se va a utilizar como host anfitrión de los contenedores una MV con Ubuntu 20.04 Desktop. Pero realmente esta configuración tendrá más gracia si el host es el ordenador físico que se utiliza en el aula, en el que el alumno ejecuta los contenedores para hacer sus prácticas.

Otra aplicación de una configuración muy similar se utiliza en el centro para ejecutar servicios con dos niveles de virtualización:

  • En el servidor Edoras se ejecuta una MV KVM a la que se le asignan unos recursos: vcpu, ram, disco, interfaz de red.
  • En esa máquina virtual, que por motivios obvios recibe como nombre el prefijo matrioska-, se lanzan contenedores dedicados a los servicios.

En este último caso se utiliza la versión de servidor en la MV matrioska- pero la configuración es muy similar ya que en ambos casos se utiliza netplan para configurar la red. Sin embargo hay que recordar que la versión de escritorio utiliza como backend a NetworkManager y la versión de servidor a systemd-systemd.

Al consultar la configuración de red de una MV Ubuntu 20.04 de escritorio con una instalación básica nos encontramos:

usuario@neva:~$ cat /etc/netplan/01-network-manager-all.yaml 
# Let NetworkManager manage all devices on this system
network:
version: 2
renderer: NetworkManager
usuario@neva:~$

Y al consultar las interfaces de red

usuario@neva:~$ ip -c a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:47:8f:82 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute enp0s3
valid_lft 84374sec preferred_lft 84374sec
inet6 fe80::61b6:2083:629:7f22/64 scope link noprefixroute
valid_lft forever preferred_lft forever
usuario@neva:~$

Como se está utilizando VirtualBox con la interfaz de red en modo NAT observamos:

  • Que en la MV la interfaz de red es enp0s3
  • Que está configurada la IP 10.0.2.15/24 que ha proporcionado el router virtual que proporciona VirtualBox a las interfaces en modo NAT.

Para nuestros propósito será necesario editar netplan para definir un puente br0 que contenga a la interfaz de red enp0s3. Es importante recordar que Linux no asigna direcciones de red a las interfaces que forman parte del puente pero sí al propio puente.

Podremos aplicar los cambios en /etc/netplan/01-network-manager-all.yaml

# Let NetworkManager manage all devices on this system
network:
version: 2
renderer: NetworkManager
ethernets:
enp0s3:
dhcp4: no
bridges:
br0:
dhcp4: yes
interfaces:
- enp0s3

Y después de reiniciar la MV encontraremos:

usuario@neva:~$ ip -c a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
link/ether 08:00:27:47:8f:82 brd ff:ff:ff:ff:ff:ff
3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 08:00:27:47:8f:82 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute br0
valid_lft 86400sec preferred_lft 86400sec
inet6 fe80::14b5:bdff:feb6:fcbf/64 scope link
valid_lft forever preferred_lft forever
usuario@neva:~$

Vale la pena observar:

  1. La interfaz enp0s3 no tiene configurada ninguna dirección IP.
  2. Aparece una nueva interfaz br0
    1. El puente br0 copia la dirección MAC de la primera interfaz que se agrega al puente, en nuestro caso enp0s3.
    2. El puente br0 tiene la dirección IP que antes tenía enp0s3.

Configuración de LXD

Podemos instalar lxd a partir de su paquete snap

usuario@neva:~$ sudo snap install lxd
[sudo] contraseña para usuario:
Se ha instalado lxd 4.14 por Canonical✓
usuario@neva:~$

Y configurar lxd utilizando el comando lxd init y contestando a las preguntas

usuario@neva:~$ lxd init
Would you like to use LXD clustering? (yes/no) [default=no]:
Do you want to configure a new storage pool? (yes/no) [default=yes]:
Name of the new storage pool [default=default]:
Name of the storage backend to use (dir, lvm, ceph, btrfs) [default=btrfs]: dir
Would you like to connect to a MAAS server? (yes/no) [default=no]:
Would you like to create a new local network bridge? (yes/no) [default=yes]: no
Would you like to configure LXD to use an existing bridge or host interface? (yes/no) [default=no]: yes
Name of the existing bridge or host interface: br0
Would you like the LXD server to be available over the network? (yes/no) [default=no]:
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]:
usuario@neva:~$

En este caso:

  • En aras de la simplicidad se ha escogido como storage backend dir aunque en el instituto utilizamos zfs :-)
  • Se ha indicado que no se debe crear un puente nuevo y que en su lugar se debe utilizar br0 que ya está definido

Ya podemos lanzar un contenedor

usuario@neva:~$ lxc launch ubuntu:20.04 contenedor1
Creating contenedor1
Starting contenedor1
usuario@neva:~$

y comprobar que obtiene una IP de la red 10.0.2.0/24

usuario@neva:~$ lxc list
+-------------+---------+------------------+------+-----------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+-------------+---------+------------------+------+-----------+-----------+
| contenedor1 | RUNNING | 10.0.2.16 (eth0) | | CONTAINER | 0 |
+-------------+---------+------------------+------+-----------+-----------+
usuario@neva:~$