September 8, 2018

Virtualization using KVM on Debian Stretch 9.5

KVM on Debian

Good morning,

a short while ago, I purchased a neat little micro ITX board and Pentium G4400 processor on Ebay. Put it in a roomy case, add some hard drives and there you go: a nice and efficient home server. What mischief could we do with that? Let's virtualize!

Preamble

Much of the following applies to a wide variety of Linux distributions. Since there are subtle differences, e.g. in regards to package management, I will be focusing on Debian here. Accordingly, before we start, you'll need to install Debian 9.5. on your system. If you need guidance for installing Debian, please take a look at the Debian manual.

When we talk about running a VM on KVM, we usually mean a wide array of software modules, most of which are not actually a part of KVM:

  • KVM is the Kernel Virtual Machine, which is the virtualization layer included in the Linux Kernel.
  • qemu is an emulator, which, thanks to its modular architecture, can emulate a large variety of hardware platforms. In our case, we will be using it to "emulate" our virtual machine hardware using KVM.
  • libvirt is an API built on top of qemu and KVM. It includes a daemon and various tools, which make VM management on qemu a lot easier.
  • virsh is the command line tool to manage libvirt instances (e.g. startig and stopping VMs).
  • virt-install is a command line tool to configure virtual machines for use with libvirt.
  • virt-manager is a GUI client built on top of libvirt. It allows management of VMs using a graphical interface. Since I'd rather use the command line tooling, virt-manager is not part of this article.

I found many tutorials on the setup of KVM and specifically libvirt to be lacking in clarity, specifically in regards to users and access rights. So let me get some basics out of the way first.

Libvirt has two different modes of operation, denoted by the respective session you connect to: using the url qemu:///system you connect to the libvirt daemon (libvirtd), a system-wide session, which is typically running under root privileges. This is the session tools like virt-manager normally connect to. Since libvirtd is running as root, it has all the necessary permissions to access network bridges, block devices and so on. Images, VM configurations, etc. are by default stored in /var/lib/libvirt. Also, autostarting VMs on system boot is only possible in qemu:///system.

Using the url qemu:///session creates a new libvirt instance for the current user. It is possible to run many user specific sessions on a single system. Privileges of the user-specific instance depend on the user starting the session. VMs, images, etc. are stored in the users home directory., thus no special access rights are needed there. You will however not be able to access block devices without setting up some udev rules to allow access to /dev/[blockdevice] for the given user. Access to network bridges may also require some additional access rights.

You can execute virsh uri to find out which session you are currently connected to. The default session will most likely be qemu:///session. To change this, you can add the following environment variable to your .bashrc, log out and log in again (otherwise you will have to add --connect qemu:///system to all virsh and virt-install commands).

export LIBVIRT_DEFAULT_URI="qemu:///system"

Since qemu:///system is widely accepted as the default mode of operation, I will be using it in this article.

Setup

Without further ado, let us start by installing the required packages:

sudo apt install qemu-kvm libvirt-clients libvirt-daemon-system virtinst

You will typically want your VMs to have network connectivity, for which you need to create a virtual network bridge to your physical NIC. To do that, edit the network configuration as follows:

nano /etc/network/interfaces
# The primary network interface
# Make sure the physical NIC does not use dhcp
allow-hotplug eth0
iface eth0 inet dhcp
iface eth0 inet  manual
iface eth0 inet6 manual

# Virtual network bridge
auto vmbr0
iface vmbr0 inet dhcp
    bridge_ports eth0    # Replace eth0 with your interface
    bridge_stp off
    bridge_maxwait 0
    bridge_fd 0

Now reboot the system.

Creating the first VM

With the preparation out of the way, let's download a boot image for our first VM. I'm going to use a Debian 9.5 netinstal image, which you can easily get with

sudo wget "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-9.5.0-amd64-netinst.iso" -O "/var/lib/libvirt/boot/debian-9.5.iso"

Finally, let us try to create our first VM:

virt-install --virt-type kvm --name debian9 --memory 512 --cdrom /var/lib/libvirt/boot/debian-9.5.iso --disk size=10 --network bridge=vmbr0 --os-variant debian9

You should be seeing something like this:

WARNING  Graphics requested but DISPLAY is not set. Not running virt-viewer.
WARNING No console to launch for the guest, defaulting to --wait -1
Starting install...
Allocating 'debian9.qcow2' | 10 GB 00:00:00
Creating domain... | 0 B 00:00:01
Domain installation still in progress. Waiting for installation to complete.

Here is what the above command means:

--virt-type kvm: The VM is meant for use with KVM

--name debian9: The VM is called "debian9". This is the name displayed using the command virsh list. It is also what you would enter to edit the VM: virsh edit debian9. Note this is NOT the hostname of the VM.

--memory 512: We want to give 512 MB of memory to our VM.

--cdrom /var/...: The ISO image to boot the VM with.

--disk size=10: Creates a new virtual hard drive with a size of 10 GB alongside the VM. The resulting drive is stored in /var/lib/libvirt/images and is named like the VM.

--network bridge=vmbr0: The VM uses our previously created network bridge.

--os-variant debian9: We will be using the VM to run Debian 9. This allows for various system specific optimizations. To get a list of possible values, run osinfo-query os (you may need to install libosinfo-bin). The often mentioned virt-install --os-variant list does not work anymore.

Connecting to the VM

You can now connect to your new VM to... wait you don't know how to connect? Right, we are running on a headless server. Hmm... let's first stop the domain installation using Ctrl+C. Let us also remove the VM we just created:

virsh destroy debian9
virsh undefine debian9
sudo rm /var/lib/libvirt/images/debian9.qcow2

There are two ways to initialize a VM on a headless server in such a way that one can install on OS on it.

1. Make the VMs console accessible in our terminal session. This is highly OS specific and will not be usable for any Windows guest. There is also an annoying restriction: the -x argument cannot be combined with --cdrom. Once upon a time, it was possible to mount an .iso-image using the -l argument, however this does no longer work. You will instead have to provide a valid install tree, which I could only get to work using an Ubuntu netinstal image:

virt-install --virt-type kvm --name ubuntu --memory 512 -l http://archive.ubuntu.com/ubuntu/dists/bionic/main/installer-amd64/ --disk size=10 --network bridge=vmbr0 --os-variant ubuntu17.10 -x='console=tty0 console=ttyS0,115200n8' --nographics

2. Make the guest accessible via VNC. This is a much better option, because it works with any guest OS and also allows the use of a desktop environment in the client. Don't forget to insert your host machines IP address in the argument --graphics vnc,listen=xyz.

virt-install --virt-type kvm --name debian9 --memory 512 --cdrom /var/lib/libvirt/boot/debian-9.5.iso --disk size=10 --network bridge=vmbr0 --os-variant debian9 --graphics vnc,listen=0.0.0.0 --noautoconsole

You can easily find the VNC port of a VM using

virsh vncdisplay debian9

Start your favorite VNC viewer, plug in the IP and port and voilà - a working VM using KVM on Debian Stretch!