KVM

QEMU-KVM: Aumentar la memoria RAM “en vivo”. Método 2

QEMU, al fin y al cabo, no es más que un emulador muy similar a veces a lo que sería…tu portátil. En el funcionamiento normal de QEMU existen métodos para insertar y extraer periféricos USB, discos, “devices” en general.

De manera que, si tu linux lo soporta, ¿porqué no simular que conectamos o desconectamos DIMMs completos de RAM en vivo?

Eso es lo que implementaron los desarrolladores de QEMU hace un tiempo, la capacidad para insertar y desconectar DIMMs de RAM sin tener que reiniciar el servidor. Ahora bien, tiene su truco. Al igual que en la placa base de tu portátil, existe una especificación que asocia ciertos módulos de RAM a un procesador. Es como cuando conectáis un servidor con dos procesadores, y tenéis que colocar RAM en un lado de la placa asociada a un procesador, y la otra parte en el otro lado, asociada al otro procesador.

Eso mismo, se puede  hacer en QEMU. Y es un poco engorroso porque a la hora de colocar un DIMM de RAM tienes que ver los “huecos” que tienes libres en la “placa base” simulada, para poder enganchar el DIMM de RAM en el sitio adecuado.

 

Antes de nada, recordad que en QEMU, con libvirtd,  si queremos ser capaces de modificar la RAM en vivo, hay que especificarle un “máximo” de RAM y un valor con el que arranca. En el caso de libvirt, tenemos que definir ese máximo, y , de paso, definir un nodo NUMA (aunque esté mal dicho, vamos a decir que es una serie de huecos disponibles)

En este caso, definimos un máximo de 16 GB y un valor actual de RAM de 1 GB. Y le especificamos, que el nodo NUMA de ID 0, puede estar asociado a las CPUs (virtuales) de ID 0 al 2.

Como casi todo en libvirt, se puede definir un DIMM de memoria RAM en formato XML.

En este caso, un DIMM de 256 MB de RAM.

Ahora, para conectarlo:

Si todo ha ido bien, el vuestro servidor virtual aparecerán esos 256 MB de RAM extra.

Para quitarlo, se puede usar el comando detach-device. Podréis quitar la RAM que hayáis añadido.

 

¿Dónde funciona esto?

En servidores Centos 6, Debian 7, y versiones similares.

Con un libvirt más o menos moderno, a partir de la 1.2.14.

Con un kernel 4 de linux en el hypervisor. Yo no lo probaría con el kernel por defecto de RedHat. Otra cosa es el kernel UEK de Oracle Linux.

 

¿Dónde está la documentación?

Esta es una pregunta recurrente en el universo de QEMU 😉

Está aquí: https://github.com/qemu/qemu/blob/master/docs/memory-hotplug.txt

Te explica cómo insertar DIMMs de RAM a través del interfaz de QEMU.

Y respecto a la documentación de libvirt: https://libvirt.org/formatdomain.html#elementsMemory

 

QEMU-kVM aumentar la memoria “en vivo”, método 1

Existen dos métodos principales para aumentar (o disminuir) la memoria “en vivo” del servidor. Ambas requieren un poco de previsión.

El primer paso, al arrancar el servidor tener en cuenta que “memory” identifica la memoria máxima que queremos que pueda ocupar ese servidor, y “currentmemory” es la que le dejamos ocupar al arrancar.

En el XML de libvirt:

En línea de comandos con QEMU:

 

Si queremos modificar la RAM que utiliza nuestro servidor:

 

En libvirt,

 

Y en libvirt, pero pasando el comando directamente al “monitor” de QEMU con “virsh qemu-monitor-command –hmp SERVIDOR –cmd ”

 

 

Veremos como se ajusta la RAM dentro del servidor, siempre que este tenga activado el soporte para “Memory Ballooning”.

 

Hay varias cosas a tener en cuenta:

 

  • En Windows, necesitas tener activo el device de ballooning y con el driver adecuado, que viene en la ISO de drivers para windows de QEMU , https://fedoraproject.org/wiki/Windows_Virtio_Drivers
  • Al arrancar, el servidor te intentará reservar lo que esté especificado como MÁXIMO. Es decir, si tu creas un servidor con 1024 de Memory y 512 de “current”, al arrancar, intentará reservar 1024. Si no cabe, porque deja sin memoria el servidor, el proceso de QEMU morirá.
  • No es bueno bajar la RAM por debajo de 128MB en los linux. Dará kernel panic. Hay que tener cuidado al especificar las unidades de medida de la RAM.

 

 

Actualizar la versión de QEMU, sin “reiniciar”

Hay dos maneras de modificar / actualizar la versión de QEMU sin “reiniciar”.

 

La primera, es haciendo una migración en caliente. Si tenemos dos servidores físicos con el mismo almacenamiento compartido (o incluso sin eso, utilizando la opción –copy-storage-all), podemos ejecutar un comando para migrar el servidor en caliente de uno a otro.

El truco aquí consiste en que al arrancar el qemu-kvm en el servidor remoto se hace con el ejecutable de la versión actualizada. Obviamente, tiene que poder arrancar, sin eso, fallará la migración, pero, al menos, el servidor virtual seguirá funcionando en origen.

 

La segunda manera, es hacer un “virsh save” y luego un “restore.

El comando “save” de libvirt te guarda un fichero con la memoria y las especificaciones de la máquina virtual. Dentro del fichero, por ejemplo, va el XML de definición.

El servidor virtual se detiene del todo, pero…con truco. Cuando se restaura la máquina virtual la ejecución continua en el mismo punto en el que se guardó 😉

De nuevo, al hacer el restore de la máquina virtual, si antes hemos modificado la versión de qemu-kvm, se intentará arrancar con la nueva versión.

 

Algún error que me ha dado alguna vez al hacer este tipo de upgrades era que el servidor estaba definido con el tipo de máquina “pc”.

Y al restaurarlo, ese “pc” en realidad es un alias a la última versión de máquina ACTUAL. Con modificar el fichero de save para poner un tipo de maquina más antiguo, funcionaba.

Para ver los diferentes tipos de VM posibles, se ejecuta el comando, donde se ve que “pc” se corresponde, en este caso, con pc-i440fx-1.5.

 

Para modificar el fichero donde hemos guardado la VM, se utiliza el comando

Cuidado con este comando, porque no permite hacer cualquier modificación. Parece ser que el tamaño de las modificaciones que se hacen en el XML guardado no pueden superar en tamaño a lo que había antes.

 

Instalar y compilar la última versión de libvirt en Ubuntu / Debian

En este mini-artículo vamos a ver cómo compilar la última versión de libvirt en un Ubuntu o Debian moderno.

Lo primero es descargar el código fuente de http://libvirt.org/sources/ . En este caso:

Lo descomprimimos con “tar xf ” y dentro del directorio creado ejecutamos:

 

Es posible que fallen dependencias, por lo que hay que instalar varios paquetes “-dev” para ser capaces de empezar a compilar.

En mi caso, un Ubuntu 16, tuve que instalar “libdevmapper-dev”, “libyajl-dev”. En un sistema recién instalado habrá que instalar bastantes más.

 

Cuando haya terminado el comando autogen correctamente, ejecutamos:

 

Si estamos en un sistema con systemctl (Debian 8 , Ubuntu 16 o superior), hay que editar el fichero

y modificar la línea que apunta al ejecutable de libvirt de esta manera

 

Luego ejecutamos

para recargar los ficheros de configuración, y para reiniciarlo:

 

 

Finalmente, con un “sudo virsh version”, tendremos que ver la versión correcta.

 

 

Es posible que os de un error indicando que no encuentra el emulador de X86_64. Eso ocurre porque no encuentra el emulador de qemu-kvm.

En mi caso, con hacer un link de /usr/local/qemu-kvm/bin/qemu-system-x86_64 a /usr/sbin/qemu-kvm ya bastó para que funcionase correctamente.

 

De igual manera que hicimos el otro día, se puede crear un paquete .deb para redistribuirlo en otros servidores físicos con checkinstall, pero no os cogerá los cambios del fichero del systemctl para el arranque.

 

 

QEMU-KVM: compilar / instalar QEMU en Debian / Ubuntu

Instalar QEMU en un Debian es cuestión de un comando, normalmente.

 

Compilar QEMU y crear un paquete en Debian no es tan fácil ni tan útil como en Redhat/CentOS. Básicamente porque es una mala idea sobreescribir los ejecutables del sistema operativo con tu propio paquete.

Pero las versiones de QEMU y libvirt que vienen con el sistema base son tan antiguas, que es casi imposible resistirse a tener una versión moderna con la que probar.

Primero descargamos el código fuente de QEMU de:

http://wiki.qemu.org/Download

Yo he probado con la versión 2.6.0. La 2.7.0 saldrá en breve. Mi sistema operativo es un Debian 8, pero con pocas variaciones, sirve también para Ubuntu.

Necesitamos instalar unos cuantos paquetes previos a la compilación:

Ahora entramos en el directorio de qemu, tras descomprimirlo, y hacemos el “configure”.

Las opciones de qemu son bastantes más. Pero eso es lo básico para funcionar, y tenerlo, además, algo optimizado utilizando la librería tcmalloc de google. Es muy posible que tengáis que tener arrancado el demonio “numad” para poder hacer funcionar ciertos servidores virtuales.

Tan solo nos interesa el ejecutable de x86_64 (en mi caso, porque mi portátil tiene dicha arquitectura), y el instalarlo en /usr/local/qemu-kvm para que no se mezcle con el previo del sistema operativo, si está instalado.

Si no queremos tener instalada SDL, se puede usar GTK, con –enable-gtk.

 

Si el configure funciona, tendremos una lista de las opciones activadas. Hay que fijarse bien en estas:

VNC support       yes , para tener soporte de VNC y poder conectar con clientes VNC al proceso de QEMU, como si fuera una pantalla

vhost-net support yes, para tener soporte para los módulos de vhost_net, que aceleran MUCHO el rendimiento de los interfaces de red

KVM support       yes, que nos indica que ha aceptado el soporte para KVM. Sin eso funciona pero mucho más lento

 

Seguidamente, ejecutamos “make”. Podemos acelerarlo con “make -jX” donde X es el número de cores que tenemos en el ordenador donde estemos compilando qemu.

Si todo va bien, podemos ejecutar el checkinstall, con el comando

Nos preguntará acerca de la documentación y los nombres de los paquetes. Yo no suelo incluirla.

Al concluir, nos mostrará un mensaje:

Lo podemos instalar con

La mayor ventaja que tiene es que al llevarlo a otro servidor físico, podremos instalar las dependencias automáticamente.

Ahora ya podemos ver que tenemos la nueva versión instalada.

 

Se puede ejecutar directamente, o bien via libvirtd, en el XML de definición de un servidor virtual, añadir

<emulator>/usr/local/qemu-kvm/bin/qemu-system-x86_64</emulator>

 

QEMU-KVM: virtio vs SCSI vs IDE

Una de las optimizaciones más importantes en cuanto a almacenamiento en QEMU-KVM es decidir el driver que vamos a utilizar para los discos duros de nuestro servidor simulado. Normalmente será VirtIO, pero, como veréis más adelante, no siempre es la mejor opción.

En las primeras versiones, tan solo existía IDE, luego añadieron SCSI, y más adelante, cuando observaron que simular de manera demasiado exacta esos protocolos hacía que no aprovechasen tanto los sistemas, inventaron VirtIO, que es una serie de módulos propios con mucha más velocidad de entrada/salida en máquinas virtuales con QEMU-KVM.

¿Qué ofrece cada uno?

  • IDE: ¿Recordáis cuando en vuestro PC casero teníais dos cables como cintas conectadas a la placa base y podíais colocar tan solo 4 dispositivos? Pues en QEMU el modo IDE hace exactamente lo mismo, de manera que se utiliza principalmente para colocar los lectores de CDROM. Los dispositivos con IDE se llaman /dev/hda, /dev/hdb, etc..
  • SCSI: Aquí el número de dispositivos aumenta, al igual que el rendimiento. Es una simulación casi completa de SCSI, lo cual tiene sus ventajas e inconvenientes. Los dispositivos con SCSI se llamarán /dev/sda, sdb, sdc…
  • VirtIO: Los dispositivos serán /dev/vda, vdb, vdc, y tendrán mayor rendimiento que los otros dos protocolos, y un uso menor de CPU (por mega transferido).

¿Cuánto es más rápido uno que el otro?

Si con IDE obtienes 512 MB/s de transferencia, con SCSI obtendrás 700-800 MB/s y con Virtio 1.2 GB/s. La regla suele ser más o menos esa, pero depende de la versión de kernel que se esté utilizando tanto en el hypervisor como en el servidor virtual y de los sistemas de almacenamiento utilizados.

Hay algunas webs que ofrecen benchmarks bastante antiguos ya:

https://www.ibm.com/support/knowledgecenter/linuxonibm/liaat/liaatbpperfmods.htm

Realmente no merece la pena ni mirarlos porque esos resultados han quedado desactualizados. En otro post comentaremos cómo hacer pruebas de carga decentes para obtener el mejor rendimiento de nuestro sistema y poder irlo comparando de unos entornos a otros.

RedBooks de IBM

La mejor documentación respecto a esto la proporciona IBM en sus RedBooks.

