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

Introducción

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.

¿Container o VM ?

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".

Ventajas de los containers y de las VMs LXD

  • 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.

Algo de léxico

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

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:

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.

Listar todos los containers e instancias VMs disponibles para instalar

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) |
+--------------------------------+--------------+--------+---------------------------------------+--------------+-----------------+-----------+-------------------------------+

Ejemplo 1: para crear una instancia VM (VIRTUAL-MACHINE) bajo LXD > 4.0

$ 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

Adentro de la VM, se le puede poner Docker, siguiendo https://docs.docker.com/engine/install/ubuntu/

Ejemplo 2: creación de containers

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

Ejemplo 2.2 (recomendado para este curso) para crear un container LXD con 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         |
+-------------------------------------+---------+-----------------------+------+-----------------+-----------+

Cambiar tamaños de quota

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

Entrar a un container, ejecutar programas

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

Crear accesos para que puedan entrar desde afuera del host, a un container / VM

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

Brindar servicios simples afuera del servidor

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

Copiar desde y hacia los containers

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:

  1. Entro al container, como indicamos recién
  2. Hago un backup simple, sin bases
tar cvzf /root/backup.tar.gz /etc /var/www
  1. Vuelvo a mi maquina cliente, y me bajo el backup (cuidado el punto al final)
scp -P 2225 [email protected]:/root/backup.tar.gz .
  1. 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/
  1. 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

Opción B: desde Filezilla (Windows o Linux)

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

Y el tráfico?

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

¿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!)

Snapshots

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

Backup, clonación y acarreo de contenedores a otro servidor

Siga este artículo.

Docker adentro de LXD

Fuentes:

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.

Opción 1

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.

Opción 2

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/

Opción 3

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 ←←
                                  ↑↑↑↑↑                           ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

Instalar Docker en el container / VM

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

Mi servicio no se "ve" hacia afuera del servidor!

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

Consumo: cual de mis containers consume más?

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

image

Y ahora?

Actividad recomendada: vea el artículo: Hypervisor Wed para LXD. Creación y personalización de VM con ventanas

Dudas, [email protected] - https://about.me/elbunkersti

⚠️ **GitHub.com Fallback** ⚠️