On Linux, Kernel-based Virtual Machine (KVM) together with the libvirt virtualization API provides an easy way to run virtual machines (VMs).

You can download KVM images for Oracle Linux 7 and 8 from the Oracle Linux VM Templates page.

These images are in compressed QCOW2 format. They are small in size (about 600MB) with a minimal install base environment which allows you to only install what you really need.

There is one caveat though: like most cloud images, there is no pre-created user and the root account is locked; you will not be able to login…

One option is to use virt-customize to set a password for the super user, but a better way to initialize your VM is to use cloud-init.

This blog post will show you how to easily create virtual machines using Oracle Linux templates for KVM using simple cloud-init configuration files.

Requirements

Obviously you will need a host system with KVM/libvirt installed and configured. The host hardware must support the virtualization extensions (Intel VT or AMD-V); if your host is a virtual machine, nested virtualization must be enabled.

Oracle Linux 8 host

On Oracle Linux 8, install the virt module as well as the virt-install package:

sudo dnf module install -y virt
sudo dnf install -y virt-install
sudo systemctl enable --now libvirtd

Oracle Linux 7 host

Oracle Linux 7 is similar, the ol7_kvm_utils repository must be enabled, which provides more recent version for the packages.

sudo yum-config-manager --enable ol7_kvm_utils
sudo yum group install -y "Virtualization Host"
sudo yum  install -y virt-install
sudo systemctl enable --now libvirtd

Oracle Cloud Infrastructure (OCI)

The above instructions are valid for OCI instances, however for VM shapes, only Intel based shapes (VM.Standard2.X / VM.DenseIO2.X) support nested virtualization.

Alternatively the Oracle Linux KVM Image and Oracle Linux KVM Image (Autonomous Linux) images have everything installed!

Creating a VM

Download the Oracle Linux VM templates

E.g:

cd  /var/lib/libvirt/images/
sudo curl -O https://yum.oracle.com/templates/OracleLinux/OL8/u4/x86_64/OL8U4_x86_64-olvm-b85.qcow2

Cloud-init

The cloud-init package is installed in the Oracle Linux KVM templates and is responsible for the initial setup of the image using instance data.

Instance data is mainly composed of:

  • cloud provided metadata: metadata for the cloud environment
  • user data: configuration provided by the user

For the KVM environment the NoCloud datasource is used to provide the metadata and user data. We need to create an ISO image with two YAML files: meta-data and user-data

The meta-data file contains an instance id and the hostname of of the VM. E.g.:

instance-id: iid-local01
local-hostname: vm-01

The instance id can be anything, and is used to determine if it is the first boot. If you change the instance id, cloud-init will attempt to re-provision the image.

Although the hostname could alternatively be specified in the user data, it is better to have it in the metadata as it is set earlier in the boot process.

The user-data file allows you to customize the instance to great extends – see Cloud config examples. The file must start with the #cloud-config stanza.

As minimal configuration we need to be able to login to new instance. By default cloud-init will create a user named cloud-user.

We can set a password for this user:

#cloud-config
password: <enter your password here>
chpasswd: { expire: False }
ssh_pwauth: True

or preferably add an ssh public key:

#cloud-config
ssh_authorized_keys:
  - ssh-rsa 
    user@host
  

We can specify a different user name for the default user:

#cloud-config
system_info:
  default_user:
    name: opc

Last step: generate an ISO image with these 2 files:

genisoimage -output /var/lib/libvirt/images/vm-01.iso -volid cidata -joliet -rock user-data meta-data

Creating the instance

Copy the the template and launch virt-install passing both disks as parameter: the actual image and the cloud-init iso.

sudo cp /var/lib/libvirt/images/OL8U4_x86_64-olvm-b85.qcow2 /var/lib/libvirt/images/vm-01.qcow2
sudo virt-install --name vm-01 \
    --memory 2048 \
    --vcpus 2 \
    --disk /var/lib/libvirt/images/vm-01.qcow2,device=disk,bus=virtio \
    --disk /var/lib/libvirt/images/vm-01.iso,device=cdrom \
    --os-type linux --os-variant ol8.4 \
    --virt-type kvm --graphics none \
    --network network=default,model=virtio \
    --noautoconsole \
    --import

You can get the list of available os variants with:

osinfo-query os vendor="Oracle America"

Depending of the version of the virt-install package you might not find an exact match for the guest os; in that case, simply pick the closest one.

Retrieving IP address

The IP address can easily be retrieved with virsh. Look for the hostname set in the meta-data file:

$ sudo virsh net-dhcp-leases --network default
 Expiry Time           MAC address         Protocol   IP address          Hostname   Client ID or DUID
-----------------------------------------------------------------------------------------------------------
 2021-08-03 14:14:22   52:54:00:19:36:6f   ipv4       192.168.122.40/24   vm-01      01:52:54:00:19:36:6f

Alternatively you can filter based on the MAC address:

$ sudo virsh domiflist vm-01
 Interface   Type      Source    Model    MAC
-------------------------------------------------------------
 vnet0       network   default   virtio   52:54:00:19:36:6f

$ sudo virsh net-dhcp-leases --network default --mac 52:54:00:19:36:6f
 Expiry Time           MAC address         Protocol   IP address          Hostname   Client ID or DUID
-----------------------------------------------------------------------------------------------------------
 2021-08-03 14:14:22   52:54:00:19:36:6f   ipv4       192.168.122.40/24   vm-01      01:52:54:00:19:36:6f

Console access

Should you need console access, you can use --graphics vnc instead of --graphics none when you create the instance. The VNC port can be retrieved with virsh:

$ sudo virsh vncdisplay vm-01
127.0.0.1:0

You can then connect with your preferred VNC client, using the --via option if your KVM host is remote. E.g.:

vncviewer -via opc@<kvm host> 127.0.0.1:0

Image size considerations

To minimize the download size, the KVM templates are in compressed QCOW2 format. This format is read-only, any change to the image will be written in non-compressed format.

If performance is an important factor you may consider using different formats for your VM image. Hereunder is a summary, from small to big in term of size.

1. Copy On Write (COW) linked file

For a quick and simple provisioning we can create a COW file using the template as backing file.

# qemu-img create -f qcow2 -F qcow2 -b OL8U4_x86_64-olvm-b85.qcow2 vm-01.qcow2
Formatting 'vm-01.qcow2', fmt=qcow2 size=39728447488 backing_file=OL8U4_x86_64-olvm-b85.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16
[root@kvm-al images]# ls -lh
-rw-r--r--. 1 qemu qemu 617M Aug  2 16:23 OL8U4_x86_64-olvm-b85.qcow2
-rw-r--r--. 1 root root 193K Aug  3 14:10 vm-01.qcow2

The vm-01.qcow2 file is (almost) empty at start and will hold the changes for the VM.

2. Compressed QCOW2 format

Simple copy of the template as illustrated above. Data will be uncompressed when accessed.

3. Uncompressed QCOW2 format

This is a good tradeoff: no decompression is needed and the QCOW2 format is quite efficient:

# qemu-img convert -O qcow2 OL8U4_x86_64-olvm-b85.qcow2 vm-01.qcow2
# ls -lh
-rw-r--r-- 1 qemu qemu 617M Aug  2 10:00 OL8U4_x86_64-olvm-b85.qcow2
-rw-r--r-- 1 root root 1.3G Aug  3 14:29 vm-01.qcow2

4. Raw disk image

This is the actual disk image, it will be as big as disk itself (37GB for the Oracle Linux Templates)

# qemu-img convert -O raw OL8U4_x86_64-olvm-b85.qcow2 vm-01.img
# ls -lh
-rw-r--r-- 1 qemu qemu 617M Aug  2 10:00 OL8U4_x86_64-olvm-b85.qcow2
-rw-r--r-- 1 root root  37G Aug  3 14:31 vm-01.img

Note that if your filesystem supports sparse files it won’t allocate all the space:

# ls -1sh
617M OL8U4_x86_64-olvm-b85.qcow2
1.3G vm-01.img

Putting everything together

The above steps can be easily scripted – the following sample script can be used to quickly spawn VMs:

#!/usr/bin/bash

# Some constants
TEMPLATE=OL8U4_x86_64-olvm-b85.qcow2
VCPUS=2
MEMORY=2048
PUB_KEY="${HOME}/.ssh/id_rsa.pub"

[[ $# -ne 1 ]] && echo "Usage: $0 VM_NAME" && exit 1
DOMAIN="$1"

echo "$0: Building cloud-init image"
CI_DIR=$(mktemp -d)

cat > "${CI_DIR}/meta-data" <<EOF
instance-id: iid-local01
local-hostname: ${DOMAIN}
EOF

cat > "${CI_DIR}/user-data" <<EOF
#cloud-config

system_info:
  default_user:
    name: opc

ssh_authorized_keys:
  - $(cat ${PUB_KEY})
EOF

(
  cd "${CI_DIR}"
  sudo genisoimage -output "/var/lib/libvirt/images/${DOMAIN}.iso" -volid cidata -joliet -rock user-data meta-data
)
rm -rf "${CI_DIR}"

echo "$0: copying disk image"
sudo qemu-img create -f qcow2 -F qcow2 -b "/var/lib/libvirt/images/${TEMPLATE}" "/var/lib/libvirt/images/${DOMAIN}.qcow2"

echo "$0: creating VM"
sudo virt-install --name "${DOMAIN}" \
  --memory ${MEMORY} \
  --vcpus ${VCPUS} \
  --disk "/var/lib/libvirt/images/${DOMAIN}.qcow2,device=disk,bus=virtio" \
  --disk "/var/lib/libvirt/images/${DOMAIN}.iso,device=cdrom" \
  --os-type linux --os-variant ol8.4 \
  --virt-type kvm --graphics none \
  --network network=default,model=virtio \
  --noautoconsole \
  --import

echo "$0: waiting for IP"
while ! sudo virsh net-dhcp-leases --network default | grep "${DOMAIN}"; do
  sleep 5
done

echo "$0: Your vm is ready to use!"

Sample session:

$ ./create-vm.sh vm-01
./create-vm.sh: Building cloud-init image
I: -input-charset not specified, using utf-8 (detected in locale settings)
Total translation table size: 0
Total rockridge attributes bytes: 331
Total directory bytes: 0
Path table size(bytes): 10
Max brk space used 0
183 extents written (0 MB)
./create-vm.sh: copying disk image
Formatting '/var/lib/libvirt/images/vm-01.qcow2', fmt=qcow2 size=39728447488 backing_file=/var/lib/libvirt/images/OL8U4_x86_64-olvm-b85.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16
./create-vm.sh: creating VM

Starting install...
Domain creation completed.
./create-vm.sh: waiting for IP
 2021-08-03 17:01:50   52:54:00:f7:af:10   ipv4       192.168.122.240/24   vm-01      01:52:54:00:f7:af:10
./create-vm.sh: Your vm is ready to use!
$ ssh opc@192.168.122.240
The authenticity of host '192.168.122.240 (192.168.122.240)' can't be established.
ECDSA key fingerprint is SHA256:RB0p2TbgZRDAprOw+1JQbok0Hb3yUwsElW2BXSgn8cE.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.122.240' (ECDSA) to the list of known hosts.
[opc@vm-01 ~]$ uname -a
Linux vm-01 5.4.17-2102.201.3.el8uek.x86_64 #2 SMP Fri Apr 23 09:05:57 PDT 2021 x86_64 x86_64 x86_64 GNU/Linux
[opc@vm-01 ~]$ logout
Connection to 192.168.122.240 closed.
$ sudo virsh destroy vm-01
Domain vm-01 destroyed
$ sudo virsh undefine vm-01
Domain vm-01 has been undefined

Summary

In this blog post, I explained how you can easily create Oracle Linux KVM virtual machines using the latest Oracle Linux VM Templates.