Migrating to KVM with WebVirtMgr

Migrating to KVM with WebVirtMgr
Jérémie Kassianoff
March 2, 2015
15 min read

Want to migrate to KVM with WebVirtMgr? Then discover: kvm, libvirt, webvirtmgr, brctl and many others! Prerequisite: a KVM-compatible server.

Real-world use case

You want to manage your migration to KVM from a web interface rather than the command line: here's how to proceed with WebVirtMgr.

A migration geared toward open source

To ensure the longevity and stability of my infrastructure, I chose KVM: an open, interoperable standard with immense features. More specifically, this new project fits into my desire to stay as close as possible to my Linux system. KVM is present in the largest cloud computing projects: OpenStack, OpenNebula, oVirt, and many others.

In addition to KVM, there's also this fabulous container system called Docker, which itself is entirely based on Linux. Both represent wonderful learning opportunities for understanding the cloud. KVM management will largely be done with an HTML5 interface. For Docker, there's Shipyard, which I won't cover in this article.

My entire article is based mainly on the KVM wiki, the WebVirtMgr wiki, and libvirt.

The SaaS solution: WebVirtMgr

WebVirtMgr presents itself as a web interface built with Bootstrap. It uses the libvirt API to manage the hypervisor(s), networks, interfaces, storage, and VMs. Access to the VM is done via a VNC console (novnc) or HTML5 "spice".
More information at: webvirtmgr.

Here's an illustration of my infrastructure:

WebVirtMgr and the KVM/QEMU hypervisor

KVM (Kernel Virtual Machine) is a Linux kernel module that lets a userspace program use the hardware virtualization features of various processors. Today, it supports recent Intel processors and AMD processors (x86 and x86_64), PPC 440, PPC 970, S/390, ARM (Cortex A15), and MIPS32 (processor emulation).

QEMU can make use of KVM when running a target architecture that's the same as the host architecture. For example, when running qemu-system-x86 on an x86-compatible processor, you can take advantage of KVM acceleration, benefiting both your host and your guest system (virtual machine emulation).

Source: http://wiki.qemu.org/KVM

Prerequisites before installation

  • The processor must be compatible with virtualization technologies:
bash
egrep '^flags.*(vmx|svm)' /proc/cpuinfo

If a result appears, then your processor is capable. Otherwise, your processor doesn't support virtualization.
No worries! There's webvirtmgr with lxc and of course Docker: check it out!

  • Install the following packages:
bash
apt-get install kvm libvirt-bin bridge-utils sasl2-bin
  • Download retspen's script and run it:
bash
wget http://retspen.github.io/libvirt-bootstrap.sh 
sh libvirt-bootstrap.sh

For your information, at the end of the script you should have the following services running:

  • ok Starting system message bus: dbus.
  • ok Starting libvirt management daemon: libvirtd.

Installing WebVirtMgr

WebVirtMgr needs the following packages to work:

bash
apt-get install git python-pip python-libvirt python-libxml2 novnc supervisor

During the (long) installation, several questions will appear:

bash
should a database be installed for NOVA: yes
bash
should nova-common's database be configured with dbconfig-common: yes
bash
type of database server to use with nova-common! sqlite3

The next step consists of configuring Python and the "django" environment:

bash
mkdir /var/www

Move into the www directory:

bash
cd /var/www

Download the WebVirtMgr git repo:

bash
git clone git://github.com/retspen/webvirtmgr.git

Go to the following directory:

bash
cd webvirtmgr

The pip tool is used because it will quickly install and automatically download the required dependencies:

bash
sudo pip install -r requirements.txt

Downloading/unpacking django==1.5.5 (from -r requirements.txt (line 1))
  Downloading Django-1.5.5.tar.gz (8.1MB): 8.1MB downloaded
  Running setup.py (path:/tmp/pip_build_root/django/setup.py) egg_info for package django

    warning: no previously-included files matching '__pycache__' found under directory '*'
    warning: no previously-included files matching '*.py[co]' found under directory '*'
