--- title: "Migración en vivo de VMs KVM con libvirt en Ubuntu 24.04" description: "Configura dos hosts KVM con Ubuntu 24.04, almacenamiento NFS compartido y SSH para mover una máquina virtual en marcha con virsh migrate sin apagarla." category: "Virtualization VPS" language: es source: https://cubepath.com/es/docs/virtualization-vps/migracion-de-vm-en-vivo updated: 2026-09-25 --- # Migración en vivo de VMs KVM con libvirt en Ubuntu 24.04 La migración en vivo mueve una máquina virtual en ejecución de un host físico a otro sin apagarla: libvirt copia la memoria de la VM mientras sigue funcionando, repite la copia de las páginas que cambian y, en el último instante, pausa la VM unos milisegundos para transferir el estado final y reanudarla en el destino. En este tutorial prepararás dos hosts KVM con Ubuntu 24.04, compartirás el almacenamiento de las VMs por NFS y migrarás una VM de prueba con `virsh migrate`, además de ver cómo hacerlo sin almacenamiento compartido. ## Requisitos previos - Dos servidores físicos o bare metal con Ubuntu 24.04 LTS y virtualización por hardware activada (Intel VT-x o AMD-V). En esta guía se llaman `host-a` (origen) y `host-b` (destino). - CPUs del mismo fabricante en ambos hosts (Intel con Intel, AMD con AMD). Entre fabricantes distintos la migración en vivo no es posible. - Un usuario no root con privilegios `sudo` en los dos hosts, con el mismo nombre (en los ejemplos, `your_user`). - Conectividad de red entre ambos hosts, idealmente una red privada de 10 Gbit/s. Con 1 Gbit/s funciona, pero las VMs con mucha memoria tardan más en converger. - Para la parte de almacenamiento compartido, un servidor NFS. En los ejemplos está en `10.0.0.30`; también puede ser un tercer servidor o un NAS. Sustituye `10.0.0.10` (host-a), `10.0.0.20` (host-b) y `10.0.0.30` (NFS) por las IPs de tu red privada. ## Paso 1: Instalar KVM y libvirt en ambos hosts Ejecuta estos comandos en `host-a` y en `host-b`. Comprueba primero que la CPU expone la virtualización por hardware: ```bash sudo apt update sudo apt install cpu-checker kvm-ok ``` ```output INFO: /dev/kvm exists KVM acceleration can be used ``` Instala QEMU/KVM, libvirt y las herramientas de gestión: ```bash sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients virtinst nfs-common ``` Añade tu usuario al grupo `libvirt` para poder usar `virsh` contra `qemu:///system` sin `sudo`, y vuelve a iniciar sesión para que el cambio surta efecto: ```bash sudo usermod -aG libvirt $USER ``` Verifica que libvirt responde: ```bash virsh -c qemu:///system list --all ``` ```output Id Name State -------------------- ``` Para no tener que escribir `-c qemu:///system` en cada comando, exporta la URI por defecto en tu `~/.bashrc`: ```bash echo 'export LIBVIRT_DEFAULT_URI=qemu:///system' >> ~/.bashrc source ~/.bashrc ``` ## Paso 2: Configurar resolución de nombres y SSH entre hosts `virsh migrate` se conecta al destino por SSH (`qemu+ssh://`) y, por defecto, el host de destino anuncia su propio nombre para el canal de datos de la migración. Si el origen no puede resolver ese nombre, la migración falla. Añade ambos hosts a `/etc/hosts` en los dos servidores: ```bash sudo nano /etc/hosts ``` ```text 10.0.0.10 host-a 10.0.0.20 host-b ``` Comprueba que `hostname` devuelve `host-a` y `host-b` respectivamente. Después, en `host-a`, genera una clave SSH (si no tienes una) y cópiala a `host-b`: ```bash ssh-keygen -t ed25519 ssh-copy-id your_user@host-b ``` Verifica que puedes abrir una conexión de libvirt remota desde `host-a` sin contraseña: ```bash virsh -c qemu+ssh://your_user@host-b/system list --all ``` ```output Id Name State -------------------- ``` Repite la operación en sentido contrario (de `host-b` a `host-a`) si quieres poder devolver las VMs a su host original. ## Paso 3: Abrir los puertos de migración Con `qemu+ssh`, la conexión de control va por SSH (puerto 22), pero la memoria de la VM viaja por una conexión TCP directa entre los dos QEMU, en un puerto del rango 49152-49215 del host de destino. Si usas UFW, permite ese rango solo desde la IP del otro host. En `host-b`: ```bash sudo ufw allow from 10.0.0.10 to any port 49152:49215 proto tcp ``` Y en `host-a`, para las migraciones de vuelta: ```bash sudo ufw allow from 10.0.0.20 to any port 49152:49215 proto tcp ``` Asegúrate también de que SSH está permitido (`sudo ufw allow OpenSSH`) antes de activar UFW. ## Paso 4: Montar el almacenamiento compartido por NFS Con almacenamiento compartido, los discos de la VM están en la misma ruta en ambos hosts y solo hay que mover la memoria, que es lo más rápido. En el servidor NFS (`10.0.0.30`), instala el servidor y crea el directorio exportado: ```bash sudo apt install nfs-kernel-server sudo mkdir -p /srv/vm-images ``` Edita `/etc/exports` para exportar el directorio solo a los dos hosts KVM: ```bash sudo nano /etc/exports ``` ```text /srv/vm-images 10.0.0.10(rw,sync,no_subtree_check,no_root_squash) 10.0.0.20(rw,sync,no_subtree_check,no_root_squash) ``` `no_root_squash` es necesario porque libvirt cambia el propietario de las imágenes de disco al arrancar cada VM. Por eso la exportación debe limitarse a las IPs de los hosts de virtualización. Aplica la configuración y compruébala: ```bash sudo exportfs -ra sudo exportfs -v ``` ```output /srv/vm-images 10.0.0.10(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash) /srv/vm-images 10.0.0.20(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash) ``` Ahora, en `host-a` y en `host-b`, monta el recurso en la misma ruta. La ruta tiene que ser idéntica en los dos hosts, porque la definición XML de la VM hace referencia a ella: ```bash sudo mkdir -p /var/lib/libvirt/images/shared sudo nano /etc/fstab ``` Añade esta línea al final: ```text 10.0.0.30:/srv/vm-images /var/lib/libvirt/images/shared nfs defaults,_netdev 0 0 ``` Monta y verifica: ```bash sudo systemctl daemon-reload sudo mount -a df -h /var/lib/libvirt/images/shared ``` ```output Filesystem Size Used Avail Use% Mounted on 10.0.0.30:/srv/vm-images 1.8T 1.2G 1.8T 1% /var/lib/libvirt/images/shared ``` Crea un archivo en un host y comprueba que aparece en el otro para confirmar que ambos ven el mismo almacenamiento. ## Paso 5: Elegir un modelo de CPU compatible La VM no puede cambiar de CPU a mitad de ejecución: todas las instrucciones que vea en el origen tienen que existir también en el destino. El modo `host-passthrough` expone la CPU física exacta y solo es seguro si ambos hosts tienen el mismo modelo de procesador y el mismo microcódigo. Para hosts distintos, usa un modelo con nombre que ambos soporten. Lista en cada host los modelos de CPU que QEMU puede usar en esa máquina: ```bash virsh domcapabilities | grep "usable='yes'" ``` ```output Skylake-Server Cascadelake-Server Icelake-Server ``` Elige el modelo más moderno que aparezca como `usable='yes'` en los dos hosts. Si los dos servidores son idénticos, puedes usar `host-model`, que libvirt traduce a un modelo concreto y lo transmite al destino durante la migración. > Nota: si ya tienes VMs en producción con `host-passthrough`, cambiar el modelo de CPU requiere apagarlas y arrancarlas de nuevo. Hazlo en una ventana de mantenimiento. ## Paso 6: Crear una VM de prueba en el almacenamiento compartido Para probar, crea una VM a partir de la imagen cloud oficial de Ubuntu 24.04. En `host-a`, descárgala al almacenamiento compartido y crea un disco de 10 GB basado en ella: ```bash cd /var/lib/libvirt/images/shared sudo wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img sudo qemu-img convert -O qcow2 noble-server-cloudimg-amd64.img testvm.qcow2 sudo qemu-img resize testvm.qcow2 10G ``` Crea la VM con `virt-install`. Sustituye `Skylake-Server` por el modelo que elegiste en el paso anterior: ```bash sudo virt-install \ --name testvm \ --memory 2048 \ --vcpus 2 \ --cpu Skylake-Server \ --disk path=/var/lib/libvirt/images/shared/testvm.qcow2,format=qcow2,bus=virtio,cache=none \ --import \ --osinfo linux2022 \ --network network=default,model=virtio \ --cloud-init root-ssh-key=$HOME/.ssh/id_ed25519.pub \ --graphics none \ --noautoconsole ``` Dos detalles importan para la migración: - `cache=none`: libvirt rechaza la migración en vivo de discos en almacenamiento compartido con otros modos de caché, porque podrían quedar datos pendientes en la caché de página del origen. - La red: el destino debe tener una red o bridge con el mismo nombre. Aquí se usa la red NAT `default` que crea libvirt en ambos hosts, suficiente para la prueba. En producción usarás un bridge (`--network bridge=br0`) presente con el mismo nombre en los dos hosts, para que la VM conserve su IP pública o privada al migrar. Comprueba que la VM está en marcha y obtén su IP: ```bash virsh list virsh domifaddr testvm ``` ```output Id Name State ------------------------ 1 testvm running Name MAC address Protocol Address ------------------------------------------------------------- vnet0 52:54:00:3a:5c:81 ipv4 192.168.122.57/24 ``` ## Paso 7: Migrar la VM en vivo Lanza la migración desde `host-a`: ```bash virsh migrate --live --persistent --undefinesource --verbose \ testvm qemu+ssh://your_user@host-b/system ``` Qué hace cada opción: - `--live`: la VM sigue funcionando durante la copia de memoria. - `--persistent`: define la VM de forma permanente en `host-b`, para que sobreviva a un reinicio de libvirt. - `--undefinesource`: elimina la definición de `host-a` al terminar, así la VM no queda duplicada. - `--verbose`: muestra el progreso. ```output Migration: [100 %] ``` Mientras la migración está en curso, puedes seguir su progreso desde otra terminal en `host-a`: ```bash virsh domjobinfo testvm ``` ```output Job type: Unbounded Operation: Outgoing migration Time elapsed: 4213 ms Data processed: 1.105 GiB Data remaining: 512.320 MiB Memory bandwidth: 268.410 MiB/s ``` ## Paso 8: Verificar la migración En `host-b`, la VM debe aparecer en ejecución: ```bash virsh list --all ``` ```output Id Name State ------------------------ 1 testvm running ``` Y en `host-a` ya no debe existir: ```bash virsh list --all ``` ```output Id Name State -------------------- ``` Para comprobar que la migración no interrumpe el servicio, repite la migración (de vuelta a `host-a`) mientras mantienes un `ping` continuo a la VM desde otra máquina de la misma red. Lo normal es no perder ningún paquete o, como mucho, uno. ## Migrar sin almacenamiento compartido Si los hosts no comparten almacenamiento, libvirt puede copiar también el disco durante la migración con `--copy-storage-all`. Es mucho más lento, porque transfiere el disco completo, pero no requiere NFS. Antes de migrar, crea en el destino una imagen vacía del mismo tamaño virtual, formato y ruta que el disco original. En `host-b`: ```bash sudo qemu-img create -f qcow2 /var/lib/libvirt/images/testvm.qcow2 10G ``` Después, desde `host-a`: ```bash virsh migrate --live --persistent --undefinesource --copy-storage-all --verbose \ testvm qemu+ssh://your_user@host-b/system ``` Consulta el tamaño virtual exacto del disco original con `qemu-img info` antes de crear la imagen de destino; si no coincide, la migración falla. ## Ajustar migraciones de VMs con mucha carga Una VM que escribe en memoria más deprisa de lo que la red puede copiarla no converge nunca. Tienes varias opciones: - Limitar el tiempo máximo de pausa aceptable en milisegundos. Un valor más alto permite terminar antes: ```bash virsh migrate-setmaxdowntime testvm 500 ``` - Usar `--auto-converge`, que frena progresivamente las vCPUs de la VM hasta que la copia alcanza a las escrituras: ```bash virsh migrate --live --auto-converge --persistent --undefinesource --verbose \ testvm qemu+ssh://your_user@host-b/system ``` - Usar post-copy (`--postcopy`): la VM arranca en el destino antes de tener toda la memoria y pide las páginas que faltan bajo demanda. Converge siempre, pero si la red falla en esa fase la VM se pierde, así que úsalo solo en redes fiables. ## Solución de problemas **`the CPU is incompatible with host CPU: Host CPU does not provide required features`**: el modelo de CPU de la VM usa instrucciones que el destino no tiene. Cambia la VM a un modelo común (paso 5) y reiníciala. **`Unsafe migration: Migration without shared storage is unsafe`**: libvirt no detecta que el disco esté en almacenamiento compartido. Comprueba que la ruta es un montaje NFS en ambos hosts y que la VM usa `cache=none`, o usa `--copy-storage-all`. **`unable to connect to server at 'host-b:49152': No route to host`**: el origen no resuelve el nombre del destino o el firewall bloquea el rango 49152-49215. Revisa `/etc/hosts` y UFW, o indica la IP explícitamente con `--migrateuri tcp://10.0.0.20`. **Error sobre una red o un bridge inexistente (por ejemplo `br0`)**: la red que usa la VM no existe en el destino. Créala con el mismo nombre en `host-b` o comprueba con `virsh net-list --all` que la red `default` está activa. Revisa los registros de libvirt en ambos hosts para cualquier otro error: ```bash sudo journalctl -u libvirtd --since "10 minutes ago" ``` ## Conclusión Has preparado dos hosts KVM con almacenamiento compartido por NFS, un modelo de CPU común y acceso SSH entre ellos, y has migrado una VM en marcha sin apagarla. Como siguientes pasos, puedes dedicar una red separada al tráfico de migración para no competir con el de las VMs, sustituir NFS por almacenamiento distribuido como Ceph para eliminar el punto único de fallo, o automatizar el vaciado de un host antes de un mantenimiento migrando todas sus VMs con un bucle sobre `virsh list --name`.