Terraform is a great tool to build infrastructures. You will see many tutorials on how to use it with AWS, Azure or GCP. But you can do more.

I use it to quickly create KVM virtual machines for ansible roles testing, and fun ! Thanks to the Terraform libvirt provider

I will explain here how to create a sandbox of three machines. I used it to develop my etcd ansible role.

Setup

Terraform installation

As a Debian user, I installed terraform with apt: https://www.terraform.io/docs/cli/install/apt.html

genisoimage

The libvirt provider needs mkisofs, you will find it in the genisoimage package.

sudo apt install genisoimage

KVM installation

The easy way is to install virt-manager:

sudo apt install virt-manager

Add yourself in the libvirt group:

sudo adduser your-user libvirt

Then log-out / log-in to this take effect, or reboot your PC.

Enable KSM (optional)

According to Wikipedia:

In computing, kernel same-page merging (KSM), also known as kernel shared memory, memory merging, memory deduplication, and page deduplication is a kernel feature that makes it possible for a hypervisor system to share memory pages that have identical contents between multiple processes and/or virtualized guests. While not directly linked, Kernel-based Virtual Machine (KVM) can use KSM to merge memory pages occupied by virtual machines.

In short, you will save RAM. And with only 8GB on my Laptop PC, saving RAM is appreciated.

To setup KSM on Debian systems:

sudo apt install ksmtuned

To check if KSM is efficient:

watch "grep -H '' /sys/kernel/mm/ksm/pages_*"

With no virtual machines, all values are set to zero:

/sys/kernel/mm/ksm/pages_shared:0
/sys/kernel/mm/ksm/pages_sharing:0
/sys/kernel/mm/ksm/pages_to_scan:300
/sys/kernel/mm/ksm/pages_unshared:0
/sys/kernel/mm/ksm/pages_volatile:0

Once your 3 virtual machines will be up and running, you will see something like this with non-zero values:

/sys/kernel/mm/ksm/pages_shared:12093
/sys/kernel/mm/ksm/pages_sharing:65774
/sys/kernel/mm/ksm/pages_to_scan:300
/sys/kernel/mm/ksm/pages_unshared:101163
/sys/kernel/mm/ksm/pages_volatile:379066

It means KSM RAM deduplication is active \0/

Using Terraform to create/destroy your KVM machines

I put needed files in this git repository: https://gitlab.com/AnatomicJC/terraform-libvirt

Terraform configuration files

Terraform uses tf files to describe your infrastructure. Each file with the tf extension are read and applied.

In my git repository, there is 3 tf files:

provider.tf

This file contains the terraform-libvirt-provider configuration.

cloud_init.tf

According to this Terraform page:

When you create a generic compute resource in Terraform, your virtual machine (VM) may not have much capability because it is a “fresh” install and needs to be provisioned with the software you want to use. Manually installing the necessary software and its respective dependencies on each VM is time consuming and difficult to maintain at scale.

cloud-init is a standard configuration support tool available on most Linux distributions and all major cloud providers. cloud-init allows you to pass a shell script to your instance that installs or configures the machine to your specifications.

cloud_init.tf is the Terraform configuration file, and cloud_init.cfg the Cloud-init configuration file. In this file, I setup a jc user with its ssh key, and install some package.

Thanks to cloud-init, my 3 VM will have the same setup, and I will be able to connect to them.

If you want to dig into cloud-init configuration file, have a look here: https://cloudinit.readthedocs.io/en/latest/topics/examples.html

libvirt.tf

This file contains your VM specification: CPU, RAM, etc.

As I want to create the 3 same virtual machines, there is a count = "3" who will be used as for the machine name:

name = "test-vm-ubuntu-${count.index + 1}"

Or to set the machine IP address:

addresses      = ["172.16.0.${count.index + 11}" ]

Another important configuration is the source image in the libvirt_volume resource.

You can set an online image who will be downloaded each time you will setup your virtual machine (it can be a bit long).

But if, like me, you are using terraform to often create/destroy machines, you can download the img file on a download folder and set it as source:

resource "libvirt_volume" "image-qcow2" {
  name = "ubuntu-amd64-${count.index + 1}.qcow2"
  count = "3"
  pool = libvirt_pool.ubuntu.name
  source ="${path.module}/downloads/bionic-server-cloudimg-amd64.img"
  #source = "https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64-disk-kvm.img"
  format = "qcow2"
}

You can find Ubuntu cloud images here and Debian cloud images here. For others distros, Google and your brain are yours friends.

Let’s create your VMs

Initialize your terraform project by downloading the libvirt provider and dependencies:

terraform init

You can know create your 3 virtual machines:

terraform apply -auto-approve

Or destroy them:

terraform destroy -auto-approve

The cloud image is first loaded, then the cloud-init script will install packages and create your user with its ssh key. Once it is done, you can connect to your VM with ssh your-user@172.16.0.11

If you got appArmor errors, you must set security_driver = "none" in /etc/libvirt/qemu.confand restart the libvirt daemon.

Please enjoy !

Resources: