Creating images with libguestfs

The first step in creating a secure and regularly updated app for VM Squared is automating a repeatable build. Automated app builds enable you to easily ship security fixes, which ensure that your new VMs have a secure and fully-featured image.

In order to create an automated app for VM Squared we will describe the process for building the disk image that will be used to deploy the VM running your app.

We will be using libguestfs to build our disk image for the app.

Basics of libguestfs

Libguestfs enables us to customize existing Linux disk images to add packages, scripts and configuration settings.

The final image will contain an operating system, customized to include the following:

  • A contextualization package ( one-context )

    When a VM is instantiated, it will be contextualized by VM Squared. This is when the VM’s individual configuration is applied, such as networking and SSH keys.

    We will use the latest one-context package from the addon-context-linux repo.

  • A firstboot script

    This script will be executed when the VM is first booted up after being instantiated in VM Squared.

    This can be used to perform any configuration or setup that cannot be applied at build time, such as editing VM’s config files with any input provided by the user during setup.

  • App files

    Anything needed for your app, such as packages, code, templates, configuration files etc.

Requirements

You will need:

  • Linux system capable of running libguestfs.
    • Packages are available for Fedora, Red Hat, Debian and Ubuntu.
  • Target OS VM image in RAW or QCOW2 format.
  • Disk space to store and manipulate your chosen image.

Image preparation

To keep things simple, we’ll create a Debian image with one-context and Grafana installed. The Grafana admin username & password will be set in VM Squared.

Writing a build.sh script

It’s not really a script

It’s mostly a virt-builder command broken into multiple lines for readability , convenience and reproducible results.

Contents of build.sh:

app_name='grafana'
root_password="correcthorsebatterystaple"

base_image='debian-11'
context_package='https://github.com/OpenNebula/addon-context-linux/releases/download/v6.6.0/one-context_6.6.0-1.deb'

image_format='qcow2'
output_file="$app_name-$base_image.$image_format"

virt-builder $base_image \
--format $image_format \
--install curl,gnupg \
--run-command "wget $context_package" \
--run-command 'apt update ; dpkg -i /root/one-context_*deb || apt-get install -fy' \
--run-command 'curl https://packages.grafana.com/gpg.key | apt-key add -' \
--run-command 'echo "deb https://packages.grafana.com/oss/deb stable main" | tee -a /etc/apt/sources.list.d/grafana.list' \
--install grafana \
--upload local-config/grafana.ini:/etc/grafana/grafana.ini \
--firstboot firstboot.sh \
--root-password password:$root_password \
--output $output_file && echo "Successfully built image: $output_file at $(date)"

Running scripts at boot

This script refers to some files that don’t exist yet, so we’ll make them:

The file firstboot.sh was specified with --firstboot which runs this script when the image first boots up, after being instantiated in VM Squared.

With the user’s inputs during setup, we can finish configuring the app.

The output of this script will be written to /root/virt-sysprep-firstboot.log

Contents of firstboot.sh:

#!/usr/bin/env bash

# Wait until one_env has been populated and source it to get the user's inputs
while [[ ! -s "/run/one-context/one_env" ]]; do
    echo "Waiting for one_env to be populated..."
    sleep 1
done

source /run/one-context/one_env

sed -i "s/admin_user =.*#MANAGED-BY-HC/admin_user = $GRAFANA_ADMIN_USERNAME #MANAGED-BY-HC/g" /etc/grafana/grafana.ini
sed -i "s/admin_password =.*#MANAGED-BY-HC/admin_password = $GRAFANA_ADMIN_PASSWORD #MANAGED-BY-HC/g" /etc/grafana/grafana.ini

systemctl enable grafana-server --now

systemctl status grafana-server | cat

Uploading files to image

We can also upload files to the image with the --upload parameter.

The file local-config/grafana.ini was specified as an upload.

[security]
admin_user = admin #MANAGED-BY-HC
admin_password = VM Squared?m0nitoring #MANAGED-BY-HC

Build the image

With all our files in place, we can run the build script:

build.sh

Successful output:

[   1.0] Downloading: http://builder.libguestfs.org/debian-11.xz
[   1.6] Planning how to build this image
[   1.6] Uncompressing
[   5.5] Converting raw to qcow2
[   6.3] Opening the new disk
[   9.6] Installing packages: curl gnupg
[  30.7] Running: wget https://github.com/OpenNebula/addon-context-linux/releases/download/v6.6.0/one-context_6.6.0-1.deb
[  31.3] Running: apt update ; dpkg -i /root/one-context_*deb || apt-get install -fy
[  32.2] Running: curl https://packages.grafana.com/gpg.key | apt-key add -
[  32.7] Running: echo "deb https://packages.grafana.com/oss/deb stable main" | tee -a /etc/apt/sources.list.d/grafana.list
[  32.7] Installing packages: grafana
[  39.2] Uploading: local-config/grafana.ini to /etc/grafana/grafana.ini
[  39.3] Installing firstboot script: firstboot.sh
[  39.4] Setting passwords
[  40.0] Finishing off
                   Output file: grafana-debian-11.qcow2
                   Output size: 6.0G
                 Output format: qcow2
            Total usable space: 6.4G
                    Free space: 5.0G (77%)
Successfully built image: grafana-debian-11.qcow2 at Wed 22 Feb 13:57:19 GMT 2023

You can now upload the built image to VM Squared as an Operating System image, and Create your VM Template