Libvirt (libvirtd) nodes (based on KVM and Qemu) are a great and cheap (read: free) alternative of deploying virtual nodes in a cloud. Required is a server which will act as a hypervisor, in our article we chose to use a Hetzner server installed with Ubuntu Linux 20.4-lts.
After the default installation of Ubuntu 20.4-lts, the following packages are required to get started as a hypervisor:
apt install qemu-kvm libvirt-daemon bridge-utils virtinst libvirt-daemon-system virt-top libguestfs-tools libosinfo-bin qemu-system virt-manager qemu pm-utils
Once these are installed, the vnet_hosts
module needs to be pre-loaded in /etc/modules
:
echo vhost_net | tee -a /etc/modules modprobe vhost_net
The hypervisor is now ready to start creating and deploying virtual machines.
In this article, Terraform will be used to manage the virtual machines in libvirtd. All example code snippets are available on Github, under https://github.com/insani4c/terraform-libvirt
Terraform has an excellent provider (dmacvicar/libvirtd) to manage the libvirtd nodes, which needs to be loaded and initialized:
terraform { required_providers { libvirt = { source = "dmacvicar/libvirt" } } } provider "libvirt" { uri = "qemu+ssh://root@192.168.1.1/system" }
In the above code snippet, Terraform will ensure the libvirt provider is loaded and it is configured to connect to the host with IP address 192.168.1.1
as the root user (this requires that a SSH public key is installed on the remote server, of the user who will be executing Terraform).
Next, the network will defined, where the virtual nodes will be deployed in.
resource "libvirt_network" "my_network" { name = "my_net" mode = "nat" addresses = ["10.1.2.0/24"] domain = var.dns_domain autostart = true dhcp { enabled = false } dns { enabled = true local_only = false forwarders { address = "127.0.0.53" } hosts { hostname = "host01" ip = "10.1.2.10" } hosts { hostname = "host02" ip = "10.1.2.20" } } }
The above code will ensure that a network of type NAT
(to allow internal IPs, reachable from the hypervisor only) with network mask 10.1.2.0/24
, will be created. It will ensure that DHCP
is disabled and it will enable a DNS
setup (the package dnsmasq
must be installed) with two predefined hosts, host01
and host02
.
Up next is the definition of the storage pools and volumes required for the virtual machines.
resource "libvirt_pool" "default" { name = "default" type = "dir" path = "/data/vms/cluster_storage" } resource "libvirt_volume" "local_install_image" { name = var.local_install_image pool = libvirt_pool.default.name source = var.os_img_url format = "qcow2" }
The above defines the libvirt_pool
, which basically configures the path on-disk for storing all sorts of volumes. Next it defines a volume called “local_install_image
“, which will be used to set up the virtual machine as the volume will contain the “cloud image” for the installation. This volume requires two variables:
variable "os_img_url" { description = "URL to the OS image" default = "https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img" } variable "local_install_image" { description = "The name of the local install image" default = "base-os-ubuntu-focal.qcow2" }