LXD - perfeccion-ar/infraestructura-clasica-y-avanzada GitHub Wiki
Este artículo tiene por propósito armar un Home Lab con pocos recursos, mediante LXD, donde aprender y divertirse.
Sin embargo, LXD no es solo un juguete. Se pueden lograr grandes cosas con él, como veremos a continuación.
- Distinción entre Container LXD, VM LXD, y mas bajo, Docker
- Instalación de LXD
- Creación de containers LXD y VMs LXD
- Cómo accederlos, subir archivos
- En el caso de los container LXD, cómo instalar Docker adentro
La idea detrás de LXD, es que se puede tener docenas de containers LXD en su equipo sin notar apenas carga, todos Linux completos y usables en todo sentido, mucho mas fáciles de usar que Docker.
También se puede tener VMs manejadas por el propio LXD. O en el léxico de LXD, "instancias". Pero a éstas se les debe garantizar cierta cantidad de recursos. La VM se apropiará de esos recursos sin devolverlos, los use o no. Es decir, siempre tendrá menos VMs que Containers en su máquina física.
Resumen de https://documentation.ubuntu.com/lxd/en/latest/explanation/instances/#containers-and-vms
¿Eso significa que a los containers LXD no se les asigna recursos? No exactamente: normalmente se deja que ocupen toda la potencia compartida del equipo. Si hay 16 núcleos, el container "ve" (ejemplo, con htop) 16 núcleos. Lo que en todo caso se hace, es limitarlos, para prevenir por ejemplo, un minero agotando toda la CPU del host hospedador. Por su parte, el container LXD es tan transparente para el host, que por ejemplo si se lanza un ps -ef
en el host, se pueden ver y matar sus procesos.
En pocas palabras, mientras que a una VM se le garantizan recursos, a los container LXD se les establece una "quota".
- Mayor aislamiento
- Si un container se pone viejo y vulnerable, allá él. No pone en riesgo al resto de los servicios instalados en el server, porque no comparte nada con ellos.
- Con containers, no hace falta que todos tengan el root del servidor: cada uno es root en su propio container
- Los containers están en una red interna, accesible solo por el [balanceador - proxy reverso]
- A los containers se les puede reenviar ciertos puertos desde afuera: Por ejemplo, el 2222 de afuera llevará al 22 (ssh) del container Oscar01. Es como el adolescente al que le abrimos una puerta en su habitación, directo a la calle, para que invite a sus amigas sin tener que pasar por el comedor.
Nótese que aquí distinguimos "LXD Container" de "Container" a secas. Esto se debe a que "Container" es una expresión muy utilizada en el mundo de Docker.
Por lo mismo usaremos "Instancia VM de LXD", porque en el mundo de Docker las "instancias" son expresiones asociadas al momento en que una imagen definida en un Dockerfile comienza a correr en su propio userspace. Es decir, en su propio container.
Finalmente, "instancia" a secas, se utilizan también mucho en la programación orientada a objetos, que es básicamente cuando una clase pasa a correr en memoria como objeto. Dada la aclaración, instancia siempre será algo que está definido como una receta (un Dockerfile), una Clase o una Template (imagen de sistema operativo que está en la nube), y que empieza a correr en memoria y en su propio espacio.
A continuación se describe el proceso en el cual Sergio como root del server "host", "real", "metal", crea un espacio de trabajo para Adrian. Un container LXD o una VM LXD.
Pero primero Sergio debe instalar LXD
Para instalar LXD, no se debería seguir los instructivos del proyecto Linux Containers, sino los oficiales de Ubuntu, llamado LXD.
Uno y otro proyecto, eran llamados genéricamente "LXC" hasta 2023. Hoy en día han divergido. Los mismos comandos no son iguales, y ese hecho de ser tenido en cuenta al momento de buscar documentación. Puesto que Canonical (creadores del hypervisor LXD) eran quien ponía la mayor parte de los recursos en Linux Containers, me decanto en seguir sus instrucciones. Linux Containers mientras, parece estar avanzando en un hypervisor llamado Incus, el cual trataré mas adelante conforme se vaya estabilizando.
Entonces, para reproducir los pasos de esta guía, si se desea contar con LXD instalado, siga primero estas guías:
- https://documentation.ubuntu.com/lxd/en/latest/tutorial/first_steps/
- https://ubuntu.com/server/docs/lxd-containers
Un resumen que no reemplaza una atenta lectura de los enlaces anteriores, consiste en ejecutar estos pasos en su propio equipo de aprendizaje, notebook o máquina virtual con Ubuntu 20.04, 22.04, o 24.04
Relajar reglas iptables de Docker: este paso es necesario no solo si usted ya tiene Docker en el equipo, sino si piensa tenerlo a futuro. En cualquier caso, HAGALO SIEMPRE.
La consecuencia de no hacerlo es esencialmente, que los container LXD (o a veces los container Docker) se queden "ciegos", es decir, no puedan salir a la red externa. Esto es: si hacen por ejemplo un apt-get update
, un ping google.com
y no hay respuesta, es muy posible que Docker y LXD se estén peleando por el control de las reglas mangle. Esa es la razón por la que recomiendo no tener Docker en el mismo equipo que LXD, sino "adentro de un container o instancia de máquina virtual LXD".
Por ahora, para prevenir el problema:
mkdir -p /etc/docker
nano /etc/docker/daemon.json
Y adentro pegue
{
"iptables": false
}
Si tiene Docker, reinicie el equipo, con sudo reboot
Ahora si, instalamos LXD
echo "root:1000:1" | sudo tee -a /etc/subuid /etc/subgid
sudo apt install snapd zfsutils-linux -y
sudo snap install lxd
A menos que usemos root todo el tiempo, lo cual no es aconsejable, debemos agregar nuestro usuario al grupo lxd. Esto nos permitirá administrar los container mediante instrucciones lxc
sin tener que llamar a sudo lxc
todo el tiempo:
sudo adduser $USER lxd
Deberíamos volver a loguearnos, para disponer de los permisos de ese grupo.
Los containers LXD necesitarán un storage pool donde "vivir". Pero si hacemos lxc storage list
no veremos ninguno creado.

❯ lxc storage list
+------+--------+--------+-------------+---------+-------+
| NAME | DRIVER | SOURCE | DESCRIPTION | USED BY | STATE |
+------+--------+--------+-------------+---------+-------+
Necesitamos entonces un pool, y también un bridge para los containers.
Trate de no usar "default" para el nombre del pool, porque es demasiado genérico, y no muestra rápida y visualmente el tipo de file system que posee adentro. Y esto es todo un detalle, porque mientras que el sistema de archivos recomendado para tener containers rápidos es ZFS, para poder correr Docker adentro de los containers necesitaremos en cambio BTRFS.
$ lxd init
Would you like to use LXD clustering? (yes/no) [default=no]: no
Do you want to configure a new storage pool? (yes/no) [default=yes]:
Name of the new storage pool [default=default]: zfspool
↑↑↑↑↑↑↑ (lea abajo)
Puesto que se pueden crear varios tipos de pools, y tenerlos a todos en el mismo servidor, conviene dejar el pool por defecto definido como ZFS, y crear mas tarde un pool con BTRFS aparte, mapeada hacia cierta carpeta que usa Docker. Vea la implementación en el capítulo "Docker adentro de LXD", al final.
Hecha esta introducción, definiremos el nombre del pool por defecto como zfspool
Name of the storage backend to use (lvm, powerflex, zfs, btrfs, ceph, dir) [default=zfs]: zfs
Create a new ZFS pool? (yes/no) [default=yes]: yes
Would you like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]: no
Size in GiB of the new loop device (1GiB minimum) [default=30GiB]: 100
Would you like to connect to a MAAS server? (yes/no) [default=no]: no
Would you like to create a new local network bridge? (yes/no) [default=yes]: yes
What should the new bridge be called? [default=lxdbr0]: lxdbr0
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: auto
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: none
Would you like the LXD server to be available over the network? (yes/no) [default=no]: yes
Esta pregunta Would you like the LXD server to be available over the network? tiene que ver con poder tener una amable interface web para controlar nuestros containers. Volveremos a eso en unos capítulos más abajo
Address to bind LXD to (not including port) [default=all]: all
Port to bind LXD to [default=8443]: 8443
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]: no
Si ahora listamos los pools veremos
$ lxc storage list
+---------+--------+--------------------------------------------+-------------+---------+---------+
| NAME | DRIVER | SOURCE | DESCRIPTION | USED BY | STATE |
+---------+--------+--------------------------------------------+-------------+---------+---------+
| zfspool | zfs | /var/snap/lxd/common/lxd/disks/zfspool.img | | 1 | CREATED |
+---------+--------+--------------------------------------------+-------------+---------+---------+
$ lxc storage show zfspool
name: zfspool
driver: zfs
config:
size: 100GiB
Esto es para que el pool se vacíe cuando liberamos datos:
sudo zfs set xattr=sa zfspool
Lo mismo si listamos las interfaces de red con ip ad
, veremos al bridge lxdbr0
creado.
lxc image list ubuntu: | grep "LTS\|amd64\|SIZE"
Muchas respuestas. Hay desde el viejo Ubuntu 14.04 al más actual. Filtremos un poco mas:
lxc image list ubuntu:24.04 | grep "24.04\|SIZE" | grep amd64
| ubuntu 24.04 LTS amd64 (release) (20240608) | x86_64 | VIRTUAL-MACHINE | 556.63MiB | Jun 8, 2024 at 12:00am (UTC) |
| ubuntu 24.04 LTS amd64 (release) (20240608) | x86_64 | CONTAINER | 239.24MiB | Jun 8, 2024 at 12:00am (UTC) |
Si deseamos instalar otras distribuciones de Linux, podemos verlas a todas listadas en https://images.linuxcontainers.org - no tienen soporte de Canonical (LXD), pero si de Linux Containers
Ejemplo con Almalinux
root@hetzner01 ~ # lxc image list images:almalinux | grep "LTS\|amd64\|SIZE\|-"
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE | UPLOAD DATE |
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
| almalinux/8 (3 more) | 7f70179ad6f3 | yes | AlmaLinux 8 amd64 (20240731_0009) | x86_64 | VIRTUAL-MACHINE | 850.77MiB | Jul 31, 2024 at 12:00am (UTC) |
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
| almalinux/8 (3 more) | bd72d28f9e75 | yes | AlmaLinux 8 amd64 (20240731_0009) | x86_64 | CONTAINER | 128.91MiB | Jul 31, 2024 at 12:00am (UTC) |
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
| almalinux/8/cloud (1 more) | 16b431d4b350 | yes | AlmaLinux 8 amd64 (20240731_0010) | x86_64 | VIRTUAL-MACHINE | 869.45MiB | Jul 31, 2024 at 12:00am (UTC) |
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
| almalinux/8/cloud (1 more) | dcb497f2381b | yes | AlmaLinux 8 amd64 (20240731_0010) | x86_64 | CONTAINER | 148.56MiB | Jul 31, 2024 at 12:00am (UTC) |
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
| almalinux/9 (3 more) | 6a2a84776200 | yes | AlmaLinux 9 amd64 (20240731_0009) | x86_64 | VIRTUAL-MACHINE | 728.72MiB | Jul 31, 2024 at 12:00am (UTC) |
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
| almalinux/9 (3 more) | 7231c2e74123 | yes | AlmaLinux 9 amd64 (20240731_0009) | x86_64 | CONTAINER | 111.12MiB | Jul 31, 2024 at 12:00am (UTC) |
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
| almalinux/9/cloud (1 more) | b343da24b8df | yes | AlmaLinux 9 amd64 (20240731_0009) | x86_64 | CONTAINER | 127.17MiB | Jul 31, 2024 at 12:00am (UTC) |
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
| almalinux/9/cloud (1 more) | e1e589d819a9 | yes | AlmaLinux 9 amd64 (20240731_0009) | x86_64 | VIRTUAL-MACHINE | 749.10MiB | Jul 31, 2024 at 12:00am (UTC) |
+----------------------------+--------------+--------+-----------------------------------+--------------+-----------------+-----------+-------------------------------+
Ejemplo con Debian
root@hetzner01 ~ # lxc image list images:debian | grep "LTS\|amd64\|SIZE\|-"
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE | UPLOAD DATE |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/11 (7 more) | 0d9c11f4de3e | yes | Debian bullseye amd64 (20240731_0002) | x86_64 | CONTAINER | 84.65MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/11 (7 more) | c0f6142809c3 | yes | Debian bullseye amd64 (20240731_0002) | x86_64 | VIRTUAL-MACHINE | 300.20MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/11/cloud (3 more) | 9caf16c0cf9a | yes | Debian bullseye amd64 (20240731_0002) | x86_64 | VIRTUAL-MACHINE | 330.85MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/11/cloud (3 more) | ece3b218e9d0 | yes | Debian bullseye amd64 (20240731_0002) | x86_64 | CONTAINER | 102.13MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/12 (7 more) | 1eb42164268f | yes | Debian bookworm amd64 (20240731_0002) | x86_64 | VIRTUAL-MACHINE | 351.16MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/12 (7 more) | 4d7d0b411305 | yes | Debian bookworm amd64 (20240731_0002) | x86_64 | CONTAINER | 95.90MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/12/cloud (3 more) | 9c42851fcba4 | yes | Debian bookworm amd64 (20240731_0002) | x86_64 | CONTAINER | 122.13MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/12/cloud (3 more) | 1466755e6046 | yes | Debian bookworm amd64 (20240731_0002) | x86_64 | VIRTUAL-MACHINE | 394.48MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/13 (7 more) | e49bc3ecf1c5 | yes | Debian trixie amd64 (20240731_0002) | x86_64 | CONTAINER | 98.11MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/13 (7 more) | ffeb1a67f02f | yes | Debian trixie amd64 (20240731_0002) | x86_64 | VIRTUAL-MACHINE | 338.87MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/13/cloud (3 more) | 4fb533ef30aa | yes | Debian trixie amd64 (20240731_0002) | x86_64 | VIRTUAL-MACHINE | 381.81MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/13/cloud (3 more) | ce46982dd682 | yes | Debian trixie amd64 (20240731_0002) | x86_64 | CONTAINER | 127.07MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/sid (3 more) | a9161612901b | yes | Debian sid amd64 (20240731_0002) | x86_64 | CONTAINER | 99.30MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/sid (3 more) | edbdad2543df | yes | Debian sid amd64 (20240731_0002) | x86_64 | VIRTUAL-MACHINE | 340.63MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/sid/cloud (1 more) | c118d47b2ddc | yes | Debian sid amd64 (20240731_0002) | x86_64 | VIRTUAL-MACHINE | 383.62MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
| debian/sid/cloud (1 more) | e1161f9d0a0a | yes | Debian sid amd64 (20240731_0002) | x86_64 | CONTAINER | 128.19MiB | Jul 31, 2024 at 12:00am (UTC) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+
$ lxc launch ubuntu:22.04 adrian01-vm --config limits.cpu=2 --config limits.memory=2048MiB --device root,size=20GB --vm
Las maquinas virtuales se crean con un tamaño por defecto, ejemplo 10 GiB. Aquí le hemos aclarado que lo queremos con 20 GB. Si queremos mas tarde agrandarlo, mas tarde podemos:
Chequear el tamaño que tenía:
lxc config device show adrian01-vm
root:
path: /
pool: lxdpool
size: 20GiB <-----------------------
type: disk
Agrandarlo
lxc stop adrian01-vm
lxc config device set adrian01-vm root size=25GiB
systemctl restart snap.lxd.daemon.service (raramente necesario, se demora varios minutos)
lxc start adrian01-vm
Lo mismo si mas tarde necesita aumentar la memoria
lxc stop adrian01-vm
lxc config set adrian01-vm limits.memory 4GiB
lxc start adrian01-vm
Si necesita ajustar mas valores, como por ejemplo deshabilitar swap para un nodo Kubernetes, quitar todos los limites de memoria de la instancia VM como si fuera un container, etc, acuda a estos ejemplos
- https://ubuntu.com/blog/lxd-2-0-resource-control-412
- Y a esta lista de parámetros https://documentation.ubuntu.com/lxd/en/latest/reference/instance_options/
Adentro de la VM, se le puede poner Docker, siguiendo https://docs.docker.com/engine/install/ubuntu/
Habíamos dicho que los containers son mucho mas eficientes en recursos que las VM. Veamos un ejemplo con Debian, y otro con Ubuntu (recomendado bajo LXD)
Ejemplo 2.1 para crear un container LXD con Debian desde https://images.linuxcontainers.org
lxc launch images:debian/bookworm adrian01
Sin embargo, y hasta que la comunidad de LXD y Linux Containers se terminen de separar, y ésta última termine de ofrecer un servicio igual de sólido como LXD, como Incus, conviene crear containers Ubuntu
$ lxc launch ubuntu:22.04 adrian01
$ lxc list
+-------------------------------------+---------+-----------------------+------+-----------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+-------------------------------------+---------+-----------------------+------+-----------------+-----------+
| adrian01 | RUNNING | 10.19.29.234 (eth0) | | CONTAINER | 0 |
+-------------------------------------+---------+-----------------------+------+-----------------+-----------+
Podemos ver que ahora adrian01 está creado, pero con una ip dinámica. Deberíamos dejar fija esa ip, por si reenviamos tráfico desde un ingress de o un [balanceador - proxy reverso] como el que planteamos en el curso:
Un excelente video sobre el tema de reenviar tráfico a VMs y a containers se encuentra en Basado en https://youtu.be/TmGvbXfwJEA?t=798 - "Accessing services running in LXD instances"
$ lxc stop adrian01
$ lxc config device override adrian01 eth0 ipv4.address=10.19.29.100
Device eth0 overridden for adrian01
$ lxc start adrian01
Ahora la ip deberia haber cambiado:
$ lxc list
+-------------------------------------+---------+-----------------------+------+-----------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+-------------------------------------+---------+-----------------------+------+-----------------+-----------+
| adrian01 | RUNNING | 10.19.29.100 (eth0) | | CONTAINER | 0 |
+-------------------------------------+---------+-----------------------+------+-----------------+-----------+
Los containers no tienen un tamño fijo de espacio reservado, como las instancias de máquinas virtuales. En cambio, y a menos que formen parte de un profile específico, todos "cuelgan" del mismo pool. Antes de aumentar la quota de un container, debe verificar que haya espacio en el pool.
Ejemplo
root@hetzner01 ~ # zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
lxdpool 150G 145.0G 5.1G - - 61% 95% 1.00x ONLINE -
Si el pool no tuviera espacio, debería agrandar primero. Ejemplo para agregar 5 GB:
root@hetzner01 ~ # truncate -s +5G /var/snap/lxd/common/lxd/disks/lxdpool.img
root@hetzner01 ~ # zpool set autoexpand=on lxdpool
root@hetzner01 ~ # zpool online -e lxdpool /var/snap/lxd/common/lxd/disks/lxdpool.img
root@hetzner01 ~ # zpool set autoexpand=off lxdpool
Controlamos...
root@hetzner01 ~ # zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
lxdpool 155G 145.0G 10.1G - - 61% 90% 1.00x ONLINE -
Veamos cuanto tiene el container
root@hetzner01 ~ # lxc shell adrian01
root@adrian01:~# df -hT
Filesystem Type Size Used Avail Use% Mounted on
lxdpool/containers/adrian01 zfs 5G 4G 1G 80% /
Es muy poco. Le aumentamos la cuota
root@hetzner01 ~ # zfs set reservation=8G lxdpool/containers/adrian01
root@hetzner01 ~ # zfs set quota=8G lxdpool/containers/adrian01
root@hetzner01 ~ # lxc restart adrian01 (probablemente no necesario)
Entramos al container, y ahora
root@hetzner01 ~ # lxc shell adrian01
root@adrian01:~# df -hT
Filesystem Type Size Used Avail Use% Mounted on
lxdpool/containers/adrian01 zfs 8G 4G 4G 50% /
En caso de problemas, revise la salida de este comando
root@hetzner01 ~ # zfs get all lxdpool/containers/adrian01 | head -n 10
NAME PROPERTY VALUE SOURCE
lxdpool/containers/adrian01 type filesystem -
lxdpool/containers/adrian01 creation Sat Jan 18 20:12 2025 -
lxdpool/containers/adrian01 used 4G -
lxdpool/containers/adrian01 available 4G -
lxdpool/containers/adrian01 referenced 4G -
lxdpool/containers/adrian01 compressratio 2.45x -
lxdpool/containers/adrian01 mounted no -
lxdpool/containers/adrian01 origin lxdpool/deleted/images/12cba40fca5f207 -
lxdpool/containers/adrian01 quota 8G
También puede efectuar mas ajustes en el modo gráfico, en el enlace Hypervisor Web para LXD
Ejemplo de clásica sesión dentro de un container o instancia VM en LXD:
Tomamos control del container
root@hetzner01 ~ # lxc shell adrian01
root@adrian01:~#
También podríamos entrar con lxc exec
root@hetzner01 ~ # lxc exec adrian01 bash
root@adrian01:~#
Dónde estamos parados?
root@adrian01:~# pwd
/root
Bien, en el home del root, es decir, /root
¿Hay mas usuarios en este container?
root@adrian01:~# ls /home
ubuntu
¿Podemos impersonar el usuario ubuntu y trabajar como él? (cuidado los espacios):
root@adrian01:~# su - ubuntu
ubuntu@adrian01:~$ pwd
/home/ubuntu
Truco, ejecutar algo en un container sin necesidad de entrar, ejemplo, top
. En el host:
root@hetzner01 ~ # lxc exec adrian01 top
Tasks: 34 total, 1 running, 33 sleeping, 0 stopped, 0 zombie
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
271 mysql 20 0 2640920 404616 17216 S 1.0 0.6 80:44.14 mysqld
1 root 20 0 166108 8468 5276 S 0.0 0.0 0:07.49 systemd
Necesitamos que el alumno desde su casa pueda entrar a su container, por su propio puerto ssh. Sin embargo, el pasaje de la contraseña está deshabilitado por defecto, para que desde afuera atacantes no puedan emplear fuerza bruta.
Usaremos por lo tanto llaves públicas:
Valga el siguiente ejemplo: para entrar, Adrian, como alumno, en la compu de su casa, genera llaves con ssh-keygen -t rsa -b 4096
.
Atención: si Adrian tiene
- WSL
- Otro Linux en otra partición
- Powershell
- Un pequeño shell otorgado por Putty o por Git para windows
En todos esos shells debe generar llaves públicasy mandarlas a Sergio, el administrador, quien tiene el root del metal. Solo debe mandar los archivos id_rsa.pub
Sergio, en el servidor, empleando el root, entra al container recién creado (el que hicimos con lxc launch
). Ejemplo: lxc shell adrian01
o con lxc exec adrian01 bash
A continuación, adentro del container, Sergio genera también un par de llaves en el root del container. Esto creará una carpeta /root/.ssh/
y adentro dos archivos, /root/.ssh/id_rsa
y /root/.ssh/id_rsa.pub
ssh-keygen -t rsa -b 4096 (enter en todas las líneas)
Luego altera el archivo /root/.ssh/authorized_keys - si no existe, debe crearlo.
nano /root/.ssh/authorized_keys <----- aqui pega la llave generada en la maquina de Adrian
El puerto 22 del metal está ocupado para tareas de administración. Por un tema de separación de ambientes, no se le da de hecho, acceso ni cuentas de usuario a los alumnos.
Por lo tanto, Sergio, abre o "proxea" un puerto 2225 para que Adrian entre al puerto 22 de su propio container, y pueda ser root allí adentro.
- Si se trata de un container LXD, Sergio ejecutará en el metal:
lxc config device add adrian01 port-adrian01-ssh2225 proxy listen=tcp:0.0.0.0:2225 connect=tcp:127.0.0.1:22
- Si se trata de una VM, debe natear (y la ip interna si o si debe ser fija, como se recomendó mas arriba). Si la ip externa del host es 37.27.49.225, entonces el nateo será así
lxc config device add adrian01-vm ssh_proxy_for_adrian01-vm proxy listen=tcp:37.27.49.225:2233 connect=tcp:0.0.0.0:22 nat=true
Nuevamente, puede ver más métodos para acceder a los containers y a las instancias (VM) LXD, accediendo al excelente video de Stéphane Graber, presente en https://youtu.be/TmGvbXfwJEA?t=798 - "Accessing services running in LXD instances"
Adrian por su parte, para entrar remoto, usará este comando
ssh -p 2225 [email protected]
root@adrian01: ~ #
Como alumno, si no conoce su puerto, haga click aquí y chequee la tabla: https://github.com/perfeccion-ar/infraestructura-clasica-y-avanzada/wiki#containers-lxd-de-los-alumnos
Supongamos que desea arrancar una aplicación simple y mostrarla hacia afuera, a compañeros de trabajo o posibles clientes. Pero no tiene ni siquiera dominio comprado, y no quiere todavía configurar el virtualhosts en el container, ni andar pidiendo al administrador de este grupo que le rutee tráfico desde el puerto 80 del [balanceador - proxy reverso], como hacemos en el ejemplo de Wordpress.
Es decir, desea iniciar un Ruby on Rails rápido, Sinatra, Django, Flask, Express.js, etc y que se vea desde afuera. Bien: los ejemplos anteriores de ruteo son perfecto para ello.
Veamos un caso súper simple. En su container LXD, o en su máquina LXD, abra un puerto 8000 sirviendo al mundo su carpeta actual
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Ahora en el host, en el metal que administra Sergio, deben rutearle ese puerto 8000
SI usted fuera el administrador del metal, o tiene su propia VPS (aprox 5 dólares/mes en Contabo o en Hetzner), correrá una línea parecida a la siguiente, para rutear tráfico hacia el container LXD
lxc config device add adrian01 port-adrian01-http-8000 proxy listen=tcp:0.0.0.0:8000 connect=tcp:127.0.0.1:8000
Si fuera una instancia VM LXD, el comando es parecido:
lxc config device add adrian01-vm port-adrian01-vm-http-8000 proxy listen=tcp:37.27.49.225:8000 connect=tcp:0.0.0.0:8000 nat=true
Listo! Ahora desde afuera, se podrá acceder a su servicio, en http://37.27.49.225:8000
Opción A (recomendada): con el comando scp. Tiene la ventaja que se puede luego convertir en un script automático
Curiosamente, en scp, el puerto se específica con -P en lugar de con -p (como el comando ssh)
Ejemplo:
- Entro al container, como indicamos recién
- Hago un backup simple, sin bases
tar cvzf /root/backup.tar.gz /etc /var/www
- Vuelvo a mi maquina cliente, y me bajo el backup (cuidado el punto al final)
scp -P 2225 [email protected]:/root/backup.tar.gz .
- Inversamente, quiero subir algo, ejemplo, al /root/.
Una mejor practica es subir al /tmp del servidor, para allí descomprimir y escoger tranquilo lo que voy a restaurar. El /tmp/ por cierto, se borra en cada reinicio.
scp -P 2225 backup.tar.gz [email protected]:/tmp/
- Me conecto de nuevo al container, y adentro de /tmp, descomprimo, y examino que cosas me sirven.
cd /tmp
tar xvzf backup.tar.gz
mc
Truco! Si se lía con la línea de comandos, puede usar mc (Midnight Commander,
sudo apt-get install mc
) para descomprimir, copiar, mover, etc

Midnight Commander incluso es capaz de meterse adentro de archivos .tar.gz y archivos zip (con Enter), para extraer de allí lo necesario. Hay muchos tutoriales para el uso de esta herramienta: aquí tiene uno

Atención, se le pone la llave privada (id_rsa.pub), no la publica (id_rsa.pub)



Muy parecido a como hacemos con la Infraestructura clásica, el trafico llega a los puertos 80 y 443 del servidor real ("host"). Pero en este caso, lo ideal es que llegue a un container especifico, tal que tenga Apache o con Nginx. Así podemos llevarlo a cualquier lado, usando de plantilla, etc.
Llamaremos a este container "balanceador" o "dispatcher" o "ingress". En sus VirtualHosts, en lugar de apuntar a carpetas del server físico (en: /var/www/sitio-adrian), proxea hacia las ips internas de los otros contenedores, que están escondidos en sus ips privadas. Los otros contenedores tienen sus propios servers web, como Apaches/Nginx/Tomcat/Rails/Django/etc.
Para ver cómo es la relación entre este container [balanceador - proxy reverso] y el container del alumno, consulte
- Los apuntes del taller Instalación de Wordpress en una VPS o Server propio
- Cómo hacemos para mandar trafico del puerto 80 y 443 a los containers LXD, VMs y Docker de los alumnos
¿Y las bases de datos? ¡Cada container LXD puede tener su MySQL/PostgreSQL! También se puede apuntar desde las aplicaciones a alguna de estas bases de datos, instaladas en el server físico, o contratadas como instancia en algún Cloud (para cuando tenemos muuuucha carga!)
Fuente y mas opciones: https://documentation.ubuntu.com/lxd/en/stable-5.0/howto/instances_snapshots/
Atención que algunos de estos comandos llevan /
Casos de uso:
- Estamos por hacer una prueba de concepto, o una instalación compleja dentro de un container, y queremos poder volver atrás en cualquier momento.
- Queremos tener una recuperación super rápida en caso de infección. No basta solo con periodicamente tener un backup de los archivos y dumps de las bases del container: queremos realmente "poder volver rápidamente en el tiempo".
Empezamos listando los containers LXD
lxc list
+----------------+---------+------------------------------+------+-----------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+----------------+---------+------------------------------+------+-----------------+-----------+
+----------------+---------+------------------------------+------+-----------------+-----------+
| pancutan-tests | RUNNING | 10.19.29.155 (eth0) | | CONTAINER | 0 |
+----------------+---------+------------------------------+------+-----------------+-----------+
Creamos tantas snapshots como hagan falta. ejemplo
lxc snapshot pancutan-tests antes-de-instalar-phpmyadmin
- Si estamos haciendo snapshot de una máquina virtual, y esta corre una base de datos con mucho tráfico, convendra agregar --stateful, cuestión que también retenga el estado de la memoria ram
- Si queremos sobreescribir una snapshot previa, y que no haga preguntas en un contexto de script, agregamos
--reuse
Bien: comenzamos a trabajar. Seguimos creando snapshots que se van acumulando sobre las anteriores:
lxc snapshot pancutan-tests antes-de-instalar-phpmyadmin-2
Si ahora listamos las snapshots
lxc info pancutan-tests
Snapshots:
+--------------------------------+----------------------+------------+----------+
| NAME | TAKEN AT | EXPIRES AT | STATEFUL |
+--------------------------------+----------------------+------------+----------+
| antes-de-instalar-phpmyadmin | 2024/06/22 18:50 -03 | | NO |
+--------------------------------+----------------------+------------+----------+
| antes-de-instalar-phpmyadmin-2 | 2024/06/22 19:04 -03 | | NO |
+--------------------------------+----------------------+------------+----------+
Para ver el estado de la snapshot, fijarle una fecha de autoborrado, etc, hacer
lxc config edit pancutan-tests/antes-de-instalar-phpmyadmin-2
También podemos crear containers en base al estado de una snapshot
lxc copy pancutan-tests/antes-de-instalar-phpmyadmin pancutan-tests-limpio
Ahora tenemos dos containers:
lxc list
+-----------------------+---------+------------------------------+------+-----------------+-----------+
| pancutan-tests | STOPPED | | | CONTAINER | 2 |
+-----------------------+---------+------------------------------+------+-----------------+-----------+
| pancutan-tests-limpio | STOPPED | | | CONTAINER | 0 |
+-----------------------+---------+------------------------------+------+-----------------+-----------+
Podemos volver atrás en cualquier momento, a la última snapshot
lxc restore pancutan-tests antes-de-instalar-phpmyadmin-2
Si en cambio hubiéramos querido hacer
lxc restore pancutan-tests antes-de-instalar-phpmyadmin
Error: Snapshot "antes-de-instalar-phpmyadmin" cannot be restored due to subsequent snapshot(s). Set zfs.remove_snapshots to override
Eso significa que hay que borrar la última snapshot (antes-de-instalar-phpmyadmin-2) para volver a la primera (antes-de-instalar-phpmyadmin)
Para borrar la última snapshot del ejemplo:
lxc delete pancutan-tests/antes-de-instalar-phpmyadmin-2
Fuentes:
- https://ubuntu.com/tutorials/how-to-run-docker-inside-lxd-containers
- https://ubuntu.com/server/docs/lxd-containers
Las instancias de máquinas virtuales LXD, los contenedores LXD y los contenedores Docker tienen diferentes propósitos.
- En tanto que las instancias y los contenedores son Linux completos, capaces de correr varios servicios (ej: Apache y MySQL a la vez), Docker sirve para contener mejor aplicaciones pequeñas y atómicas (ej: solo Flask).
- Además, si ese Flask se vé muy exigido, y se muere por falta de memoria, se muere el container Docker también. Está bien, es el comportamiento esperado, sobre todo si están orquestados bajo Swarm o bajo Kubernetes.
- No obstante, ese comportamiento puede ser confuso para principiantes. Los containers LXD en cambio seguirán operando incluso si un Apache, un Flask, MySQL, etc, no arrancan o se caen.
La opción más directa en realidad, es instalar Docker ademas de LXD, en la misma máquina real. Sin embargo hay dos temas a considerar
- Puede que las reglas de iptables de Docker interfieran con la red de los containers LXD. Básicamente: los containers LXD dejan de poder "salir" al exterior. Ni con ping, ni con apt-get, etc. Se soluciona simplemente agregando esta línea a /etc/docker/daemon.json en la máquina real, y reiniciando el equipo
{
"iptables": false
}
Pero esto puede dejar a los containers Docker sin red, en lugar de dejar sin red a los containers LXD.
- El segundo tema es que quizás no queramos crearle cuentas en el servidor a los alumnos ni a los usuarios, para no dejarlos usar comandos
docker rm
,docker stop
, etc.
Por las dos razones anteriormente expuestas, quizás convenga poner Docker dentro de una instancia VM. Ello, porque habíamos dicho al principio de este documento que Docker requiere de BTRFS. Y los contenedores LXD por defecto vienen con XFS por performance, no BTRFS.
Si hemos decidido seguir este camino, es decir, instalar Docker en una VM como la creada en el ejemplo de más arriba...
Ver arriba cuando hicimos
lxc launch ubuntu:22.04 adrian01-vm --config limits.cpu=2 --config limits.memory=2048MiB --vm
... entonces no hay mucho más que decir. Entramos a la Virtual Machine con lxc shell <vm>
, y adentro instalamos Docker, con algún tutorial como https://docs.docker.com/engine/install/ubuntu/
El problema con la opción anterior, es que las VM usan todos los recursos que les hemos asignado, de modo que, si queremos aprovechar el mejor uso de recursos compartidos que hacen de nuestro equipo los contenedores LXD, necesitaremos agregar un pool storage de tipo BTRFS. Como hicimos con el pool storage ZFS que creamos al principio de este documento con lxc init
.
En este punto deberíamos tener solo un storage pool
lxc storage list
+---------+--------+--------------------------------------------+-------------+---------+---------+
| NAME | DRIVER | SOURCE | DESCRIPTION | USED BY | STATE |
+---------+--------+--------------------------------------------+-------------+---------+---------+
| zfspool | zfs | /var/snap/lxd/common/lxd/disks/zfspool.img | | 5 | CREATED |
+---------+--------+--------------------------------------------+-------------+---------+---------+
Creamos entonces un pool formateado con BTRFS, con algún tamaño.
lxc storage create btrfspool btrfs size=100GB
Storage pool btrfspool created
lxc storage list
+-----------+--------+----------------------------------------------+-------------+---------+---------+
| NAME | DRIVER | SOURCE | DESCRIPTION | USED BY | STATE |
+-----------+--------+----------------------------------------------+-------------+---------+---------+
| btrfspool | btrfs | /var/snap/lxd/common/lxd/disks/btrfspool.img | | 0 | CREATED |
+-----------+--------+----------------------------------------------+-------------+---------+---------+
| zfspool | zfs | /var/snap/lxd/common/lxd/disks/zfspool.img | | 5 | CREATED |
+-----------+--------+----------------------------------------------+-------------+---------+---------+
Veamos el tamaño:
lxc storage info btrfspool
info:
description: ""
driver: btrfs
name: btrfspool
space used: 5.78MiB
total space: 89.40GiB
used by: {}
Los pools necesitan volúmenes adentro. Serían algo así como particiones lógicas. Crearemos entonces un volumen "btrfsvolume-para-adrian01"
lxc storage volume create btrfspool btrfsvolume-para-adrian01
Storage volume btrfsvolume-para-adrian01 created
Listamos algún container LXD que hayamos creado anteriormente con lxc list
, o creamos uno nuevo:
lxc launch ubuntu:24.04 ubuntu-24-test
Ahora usamos un truco que se usa mucho tanto en LXD como en Docker: mapearemos una carpeta interna, en este caso la que empleará adentro el servicio Docker, /var/lib/docker, externamente hacia el pool y el volúmen que habiamos creado en el host real:
lxc config device add ubuntu-24-test docker disk pool=btrfspool source=btrfsvolume-para-adrian01 path=/var/lib/docker ⏎
Device docker added to ubuntu-24-test
Necesitamos ahora unas configuraciones más para que Docker funcione bien adentro del LXD container
Primero vamos a permitir anidación o "nesting". Luego, relajaremos dos opciones de seguridad, para interceptar y emular llamadas de sistema. Algo que normalmente se permite a contenedores privilegiados.
Nos podemos permitir que docker trabaje privilegiado: al fin y al cabo, el mismo demonio está también contenido (en LXD)
lxc config set ubuntu-24-test security.nesting=true security.syscalls.intercept.mknod=true security.syscalls.intercept.setxattr=true
Resta reiniciar el container
lxc restart ubuntu-24-test
Entramos al container
lxc shell ubuntu-24-test
Y si miramos los filesystems... podremos ver una carpeta montada de tipo btrfs :D !!
❯ lxc shell ubuntu-24-test
root@ubuntu-24-test:~# df -hT
Filesystem Type Size Used Avail Use% Mounted on
zfspool/containers/ubuntu-24-test zfs 97G 449M 96G 1% /
/dev/loop43 btrfs 30G 5.9M 30G 1% /var/lib/docker ←←
↑↑↑↑↑ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
Ya podemos instalar Docker, mediante el tutorial oficial de Ubuntu
Si hubiera instalado Almalinux en lugar de Ubuntu, sírvase por favor en cambio emplear este otro tutorial.
Primero, sacamos todo Docker viejo o instalado desde los potencialmente obsoletos repos de Ubuntu
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
Ahora si:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl restart docker
Ya puede usar Docker! - ejemplo de uso. Adentro del container instalamos este pequeño servicio en Flask procedente de nuestro tutorial de Docker
docker run --rm --name contenedor-michos -p 8888:5000 pancutan/mi-imagen-catnip:3
El servicio se encuentra sirviendo en el puerto 8888. Pero el host real, que nos hace en cierta forma de firewall, no lo dejará pasar. Requiere que haya un proxy LXD reenviando, o bien desde 0.0.0.0 (todas las interfaces del host real) o mas prudente, la ip del bridge lxbrd0 para que solamente el proxy forward pueda llegar. En el ejemplo del lxbrd0, si la ip fuera 10.19.29.1, la configuración desde el LXD del host sería
lxc config device add contenedor-michos port-8888 proxy listen=tcp:10.19.29.1:8888 connect=tcp:127.0.0.1:8888
Ejemplo empleado en container certificados
Para servirlo hacia afuera, tiene dos opciones:
-
Puede proxear con ProxyPassReverse, desde un Apache, Nginx, HAProxy, etc. En cualquiera de esos programas puede crear Virtualhost de dominios reales, y, actuando de Dispatcher, o "Balanceador", proxear hacia otro container. Este "Balanceador" puede estar instalado en el host, o en otro container. En este taller ya contamos con un Apache. Si desea construirse uno, consulte este artículo en el Wiki.
-
Si está apurado y no le preocupa exponer al container como un puerto externo, vuelva al server y habilite, o pida al administrador que habilite, el puerto externo del container (en nuestro ejemplo, el 8888) como un puerto externo del host.
Ejemplo para proxear el 8888 del host al 8888 del container LXD:
lxc config device add CONTAINER puerto-CONTAINER-flask8888 proxy listen=tcp:0.0.0.0:8888 connect=tcp:127.0.0.1:8888
Atención VMs: Si se trata de reenviar un puerto hacia una VM, esta pedirá que la nateen. Acuda arriba al capítulo sobre VMs y nateos
Excelente! ahora ya podemos acceder por la ip externa :8888 a un bonito generador de gif animados de gatitos - http://37.27.49.225:8888

Prepare este alias
alias lxcstats="printf \"%-20s %-8s %-5s %-5s %-s\n\" name status mem disk cpu-time; lxc list status=running --format=json | \
jq -r '.[] | \"\(.name) \(.status) \(.state.memory.usage) \(.state.disk.root.usage) \(.state.cpu.usage)\"' | \
numfmt --field=3,4 --to=iec|\
printf '%-20s %-8s %-5s %-5s %(%-dd %-Hh %-Mm )T\n' \$(</dev/stdin)"
Y uselo así
lxcstats
Actividad recomendada: vea el artículo: Hypervisor Wed para LXD. Creación y personalización de VM con ventanas