Overview
Terraform is an open-source infrastructure as code (IaC) tool that allows you to define and provision infrastructure using declarative scripts. The core lifecycle commands of Terraform are:
init: Initialize a new or existing Terraform configuration.plan: Create an execution plan.apply: Apply the changes required to reach the desired state of the configuration.destroy: Destroys everything you provisioned.
In this guide, we will use Terraform to create three KVM virtual machines (VMs): base, client, and xdp.
- Base: This is the base QEWU server VM.
- XDP: This is the base VM with XDP (eBPF program) loaded.
- Client: This VM will be used to measure the throughput of the
baseandxdpVMs.
For simplicity, the .img files for the VMs will initially be the same as the Ubuntu 24.04 Server Cloud Image.
Here’s a brief overview of what each VM will represent:
base_root.img: Image for thebaseVM.xdp_root.img: Image for thexdpVM.client_root.img: Image for theclientVM.
Downloading the Base Cloud Image
First, download the base cloud image .img file from the official Ubuntu cloud image repository:
Download Ubuntu 24.04 Server Cloud Image
Store the downloaded image in the images/ directory.
Directory Structure
Here is the directory structure for your Terraform configuration:
.
├── cloudinit_base_template.tf
├── cloudinit_base.tf
├── cloudinit_client.tf
├── cloudinit_xdp.tf
├── common_domain.tf
├── images
│ ├── base_root.img
│ ├── client_root.img
│ ├── ubuntu-24.04-server-cloudimg-amd64.img
│ └── xdp_root.img
├── main.tf
├── providers.tf
├── terraform.tfvars
└── variables.tf
cloudinit_base_template.tf
data "template_file" "cloudinit_template" {
template = <<EOF
#cloud-config
fqdn: ${var.vm_domain}
manage_etc_hosts: true
package_update: true
package_upgrade: true
ssh_pwauth: 1
timezone: "America/New_York"
users:
- name: user
passwd: $y$j9T$90KTrNAm1VThpSJR99lD/1$V28A3c/tiTBCC1drC6jnu4LVChD1XpcLA2uQ.RD6uXB
home: /home/user
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: false
chpasswd: { expire: False }
groups: sudo, users, admin
EOF
vars = {
fqdn = var.vm_domain
}
}
cloudinit_base.tf
data "template_file" "cloudinit_base" {
template = data.template_file.cloudinit_template.rendered
vars = {
fqdn = "base.${var.vm_domain}"
}
cloudinit_xdp.tf
data "template_file" "cloudinit_xdp" {
template = data.template_file.cloudinit_template.rendered
vars = {
fqdn = "xdp.${var.vm_domain}"
}
}
cloudinit_client.tf
data "template_file" "cloudinit_client" {
template = data.template_file.cloudinit_template.rendered
vars = {
fqdn = "client.${var.vm_domain}"
}
}
common_domain.tf
# common_domain.tf
locals {
common_memory_vcpu = {
memory = var.vm_memory
vcpu = var.vm_vcpu
}
common_console = [
{
type = "pty"
target_port = "0"
target_type = "serial"
},
{
type = "pty"
target_type = "virtio"
target_port = "1"
}
]
common_graphics = {
type = "spice"
listen_type = "address"
autoport = true
}
}
providers.tf
# Define Terraform configuration requirements and providers
terraform {
required_version = ">= 1.9.2"
required_providers {
random = {
source = "hashicorp/random"
# version = "~> 3.6.1" # Allows any version >= 3.6.1 and < 3.7.0
version = "~> 3.6.1" # Use the latest patch version of 3.6.x
}
libvirt = {
source = "dmacvicar/libvirt"
version = "~> 0.7.6" # Use the latest patch version of 0.7.x
}
}
# Optional: Configure backend for state storage
# backend "s3" {
# bucket = "my-tf-state"
# key = "terraform/state"
# region = "us-east-1"
# }
}
terraform.tfvars
# terraform.tfvars is a file where you provide values for the variables defined in variables.tf.
# This file is used to set or override the default values of variables.
# Purpose
# Assign Values: You use terraform.tfvars to assign actual values to the variables that were declared in variables.tf.
# Override Defaults: If a variable has a default value in variables.tf, the value in terraform.tfvars will override it.
# Provide Environment-Specific Configurations: You can use this file to specify different values
# for different environments or scenarios.
# VM Configurations
vms = {
client = {
hostname = "client"
vm_ip = ["192.168.100.2"]
},
base = {
hostname = "base"
vm_ip = ["192.168.100.3"]
},
xdp = {
hostname = "xdp"
vm_ip = ["192.168.100.4"]
},
}
# Node Configuration Parameters
vm_vcpu = 2
vm_memory = 4096 # Memory in MiB
vm_disksize = 40 # Disk size in GiB
vm_domain = "local"
# VM Image Paths
vm_baseimage = "images/ubuntu-24.04-server-cloudimg-amd64.img"
client_image = "images/client_root.img"
base_image = "images/base_root.img"
xdp_image = "images/xdp_root.img"
# Host Parameters
libvirt_disk_path = "/var/lib/libvirt/images/kernel_absorb"
variables.tf
# variables.tf is a Terraform configuration file where you define variables that will be used throughout
# your Terraform configuration. This file contains variable declarations, including descriptions,
# types, and default values.
# Purpose
# Declare Variables: You use variables.tf to declare what variables your Terraform configuration requires.
# Provide Defaults: You can specify default values for variables, which will be used if no other value is provided.
# Describe Variables: You can include descriptions to explain what each variable is for, which helps with
# documentation and clarity.
# Host Parameters
variable "libvirt_disk_path" {
description = "Path for the libvirt pool"
type = string
default = "/var/lib/libvirt/images/kernel_absorb"
}
# Node Config
variable "vm_baseimage" {
description = "Path to the Ubuntu 24.04 base image"
type = string
default = "images/ubuntu-24.04-server-cloudimg-amd64.img"
}
variable "client_image" {
description = "Path to the client VM image"
type = string
default = "images/client_root.img"
}
variable "base_image" {
description = "Path to the base VM image"
type = string
default = "images/base_root.img"
}
variable "xdp_image" {
description = "Path to the XDP VM image"
type = string
default = "images/xdp_root.img"
}
variable "vm_vcpu" {
description = "Number of vCPUs for each VM"
type = number
default = 2
}
variable "vm_memory" {
description = "Memory in MiB for each VM"
type = number
default = 4096
}
variable "vm_disksize" {
description = "Disk size in GiB for each VM"
type = number
default = 40
}
variable "vm_domain" {
description = "FQDN for the network"
type = string
default = "local"
}
# VMs
variable "vms" {
description = "Map of VM configurations"
type = map(object({
hostname = string
vm_ip = list(string)
}))
}
main.tf
provider "libvirt" {
uri = "qemu:///system"
}
resource "libvirt_pool" "kernel_absorb_pool" {
name = "kernel_absorb_pool"
type = "dir"
path = var.libvirt_disk_path
}
# this denotes official cloud .img file
resource "libvirt_volume" "ubuntu_24_base" {
name = "ubuntu_24.04"
pool = libvirt_pool.kernel_absorb_pool.name
format = "qcow2"
source = var.vm_baseimage
}
# client VM storage volume
resource "libvirt_volume" "qcow2_client" {
name = "client_root.img"
pool = libvirt_pool.kernel_absorb_pool.name
format = "qcow2"
source = var.client_image
}
# base server VM storage volume
resource "libvirt_volume" "qcow2_base" {
name = "base_root.img"
pool = libvirt_pool.kernel_absorb_pool.name
format = "qcow2"
source = var.base_image
}
# xdp server VM storage volume
resource "libvirt_volume" "qcow2_xdp" {
name = "xdp_root.img"
pool = libvirt_pool.kernel_absorb_pool.name
format = "qcow2"
source = var.xdp_image
}
resource "libvirt_cloudinit_disk" "cloudinit_client" {
name = "cloudinit_client.iso"
pool = libvirt_pool.kernel_absorb_pool.name
user_data = data.template_file.cloudinit_client.rendered
}
resource "libvirt_cloudinit_disk" "cloudinit_base" {
name = "cloudinit_base.iso"
pool = libvirt_pool.kernel_absorb_pool.name
user_data = data.template_file.cloudinit_base.rendered
}
resource "libvirt_cloudinit_disk" "cloudinit_xdp" {
name = "cloudinit_xdp.iso"
pool = libvirt_pool.kernel_absorb_pool.name
user_data = data.template_file.cloudinit_xdp.rendered
}
# see https://github.com/dmacvicar/terraform_provider_libvirt/blob/v0.7.6/website/docs/r/network.markdown
resource "libvirt_network" "net" {
name = "net"
mode = "nat"
domain = var.vm_domain
addresses = ["192.168.100.0/24"]
dhcp {
enabled = false
}
dns {
enabled = true
local_only = false
}
}
resource "libvirt_domain" "domain_client" {
name = var.vms["client"].hostname
cloudinit = libvirt_cloudinit_disk.cloudinit_client.id
network_interface {
network_name = "net"
wait_for_lease = true
hostname = var.vms["client"].hostname
addresses = var.vms["client"].vm_ip
}
disk {
volume_id = libvirt_volume.qcow2_client.id
}
# common part
memory = local.common_memory_vcpu.memory
vcpu = local.common_memory_vcpu.vcpu
dynamic "console" {
for_each = local.common_console
content {
type = console.value.type
target_port = console.value.target_port
target_type = console.value.target_type
}
}
graphics {
type = local.common_graphics.type
listen_type = local.common_graphics.listen_type
autoport = local.common_graphics.autoport
}
}
resource "libvirt_domain" "domain_base" {
name = var.vms["base"].hostname
cloudinit = libvirt_cloudinit_disk.cloudinit_base.id
network_interface {
network_name = "net"
wait_for_lease = true
hostname = var.vms["base"].hostname
addresses = var.vms["base"].vm_ip
}
disk {
volume_id = libvirt_volume.qcow2_base.id
}
# common part
memory = local.common_memory_vcpu.memory
vcpu = local.common_memory_vcpu.vcpu
dynamic "console" {
for_each = local.common_console
content {
type = console.value.type
target_port = console.value.target_port
target_type = console.value.target_type
}
}
graphics {
type = local.common_graphics.type
listen_type = local.common_graphics.listen_type
autoport = local.common_graphics.autoport
}
}
resource "libvirt_domain" "domain_xdp" {
name = var.vms["xdp"].hostname
cloudinit = libvirt_cloudinit_disk.cloudinit_xdp.id
network_interface {
network_name = "net"
wait_for_lease = true
hostname = var.vms["xdp"].hostname
addresses = var.vms["xdp"].vm_ip
}
disk {
volume_id = libvirt_volume.qcow2_xdp.id
}
# common part
memory = local.common_memory_vcpu.memory
vcpu = local.common_memory_vcpu.vcpu
dynamic "console" {
for_each = local.common_console
content {
type = console.value.type
target_port = console.value.target_port
target_type = console.value.target_type
}
}
graphics {
type = local.common_graphics.type
listen_type = local.common_graphics.listen_type
autoport = local.common_graphics.autoport
}
}
images/
upgautamvt@fedora:~/CLionProjects/KernelWithBpfPrograms/benchmarks/macrobenchmarks/terraform/xdp_terraform$ ls images/
base_root.img client_root.img ubuntu-24.04-server-cloudimg-amd64.img xdp_root.img
Refer to: https://github.com/uddhavpgautam