Este artículo forma parte de una serie con el fin instalar, configurar y optimizar un servidor basado en la distro de Linux llamada Arch Linux. Para esta ocasión veremos como instalar KVM, el sistema de virtualización basado integrado al kernel de Linux.

¿Qué es KVM?1

Citando a la definición que mejor lo describe en el sitio de RedHat, la máquina virtual basada en kernel (KVM) es una tecnología de virtualización de open source integrada a Linux®. Específicamente, con KVM puede convertir a Linux en un hipervisor que permite que una máquina de host ejecute entornos virtuales múltiples y aislados llamados máquinas virtuales (VM) o huéspedes.

Mediante KVM podemos compartir los recursos de CPU, memoria, disco y conectividad entre diferentes instancias virtualizadas, denominadas huéspedes, que convergen en un mismo equipo físico donde cada máquina virtual dispone de su propio sistema operativo cumpliendo la finalidad deseada.

1. Instalación base de Arch Linux

Siguiendo los pasos que se describen en Instalar Arch Linux compatible con uefi y finalizada su instalación se podrá proceder a la configuración del sistema base preparándose así para montar el hipervisor KVM que, entre otras opciones como puede ser Xen Project hoy tomado por Citrix ó vmware vSphere que tanto tiempo lleva desarrollando en el mercado como solución de pago, mi inclinación es más hacia KVM habiendo montado varios proyectos con los antes mencionados. Los motivos son varios, pero el principal es la facilidad de administración mediante libvirt, la solidez operacional donde podrás tener la misma versión funcionando sin necesidad de largas horas de mantenimiento y con soluciones HA y backup mediante snapshots, por todo ello, es el que elijo tanto para ambientes en desarrollo como de producción a gran escala.

1.1. Configurar una interfaz como bridge (br0)

Será la interfaz de red que hará de puente entre la red física y los diferentes huéspedes (instancias virtualizadas) en el servidor hipervisor KVM basado en Arch Linux como sistema operativo base. La administración de las diferentes interfaces serán gestionadas mediante netctl, ubicándose sus carpetas de configuración en /etc/netctl.

1.2. Identificar las tarjetas de red instaladas en el servidor

Este paso permite entender los puntos posteriores y como arch linux identifica cada interfaz a la hora de aplicar los parámetros de red e iniciarlas con netctl.

nandu@nvg ~ # lspci | egrep -i --color 'network|ethernet'
00:19.0 Ethernet controller: Intel Corporation <strong>Ethernet</strong> Connection I217-V
04:00.0 <strong>Ethernet controller: Intel Corporation 82571EB/82571GB Gigabit <strong>Ethernet</strong> Controller (Copper) (rev 06)
04:00.1 <strong>Ethernet controller: Intel Corporation 82571EB/82571GB Gigabit <strong>Ethernet</strong> Controller (Copper) (rev 06)
05:00.0 <strong>Ethernet controller: Intel Corporation 82571EB/82571GB Gigabit <strong>Ethernet</strong> Controller (Copper) (rev 06)
05:00.1 <strong>Ethernet controller: Intel Corporation 82571EB/82571GB Gigabit <strong>Ethernet</strong> Controller (Copper) (rev 06)
07:00.0 Ethernet controller: Qualcomm Atheros Killer E220x Gigabit <strong>Ethernet</strong> Controller (rev 10)

El nombre para cada interfaz de red se basa en el cambio de como udevd asigna los nombres a cada dispositivo de conectividad. Ahora nuestros dispositivos usan “Predictable Interface Names”, basado en:

  1. Nombres incorporando Firmware/BIOS provisto por el nombre índice para los dispositivos integrados (ejemplo: eno1)
  2. Nombres incorporando Firmware/BIOS privisionado por el número índice del slot PCI Express (ejemplo: ens1)
  3. Nombres incoporando locación física/posicional del conector de hardware (ejemplo: enp2s0)
  4. Nombres incorporando la dirección MAC de las interfaces (ejemplo: enx78e7d1ea46da)
  5. Clásico, nombre nativo basado en el kernel el cual es poco preciso, ethX (ejemplo: eth0)

Para nuestro caso, siendo el dispositivo seleccionado Qualcomm Atheros Killer E220x siendo como el caso #3, el nombre correcto sería enp7s0.

1.3 Configurar interfaces en netctl

Directo desde la terminal copiar uno de los ejemplos brindados por netctl en /etc/netctl/examples/ethernet-static

nandu@nvg ~ # cp /etc/netctl/example/ethernet-static /etc/netctl/enp7s0
nandu@nvg ~ # nano /etc/netctl/enp7s0

Editar el archivo de la siguiente forma.

