Saltar al contenido
sudoquest@root
Proxmox

VMs y contenedores

Contenedores desde una plantilla, VMs desde una imagen cloud, su consola, más recursos, snapshots, plantillas con un clon por alumno y el agente invitado.

40 min de lectura

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.