https://www.ibm.com/support/knowledgecenter/linuxonibm/liaat/liaatbestpractices_pdf.pdf

Y es lo que recomiendo leer si se quiere exprimir al máximo los diferentes sistemas.

 

Trim y comandos SCSI

Aunque esos libros estén realmente bien esto se explica en poco sitios, y es algo a tener REALMENTE en cuenta antes de tomar la decisión de cual sistema de almacenamiento utilizar.

VirtIO no soporta TRIM ni los comandos necesarios para hacer que, si eliminas un bloque de datos en el disco duro del servidor virtual, este cambio llegue exacto al sistema de almacenamiento que hay por debajo de todo, y se liberen los bloques no utilizados.

Es decir, imaginad que hemos comprado SSD último modelo y creamos dentro de este SSD una imagen de disco duro con un “truncate -s” o con un “LVM” en modo de thin provisioning. Si desde la VM escribimos un sector, luego escribimos más a posteriori después de ese, y luego borramos el sector del medio, el sistema de almacenamiento del hypervisor creerá que ese sector sigue ocupado, y no lo liberará.

La manera de corregir esto es utilizar SCSI. Pero a costa de perder rendimiento ( y mucho).

Si se utiliza VirtIO, por mucho que hagáis “fstrim” en vuestro sistema operativo, eso no se traducirá en liberación de espacio. De hecho, probablemente os salte un error comentando que no está soportado ese comando.

Para activar esta funcionalidad en SCSI se utiliza el valor “discard=unmap” en las opciones del disco duro, ya sea en el comando de qemu-KVM o  en el XML de definición del servidor en libvirt.

 

VirtIO en Windows

Los drivers de VirtIO para servidores virtuales con sistema operativo Windows se pueden conseguir en la web de Fedora:

https://fedoraproject.org/wiki/Windows_Virtio_Drivers

O bien en la web de Oracle:

http://www.oracle.com/us/technologies/virtualization/virtualilzation-066470.html

Cabe destacar que no siempre tener instalada la última versión significa tener, ni más rendimiento, ni más estabilidad. Yo he hecho pruebas de carga sobre diferentes versiones y con algunas obtenía resultados sorprendentes.

Suelo utilizar una suite de pruebas gratuita muy fácil de instalar llamada CrystalDiskMark para asegurarme que la versión que utilizo es la mejor.

http://crystalmark.info/download/index-e.html

 

 

Optimización: Host-Passthrough CPU

Cuando se arranca una máquina virtual con libvirt y qemu-kvm, lo que suele ocurrir es que dejamos la configuración por defecto de las características de la CPU, y, por defecto, la CPU “emulada” por qemu no tiene todas las instrucciones que lleva el servidor físico, el hypervisor.

Realmente, no es algo “mal hecho”, ni “erróneo”, pero se puede optimizar más, sobre todo si no vamos a migrar ese servidor de hypervisor. Más adelante comentaré el porqué de esto.

En la configuración XML de libvirt, si en el bloque de CPU escribimos:

Al reiniciar el servidor por completo, nos aparecerá la CPU del hypervisor.

Si os fijáis, en “flags” ya tenemos todas las instrucciones de CPU, por lo que para ciertas tareas debería ir algo más rápido.

¿Cuánta mejora supone esto? Entre un 3% y un 10%, quizás más dependiendo de si el programa en cuestión hace uso de las instrucciones que acabamos de activar para esa máquina virtual.

El único inconveniente aparece a la hora de migrar el servidor virtual de servidor físico. Para migrarlo, libvirt “arranca” una copia suspendida de la máquina virtual en el servidor físico de destino, y, si en dicho servidor no están todas las instrucciones ni es muy parecido al de origen, lo que puede ocurrir es que la máquina virtual no funcione correctamente, o directamente ni arranque.

Migrar de un AMD a un Intel no suele funcionar, ni al contrario. Ni migraciones entre diferentes kernels. Pero en el caso que nos ocupa, si la CPU de destino no se parece en nada a la de origen el sistema no funcionará. Otro día comentaré más acerca de la magia de las migraciones y como funcionan internamente.

Guardar y cargar la configuración en qemu-kvm

Normalmente, se utiliza libvirt para arrancar las máquinas virtuales. Gran parte de los sistemas de gestión de VMs en KVM se integran con libvirt por simplicidad, y parten de una configuración en un fichero XML.

Pero hay muchos que arrancan los servidores a mano utilizando directamente el comando de QEMU. Para ellos, hay una manera de guardar y cargar parte de las opciones.

El comando se llama “writeconfig”.

