La Academia Faro da cursos de informática. El lunes empieza uno nuevo y hay que preparar las máquinas: un contenedor para la web del curso, una VM para el profesor y, para los alumnos, una VM modelo de la que sacar un clon por alumno. El servidor ya tiene Proxmox, la plantilla de Debian y la imagen cloud de Debian bajadas. Lo que falta es lo de dentro.
VM o contenedor
Proxmox corre dos clases de invitados (guests), y cada uno lleva un número, su VMID (del 100 en adelante):
- Una VM (máquina virtual, KVM/QEMU) es un ordenador entero de mentira: su BIOS, sus
discos, su tarjeta de red y su propio núcleo. Puede llevar cualquier sistema (Linux,
Windows, BSD). Se manejan con
qm. - Un contenedor (LXC) comparte el núcleo del nodo: es un Linux aislado, con sus procesos,
su red y sus archivos, pero sin arrancar un núcleo propio. Gasta mucho menos y arranca en un
segundo, pero solo puede ser Linux. Se manejan con
pct.
Para una web pequeña, un contenedor sobra. Para lo que necesite su propio núcleo (otro sistema, módulos propios, aislamiento fuerte), una VM.
Un contenedor desde una plantilla
Un contenedor se crea desde una plantilla (CT template): un Debian ya instalado, comprimido.
La de la academia ya está en local:
root@pve1:~# pveam list local NAME SIZE local:vztmpl/debian-13-standard_13.6-1_amd64.tar.zst 123.93MB
En la web, Create CT abre un asistente: General (el VMID, el hostname y la contraseña de
root), Template (la plantilla), Disks (el disco y su almacén), CPU (los núcleos),
Memory (la memoria y el swap), Network (la red: el bridge vmbr0 y la IP, fija o por
DHCP) y DNS. Al acabar, Confirm enseña lo que se va a hacer. En la shell es una sola
orden:
root@pve1:~# pct create 101 local:vztmpl/debian-13-standard_13.6-1_amd64.tar.zst --hostname web-curso --cores 1 --memory 1024 --swap 512 --net0 name=eth0,bridge=vmbr0,ip=dhcp --rootfs local-lvm:8 --unprivileged 1 --password Faro-2026 Logical volume "vm-101-disk-0" created. Logical volume pve/vm-101-disk-0 changed. Creating filesystem with 2097152 4k blocks and 524288 inodes … extracting archive '/var/lib/vz/template/cache/debian-13-standard_13.6-1_amd64.tar.zst' Total bytes read: 553512960 (528MiB, 144MiB/s) Detected container architecture: amd64 Creating SSH host key 'ssh_host_rsa_key' - this may take some time ... …
Proxmox crea un disco de 8 GiB en local-lvm, le pone un sistema de archivos y descomprime ahí la
plantilla. Unprivileged (sin privilegios, lo normal) quiere decir que el root de dentro no es
el root del nodo. Se arranca con Start (o pct start 101); con la red por DHCP, el router le
da una IP:
root@pve1:~# pct exec 101 -- ip -br a lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 192.168.70.150/24 fe80::be24:11ff:feba:b368/64
pct exec corre una orden dentro; pct enter 101 abre una shell dentro (y exit vuelve al
nodo). En la web, la pestaña Console del contenedor es su pantalla: pide usuario y contraseña,
como un ordenador de verdad.
Ejercicio
La web del curso
La web del curso va en un contenedor. Créalo desde la plantilla de Debian 13 que ya está en local: hostname web-curso, 1 núcleo, 1 GiB de memoria, 8 GiB de disco en local-lvm y la red por DHCP. Y arráncalo.
Encendiendo el nodo…
Una VM desde una imagen cloud
Una VM se puede instalar desde una ISO, como un PC: Create VM, la ISO en OS, arrancar y seguir el instalador por su consola. Para muchas VM iguales hay algo más rápido: una imagen cloud, un disco con Debian ya instalado que se importa como disco de la VM. Y lo que cambia en cada una (el usuario, la contraseña, la red) se lo da cloud-init: una unidad pequeña que la VM lee en su primer arranque.
root@pve1:~# qm create 100 --name profe --memory 1024 --cores 1 --ostype l26 --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-single --ide2 local-lvm:cloudinit --ciuser profe --cipassword Faro-2026 --cicustom vendor=local:snippets/agente.yaml --serial0 socket --vga serial0 --agent enabled=1 --scsi0 local-lvm:0,import-from=local:import/debian-13-genericcloud-amd64.qcow2 Logical volume "vm-100-cloudinit" created. Logical volume pve/vm-100-cloudinit changed. ide2: successfully created disk 'local-lvm:vm-100-cloudinit,media=cdrom' create full clone of drive (local:import/debian-13-genericcloud-amd64.qcow2) Logical volume "vm-100-disk-0" created. Logical volume pve/vm-100-disk-0 changed. allocated target volume 'local-lvm:vm-100-disk-0' transferred 0.0 B of 3.0 GiB (0.00%) … transferred 3.0 GiB of 3.0 GiB (100.00%) scsi0: successfully created disk 'local-lvm:vm-100-disk-0,size=3G'
Por partes: ide2: local-lvm:cloudinit es la unidad de cloud-init; --ciuser y --cipassword, el
usuario que creará; --cicustom vendor=… añade un archivo propio (aquí, que instale el agente
invitado; luego lo verás). --serial0 socket --vga serial0 hace que su pantalla sea una consola
serie: la que enseña la pestaña Console (las imágenes cloud no traen escritorio). Y
import-from copia la imagen como su disco scsi0.
En la web todo esto se ve en Hardware (los discos, la red, la unidad de cloud-init) y en Cloud-Init (el usuario y la contraseña).
Su consola
Con la VM encendida, Console enseña su pantalla. Una imagen cloud no tiene contraseña de root:
se entra con el usuario de cloud-init. La contraseña no se ve al escribirla (ni asteriscos):
se escribe a ciegas y Enter. Si está mal, Login incorrect y vuelve a pedir el usuario.
Ejercicio
La consola de la VM
La VM del profesor (100, profe) está hecha de la imagen cloud de Debian: cloud-init le puso el usuario profe con la contraseña Faro-2026. Arráncala, entra por su consola de la web y mira qué IP le dio el router (ip -br a).
Encendiendo el nodo…
Más memoria, más núcleos, más disco
Lo que tiene una VM se cambia en Hardware: elige Memory o Processors y Edit; un disco, Disk Action ▸ Resize (cuánto más: un disco se agranda, nunca se encoge). En la shell:
root@pve1:~# qm set 100 --memory 2048 --cores 2 update VM 100: -cores 2 -memory 2048
Con la VM encendida, la memoria y los núcleos no se aplican en caliente: quedan pendientes
hasta que la VM se reinicie. La web los enseña en naranja, debajo de lo de ahora; la shell, con
qm pending (cur, lo de ahora; new, lo que será):
root@pve1:~# qm pending 100 cur agent: enabled=1 cur boot: order=scsi0;ide2;net0 … cur cores: 1 new cores: 2 cur ide2: local-lvm:vm-100-cloudinit,media=cdrom cur memory: 1024 new memory: 2048 …
El disco sí se agranda al momento (dentro, la imagen cloud estira su partición al arrancar):
root@pve1:~# qm resize 100 scsi0 +2G Size of logical volume pve/vm-100-disk-0 changed from 3.00 GiB (768 extents) to 5.00 GiB (1280 extents). Logical volume pve/vm-100-disk-0 successfully resized.
Para que lo pendiente se aplique, reinicia la VM (Shutdown ▸ Reboot, o qm reboot 100).
Después, qm config 100 ya dice memory: 2048 y cores: 2, y nada queda en naranja.
En un contenedor es parecido (Resources: Memory, Swap, Cores y Volume Action ▸ Resize del disco), pero la memoria sí se aplica al momento: comparte el núcleo con el nodo.
Ejercicio
La VM se queda corta
A la VM del profesor (100, encendida) le falta de todo: dale 2 GiB de memoria, 2 núcleos y 2 GiB más de disco. Lo que no se aplique en caliente, que se aplique.
Encendiendo el nodo…
Snapshots: volver atrás
Una snapshot es una foto de un invitado en un momento: sus discos (y, en una VM encendida, si se quiere, su memoria: Include RAM). Antes de algo que pueda salir mal (actualizar, cambiar la configuración), se hace una; si sale mal, Rollback vuelve a ella y lo de después se pierde.
root@pve1:~# pct snapshot 101 antes-de-actualizar --description "Antes de actualizar la web" freeze guest filesystem Logical volume "snap_vm-101-disk-0_antes-de-actualizar" created. thaw guest filesystem
root@pve1:~# pct listsnapshot 101 `-> antes-de-actualizar 2026-10-07 08:49:51 Antes de actualizar la web `-> current You are here!
En la web, Snapshots las enseña en árbol, con NOW («You are here!») debajo de la última. Un
rollback deja el disco como estaba en la foto (pct rollback 101 antes-de-actualizar, o
Rollback en la web); un contenedor queda parado: hay que volver a arrancarlo. Vuelve
también su configuración: si después de la foto le diste más memoria o más núcleos, tras el
rollback tiene los de entonces, y hay que volver a ponérselos.
Ejercicio
Antes de actualizar
Van a actualizar la web del curso (el contenedor 101). Antes, haz una snapshot. Luego, haz como que la actualización la rompe: en la consola del contenedor (root, Faro-2026) borra /srv/curso/index.html. Y vuelve a la snapshot: la web tiene que volver.
Encendiendo el nodo…
Plantillas y clones
Para los alumnos no se instala una VM por alumno: se prepara una, se convierte en
plantilla (Convert to template) y se clona. Una plantilla ya no arranca (es el molde),
y su disco pasa a llamarse base-…:
root@pve1:~# qm template 100 Renamed "vm-100-disk-0" to "base-100-disk-0" in volume group "pve" Logical volume pve/base-100-disk-0 changed.
root@pve1:~# qm start 100 you can't start a vm if it's a template
Un clon puede ser completo (Full Clone: una copia entera del disco, independiente) o
enlazado (Linked Clone: solo guarda lo que cambia respecto a la plantilla; se hace en un
momento y apenas ocupa, pero necesita la plantilla). En la web, More ▸ Clone, con su Mode;
en la shell, qm clone hace uno enlazado de una plantilla (y --full, uno completo):
root@pve1:~# qm clone 100 110 --name alumno1 creating a clone of VM 100 with ID 110 create full clone of drive ide2 (local-lvm:vm-100-cloudinit) Logical volume "vm-110-cloudinit" created. Logical volume pve/vm-110-cloudinit changed. allocated target volume 'local-lvm:vm-110-cloudinit' create linked clone of drive scsi0 (local-lvm:base-100-disk-0) Logical volume "vm-110-disk-0" created. Logical volume pve/vm-110-disk-0 changed.
lvs enseña de dónde sale cada uno (la columna Origin):
root@pve1:~# lvs LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert base-100-disk-0 pve Vri-a-tz-k 5.00g data 18.63 … vm-110-disk-0 pve Vwi-a-tz-- 5.00g data base-100-disk-0 18.63 vm-111-disk-0 pve Vwi-a-tz-- 5.00g data 18.63
vm-110-disk-0 (enlazado) sale de base-100-disk-0; vm-111-disk-0 (completo) no. Cada clon
lleva su propia unidad de cloud-init: en su primer arranque se pone su nombre (alumno1) y
pide su IP, como una máquina nueva.
Ejercicio
Un clon por alumno
La VM aula-base (100) ya está preparada para el curso. Conviértela en plantilla y haz dos clones enlazados: alumno1 y alumno2. Cuidado: la 105 (notas) tiene las notas de los alumnos; esa ni se toca.
Encendiendo el nodo…
El agente invitado
Proxmox ve una VM desde fuera: su memoria, su CPU, sus discos. Lo de dentro (sus IP, su sistema)
solo lo sabe si dentro corre el agente invitado (QEMU Guest Agent, el paquete
qemu-guest-agent) y la VM lo tiene activado en Options ▸ QEMU Guest Agent. Sin él, el
Summary dice «No Guest Agent configured» en IPs; con él, enseña las IP de la VM. Con el agente,
además, Shutdown apaga la VM pidiéndoselo al agente, y las copias congelan sus archivos antes
de copiar.
root@pve1:~# qm agent 100 ping QEMU guest agent is not running
Hacen falta las dos cosas: el agente dentro (en la academia, cloud-init lo instala en el primer
arranque, con el --cicustom) y la opción activada en Proxmox. Activarla con la VM encendida
queda pendiente, como la memoria: hasta reiniciar, no vale. Y tras arrancar, tarda unos segundos en
contestar. Cuando contesta, qm guest cmd 100 network-get-interfaces da sus interfaces (es lo que
lee el Summary):
root@pve1:~# qm guest cmd 100 network-get-interfaces [ … { "hardware-address" : "bc:24:11:40:93:76", "ip-addresses" : [ { "ip-address" : "192.168.70.151", "ip-address-type" : "ipv4", "prefix" : 24 }, …
Ejercicio
La IP en la web
La VM del profesor (100) ya tiene dentro el agente invitado (QEMU Guest Agent), pero la web dice «No Guest Agent configured» donde deberían salir sus IP. Actívalo en sus opciones y haz que la web las vea.
Encendiendo el nodo…
Resumen
- Una VM (
qm) es un ordenador entero con su núcleo; un contenedor (pct) comparte el del nodo: más ligero, solo Linux. Cada invitado, su VMID. - Un contenedor sale de una plantilla (Create CT); una VM, de una ISO o de una imagen cloud importada, con cloud-init para su usuario, su contraseña y su red.
- Console es la pantalla del invitado: pide usuario y contraseña (que no se ve).
- La memoria y los núcleos de una VM encendida quedan pendientes (en naranja) hasta reiniciarla; los discos solo crecen (Resize).
- Una snapshot vuelve atrás (Rollback) pero no es una copia: vive en el mismo disco.
- Una plantilla no arranca: se clona (enlazado, rápido y pequeño; completo, independiente). Convertir en plantilla y Remove no tienen vuelta atrás: mira el número.
- El agente invitado (dentro y activado) le cuenta a Proxmox las IP de la VM.