Description='Qualcomm Atheros Killer E220x Gigabit <strong>Ethernet</strong> Controller (rev 10)'
Interface=enp7s0
Connection=ethernet
IP=no
#Address=('192.168.1.132/24'')
#Routes=('192.168.0.0/24 via 192.168.1.2')
#Gateway='192.168.1.1'
#DNS=('8.8.8.8')
## For IPv6 autoconfiguration
#IP6=stateless
## For IPv6 static address configuration
#IP6=static
#Address6=('1234:5678:9abc:def::1/64' '1234:3456::123/96')
#Routes6=('abcd::1234')
#Gateway6='1234:0:123::abcd'

Crear un nuevo archivo en /etc/netctl/br0 e ingresar la siguiente configuración.

Description="Bridge br0-enp7s0"
Interface=br0
Connection=bridge
BindsToInterfaces=(enp7s0)
IP=static
Address=('10.12.80.22/24')
Gateway='10.12.80.252'
DNS=('8.8.8.8' '8.8.4.4')
## Ignore (R)STP and immediately activate the bridge
SkipForwardingDelay=yes

Los parámetros de red son personalizables dependiendo del bloque utilizado allí utilizado.

2 . Crear nuevo usuario con permisos sudo

Añadir el usuario local nandu a la lista de sudoers para poder obtener permisos de nivel administrador.

nandu@nvg ~ # su root
nandu@nvg ~ # nano /etc/sudoers

Buscar la línea con el siguiente contenido:

##
## User privilege specification
##
root ALL=(ALL) ALL

Añadir debajo nandu ALL=(ALL) ALL quedando así

##
## User privilege specification
##
root ALL=(ALL) ALL
nandu ALL=(ALL) ALL

3. Instalar pacaur

Ésta aplicación pacaur, como entre otras de su mismo tipo, facilita la instalación de aplicaciones en archlinux; pero antes requiere disponer de otros paquetes instalados para poder proceder con su configuración.

nandu@nvg ~ # sudo pacman -S expac yajl git bash-completion --noconfirm
nandu@nvg ~ # mkdir ~/Documents/ARCH/pacaur
nandu@nvg ~ # cd ~/Documents/ARCH/pacaur
nandu@nvg ~ # gpg -recv-keys --keyserver hkp://pgp.mit.edu 1EB2638FF56C0C53
nandu@nvg ~ # curl -o PKGBUILD https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=cower
nandu@nvg ~ # makepkg -i PKGBUILD --noconfirm

Finalizada la instalación de dependencias retomar la instalación de pacaur.

nandu@nvg ~ # curl -o PKGBUILD https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=pacaur
nandu@nvg ~ # makepkg -i PKGBUILD --no-confirm

4. Instalación de KVM y utils

Preparar instalación de aplicaciones necesarias para montar el hypervizor de kvm.

nandu@nvg ~ # pacaur -S qemu bridge-utils virt-install linux-vfio-lts

Durante el proceso de instalación de linux-vfio-lts es posible que tenga un error con las keys PGP.

error linux-vfio-lts
==> Verifying source file signatures with gpg...
 linux-4.9.tar ... FAILED (unknown public key 79BE3E4300411886)
 patch-4.9.20 ... FAILED (unknown public key 38DBBDC86092693E)
 ==> ERROR: One or more PGP signatures could not be verified!
 :: failed to verify linux-vfio-lts integrity

Solución

nandu@nvg ~ # gpg --recv-key 79BE3E4300411886
nandu@nvg ~ # gpg --recv-key 38DBBDC86092693E

5. Configurar kernel y modulos adicionales

Es necesario activar algunas funciones adicionales para poder reservar la vga a la instancia virtualizada, por ello es necesario volver a cargar los siguientes módulos.

nandu@nvg ~ # sudo nano /etc/default/grub

Buscar GRUB_CMDLINE_LINUX_DEFAULT="quiet" para luego añadir seguido de quiet
GRUB_CMDLINE_LINUX_DEFAULT="quiet rd.modules-load=vfio-pci intel-iommu=on"

nandu@nvg ~ # sudo grub-mkconfig -o /boot/grub/grub.cfg
nandu@nvg ~ # sudo nano /etc/mkinitcpio.conf

Modificar la línea MODULES y añadir el siguiente contenido

MODULES="vfio vfio_iommu_type1 vfio_pci vfio_virqfd"
nandu@nvg ~ # sudo mkinitcpio -p linux-vfio-lts

6. Reservar dispositivo con vfio

Isolating the GPU

In order to assign a device to a virtual machine, this device and all those sharing the same IOMMU group must have their driver replaced by a stub driver or a VFIO driver in order to prevent the host machine from interacting with them. In the case of most devices, this can be done on the fly right before the VM starts.

