Testing your webserver locally with KVM and QEMU

Posted on

Table of contents

1 Can I run KVM

Before we can start with setting everything up, we first need to check if we can even run KVM.

We can do so with the following commands.

lira@computer:~$ egrep -c '(vmx|svm)' /proc/cpuinfo
1
lira@computer:~$ kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used
Checking KVM-support

If the first command returns a number that is higher than zero, we are good. The next way to check if we can run KVM is with kvm-ok.

2 Installing KVM and QEMU

To install the packages we need, run the following command

lira@computer:~$ sudo apt install qemu-kvm \
		libvirt-daemon-system \
		libvirt-clients \
		virt-manager \
		ovmf \
		bridge-utils

Installing the needed packages.

Below a list of the different packages and why we need them.

qemu-kvm
The processor-emulator QEMU with additional KVM-support.
libvirt-daemon-system
Functionality to interact with a virtualised Linux-system. It has support for multiple virtualisers, including QEMU and KVM.
libvirt-clients
Includes the application Virsh, which we will use to connect with the server.
virt-manager
A graphical virtual machine viewer. We will only need virt-install, which is included in this package. Also, having a GUI can come in handy later.
ovmf
An UEFI-implementation for QEMU.
bridge-utils
A bridge to get our server connected to the outside world.

Before we continue, lets check if libvirt is enabled. We can do that with systemctl status.