Por ejemplo, para arrancar un servidor:

Eso nos guarda un fichero en /configs/virtestest.cfg

 

Luego podemos volver a arrancar el servidor con

Esto no sirve para todas las opciones exactamente, pero si para las partes de red y disco.

Opciones de writeconfig y readconfig

Opciones de writeconfig y readconfig

 

Hotplug de CPU en QEMU+KVM

Desde la versión 2.7 rc1 de QEMU (publicada el 1 de Agosto 2016) es posible realizar, al fin, una modificación en el número de CPUs sin reiniciar el servidor. A esta función se le denomina “hotplug” de CPU.

Hasta ahora tan solo se podía aumentar el número de cores, ahora es posible también disminuirlos sin tener que apagar del todo el servidor, pero eso si, con ciertas restricciones.

  • No es posible quitar el último core (el primero de todos según se inicia el servidor).
  • Ciertos sistemas operativos no lo soportan porque no son capaces de “activar” las CPUs cuando se les engancha. En los Redhat’s y variantes (CentOS, p.ej) funciona bien. En los sistemas operativos Windows, también funciona adecuadamente. Los sistemas operativos Debian en cambio requieren de ciertas modificaciones en el “udev” para que el sistema operativo active los nuevos cores.
  • Solo lo he probado a partir de kernels 4.4, que es lo que suelo utilizar. En cualquier caso, necesitaría un kernel moderno. Yo no probaría aun a utilizar kernel 4.6.

Esta funcionalidad no está integrada en libvirt 2.0 aun, por lo que se ha de hacer a mano desde qemu, via “monitor”. Por simplificar, y como casi todos arrancamos los procesos con qemu+libvirt pongo el comando para libvirt exacto.

Primero vemos a ver el ID de nuestro servidor virtual. Se puede hacer igual con el nombre, para el argumento de –domain.

 

Seguidamente vamos a obtener las posiciones disponibles en las que enganchar las CPUs nuevas.

 

Hay que fijarse en la parte del “qom_path” de cada CPU. Para el siguiente comando “device_add” necesitamos pasarle el tipo de objeto, “qemu64-x86_64-cpu”, y donde lo vamos a colocar.

Según el comando anterior, y mirando el qom_path, tenemos dos CPUs ocupadas y nos quedan dos huecos libres.

 

Ahora para enganchar una nueva CPU tecleamos.

Explico todos los argumentos del comando virsh.

  • qemu-monitor-command: sirve para comunicarse con el proceso de qemu via un socket llamado “monitor”. Si tecleamos, por ejemplo, qemu-monitor-command –domain 13 –hmp help, enviaría al proceso de qemu el –help para que sacase las diferentes opciones disponibles. Este socket “monitor” está bloqueado por libvirt para poder gestionar el proceso de QEMU.
  • hmp: hay dos maneras de enviar comandos al proceso de qemu, una es via JSON, y otra, que es esta, via comandos.
  • device_add: el comando para añadir nuevos devices, ya sean tarjetas de red, CPUs, etc..
  • qemu64-x86_64-cpu: el tipo de objeto que estamos añadiendo. En este caso una CPU x86. Obviamente, no se pueden añadir diferentes tipos de CPU, y para cada sistema emulado hay que añadir el tipo adecuado. Hay ciertos tipos de emulaciones que aun no admiten este tipo de cambios en las CPUs.
  • socket-id=3, code-id=0, thread-id=0: Cuando creamos el servidor le especificamos la “estructura” de las CPUs, como si fuera la placa base de nuestro PC casero. P.ej:

-smp 2,sockets=2,cores=1,threads=1 (2 sockets de 1 core cada 1, y 1 thread por si queremos emular hyperthreading)

  • id: le ponemos un nombre. Es importante para simplificar el comando de eliminar. En este caso, cpu2.

En este caso, nosotros sabemos que tenemos el socket 3 y 4 libres, por el comando “info hotpluggable-cpus”, y le especificamos que se inserte en el socket 3.

Si en el servidor virtual miramos el fichero /var/log/messages, veremos un mensaje de ACPI indicando una nueva CPU. Aparecerá también en el comando “top” como una nueva CPU si pulsamos “1” para desplegar las CPUs.

Si no aparece en el “top” o en /proc/cpuinfo es probable que haya que activarla

 

Antes de añadir una nueva CPU

Antes y después de añadir una nueva CPU

Después de añadir una nueva CPU

 

 

Para quitar una CPU, ejecutamos:

Y la CPU desaparece del sistema, salvo error.