Downloading/unpacking gunicorn==18.0 (from -r requirements.txt (line 2))
  Downloading gunicorn-18.0.tar.gz (366kB): 366kB downloaded
  Running setup.py (path:/tmp/pip_build_root/gunicorn/setup.py) egg_info for package gunicorn

    warning: no previously-included files matching '*.pyc' found under directory 'docs'
    warning: no previously-included files matching '*.pyo' found under directory 'docs'
    warning: no previously-included files matching '*.pyc' found under directory 'tests'
    warning: no previously-included files matching '*.pyo' found under directory 'tests'
    warning: no previously-included files matching '*.pyc' found under directory 'examples'
    warning: no previously-included files matching '*.pyo' found under directory 'examples'
Downloading/unpacking lockfile>=0.9 (from -r requirements.txt (line 5))
  Downloading lockfile-0.10.2-py2-none-any.whl
Installing collected packages: django, gunicorn, lockfile
  Running setup.py install for django
    changing mode of build/scripts-2.7/django-admin.py from 644 to 755

    warning: no previously-included files matching '__pycache__' found under directory '*'
    warning: no previously-included files matching '*.py[co]' found under directory '*'
    changing mode of /usr/local/bin/django-admin.py to 755
  Running setup.py install for gunicorn

    warning: no previously-included files matching '*.pyc' found under directory 'docs'
    warning: no previously-included files matching '*.pyo' found under directory 'docs'
    warning: no previously-included files matching '*.pyc' found under directory 'tests'
    warning: no previously-included files matching '*.pyo' found under directory 'tests'
    warning: no previously-included files matching '*.pyc' found under directory 'examples'
    warning: no previously-included files matching '*.pyo' found under directory 'examples'
    Installing gunicorn_paster script to /usr/local/bin
    Installing gunicorn script to /usr/local/bin
    Installing gunicorn_django script to /usr/local/bin
  Found existing installation: lockfile 0.8
    Uninstalling lockfile:
      Successfully uninstalled lockfile
Successfully installed django gunicorn lockfile
Cleaning up...

Start creating the database via django:

bash
./manage.py syncdb
bash
WARNING:root:No local_settings file found.
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table servers_compute
Creating table instance_instance
Creating table create_flavor

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Please enter either "yes" or "no": yes
Please enter either "yes" or "no": yes
Username (leave blank to use 'root'): kassianoff
Email address: [email protected]
Password:
Password (again):
Superuser created successfully.
Installing custom SQL ...
Installing indexes ...
Installed 6 object(s) from 1 fixture(s)

We start again, this time with collectstatic:

bash
./manage.py collectstatic

Type 'yes' to continue, or 'no' to cancel: yes
75 static files copied

Give the web server rights to the WebVirtMgr directory:

bash
chown -R www-data:www-data /var/www/webvirtmgr/

Modifying the script and the VNC startup:

bash
sudo service novnc stop
[ ok ] Stopping OpenStack NoVNC proxy: nova-novncproxy.

Removing the startup script for our Debian distribution:

bash
update-rc.d -f novnc remove
update-rc.d: using dependency based boot sequencing

Removing the script in init.d:

bash
rm /etc/init.d/novnc

Fill in the startup settings in the webvirtmgr.conf file:

bash
nano /etc/supervisor/conf.d/webvirtmgr.conf
ini
[program:webvirtmgr]
command=/usr/bin/python /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py
directory=/var/www/webvirtmgr
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/webvirtmgr.log
redirect_stderr=true
user=www-data

program:webvirtmgr-console

command=/usr/bin/python /var/www/webvirtmgr/console/webvirtmgr-console directory=/var/www/webvirtmgr autostart=true autorestart=true stdout_logfile=/var/log/supervisor/webvirtmgr-console.log redirect_stderr=true user=www-data

We stop the supervisor service and start it:

bash
service supervisor stop
service supervisor start

The installation is complete, and we can now access the WebVirtMgr interface via port 8000 of our machine.

SSH tunneling under Linux