lira@computer:~$ sudo systemctl status libvirtd
● libvirtd.service - Virtualization daemon
     Loaded: loaded (/lib/systemd/system/libvirtd.service; enabled; vendor pres>
     Active: active (running) since Thu 2001-02-30 01:02:30 CET; 1h 20min ago
TriggeredBy: ● libvirtd-admin.socket
             ● libvirtd-ro.socket
             ● libvirtd.socket
       Docs: man:libvirtd(8)
             https://libvirt.org
   Main PID: 978 (libvirtd)
      Tasks: 19 (limit: 32768)
     Memory: 34.3M
     CGroup: /system.slice/libvirtd.service
             ├─ 978 /usr/sbin/libvirtd
             ├─1198 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/defa>
             └─1201 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/defa>

Jan 10 01:02:30 computer dnsmasq-dhcp[1198]: DHCP, sockets bound exclusivel>
Jan 10 01:02:30 computer dnsmasq[1198]: reading /etc/resolv.conf
Jan 10 01:02:30 computer dnsmasq[1198]: using nameserver 127.0.0.53#53
Jan 10 01:02:30 computer dnsmasq[1198]: read /etc/hosts - 7 addresses
Jan 10 01:02:30 computer dnsmasq[1198]: read /var/lib/libvirt/dnsmasq/defau>
Jan 10 01:02:30 computer dnsmasq-dhcp[1198]: read /var/lib/libvirt/dnsmasq/>
Jan 10 01:02:30 computer dnsmasq[1198]: reading /etc/resolv.conf
Is libvirt running?

To quit, press the Q-key.

If libvirtd is not running, you can enable it with systemctl enable.

lira@computer:~$ sudo systemctl enable --now libvirtd

Enabling libvirt

3 Setting permissions

In order to use KVM we need to add our user to the groups libvirt and kvm.

lira@computer:~$ sudo adduser lira libvirt
lira@computer:~$ sudo adduser lira kvm

Adding a user to the groups libvirt and kvm

The images for our OS will be put in /var/lib/libvirt/images/. In order to access that library, we need to set the permissions it got from the parent-folders from root to libvirt.

lira@computer:~$ sudo chgrp libvirt /var/lib/libvirt/images
lira@computer:~$ sudo chmod 775 /var/lib/libvirt/images
lira@computer:~$ ls -ld /var/lib/libvirt/images
drwxrwxr-x 2 root libvirt 4096
Changing permissions of the folder where we will store our OS-images

Now you will have to relog for group-permissions to take effect. Once you are back you can run groups to check if kvm and libvirt are among your groups.

lira@computer:~$ groups
lira cdrom sudo kvm libvirt
Are kvm and libvirt among your groups?

4 Installing an OS

Next we need to download the OS for our VM. In this article I will use Ubuntu Server. Download the latest Ubuntu via the download page, I chose version 20.04.3

Move the ISO-file to the libvirt-images-folder

lira@computer:~$ mv \
		Downloads/ubuntu-20.04.3-live-server-amd64.iso \
		/var/lib/libvirt/images/ubuntu-20.04.3-live-server-amd64.iso

Lets clean up the downloads-folder

Onto the installation itself!

Ubuntu Installation guide recommends 2GB RAM, but actually wants 2GiB. Let's just give it the 2,048GB it actually wants.

lira@computer:~$ cd /var/lib/libvirt/images
lira@computer:/var/lib/libvirt/images$ virt-install \
		--name=UbuntuServer20 \
		--vcpus=1 \
		--ram=2048 \
		--disk path=./ubuntuserver20.qcow2,size=15 \
		--cdrom ./ubuntu-20.04.3-live-server-amd64.iso \
		--graphics vnc
lira@computer:/var/lib/libvirt/images$ cd ~

Installing Ubuntu Server.

You might get a lot of failure-codes, but you can just ignore those. It is mostly about not finding a CD-ROM and things like that. Just press the enter-key whenever the installation pauses.

After rebooting it will show some more failure-codes, which we will again ignore with the mighty enter-key, after which we can log in

Once logged in, the first thing we do is bringing the system up-to-date. Afterwards we shut it down again. We will restart it later.

lira@ubuntuserver:~$ sudo apt update
lira@ubuntuserver:~$ sudo apt upgrade
lira@ubuntuserver:~$ sudo shutdown now

Bringing Ubuntu Server up-to-date

Now we can start the server again. Virsh allows us to act on a VM by specifying the tool-URI, qemu in this case. We pass it the start-command.

lira@computer:~$ virsh --connect qemu:///system start UbuntuServer20

Restarting our Ubuntu Server.

It will take a few seconds before the server is fully operational. If you run virsh domifaddr while it is not ready yet, it will not show any rows. So keep trying for a while until it does.

lira@computer:~$ virsh domifaddr UbuntuServer20
 Name       MAC address          Protocol     Address
-----------------------------------------------------------------
 vnet0      52:54:00:f3:4f:d0    ipv4         192.168.122.179/24
Retrieving the IP-address of our Ubuntu Server.

Great! We got the IP-address. Lets do a little ping-test, after which we connect to it via SSH to start working on the web-server itself.

lira@computer:~$ ping -c 4 192.168.122.179
lira@computer:~$ ssh adminadmin@192.168.122.179

Connecting to our Ubuntu Server via SSH.

5 The web-server

Now you can set up your web-server. If you don't have one but still want to continue this article, here is a very basic web-server written in C you can use.

#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(){
	int s, i, size;
	struct sockaddr_in a, o;
	if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) return -10;
	a.sin_family = AF_INET;
	a.sin_port = htons(8080);
	a.sin_addr.s_addr = htonl(INADDR_ANY);
	memset(&(a.sin_zero), '\0', 8);
	if(bind(s, (struct sockaddr*)&a, sizeof(struct sockaddr)) < 0) return -20;
	if(listen(s, 1) < 0) return -30;
	repeat:
		size = sizeof(struct sockaddr_in);
		if((i = accept(s, (struct sockaddr*)&o, &size)) < 0) goto repeat;
		send(i, "Hello\n", 6, 0);
		close(i);
		goto repeat;
	close(s);
}
A very very very basic web-server that will only return Hello once reached from port 8080

To compile the code run gcc BasicWebServer.c -o BasicWebServer.

Now we can run the web-server!

lira@ubuntuserver:~$ ./BasicWebServer

Start the web-server

Now we can test the web-server! Since it is just a basic string that gets returned, let's give cURL a try.

lira@computer:~$ curl -X GET --http0.9 http://192.168.122.179:8080

Connect to our web-server so it can say Hello to us!

Once we are done, we can go back to the server and shut it down.

lira@ubuntuserver:~$ sudo shutdown now

Turning of our Ubuntu Server.

6 Sources