Clonar una VM en Virt-Manager con virt-clone y virt-sysprep

Clonar una VM copia bytes, no identidad.

Índice

Virt-Manager (la interfaz gráfica de libvirt) copia un disco KVM y asigna al clon un UUID y una MAC. El invitado mantiene su machine-id y las claves SSH del host hasta que virt-sysprep las borre.

Esta guía de práctica utiliza un invitado Ubuntu 26.04 y una imagen qcow2 de 14 GB almacenada en /mnt/extra2/vms. Los comandos de disco son los mismos para otros invitados Linux. virt-sysprep no opera con invitados Windows.

Clonado de un disco qcow2 entre dos invitados KVM

El host ya debe tener KVM y libvirt. Instalar KVM en Ubuntu 24.04 es esa configuración, y trae virtinst, que proporciona virt-clone. virt-sysprep, virt-customize, virt-cat y guestfish provienen de un paquete separado:

sudo apt install -y libguestfs-tools

Qué es lo que realmente clona una VM

libvirt divide el trabajo entre tres herramientas. virt-clone copia las imágenes de disco y define un nuevo dominio con el mismo hardware virtual. Cambia el nombre del dominio, el UUID y la MAC del lado del host. Las contraseñas, el nombre de host, las direcciones estáticas y los archivos dentro del invitado permanecen sin cambios, que es el comportamiento que documenta virt-clone(1).

virt-sysprep abre el disco con libguestfs y edita el sistema de archivos del invitado. El manual virt-sysprep(1) es explícito de que la edición es en el lugar: no hay una bandera de archivo de salida, por lo que la imagen que pasas a -a es la imagen que cambia. Copia o clona primero si todavía necesitas la fuente.

qemu-img info informa si ese archivo es independiente o la parte superior de una cadena de respaldo (backing chain). qemu-img convert escribe un nuevo archivo con la cadena resuelta.

flowchart LR A[invitado apagado] -->|virt-clone| B[nuevo dominio y disco] B -->|qemu-img info| C{¿archivo de respaldo?} C -->|sí| D[qemu-img convert] C -->|no| E[qcow2 independiente] D --> E E -->|copiar, luego virt-sysprep| F[plantilla] F -->|personalizar por instancia| G[invitado importado]

Aplana antes de sysprep cuando está presente una línea de respaldo. Haz sysprep de la copia, luego deriva cada invitado en ejecución de ese archivo.

Clonado del disco en Virt-Manager

Apaga el invitado. virt-clone rechaza un dominio en ejecución, y una copia de un qcow2 en vivo no es un sistema de archivos coherente.

En Virt-Manager, haz clic derecho sobre el invitado y elige Clonar (Clone). Establece el nuevo nombre. En el paso de almacenamiento, dale a cada disco una nueva ruta. Dejar la ruta original en su lugar define un segundo dominio que abre el mismo archivo, por lo que ambos invitados escriben en los mismos bloques.

El nuevo archivo se coloca junto a la fuente. Un pool por defecto usa /var/lib/libvirt/images/. Un disco que ya vive en /mnt/extra2/vms se clona en ese directorio. Anota la ruta que imprime virt-clone, o léela de vuelta:

virsh domblklist ubuntu26.04-base14gb-clone

El equivalente desde la línea de comandos, con el nombre y la ruta del disco elegidos explícitamente:

virt-clone --original ubuntu26.04-base14gb \
  --name ubuntu26.04-base14gb-clone \
  --file /mnt/extra2/vms/ubuntu26.04-base14gb-clone.qcow2

--auto-clone inventa el nombre y la ruta del disco. Eso es conveniente para una copia desechable. Una ruta de plantilla es más fácil de mantener cuando tú mismo pasas --name y --file. Omite --mac, o pasa --mac RANDOM, y virt-clone generará una dirección en el rango de QEMU 52:54:00:xx:xx:xx.

--reflink pide una copia ligera. El manual de virt-clone de Ubuntu 26.04 limita esa bandera a imágenes raw en el mismo sistema de archivos btrfs, por lo que omite estos archivos qcow2. Un overlay qcow2 es una configuración separada, cubierta con qemu-img create -b más abajo.

¿Tu clon tiene un archivo de respaldo (backing file)?

Ejecuta esto en el nuevo disco antes de tratarlo como independiente:

qemu-img info --backing-chain /mnt/extra2/vms/ubuntu26.04-base14gb-clone.qcow2

Una imagen independiente lista file format, virtual size y disk size. Un overlay también lista una línea backing file: que apunta a la imagen a través de la cual lee. virtual size es el tamaño del disco del invitado, aquí unos 14 GB. disk size es la cantidad de espacio que el archivo ocupa actualmente, que es pequeña para un overlay nuevo y crece a medida que el invitado escribe.

virt-clone no es la fuente usual de esa línea. Un overlay proviene de qemu-img create -b, de una instantánea solo de disco, o de una herramienta que creó un delta qcow2 a propósito. La verificación de info es lo que distingue esos casos de una copia completa.

Mantén el overlay solo cuando la base se quede donde está y no se vuelva a escribir. Cada escritura desde el clon cae en el archivo pequeño; las lecturas que fallan en el overlay provienen de la base. Mover o editar la base rompe cada invitado que la nombra.

flowchart TB subgraph linked O[base.qcow2 solo lectura] C1[vm1.qcow2 solo deltas] C2[vm2.qcow2 solo deltas] C1 -->|backing file| O C2 -->|backing file| O end subgraph flat F1[vm1-flat.qcow2 independiente] F2[vm2-flat.qcow2 independiente] end

Para hacer un archivo independiente, convierte el overlay. -p imprime progreso. El destino es un nuevo archivo; el overlay y la base se dejan como están:

qemu-img convert -p -O qcow2 \
  /mnt/extra2/vms/ubuntu26.04-base14gb-clone.qcow2 \
  /mnt/extra2/vms/ubuntu26.04-base14gb-flat.qcow2

Ejecuta qemu-img info en el archivo plano y confirma que no hay una línea backing file:. Una plantilla es ese archivo independiente. Los overlays son cómo sellas invitados desde él más tarde, no la plantilla misma.

Un reflink de btrfs es un tercer caso. cp --reflink=auto puede compartir extensiones físicas entre dos archivos, y qemu-img info todavía no muestra un archivo de respaldo, porque cada ruta es una imagen completa. Esa copia es independiente en la capa qcow2 aunque el sistema de archivos no haya duplicado todos los bloques todavía.

virt-sysprep: reiniciando la identidad

Copia la imagen independiente, luego haz sysprep de la copia. --reflink=auto comparte extensiones cuando el sistema de archivos lo soporta y de otra manera hace una copia normal, incluyendo en ext4. -n es una ejecución en seco (dry run) que descarta sus escrituras:

cp --reflink=auto \
  /mnt/extra2/vms/ubuntu26.04-base14gb-flat.qcow2 \
  /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2

virt-sysprep -n -a /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2
virt-sysprep -a /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2 \
  --hostname ubuntu-template

En un pool Ubuntu por defecto, la imagen a menudo tiene modo 0600 y es propiedad de libvirt-qemu. El manual recomienda dar a tu usuario acceso de escritura en lugar de ejecutar como root. Cuando el usuario qemu posee el archivo, sudo virt-sysprep -a ... es la invocación que puede abrirlo. El invitado debe estar apagado, y nada más debería tener la imagen abierta.

--hostname es una opción de personalización, y personalización es parte del conjunto de operaciones por defecto, por lo que se ejecuta en la misma invocación. No hay una operación por defecto separada que borre /etc/hostname por sí sola. --operations reemplaza ese conjunto por defecto cuando se pasa; --operations defaults,-ssh-userdir mantiene los defectos y apaga una operación.

Estas son las operaciones que deciden si el clon colisiona con el original. La lista es la del manual actual de virt-sysprep, que vale la pena leer porque una lista corta de --operations descarta silenciosamente el resto:

Operación Por defecto Efecto en el invitado
machine-id sí Borra el machine ID. systemd genera uno nuevo en el próximo arranque cuando /etc/machine-id está vacío.
ssh-hostkeys sí Elimina /etc/ssh/ssh_host_*. El próximo arranque crea nuevas claves de host.
ssh-userdir sí Elimina .ssh bajo /root y /home/*, incluyendo authorized_keys y claves privadas almacenadas en el invitado.
dhcp-client-state sí Elimina las asignaciones de cliente DHCP.
udev-persistent-net sí Elimina reglas de udev que mapean una MAC antigua a un nombre fijo como eth0.
net-hwaddr sí Elimina HWADDR de los archivos ifcfg-* de Fedora y RHEL. No edita netplan de Ubuntu.
lvm-uuids, lvm-system-devices sí Asigna nuevos UUIDs de LVM y elimina /etc/lvm/devices/system.devices, que de lo contrario ancla el invitado a los WWIDs del disco de origen.
logfiles, bash-history, tmp-files sí Elimina registros, historial de shell y archivos temporales.

ssh-userdir es la que sorprende a la gente que clona una máquina a la que todavía se conecta. La plantilla no debería llevar las claves privadas de la fuente, por lo que deja la operación activada e inyecta una clave por instancia más tarde. Para una copia única que debe mantener los authorized_keys existentes, pasa --operations defaults,-ssh-userdir.

user-account no está en el conjunto por defecto, y las contraseñas permanecen a menos que pases --password o --root-password. fs-uuids también está desactivado por defecto. El manual dice que no actualiza todas las referencias, incluyendo /etc/fstab, y que activarlo es probable que haga que el invitado no pueda arrancar.

Las imágenes de Ubuntu Server suelen tener cloud-init, y virt-sysprep no tiene ninguna operación de cloud-init. Un id de instancia antiguo bajo /var/lib/cloud hace que el clon omita la configuración de primer arranque. Limpiarlo en la plantilla, y decirle a cloud-init que deje el nombre de host que escribió --hostname:

virt-customize -a /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2 \
  --run-command 'if command -v cloud-init >/dev/null 2>&1; then cloud-init clean --logs --seed; fi' \
  --write '/etc/cloud/cloud.cfg.d/99-preserve-hostname.cfg:preserve_hostname: true'

El mismo manual advierte contra clonar un invitado que usa cifrado completo de disco interno para distribución: cada copia comparte la clave del volumen. También nota que los archivos eliminados son desvinculados (unlinked), no limpiados (scrubbed). --scrub sobrescribe un archivo nombrado, y virt-sparsify puede soltar espacio liberado de la imagen.

Cómo el clon obtiene su dirección IP

virt-sysprep no tiene ninguna operación que reescriba una dirección. Un invitado que usó DHCP pregunta de nuevo. El clon tiene una nueva MAC, un machine-id vacío y ningún archivo de asignación antiguo, por lo que el servidor DHCP en la red default de libvirt lo trata como un cliente nuevo.

Un bloque estático addresses: en netplan se copia con el disco, y ambos invitados luego lo reclaman. Lee los archivos antes del primer arranque. virt-ls y virt-cat son las herramientas de un solo uso; guestfish --ro -a <disk> -i es la misma inspección como un shell, después de lo cual ls /etc/netplan y cat se ejecutan contra el invitado:

virt-ls -a /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2 /etc/netplan
virt-cat -a /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2 \
  /etc/netplan/00-installer-config.yaml

Las imágenes de instalador a menudo usan 00-installer-config.yaml o 01-netcfg.yaml. Las imágenes de nube usan 50-cloud-init.yaml, que cloud-init recreará a menos que la configuración de red esté deshabilitada. Netplan combina cada archivo YAML en el directorio, por lo que un archivo estático sobrante todavía se aplica después de que agregas un archivo DHCP.

Para hacer que la plantilla sea un cliente DHCP, elimina esos archivos y sube un reemplazo. Usa el nombre de interfaz que mostró virt-cat. En un invitado instalado bajo KVM, ese nombre a menudo es ens3 o enp1s0, y virt-clone mantiene el mismo slot PCI virtual, por lo que el nombre permanece válido:

cat > /tmp/99-dhcp.yaml <<'EOF'
network:
  version: 2
  ethernets:
    ens3:
      dhcp4: true
EOF

virt-customize -a /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2 \
  --delete '/etc/netplan/*.yaml' \
  --upload /tmp/99-dhcp.yaml:/etc/netplan/99-dhcp.yaml \
  --write '/etc/cloud/cloud.cfg.d/99-disable-network.cfg:network: {config: disabled}'

Para una dirección, puerta de enlace y DNS estáticos, usa la misma subida con el YAML de Cómo cambiar una dirección IP estática en Ubuntu Server. Cada instancia luego necesita su propio archivo, aplicado al disco de la instancia en lugar de a la plantilla.

El nombre en el YAML tiene que existir dentro del invitado. Un disco movido desde hardware físico a menudo nombra la NIC enp2s0, mientras que el mismo sistema bajo virtio presenta un nombre diferente, y netplan luego configura una interfaz que no está ahí. Para una plantilla que debería arrancar en más de un tipo de máquina, fija el kernel a eth0 y usa esa clave en netplan. Ubuntu fuente /etc/default/grub.d/*.cfg mientras construye grub.cfg:

virt-customize -a /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2 \
  --write '/etc/default/grub.d/99-eth0.cfg:GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX net.ifnames=0 biosdevname=0"' \
  --run-command 'update-grub'

update-grub se ejecuta dentro de la aplicación libguestfs. Si grub-probe no puede ver el disco de arranque allí, el comando falla y el cmdline permanece sin cambios. En ese caso, establece el nombre de interfaz en netplan al nombre que el invitado ya usa, y omite el cambio a eth0.

Personalización por instancia con virt-customize

Arranca instancias desde copias del archivo limpio. No apuntes un dominio a la plantilla misma.

Una copia completa no tiene ninguna dependencia de tiempo de ejecución en la plantilla:

cp --reflink=auto \
  /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2 \
  /mnt/extra2/vms/ubuntu26.04-vm1.qcow2

Un overlay almacena solo los bloques que vm1 escribe. -F qcow2 registra el formato de la base para que QEMU no tenga que explorarlo:

qemu-img create -f qcow2 \
  -b /mnt/extra2/vms/ubuntu26.04-base14gb-clean.qcow2 \
  -F qcow2 \
  /mnt/extra2/vms/ubuntu26.04-vm1.qcow2

La ruta de la base se almacena dentro del overlay. Tiene que mantener esa ruta, y tiene que permanecer de solo lectura, para cada invitado construido de esta manera.

Las notas de seguridad de virt-sysprep describen un segundo pase en cada disco nuevo. Sysprep escribe una semilla aleatoria en la plantilla; los invitados clonados desde ese archivo de lo contrario comenzarían con la misma semilla, lo que hace que las claves SSH de host y los números de secuencia TCP del primer arranque sean predecibles. --enable customize reescribe la semilla. --hostname y --ssh-inject son opciones de personalización, por lo que pertenecen en el mismo comando. Sustituye la cuenta que realmente existe en la imagen por ubuntu:

virt-sysprep --enable customize \
  -a /mnt/extra2/vms/ubuntu26.04-vm1.qcow2 \
  --hostname vm1 \
  --ssh-inject "ubuntu:file:${HOME}/.ssh/id_ed25519.pub"

Entrega el archivo de la instancia a libvirt, no la plantilla. Si lo creaste como tu usuario fuera de un pool de almacenamiento, libvirt-qemu necesita acceso de lectura o el dominio falla al inicio con un error de permiso en el qcow2.

Importando en Virt-Manager

Para cada disco de instancia, abre Virt-Manager, elige Nueva máquina virtual (New Virtual Machine), y selecciona Importar imagen de disco existente (Import existing disk image). Apunta a ubuntu26.04-vm1.qcow2. La entrada del SO debería ser Ubuntu 26.04 cuando la base de datos osinfo del host la tenga.

El id corto coincidente es ubuntu26.04. Revisa la base de datos del host antes de confiar en él:

osinfo-query os | grep ubuntu26

Si eso no imprime nada, la base de datos instalada es anterior a la entrada de 26.04. --os-variant ubuntu24.04 todavía selecciona un tipo de máquina amigable con virtio para un invitado que ya está instalado. --import le dice a virt-install que el disco ya contiene un SO.

sudo virt-install --name ubuntu26.04-vm1 \
  --memory 4096 --vcpus 2 \
  --disk path=/mnt/extra2/vms/ubuntu26.04-vm1.qcow2,format=qcow2 \
  --import \
  --os-variant ubuntu26.04 \
  --network network=default \
  --noautoconsole

--network network=default adjunta la red NAT cuya instancia dnsmasq entrega la asignación DHCP. Después del arranque:

virsh domifaddr ubuntu26.04-vm1 --source lease
virsh dumpxml ubuntu26.04-vm1 | grep "<mac"

--source lease lee esa tabla dnsmasq. Permanece vacío para una dirección estática, para un bridge, o para una NIC que no está en una red virtual de libvirt. Esos invitados necesitan qemu-guest-agent en la imagen y virsh domifaddr ubuntu26.04-vm1 --source agent, o una búsqueda ARP con --source arp. virsh net-dhcp-leases default lista cada asignación en la red NAT cuando quieres la vista del pool en lugar de un dominio.

Estrategia Espacio por VM Tiempo para crear Restricción Usar cuando
Copia completa, o cp --reflink=auto unos 14 GB lógicos; reflink comparte bloques hasta que cambian minutos, o casi instantáneo con reflink reflink necesita un sistema de archivos que lo soporte, como btrfs un invitado que debería sobrevivir a la plantilla
overlay qcow2 megabytes al principio segundos la ruta de la base debe permanecer, y la base debe mantenerse de solo lectura muchos invitados similares desde una imagen sellada
virt-clone --reflink imagen raw, extensiones compartidas casi instantáneo discos raw en el mismo sistema de archivos btrfs; qcow2 se omite imágenes raw ya almacenadas en btrfs

Un invitado que esperas mantener por meses es una copia completa o un reflink. Un invitado de vida corta puede ser un overlay, siempre que el archivo de base todavía esté ahí cuando se ejecute. Proxmox es el otro camino autoalojado cuando quieres un modelo de plantilla y clonado estilo centro de datos en su propia interfaz web. Multipass lanza imágenes de nube de Ubuntu para un solo usuario y aplica cloud-init él mismo, lo cual es un flujo de trabajo diferente de sellar un disco que ya instalaste.

Solución de problemas: cuando el clon no arranca

Sin dirección. virt-cat el archivo de netplan y compara la clave de interfaz con ip link en la consola. Una entrada match: macaddress todavía establecida en la MAC de origen descarta la nueva NIC. Una clave que nombra una interfaz que el invitado no tiene hace lo mismo. Corrige el archivo en el disco, o aplica el cmdline de eth0 anterior y reconstruye la instancia desde la plantilla.

Dirección arriba, igual que la original. El bloque estático addresses: todavía está en uno de los archivos de netplan. virt-sysprep no lo eliminó. Sube un reemplazo y elimina los otros archivos YAML en /etc/netplan.

SSH dice que la clave de host cambió, o acepta el clon como el original. Las claves de host son compartidas cuando ssh-hostkeys no se ejecutó en el archivo que el dominio abre. Confirma con virsh domblklist que el dominio usa el disco sysprepped, luego revisa qemu-img info para asegurarte de que no hiciste sysprep de un overlay mientras el invitado arranca desde una ruta diferente. Si el clon mantuvo la IP original a propósito, los clientes que almacenaron la clave antigua en known_hosts emitirán una advertencia hasta que esa línea se elimine. Esa advertencia es la regeneración que el manual describe.

El inicio de sesión con clave falla en un clon de una máquina a la que podías llegar antes. El sysprep por defecto eliminó .ssh de los directorios de inicio. Inyecta una clave con --ssh-inject, o vuelve a ejecutar con --operations defaults,-ssh-userdir si esta copia debería haberlos mantenido.

Volúmenes LVM faltantes después del arranque. El invitado tiene /etc/lvm/devices/system.devices de la fuente, y el WWID del disco virtual cambió. La operación por defecto lvm-system-devices elimina ese archivo. Una lista --operations elegida a mano tiene que incluirlo, y lvm-uuids, cuando el invitado usa LVM.

Un clon o una plantilla

Una máquina extra única es virt-clone, una verificación de archivo de respaldo, y virt-sysprep en el nuevo disco. Una flota es un archivo independiente sellado, luego una copia o un overlay por invitado, con --enable customize en el propio disco de ese invitado antes de que arranque.

Los overlays hacen que el archivo sellado sea parte de la ruta de tiempo de ejecución de cada invitado que lo nombra. Las copias completas y los reflinks no. Elige esa dependencia con la vida útil del invitado, y deja fs-uuids desactivado para que el cargador de arranque y /etc/fstab todavía coincidan con el sistema de archivos.

Enlaces útiles

Referencias

Suscribirse

Recibe nuevas publicaciones sobre sistemas, infraestructura e ingeniería de IA.