The SSH tunneling method allows forwarding ports 8000 and 6080 (novnc) to our (secured) client machine:

bash
ssh [email protected]:port -L localhost:8000:localhost:8000 -L localhost:6080:localhost:6080

Go to the url: localhost:8000 and enter your login credentials configured during ./manage.py syncdb:

Connection between WebVirtMgr and KVM/QEMU

Several steps will be needed before we can configure the infrastructure.

  • Create your TCP connection credentials like this:
bash
saslpasswd2 -a libvirt kassianoff.fr
Password: 
Again (for verification):
  • Declare your hypervisor in virsh:
bash
virsh -c qemu+tcp://localhost/system nodeinfo
Please enter your authentication name: kassianoff.fr
Please enter your password:
CPU model:           x86_64
CPU(s):              8
CPU frequency:       1632 MHz
CPU socket(s):       1
Core(s) per socket:  4
Thread(s) per core:  2
NUMA cell(s):        1
Memory size:         32917020 KiB

Now let's add our TCP connection by clicking the "Add connection" button

Here's the result:

Getting started with WebVirtMgr

I think it's important to understand the different sections that make up WebVirtMgr:

  • Instances: Managing and creating virtual machines; these are our instances.
  • Storages: Managing and creating the different storage pools (directory, iso, nfs, lvm...).
  • Networks: Managing and creating networks (bridge, route, nat...).
  • Interfaces: Managing and creating the various interfaces that networks can use.
  • Secrets: I didn't get any information about it; if a visitor has information, I'm interested!
  • Overview: Monitor our entire infrastructure: resources used (cpu, memory, etc...).

I used all the sections (except Secrets); start by following the steps below to be able to create an instance.

Creating storage pools

Before starting to create a machine, we'll need storage pools; click on Storages then on New storages, the configuration is as follows:

Creating a DIR directory with the path /var/lib/libvirt/images.

Creating the ISO directory with the path /var/www/webvirtmgr/images:

On an OVH dedicated server, use NFS-common, and create an nfs directory to connect it to the NFS backup storage

bash
apt-get install nfs-common
mkdir /var/lib/libvirt/nfs/
mount -t nfs ftpback-rbxX-XXX.ovh.net:/export/ftpbackup/nsXXXXXXX.ovh.net /var/lib/libvirt/nfs/

When creating the pool, there's a NETFS mode compatible with NFS, however the format isn't taken into account.
The solution is to use the dir mode by specifying the previously mounted nfs directory:

Check your different storage pools: nfs, iso, images:

I won't cover creating a machine in the images menu, nor adding an ISO file via WebVirtMgr.
The reason is simple: I use the terminal for those tasks. Let's focus on the networks before creating our VM.

The networks section in WebVirtMgr

Networks are added via the networks tab. Add two bridge networks lbr0 and lbr1:

Repeat the operation above to create the second bridge interface, which will give:

Interfaces and bridged networks under Linux

The interfaces section displays the configuration file /etc/networks/interface, where we'll find lo, br0, lbr0, lbr1.
But before configuring our bridge interfaces, let's look at the current configuration:

bash
ifconfig
bash
eth0      Link encap:Ethernet  HWaddr 08:60:6e:e5:bd:47
          inet adr:XX.XXX.XXX.XXXX  Bcast:XX.XXX.XXX.XXX  Masque:255.255.255.0
          adr inet6: XXXX:XXXX:X:XXXX::/xx Scope:Global
          adr inet6: XXXX::XXX:XXXX:XXXX:XXXX/xx Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Packets reçus:2243 erreurs:0 :0 overruns:0 frame:0
          TX packets:1618 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000
          Octets reçus:490019 (490.0 KB) Octets transmis:243883 (243.8 KB)

lo        Link encap:Boucle locale
          inet adr:127.0.0.1  Masque:255.0.0.0
          adr inet6: ::1/128 Scope:Hôte
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          Packets reçus:140 erreurs:0 :0 overruns:0 frame:0
          TX packets:140 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:0
          Octets reçus:22966 (22.9 KB) Octets transmis:22966 (22.9 KB)