However, due to their size and complexity, GPU drivers do not tend to support dynamic rebinding very well, so you cannot just have some GPU you use on the host be transparently passed to a VM without having both drivers conflict with each other. Because of this, it is generally advised to bind those placeholder drivers manually before starting the VM, in order to stop other drivers from attempting to claim it.

The following section details how to configure a GPU so those placeholder drivers are bound early during the boot process, which makes said device inactive until a VM claims it or the driver is switched back. This is the preferred method, considering it has less caveats than switching drivers once the system is fully online.

Warning: Once you reboot after this procedure, whatever GPU you have configured will no longer be usable on the host until you reverse the manipulation. Make sure the GPU you intend to use on the host is properly configured before doing this – your motherboard should be set to display using the host GPU.

Using vfio-pci

Starting with Linux 4.1, the kernel includes vfio-pci. This is a VFIO driver, meaning it fulfills the same role as pci-stub, but it can also control devices to an extent, such as by switching them into their D3 state when they are not in use. If your system supports it, which you can try by running the following command, you should use it. If it returns an error, use pci-stub instead.

$ modinfo vfio-pci
filename:       /lib/modules/4.4.5-1-ARCH/kernel/drivers/vfio/pci/vfio-pci.ko.gz
description:    VFIO PCI - User Level meta-driver
author:         Alex Williamson <alex.williamson@redhat.com>
...

Vfio-pci normally targets PCI devices by ID, meaning you only need to specify the IDs of the devices you intend to passthrough. For the following IOMMU group, you would want to bind vfio-pci with 10de:13c2 and 10de:0fbb, which will be used as example values for the rest of this section.

IOMMU Group 13 06:00.0 VGA compatible controller: NVIDIA Corporation GM204 [GeForce GTX 970] [10de:13c2] (rev a1)
IOMMU Group 13 06:00.1 Audio device: NVIDIA Corporation GM204 High Definition Audio Controller [10de:0fbb] (rev a1)}}
Note: You cannot specify which device to isolate using vendor-device ID pairs if the host GPU and the guest GPU share the same pair (i.e : if both are the same model). If this is your case, read #Using identical guest and host GPUs instead.

You can then add those vendor-device ID pairs to the default parameters passed to vfio-pci whenever it is inserted into the kernel.

/etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:13c2,10de:0fbb
Note: If, as noted in #Plugging your guest GPU in an unisolated CPU-based PCIe slot, your pci root port is part of your IOMMU group, you must not pass its ID to vfio-pci, as it needs to remain attached to the host to function properly. Any other device within that group, however, should be left for vfio-pci to bind with.

This, however, does not guarantee that vfio-pci will be loaded before other graphics drivers. To ensure that, we need to statically bind it in the kernel image alongside with its dependencies. That means adding, in this order, vfio_pci, vfio, vfio_iommu_type1, and vfio_virqfd to mkinitcpio:

/etc/mkinitcpio.conf
MODULES=(... vfio_pci vfio vfio_iommu_type1 vfio_virqfd ...)
Note: If you also have another driver loaded this way for early modesetting (such as nouveau, radeon, amdgpu, i915, etc.), all of the aforementioned VFIO modules must precede it.

Also, ensure that the modconf hook is included in the HOOKS list of mkinitcpio.conf:

/etc/mkinitcpio.conf
HOOKS=(... modconf ...)

Since new modules have been added to the initramfs configuration, you must regenerate the initramfs. Should you change the IDs of the devices in /etc/modprobe.d/vfio.conf, you will also have to regenerate it, as those parameters must be specified in the initramfs to be known during the early boot stages.

Reboot and verify that vfio-pci has loaded properly and that it is now bound to the right devices.

$ dmesg | grep -i vfio
[    0.329224] VFIO - User Level meta-driver version: 0.3
[    0.341372] vfio_pci: add [10de:13c2[ffff:ffff]] class 0x000000/00000000
[    0.354704] vfio_pci: add [10de:0fbb[ffff:ffff]] class 0x000000/00000000
[    2.061326] vfio-pci 0000:06:00.0: enabling device (0100 -> 0103)

It is not necessary for all devices (or even expected device) from vfio.conf to be in dmesg output. Sometimes a device does not appear in output at boot but actually is able to be visible and operatable in guest VM.

$ lspci -nnk -d 10de:13c2
06:00.0 VGA compatible controller: NVIDIA Corporation GM204 [GeForce GTX 970] [10de:13c2] (rev a1)
	Kernel driver in use: vfio-pci
	Kernel modules: nouveau nvidia
