Introducción a las redes y almacenamiento en LXC
En el anterior artículo: Introducción a LinuX Containers (LXC) estudiamos los aspectos fundamentales de la gestión de los contenedores LXC. En este artículo vamos a profundizar en las redes y en el almacenamiento que podemos configurar en los LinuX Containers.
Redes en LXC
LXC nos ofrece distintos mecanismos para conectar nuestros contenedores a una red. En este artículo nos vamos a centrar en las conexiones de tipo bridge. Tenemos dos posibilidades:
- Podemos crear manualmente el bridge o usar libvirt para su creación (podemos crear distintos tipos de redes con libvirt).
- Podemos usar
lxc-net
, servicio ofrecido por LXC, que nos facilita la creación de un bridge, que por defecto se llamalxcbr0
, y que nos ofrece una una red de tipo NAT con los servicios de DHCP y DNS.
Redes con lxc-net
Veamos en primer lugar la segunda opción. Como en el artículo anterior, este manual se basa en la distribución GNU/Linux Debian 11. En versiones anteriores de LXC no había configurada una red por defecto para conectar nuestros contenedores. En la versión actual y utilizando lxc-net
se crea un bridge llamado lxcbr0
que nos ofrece una red de tipo NAT con los servicios DHCP y DNS. Veamos su configuración:
En primer lugar, en el fichero /etc/default/lxc-net
se configura los parámetros de la red que se va a crear:
USE_LXC_BRIDGE="true"
...
Con el parámetro USE_LXC_BRIDGE="true"
activamos la funcionalidad ofrecida por el servicio lxc-net
: gestionar la red creada.
No hay más parámetros porque se coge la configuración por defecto que es la siguiente:
LXC_BRIDGE="lxcbr0"
LXC_ADDR="10.0.3.1"
LXC_NETMASK="255.255.255.0"
LXC_NETWORK="10.0.3.0/24"
LXC_DHCP_RANGE="10.0.3.2,10.0.3.254"
LXC_DHCP_MAX="253"
Es decir, se crea el bridge lxcbr0
, el direccionamiento de la red que se crea es 10.0.3.0/24
y podemos indicar el rango de direcciones que se reparten. Si queremos cambiar algunos de estos parámetros habrá que introducirlos en el fichero de configuración y reiniciar el servicio lxc-net
.
Vemos que se crea un proceso dnsmasq
que ofrece el servidor DHCP y DNS a los contenedores:
$ ps aux|grep dnsmasq
dnsmasq 433 0.0 0.0 13440 392 ? S 19:32 0:00 dnsmasq --conf-file=/etc/lxc/dhcp.conf \
-u dnsmasq --strict-order --bind-interfaces --pid-file=/run/lxc/dnsmasq.pid --listen-address 10.0.3.1 \
--dhcp-range 10.0.3.2,10.0.3.254 --dhcp-lease-max=253 --dhcp-no-override --except-interface=lo \
--interface=lxcbr0 --dhcp-leasefile=/var/lib/misc/dnsmasq.lxcbr0.leases --dhcp-authoritative
Además se crea una regla iptables para que nuestro host haga SNAT para que los contenedores tengan salida al exterior:
$ iptables -L -n -v -t nat
...
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1 40 MASQUERADE all -- * * 10.0.3.0/24 !10.0.3.0/24
Conexión de los contenedores a lxcbr0
La configuración por defecto posibilita que los contenedores que creemos se conecten a esta red. Lo podemos ver en la configuración general, en el fichero /etc/lxc/default.conf
:
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
...
Si hemos creado un contenedor llamado contenedor1
recibirá esta configuración que podremos encontrar en su fichero de configuración /var/lib/lxc/contenedor1/config
:
lxc.net.0.type = veth
lxc.net.0.hwaddr = 00:16:3e:cf:8f:c3
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
...
Por lo tanto podemos comprobar que el contenedor1
esta conectado a sea red. Por ejemplo, si mostramos los contenedores que hemos creado, vemos que ha recibido una ip en ese rango:
$ lxc-ls -f
NAME STATE AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED
contenedor1 RUNNING 1 - 10.0.3.180 - false
Si accedemos al contenedor podemos hacer varias comprobaciones:
$ lxc-attach contenedor1
root@contenedor1:~# ip a
...
2: eth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
inet 10.0.3.180/24 brd 10.0.3.255 scope global dynamic eth0
...
root@contenedor1:~# ip r
default via 10.0.3.1 dev eth0
10.0.3.0/24 dev eth0 proto kernel scope link src 10.0.3.180
root@contenedor1:~# cat /etc/resolv.conf
nameserver 10.0.3.1
root@contenedor1:~# apt install iputils-ping
...
root@contenedor1:~# ping www.josedomingo.org
PING endor.josedomingo.org (37.187.119.60) 56(84) bytes of data.
64 bytes from ns330309.ip-37-187-119.eu (37.187.119.60): icmp_seq=1 ttl=49 time=28.7 ms
...
- Comprobamos que se ha configurado con la ip
10.0.3.180
. - Vemos que la puerta de enlace es la
10.0.3.1
que corresponde a nuestra máquina física. - Del mismo modo la máquina física es el servidor DNS.
- Hemos instalado la herramienta
ping
y comprobamos que tenemos resolución y acceso al exterior.
Configuración del servidor DHCP y DNS al usar lxc-net
Como hemos visto, el servicio lxc-net
crea un bridge y configura un servicio dnsmasq
para ofrecer DHCP y DNS a los contenedores conectados a ese bridge. Por lo tanto podemos configurar el servicio dnsmasq
, para ello vamos a descomentar la línea que encontramos en el fichero de configuración /etc/default/lxc-net
:
USE_LXC_BRIDGE="true"
LXC_DHCP_CONFILE=/etc/dnsmasq.conf
Con el parámetro LXC_DHCP_CONFILE
indicamos el fichero de configuración del servicio dnsmasq
que se va a crear (el fichero de configuración se puede llamar dnsmasq.conf
o tener otro nombre).
Por ejemplo, si queremos realizar una reserva para otorgar la misma ip al contenedor1
, crearíamos el fichero /etc/dnsmasq.conf
con el siguiente contenido:
dhcp-host=contenedor1,10.0.3.10
Reiniciamos el servicio lxc-net
:
$ systemctl restart lxc-net
Y al reiniciar el contenedor comprobamos que ha tomado la ip que hemos indicado en la reserva:
$ lxc-stop -r contenedor1
$ lxc-ls -f
NAME STATE AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED
contenedor1 RUNNING 1 - 10.0.3.10 - false
Otro ejemplo: si queremos enviar un nombre de dominio a los contenedores, añadimos el fichero /etc/default/lxc-net
la línea:
LXC_DOMAIN="example.org"
Reiniciamos el servicio lxc-net
, reiniciamos el contenedor y comprobamos su FQDN:
$ systemctl restart lxc-net
$ lxc-stop -r contenedor1
$ lxc-attach contenedor1
root@contenedor1:~# hostname -f
contenedor1.example.org
Conexión de los contenedores a un bridge existente
Imaginemos que tenemos en nuestro host instalado libvirt para manejar los recursos de KVM/Qemu y hemos creado una red con virsh
de tipo NAT, que ha creado un bridge llamado virbr0
, con las siguientes características:
$ virsh net-dumpxml default
<network>
<name>default</name>
<uuid>c411a5a1-f998-42a9-bc8a-9a9052fc36f6</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:fc:32:a2'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
</network>
Podemos modificar el fichero de configuración por defecto /etc/lxc/default.conf
, indicando el bridge virbr0
:
lxc.net.0.type = veth
lxc.net.0.link = virbr0
lxc.net.0.flags = up
...
Todos los nuevos contenedores que creemos se conectarán a la red default
:
$ lxc-create -n contenedor2 -t debian -- -r bullseye
$ lxc-start contenedor2
$ lxc-ls -f
NAME STATE AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED
contenedor1 RUNNING 1 - 10.0.3.10 - false
contenedor2 RUNNING 1 - 192.168.122.228 - false
Vemos como el contenedor2
ha tomado en una ip de la red default
.
Si quisiéramos cambiar la conexión del un contenedor ya existente deberiamos hacer la modificación en su fichero de configuración: /var/lib/lxc/<NOMBRE_CONTENEDOR>/config
y reiniciar el contenedor.
También podríamos conectar el contenedor1
a la red default
, para ello vamos a añadir la información de la conexión en su fichero de configuración /var/lib/lxc/contenedor1/config
:
lxc.net.0.type = veth
lxc.net.0.hwaddr = 00:16:3e:cf:8f:c3
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.1.type = veth
lxc.net.1.hwaddr = 00:16:3e:cf:8f:d3
lxc.net.1.link = virbr0
lxc.net.1.flags = up
...
Indicamos la segunda conexión utilizando el nombre de los parámetros como lxc.net.1.*
. Además hemos cambiado la mac de la segunda tarjeta de red. Ahora reiniciamos y accedemos al contenedor:
$ lxc-stop -r contenedor1
$ lxc-attach contenedor1
root@contenedor1:~# apt install nano
root@contenedor1:~# nano /etc/network/interfaces
Configuramos la segunda interfaz de red:
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
auto eth1
iface eth1 inet dhcp
Y obtenemos una nueva dirección ip en la nueva red:
root@contenedor1:~# ifup eth1
root@contenedor1:~# ip a
...
2: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
...
inet 10.0.3.10/24 brd 10.0.3.255 scope global dynamic eth0
...
3: eth1@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
...
inet 192.168.122.196/24 brd 192.168.122.255 scope global dynamic eth1
...
Si listamos los contenedores que tenemos, podemos ver las dos direcciones ip:
$ lxc-ls -f
NAME STATE AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED
contenedor1 RUNNING 1 - 10.0.3.10, 192.168.122.196 - false
contenedor2 RUNNING 1 - 192.168.122.228 - false
Almacenamiento en LXC
Veamos como montar un directorio del host en un contenedor. Imaginemos que tenemos el directorio /opt/contenedor1
con un fichero index.html
y lo queremos montar en el contenedor1
en el directorio /srv/www
. Tenemos que tener en cuenta los siguiente:
El directorio de montaje debe existir en el contenedor:
$ lxc-attach contenedor1
root@contenedor1:~# cd /srv
root@contenedor1:/srv# mkdir www
En el fichero de configuración del contenedor (/var/lib/lxc/contenedor1/config
) añadimos la siguiente línea:
lxc.mount.entry=/opt/contenedor1 srv/www none bind 0 0
Hay que tener en cuenta que al indicar el directorio de montaje hay que usar una ruta relativa (es relativa al directorio donde se encuentra el sistema de fichero del contenedor, en este caso /var/lib/lxc/contenedor1/rootfs/
).
Reiniciamos el contenedor y comprobamos que se ha montado el directorio de forma correcta:
$ lxc-stop contenedor1
$ lxc-start contenedor1
$ lxc-attach contenedor1
root@contenedor1:~# cd /srv/www
root@contenedor1:/srv/www# ls
index.html
Conclusiones
Con los dos últimos artículos hemos hecho una breve descripción a los aspectos más interesantes del trabajo con LinuX Containers (LXC). Como siempre para seguir profundizando lo mejor es estudiar la documentación oficial.
Comments
kato
AAGHH….maldito LXC. Uno de la vieja escuela reniega con la containerizacion viniendo mas de la virtualizacion. Aprende como puede docker y casi que se acostumbra y me toca migrar un container LXC productivo y trato de tirarlo en otro servidor host volviendome loco con la parte de red, y leyendo diferentes documentaciones para intentar hacer una red nateada…
Por suerte me encontré con tu nota, que va al grano, claro y conciso, para leer, entender y hacer. Y funcionar, claro.
Gracias por hacerme ganar tiempo!
Leave a Comment
Your email address will not be published. Required fields are marked *