Automating Virtual Machine Installation Using libvirt, virsh And cloud-init
By Sudheer S
Introduction
We have the host machine with the OS Ubuntu 22.04. On this PC or server, we will create two virtual machine guests:
myubuntu2204test01
having static IP of192.168.122.146
myubuntu2204test02
having static IP of192.168.122.147
The guest VMs will use the default network created by libvirt
. The gateway IP for the default network
is 192.168.122.1
.
We will achieve automation using libvirt
, qemu
and cloud-init
. To go through the article and exercise,
you should have a rudimentary understanding of Linux system administration and networking.
Prepare The Backing Image
We will execute the commands in the /var/lib/libvirt/images
directory.
cd /var/lib/libvirt/images
Download The Ubuntu Cloud Backing Image
sudo wget http://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img -O /var/lib/libvirt/images/jammy-server-cloudimg-amd64.img
A cloud backing image refers to a virtual machine image that has been specifically designed to work with cloud-init
.
cloud-init
is a tool used in cloud computing environments to customize virtual machines at boot time. It
allows for the automatic configuration of network settings, user accounts, and other system settings that
are required for the virtual machine to function properly. cloud-init
works by reading configuration
data from a variety of sources, including user data, metadata, and vendor data.
A cloud backing image is an image that has been created with cloud-init
support built-in. It typically
includes a cloud-init
configuration file that specifies the initial configuration of the virtual machine when it is
launched. This can include things like network settings, SSH keys, and other system configuration settings.
By using a cloud backing image, cloud users can easily launch virtual machines that are pre-configured for their specific needs. This can save time and effort compared to manually configuring each virtual machine individually. Many cloud providers offer a selection of pre-configured cloud backing images for common operating systems and applications, which can be easily launched from their cloud dashboard or API.
Directory For New VMs
mkdir myubuntu2204test01
mkdir myubuntu2204test02
Prepare The VM myubuntu2204test01
Change Directory To myubuntu2204test01
cd myubuntu2204test01
Create the base image for the VM myubuntu2204test01
using the cloud backing Image in the
directory myubuntu2204test01
# within the directory myubuntu2204test01
qemu-img create -b ../jammy-server-cloudimg-amd64.img -f qcow2 -F qcow2 myubuntu2204test01-base.img 10G
Use the qemu-img
tool to create a new qcow2
image file named myubuntu2204test01-base.img
with a
size of 10 GB
, based on the image file ../jammy-server-cloudimg-amd64.img
.
Here’s a breakdown of the options used in this command:
- create: This option tells
qemu-img
to create a new disk image. - -b jammy-server-cloudimg-amd64.img: This specifies the backing file for the new image. The new image will be created as a snapshot of the backing file, which means that the backing file will not be modified when changes are made to the new image.
- -f qcow2: This specifies the format of the new image file. In this case, it’s set to
qcow2
, which is a popular disk image format used by QEMU and other virtualization platforms. - -F qcow2: This specifies the format of the backing file. In this case, it’s also
qcow2
. - myubuntu2204test01-base.img: This is the name of the new image file that will be created.
- 10G: This sets the size of the new image file to 10 GB.
Create the file meta-data.yaml
in the directory myubuntu2204test01
:
instance-id: myubuntu2204test01
local-hostname: myubuntu2204test01.example.com
Create the file user-data.yaml
in the directory myubuntu2204test01
:
#cloud-config
users:
- name: sudheer
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMTU9mySQ3UQU3zv69mt1z/2eXwTPzUNamKQi0ZCTG3m sudheer
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
groups: sudo
shell: /bin/bash
Create the file network-config.yaml
in the directory myubuntu2204test01
network:
version: 2
ethernets:
enp1s0:
dhcp4: no
addresses: [192.168.122.146/24]
nameservers:
addresses: [192.168.122.1]
routes:
- to: 0.0.0.0/0
via: 192.168.122.1
Create The VM myubuntu2204test01
# within the directory myubuntu2204test01
virt-install \
--name=myubuntu2204test01 \
--ram=512 \
--vcpus=1 \
--import \
--disk path=myubuntu2204test01-base.img,format=qcow2 \
--os-variant=ubuntu22.04 \
--network bridge=virbr0,model=virtio \
--graphics vnc,listen=0.0.0.0 --noautoconsole \
--cloud-init user-data=user-data.yaml,meta-data=meta-data.yaml,network-config=network-config.yaml
SSH Onto The Guest VM
ssh youruser@192.168.122.146
Repeat The Steps For myubuntu2204test02
Change the directory to myubuntu2204test02
cd /var/lib/libvirt/images/myubuntu2204test02
The following commands will be executed with the directory myubuntu2204test02
.
Create the base image for the VM myubuntu2204test02
using the cloud backing Image in the
directory myubuntu2204test02
# within the directory myubuntu2204test02
qemu-img create -b ../jammy-server-cloudimg-amd64.img -f qcow2 -F qcow2 myubuntu2204test02-base.img 10G
The user-data remains same as myubuntu2204test01
. Therefore, copy it from myubuntu2204test01
.
cp ../myubuntu2204test01/user-data.yaml .
Create the meta-data.yaml
file with these contents:
instance-id: myubuntu2204test02
local-hostname: myubuntu2204test02.example.com
Create the network-data.yaml
file with these contents:
network:
version: 2
ethernets:
enp1s0:
dhcp4: no
addresses: [192.168.122.147/24]
nameservers:
addresses: [192.168.122.1]
routes:
- to: 0.0.0.0/0
via: 192.168.122.1
Create The VM myubuntu2204test02
# within the directory myubuntu2204test02
virt-install \
--name=myubuntu2204test02 \
--ram=512 \
--vcpus=1 \
--import \
--disk path=myubuntu2204test02-base.img,format=qcow2 \
--os-variant=ubuntu22.04 \
--network bridge=virbr0,model=virtio \
--graphics vnc,listen=0.0.0.0 --noautoconsole \
--cloud-init user-data=user-data.yaml,meta-data=meta-data.yaml,network-config=network-config.yaml
SSH Onto The Guest VM
ssh youruser@192.168.122.147
cloud-init
Debugging Tips
Password For User
For testing purposes, you might need to logon using the Virtual Machine Manager graphical user interface when
networking is not working. For such situations, you might want to set the user’s password. Edit the user object
in user-data
to enable password, set these fields:
passwd: "$6$6.hH1RjmaGDFqA3L$BpCtJAhgJ5xyEQJpmX36Nf3lJkphwoJz9SiKIy3b840qnm7jagX9ScBHB1sjGrM/w7iKoIIUCzUlX5mqfmAnc."
lock-passwd: false
chpasswd: { expire: False }
ssh_pwauth: True
You can generate the password hash using the command:
echo password | mkpasswd -m sha-512 -s
DHCP Lease
If cloud-init
did not pick up your static networking settings, it is possible that it defaulted to DHCP. To find
the virtual machine’s IP address lookup the DHCP lease:
virsh net-dhcp-leases default
The virsh net-dhcp-leases
command is used to display the DHCP leases for a particular libvirt
network. In this
case, the default
network is specified as the network to display the DHCP leases for. The output of the command
will show the MAC address of each device that has been assigned an IP address by the DHCP server, along with the IP
address and lease expiration time.
cloud-init
logs
You can inspect cloud-init
logs in several ways, depending on the specific cloud environment you are using. Here are
some general methods:
- Console logs: In many cloud environments, console logs are available for virtual machines. You can access these
logs by logging onto the console or web interface of the virtual machine and checking the system logs for
cloud-init
messages. The logs are usually located in the/var/log/cloud-init.log
file. - cloud-init logs via SSH or Virtual Machine Manager: If you have SSH access to the virtual machine, you can view
the
cloud-init
logs directly by running the following command:
sudo tail /var/log/cloud-init-output.log
- cloud-init logs via API: If you are using a cloud provider’s API, you may be able to retrieve the
cloud-init
logs programmatically. For example, in AWS, you can use theGetConsoleOutput
API call to retrieve the console logs of a virtual machine, which may includecloud-init
logs. - cloud-init status: You can check the status of cloud-init to see if it has completed successfully or encountered any errors. The status can be checked by running the following command:
sudo cloud-init status
These methods should provide you with the information you need to diagnose and troubleshoot cloud-init
issues.
Further Steps
- Create an Ansible playbook to automate the process. Declaratively define the VM guests in a YAML file and have a reproducible way to create the virtual machines with static IP configuration.
- Extend the Ansible playbook to a role to support multiple Linux distributions such as Ubuntu, Debian, CentOS, Fedora.