$ lspci -nnk -d 10de:0fbb
06:00.1 Audio device: NVIDIA Corporation GM204 High Definition Audio Controller [10de:0fbb] (rev a1)
	Kernel driver in use: vfio-pci
	Kernel modules: snd_hda_intel
Buscar VGA AMD y reservar a VM

nandu@nvg ~ # sudo lspci -nn | grep AMD
nandu@nvg ~ # sudo nano /etc/modprobe.d/vfio.conf

Añadir el siguiente contenido al archivo vfio.conf

options vfio-pci ids="1002:68f9,1002:aa68"

7. Instalar libvirt mediante repo externo

Copiar instalador de libvirt-3.0.0-1-x86_64.pkg.tar con los parches aplicados según

nandu@nvg ~ # sudo pacaur -U libvirt-3.0.0-1-x86_64.pkg.tar.xz

Independientemente del orden de instalación de libvirt, será necesario instalar una serie de librerias de las que depende libvirt.

nandu@nvg ~ # pacaur -S pm-utils libssh

Añadir el usuario nandu dentro del grupo kvm y libvirt

nandu@nvg ~ # sudo usermod -G kvm -a nandu
nandu@nvg ~ # sudo usermod -G libvirt -a nandu

Luego levantamos los módulos necesarios, que pueden variar en dependencia de nuestra tarjeta de video:

~ # sudo modprobe kvm-intel
~ # sudo modprobe kvm

Ahora activamos el servicio:

~ #sudo systemctl enable libvirtd.service

Y si queremos arrancarlo, ya saben:

~ # systemctl start libvirtd.service

Reiniciar el sistema

~ # sudo reboot -n

Permitir acceso a libvirt de forma remota1

Editar /etc/libvirt/libvirtd.conf y quitar el comentario de las líneas con el siguiente contenido

 unix_sock_group = "libvirt"
 unix_sock_rw_perms = "0770"

Probar de realizar una conexiòn 
~ #  virsh -c qemu+ssh://root@192.168.1.132:22132/system

Instalar netcat(2) seleccionando el paquete de openbsd ya que es compatible con la opción -U, el gnu da el mensaje de error 

error: failed to connect to the hypervisor
error: End of file while reading data: sh: nc: command not found: Input/output error

[nandu@nvg ~]$ pacaur -S netcat
:: There are 2 providers available for netcat:
:: Repository extra
1) gnu-netcat
:: Repository community
2) openbsd-netcat

Enter a number (default=1): 2

Crear nuevo repo para archivos del servidor
ISOS: mkdir -p /home/_nvs/kvm/archive/iso/ y montar tras cada inicio del sistema

~ # sudo mount /dev/md126 /env/vdisk/pool
~ # sudo echo ‘/dev/md126 /env/vdisk/pool ext4 defaults 0 0’ | sudo tee /etc/fstab

Crear disco para vm

~ #  sudo qemu-img create -f qcow2 ./vdisk_opnsense-amd64_sys.img 40960M

Instalar BIOS ed2k

 

Evitar high DPC en Windows activando los siguientes módulos del kernel al inicio del sistema.

~ # sudo nano /etc/modules-load.d/virtio-ring.conf
~ # sudo nano /etc/modules-load.d/virtio-net.conf
~ # sudo nano /etc/modules-load.d/virtio-pci.conf
~ # sudo nano /etc/modules-load.d/virtio-blk.conf
~ # sudo nano /etc/modules-load.d/virtio-ballon.conf
~ # sudo nano /etc/modules-load.d/virtio.conf

Dentro de cada archivo ingresar únicamente la línea con el contenido en referencia al módulo con el que se está trabajando, ejemplo, si el archivo sobre el que estamos es “virtio-ring.conf” el contenido sería “virtio-ring”.

Fuentes

  1. RedHat Linux. ¿Qué es KVM?

1- https://wiki.libvirt.org/page/SSHSetup
2- http://unix.stackexchange.com/questions/285089/virsh-libvirt-bin-is-not-able-to-communicate-with-xen-server#comment496623_285089

3 – Sobre desktop : https://blog.desdelinux.net/instalar-y-configurar-qemu-kvm-en-arch-linux/

4 –  High DPC: https://wiki.archlinux.org/index.php/PCI_passthrough_via_OVMF#High_DPC_Latency

5- Inst. yaourt para libvirt y config de audio en arch https://github.com/Frans-Willem/ArchPassthroughHypervisor/wiki

6- vfio linux-vfio-lts config vga passthrough en kernel https://wiki.archlinux.org/index.php/PCI_passthrough_via_OVMF#Using_pci-stub_.28legacy_method.2C_pre-4.1_kernels.29