virbr0    Link encap:Ethernet  HWaddr 22:e2:0b:e7:c2:d1
          inet adr:192.168.122.1  Bcast:192.168.122.255  Masque:255.255.255.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          Packets reçus:0 erreurs:0 :0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:0
          Octets reçus:0 (0.0 B) Octets transmis:0 (0.0 B)

We notice the following configuration: the eth0 interface which represents the server's public IP address, the (logical) loopback interface, as well as the virbr0 interface which is created automatically in NAT mode.

In setting up my infrastructure, networking has an important place. So I created three interfaces:

  • br0 = bridged to the main eth0 interface of my Debian host (used for my OVH failover IPs).
  • lbr1 = a bridge interface used by VyOS for my web services.
  • lbr2 = a bridge interface used by VyOS for my Dockers and VMs, with the possibility of Docker/KVM interconnections.

My router/firewall manages my different br0, lbr1, lbr2 interfaces. Here's an illustration diagram:

Let's start by configuring the br0 interface: (Debian wiki)

bash
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet manual

auto br0
iface br0 inet static
        address XX.XXX.XXX.XXX
        netmask 255.255.255.0
        network XX.XXX.XXX.0
        broadcast XX.XXX.XXX.255
        gateway XX.XXX.XXX.254
        bridge_ports eth0
        bridge_stp off
        bridge_fd 0
        bridge_maxwait 0

iface eth0 inet6 static
        address XXXX:XXXX:X:XXXX::
        netmask 64
        post-up /sbin/ip -family inet6 route add XXXX:XXXX:X:XXXX:XX:XX:XX:XX dev eth0
        post-up /sbin/ip -family inet6 route add default via XXXX:XXXX:X:XXXX:XX:XX:XX:XX
        pre-down /sbin/ip -family inet6 route del default via XXXX:XXXX:X:XXXX:XX:XX:XX:XX
        pre-down /sbin/ip -family inet6 route del XXXX:XXXX:X:XXXX:XX:XX:XX:XX dev eth0

