Packer is a tool that enables us to create identical machine images for multiple platforms from a single source template.
We are going to build a Rocky 8 image using Packer.
Why Packer?
It’s fast. Deploying KVM guests with Packer-built images takes less time than provisioning servers using PXE boot.
We still use PXE boot to provision physical hosts (KVM hypervisors), but virtual guests are deployed using Packer images.
Pre-requisites
You need to have a server with KVM installed on it. We use our Kubernetes homelab in this article.
Install Packer
Install Packer on an existing RHEL-based KVM hypervisor:
$ sudo yum install -y yum-utils $ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo $ sudo yum install -y packer
Build Rocky 8 Image with Qemu Packer Builder
We will use the Qemu Packer builder which is able to create KVM virtual machine images.
First, we need to define a builder configuration for Rocky 8. Builders are responsible for creating machines and generating images for various platforms.
Create a file rocky8.json
with the following content:
{ "variables": { "cpu": "2", "ram": "2048", "name": "rocky", "disk_size": "32768", "version": "8", "iso_checksum_type": "sha256", "iso_urls": "http://10.11.1.20/pub/isos/Rocky-8.5-x86_64-boot.iso", "iso_checksum": "5a0dc65d1308e47b51a49e23f1030b5ee0f0ece3702483a8a6554382e893333c", "headless": "true", "config_file": "rocky8-packer-ks.cfg", "ssh_username": "root", "ssh_password": "packer" }, "builders": [ { "name": "{{user `name`}}{{user `version`}}", "type": "qemu", "format": "qcow2", "accelerator": "kvm", "qemu_binary": "/usr/libexec/qemu-kvm", "net_device": "virtio-net", "disk_interface": "virtio", "disk_cache": "none", "qemuargs": [ [ "-m", "{{user `ram`}}M" ], [ "-smp", "{{user `cpu`}}" ] ], "ssh_wait_timeout": "30m", "http_directory": ".", "ssh_username": "{{user `ssh_username`}}", "ssh_password": "{{user `ssh_password`}}", "iso_urls": "{{user `iso_urls`}}", "iso_checksum": "{{user `iso_checksum`}}", "boot_wait": "40s", "boot_command": [ " net.ifnames=0 biosdevname=0 inst.text inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/http/{{user `config_file`}}" ], "disk_size": "{{user `disk_size`}}", "disk_discard": "unmap", "disk_compression": true, "headless": "{{user `headless`}}", "shutdown_command": "sudo /usr/sbin/shutdown -h now", "output_directory": "artifacts/qemu/{{user `name`}}{{user `version`}}" } ] }
We do not use any post-processors in this article, but we do on GitHub.
Packer can start an HTTP server to serve Kickstart files at boot time. That’s something that we want to take advantage of. Create an http
directory:
$ mkdir http
Create a Rocky 8 Kickstart file ./http/rocky8-packer-ks.cfg
with the following content:
# Use network installation url --url="ftp://10.11.1.20/pub/pxe/Rocky8/BaseOS" repo --name="AppStream" --baseurl="ftp://10.11.1.20/pub/pxe/Rocky8/AppStream" # Disable Initial Setup on first boot firstboot --disable # Use text mode install text # Keyboard layouts keyboard --vckeymap=gb --xlayouts='gb' # System language lang en_GB.UTF-8 # SELinux configuration selinux --enforcing # Firewall configuration firewall --enabled --ssh # Do not configure the X Window System skipx # Network information network --bootproto=dhcp --device=eth0 --nameserver=10.11.1.2,10.11.1.3 --noipv6 --activate network --hostname=rocky8.localdomain # System authorisation information auth --useshadow --passalgo=sha512 # Root password rootpw packer # System timezone timezone Europe/London --isUtc ignoredisk --only-use=vda # System bootloader configuration bootloader --location=mbr --timeout=1 --boot-drive=vda # Clear the Master Boot Record zerombr # Partition clearing information clearpart --all --initlabel # Reboot after installation reboot # Disk partitioning information part /boot --fstype="xfs" --ondisk=vda --size=1024 --label=boot --asprimary part pv.01 --fstype="lvmpv" --ondisk=vda --size=31743 volgroup vg_os pv.01 logvol /tmp --fstype="xfs" --size=1024 --label="lv_tmp" --name=lv_tmp --vgname=vg_os logvol / --fstype="xfs" --size=30716 --label="lv_root" --name=lv_root --vgname=vg_os %packages # dnf group info minimal-environment @^minimal-environment sudo # Exclude unnecessary firmwares -iwl*firmware %end %addon com_redhat_kdump --disable --reserve-mb='auto' %end %post sed -i 's/^.*requiretty/#Defaults requiretty/' /etc/sudoers sed -i 's/rhgb //' /etc/default/grub # SSHD PermitRootLogin and enable the service sed -i "s/#PermitRootLogin yes/PermitRootLogin yes/g" /etc/ssh/sshd_config /usr/bin/systemctl enable sshd # Update all packages /usr/bin/yum -y update %end %anaconda pwpolicy root --minlen=10 --minquality=1 --notstrict --nochanges --notempty pwpolicy user --minlen=10 --minquality=1 --notstrict --nochanges --emptyok pwpolicy luks --minlen=10 --minquality=1 --notstrict --nochanges --notempty %end
Build a Rocky 8 image:
$ PACKER_LOG=1 packer build ./rocky8.json
Monitoring Build Process
Packer uses VNC, therefore we can check its window with a VNC client, e.g.:
$ vncviewer -shared 127.0.0.1:5934
Deploy KVM Guests from Packer Images
Copy the virtual machine image that was created by Packer:
$ sudo cp --sparse=always ./artifacts/qemu/rocky8/packer-rocky8 /var/lib/libvirt/images/rocky8.qcow2
Provision a new Rocky 8 KVM guest:
$ sudo virt-install \ --name rocky8 \ --network bridge=br0,model=virtio,mac=C0:FF:EE:D0:5E:40 \ --disk path=/var/lib/libvirt/images/rocky8.qcow2,size=32 \ --ram 2048 \ --vcpus 2 \ --os-type linux \ --os-variant centos7.0 \ --sound none \ --rng /dev/urandom \ --virt-type kvm \ --import \ --wait 0
References
https://www.packer.io/plugins/builders/qemu
Hello, and thank you for your blogs
—
Could you assist me with a problem I encountered while attempting to build a qemu image for Rocky Linux 9.1 using packer? Specifically, after completing the build process, packer was unable to access the VM and run the provisioning script via SSH. Upon logging into the VM, I discovered multiple failed SSH login attempts. Have you experienced this issue before, and if so, could you provide any guidance or solutions? Thank you in advance.
Rocky Linux changed the default user in 9.1. See if this is applicable to your case:
https://forums.rockylinux.org/t/rocky-9-cloud-init-update-changes-default-user/8770