Booting Cloud Images with QEMU

Do you ever get frustrated with waiting for a heavy VM image to download or with installing operating systems onto virtual machines manually? It can start to feel cumbersome after a while, especially if you bring up and tear down lots of virtual machines as part of your workflow. It’d be nice if spawning a ready-to-use VM was as quick and as easy as it is when using a public cloud.

Good news! It can be. Many Linux distributions (and BSDs) publish what are known as “cloud images” for use in virtual machines. These images are purposefully tiny and contain the bare minimum for what is required to provision a virtual machine.

Create a directory structure for your VMs

This is up to you, however, here’s mine (I like to keep it simple):

$ mkdir -p $HOME/VM/images/base

Download a cloud base image

I’ll use a Fedora Cloud Base Image for the virtual machines, but like the previous step, you can find your own cloud image from your favorite OS distribution. Distributions typically do a good job of explicitly pointing out which download is a cloud image, so I don’t think you’ll have much trouble finding one.

$ curl -Lk https://download.fedoraproject.org/pub/fedora/linux/releases/33/Cloud/x86_64/images/Fedora-Cloud-Base-33-1.2.x86_64.qcow2 > $HOME/VM/images/base/fedora_cloud_base_33.qcow2

The important thing here is that the cloud image is a qcow2 image. If it’s a raw image, you’ll have to convert it like so:

$ qemu-img convert -f raw -O qcow2 path_to_raw_image.raw new_filename_for_qcow2_image.qcow2

Create a new image from the cloud base image

I usually make a new folder where I can put artifacts related to my individual VMs as needed:

$ mkdir $HOME/VM/fedora_33
$ qemu-img create -f qcow2 -b $HOME/VM/images/base/fedora_cloud_base_33.qcow2 $HOME/VM/fedora_33/hdd.qcow2 8G

Deriving a new image from the base image is a neat trick because it allows us to create new VM images from the base image without having to download a new image again.

Create a cloud-init bootstrap image

The cloud image that we’re using requires cloud-init to perform some first-time setup. I’m trying to use only the bare minimum configuration to bring up a virtual machine here, but curious readers should head over to cloud-init’s documentation if they’d like to learn other ways that it can be used to customize their virtual machine.

$ cd $HOME/VM/fedora_33
$ touch meta-data
$ cat > user-data << EOF
#cloud-config

system_info:
   default_user:
     name: fedora

chpasswd:
  list: |
    fedora:password
  expire: False
 

resize_rootfs: True
ssh_authorized_keys:
   - ssh-rsa AAAAB3Nza...
EOF
$ genisoimage -output seedci.iso -volid cidata -joliet -rock user-data meta-data

Launch your virtual machine

Here is a super minimal QEMU command line for launching your new VM! You’ll want to supplement this with more arguments to fit your needs (i.e., -accel, -cpu, etc).

$ qemu-system-x86_64 -m 512 -drive file=hdd.qcow2 -cdrom seedci.iso

Log in with the user account that was described in the user-data file. In the case of this article, the username is fedora and the password is password. Obviously this configuration is mainly for cheap, disposable VMs that will be used for local development/testing. This configuration is unsuitable for a production environment.

You can remove the -cdrom argument from the command line for subsequent launches because cloud-init will only use it for the instance’s first boot to configure things.