Building a custom OpenStack Image on OPCP
Objective
This guide focuses on building customized disk images for both OpenStack Ironic (bare metal) and Nova (compute) using diskimage-builder.
It's a good starting point to understand how diskimage-builder (DiB) works with a practical example. We will build a custom Debian 13 image from the upstream image and customize it with Ansible.
At the end, we will generate a debian13.qcow2 whole disk image (with the kernel and initramfs), ready to be imported into OpenStack Glance.
OpenStack Ironic/Nova expectations
Image Format
- qcow2 (recommended): Compressed and efficient for Glance storage
- raw: Uncompressed disk image
The image should be a whole disk image that includes:
- Boot sector/EFI system partition
- Operating system partition(s)
- Kernel and initramfs embedded in the disk
Partitioning and Boot Requirements
For BIOS Boot:
- GPT or MBR partition table
- BIOS boot partition (1-2MB, type
ef02for GPT) - Root partition with bootloader installed (GRUB2)
- Bootloader must be installed to MBR/boot sector
For EFI Boot:
- GPT partition table required
- EFI System Partition (ESP): 512MB, FAT32, mounted at
/boot/efi - Root partition with GRUB2 EFI bootloader
- EFI boot entries properly configured
For Ironic Bare Metal (Recommended Configuration):
Standard Partition Layout (Simple):
Requirements
System Requirements:
- Root permissions
-
At least 10GB available
-
Some packages:
Install diskimage-builder (DiB) in a virtualenv:
Understanding diskimage-builder Elements
Elements are modular components that customize your image.
| Directory | Purpose |
|---|---|
environment.d/ | Environment variables |
extra-data.d/ | Files added before installation |
pre-install.d/ | Pre-installation scripts |
post-install.d/ | Post-installation scripts |
finalise.d/ | Final customization |
cleanup.d/ | Cleanup tasks |
package-installs.yaml | Package declarations |
block-device-default.yaml | Disk partitioning |
Scripts in these directories execute in numerical/alphabetical order.
Creating a Customization Element with Ansible
Create an element that uses Ansible for customization:
Directory Structure:
Post-install Scripts:
elements/os-custom/post-install.d/00-install-ansible:
elements/os-custom/post-install.d/01-apply-ansible:
elements/os-custom/post-install.d/02-remove-ansible:
Make scripts executable:
Ansible Configuration:
elements/os-custom/extra-data.d/ansible/main.yml:
elements/os-custom/extra-data.d/ansible/inventory/hosts:
In this example, we are using cloud-init with the netplan renderer to configure systemd-networkd. This is a working example of customizing network configuration; feel free to adapt it to your needs.
Note: When Ironic configures bare metal on first boot, it will propagate a network_metadata manifest (configdrive) that can be interpreted by cloud-init to automatically configure the network (such as static IP, LACP, etc.).
elements/os-custom/extra-data.d/ansible/roles/customize/tasks/main.yml:
elements/os-custom/extra-data.d/ansible/roles/customize/files/50-netplan.cfg:
Additional Packages:
elements/os-custom/package-installs.yaml:
Element Dependencies:
elements/os-custom/element-deps:
debian
Building the Image
Create an environment file debian13.env:
Build the image:
You should get a file debian13.qcow2.
If you want environment and packages SBOM files:
Testing the Image
A quick way to test the generated image is using qemu to spawn a virtual machine using the qcow2 image and a VNC client to connect to the monitor. We can test both EFI and BIOS boot.
BIOS Boot
Use any VNC client to connect :5200
EFI
Use any VNC client to connect :5200
Upload Image to OpenStack:
Done! You can now create a baremetal instance or compute instance with the new created image.