Restart the interface (your SSH connection to your server shouldn't be interrupted):

bash
/etc/init.d/network restart

Check the bridge interface configuration on the host:

bash
ifconfig br0
bash
br0       Link encap:Ethernet  HWaddr 08:60:6e:e5:bd:47
          inet adr:XX.XXX.XXX.XXX  Bcast:XX.XXX.XXX.255  Masque:255.255.255.0
          adr inet6: XXXX::XXX:XXXX:XXXX:XXXX/xx Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2344432 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1599970 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:0
          RX bytes:9383508945 (8.7 GiB)  TX bytes:312106046 (297.6 MiB)

The command to view the bridged interface:

bash
btctl show
br0             8000.08606ee5bd47       no              eth0

The bridge package lets us create our interfaces directly, such as:

bash
brctl addbr lbr0
brctl addbr lbr1

Next, we need to start the bridged interfaces lbr0 and lbr1:

bash
ip link set dev lbr0 up
ip link set dev lbr1 up

However, the libvirt method doesn't make our interfaces permanent! Add the two interfaces permanently:

bash
nano /etc/network/interface
bash
auto lbr0
iface lbr0 inet manual
    bridge_ports none
    bridge_stp off

auto lbr1
iface lbr1 inet manual
    bridge_ports none
    bridge_stp off
bash
/etc/init.d/network restart
bash
brctl show

br0             8000.08606ee5bd47       no              eth0
lbr0            8000.fe5400047801       no              
lbr1            8000.fe54000a522b       no              
virbr0          8000.000000000000       yes

The host server can now restart without fear of losing its configuration. Here are the different bridged interfaces:

You have the option to disable the interface at startup:

bash
virsh net-autostart --disable default

Or even permanently remove the default interface:

bash
virsh net-destroy default

Creating an instance in WebVirtMgr

In the instances section, click the New Instance button and several options are available to you.

  • Custom instance, Template, or XML configuration file.

Below, you'll also find pre-defined templates numbered 1 to 6 that let you create an image.

In this specific case, I'll use a custom instance. But first we'll need to create a disk for the VM.
As explained earlier, I prefer to use cli mode to create the disk for my future machine:

bash
qemu-img create -f qcow2 vdisk 10G

For those who want to convert a VMware machine to KVM:

bash
qemu-img convert vm1-flat.vmdk -O qcow2 vm1

Display information about our machine:

yaml
qemu-img info vdisk

file format: qcow2
virtual size: 10G (10737418240 bytes)
disk size: 136K
cluster_size: 65536

Now use the vdisk HDD to create a vdisk VM:

Start the creation by clicking the create button: the "vdisk" machine management appears with its options:

At this point, feel free to explore the sections above, but don't forget:

  1. "Access" to select your console type, then change the keyboard layout (if in novnc mode).
  2. "Settings" to add an ISO Media to the virtual machine.
  3. In "Settings", edit the vdisk machine's xml configuration. Only if you want to assign an OVH Failover IP to your VM. The interfaces section of the machine must contain (example on my router/firewall):
    bash
    <interface type='bridge'>
    <mac address='xx:xx:xx:xx:xx:xx'/>
    <source bridge='br0'/>
    <target dev='vnet0'/>
    <alias name='net0'/>
    <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>
    
  4. Starting the machine: Power then Start, access to the console is done via Access/Console
  5. A pop-up opens with novnc and access to the VM:

That's it, you're now ready to use your infrastructure's resources and your VM!
But first, back up your VM live.

Backing up your KVM machines live

The backup can be done by cloning via the webvirtmgr interface, or via a simple cp: (to NFS)

bash
cp /var/lib/libvirt/images/vdisk /var/lib/libvirt/nfs/vdisk.02.03.2015

Infrastructure overview

The infrastructure's usage load is available in the overview section, which provides a real-time overview. Here's the usage of my webvirt-kassianoff infrastructure once it's in place:

Updating WebVirtMgr

Technology and security updates are of course very important; nothing could be simpler:
Go to the WebVirtMgr directory:

bash
cd /var/www/webvirtmgr

Then run the following commands:

bash
 remote: Counting objects: 27, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 27 (delta 7), reused 6 (delta 6), pack-reused 7
Unpacking objects: 100% (27/27), done.
From git://github.com/retspen/webvirtmgr
   ed176e4..c875e0d  master     -> origin/master
Updating ed176e4..c875e0d
Fast-forward
 console/views.py         |    3 ++-
 create/views.py          |    7 ++++---
 hostdetail/views.py      |    5 +++--
 instance/views.py        |   15 ++++++++-------
 interfaces/views.py      |    7 ++++---
 networks/views.py        |    9 +++++----
 secrets/views.py         |    3 ++-
 servers/views.py         |    9 +++++----
 storages/views.py        |    9 +++++----
 templates/base.html      |    2 +-
 templates/base_auth.html |    2 +-
 templates/login.html     |    2 +-
 12 files changed, 41 insertions(+), 32 deletions(-)
bash
./manage.py collectstatic
bash
WARNING:root:No local_settings file found.

You have requested to collect static files at the destination
location as specified in your settings.

This will overwrite existing files!
Are you sure you want to do this?

Type 'yes' to continue, or 'no' to cancel: yes

0 static files copied, 75 unmodified.
bash
service supervisor stop

I'm finishing my migration, and I hope you like your new infrastructure!

Conclusion

This article presented setting up a KVM/QEMU virtualization infrastructure managed by the WebVirtMgr web interface: installing the required packages, configuring the storage pools, networks and bridged interfaces, then creating and backing up an instance. This open-source, interoperable solution makes it easy to manage a KVM hypervisor without depending on a proprietary vendor, while retaining the flexibility of the Linux terminal for the most technical tasks. I hope this new infrastructure, closer to my Linux habits, keeps working well over time.