Skip to content

Instantly share code, notes, and snippets.

@dmccuk
Last active March 28, 2025 12:30
Show Gist options
  • Save dmccuk/b1bd2de49442e8b54b2530f69bfa8aac to your computer and use it in GitHub Desktop.
Save dmccuk/b1bd2de49442e8b54b2530f69bfa8aac to your computer and use it in GitHub Desktop.
Packer_stuff.md
Building a RHEL 9 ISO image using Packer 1.12 on RHEL 8 requires multiple steps. This guide will take you from installing dependencies to creating the ISO with Packer.
---
Step 1: Install Required Packages
Ensure your RHEL 8 system has the necessary tools installed.
# Enable required repositories
sudo subscription-manager repos --enable=rhel-8-server-rpms \
--enable=rhel-8-server-extras-rpms \
--enable=rhel-8-server-optional-rpms
# Update system and install dependencies
sudo dnf update -y
sudo dnf install -y wget curl unzip git qemu-kvm virt-install libvirt libvirt-devel libvirt-daemon-kvm virt-manager jq
# Start and enable libvirt
sudo systemctl start libvirtd
sudo systemctl enable libvirtd
---
Step 2: Install Packer 1.12
HashiCorp distributes Packer as a standalone binary.
# Download Packer 1.12
wget https://releases.hashicorp.com/packer/1.12.0/packer_1.12.0_linux_amd64.zip
# Unzip and move to a system-wide path
unzip packer_1.12.0_linux_amd64.zip
sudo mv packer /usr/local/bin/
# Verify installation
packer version
---
Step 3: Download the RHEL 9 ISO
You'll need an official RHEL 9 ISO from Red Hat.
# Create an ISO storage directory
mkdir -p ~/iso
# Download the RHEL 9 ISO (replace with the correct URL)
wget -O ~/iso/rhel-9.iso "https://access.redhat.com/downloads/content/479/ver=/rhel---9/9.0/x86_64/product-software"
If you don’t have a Red Hat subscription, you can use the developer edition from Red Hat Developer.
---
Step 4: Configure Packer Template
Create a new directory for your Packer build.
mkdir -p ~/packer-rhel9
cd ~/packer-rhel9
Create a Packer JSON file: rhel9.json
{
"variables": {
"iso_url": "~/iso/rhel-9.iso",
"iso_checksum": "sha256:PUT_THE_CHECKSUM_HERE"
},
"builders": [
{
"type": "vmware-iso",
"iso_url": "{{user `iso_url`}}",
"iso_checksum": "{{user `iso_checksum`}}",
"iso_checksum_type": "sha256",
"vm_name": "rhel9-vmware",
"disk_size": 20000,
"format": "vmx",
"cpus": 2,
"memory": 4096,
"headless": false,
"http_directory": "http",
"boot_wait": "5s",
"boot_command": [
"<esc><wait>",
" linux inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg<enter>"
],
"ssh_username": "root",
"ssh_password": "password",
"ssh_wait_timeout": "30m",
"shutdown_command": "shutdown -h now"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"dnf update -y",
"dnf install -y open-vm-tools"
]
}
]
}
Replace "sha256:PUT_THE_CHECKSUM_HERE" with the actual SHA256 checksum of your ISO. You can get it using:
sha256sum ~/iso/rhel-9.iso
---
Step 5: Create a Kickstart File
Packer needs a kickstart file to automate installation.
Create the directory for HTTP serving:
mkdir -p ~/packer-rhel9/http
Create ks.cfg inside http/:
cat <<EOF > ~/packer-rhel9/http/ks.cfg
#version=RHEL9
text
install
cdrom
keyboard us
lang en_US
network --bootproto=dhcp
rootpw --plaintext password
firewall --disabled
services --disabled=chronyd
timezone America/New_York --utc
bootloader --location=mbr
autopart --type=lvm
clearpart --all --initlabel
zerombr
reboot
EOF
---
Step 6: Build the Image
Run Packer build command:
cd ~/packer-rhel9
packer build rhel9.json
This will:
1. Boot a QEMU VM.
2. Install RHEL 9 using Kickstart.
3. Run post-installation scripts.
4. Output a QCOW2 disk image of RHEL 9.
---
Step 7: Convert to an ISO (Optional)
If you need an installable ISO, you’ll have to package the modified filesystem.
Extract the Image
mkdir ~/rhel9-custom
virt-copy-out -a output-qemu/rhel9.qcow2 / ~/rhel9-custom/
Create a Bootable ISO
mkisofs -o ~/rhel9-custom.iso \
-b isolinux.bin -c boot.cat \
-no-emul-boot -boot-load-size 4 -boot-info-table \
~/rhel9-custom
---
Step 8: Verify and Deploy
Run the image in a virtual machine:
virt-install --name rhel9-test \
--memory 4096 --vcpus 2 \
--disk path=~/packer-rhel9/output-qemu/rhel9.qcow2,format=qcow2 \
--os-variant rhel9.0 \
--network bridge=virbr0 \
--graphics vnc
---
Step 9: Clean Up
After testing, clean unnecessary files:
rm -rf ~/packer-rhel9/output-qemu
rm ~/iso/rhel-9.iso
---
Step 4: Create the Kickstart File (Partition Layout)
The Kickstart (ks.cfg) file defines the partitioning scheme.
mkdir -p ~/packer-rhel9/http
cat <<EOF > ~/packer-rhel9/http/ks.cfg
#version=RHEL9
text
install
cdrom
keyboard us
lang en_US
network --bootproto=dhcp
rootpw --plaintext password
firewall --disabled
services --disabled=chronyd
timezone America/New_York --utc
bootloader --location=mbr
zerombr
clearpart --all --initlabel
# Partition layout
part /boot --fstype=xfs --size=1024
part / --fstype=xfs --size=8000
part /home --fstype=xfs --size=2048
part /opt --fstype=xfs --size=2048
part /var/log --fstype=xfs --size=2048
part swap --size=2048
reboot
EOF
---
Conclusion
You’ve successfully created a RHEL 9 image using Packer 1.12 on RHEL 8. Now, you can use the QCOW2 image in virtualization platforms or convert it into an installable ISO.
Let me know if you need refinements!
---
# Define variables
variable "iso_url" {
default = "~/iso/rhel-9.iso"
}
variable "iso_checksum" {
default = "sha256:PUT_THE_CHECKSUM_HERE"
}
# Define the builder
source "vmware-iso" "rhel9" {
iso_url = var.iso_url
iso_checksum = var.iso_checksum
iso_checksum_type = "sha256"
vm_name = "rhel9-vmware"
format = "vmx"
disk_size = 20000
cpus = 2
memory = 4096
headless = false
http_directory = "http"
boot_wait = "5s"
boot_command = [
"<esc><wait>",
" linux inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg<enter>"
]
shutdown_command = "shutdown -h now"
communicator = "ssh"
ssh_username = "root"
ssh_password = "password"
ssh_timeout = "30m"
}
# Define the provisioner
build {
sources = ["source.vmware-iso.rhel9"]
provisioner "shell" {
inline = [
"dnf update -y",
"dnf install -y open-vm-tools"
]
}
}
---
```ansible -i inventory all -m shell -a 'if grep -q "release 6" /etc/redhat-release; then grep SINGLE /etc/sysconfig/init | grep -q "sulogin" && echo "RHEL6: Secure" || echo "RHEL6: Insecure"; elif grep -q "release 7" /etc/redhat-release; then grep ExecStart /usr/lib/systemd/system/rescue.service | grep -q "sulogin" && echo "RHEL7: Secure" || echo "RHEL7: Insecure"; elif grep -q "release 8" /etc/redhat-release; then grep ExecStart /usr/lib/systemd/system/rescue.service | grep -q "sulogin" && echo "RHEL8: Secure" || echo "RHEL8: Insecure"; else echo "Unknown OS"; fi' -u your_user --become```
---
ansible -i inventory all -m shell -a 'if [[ -f /etc/os-release ]] && grep -qE "Oracle Linux Server (7|8|9)" /etc/os-release; then echo -n "$(hostname): "; rpm -q vasclnt || echo "vasclnt not installed"; fi' -u your_user --become
ansible -i inventory all -m shell -a 'echo -n "$(hostname): "; os_ver=$(cat /etc/os-release | grep -E "PRETTY_NAME" | cut -d= -f2 | tr -d \" ); echo -n "$os_ver, "; rpm -q vasclnt || echo "vasclnt not installed"' -u your_user --become
----
ansible all -i hosts.txt -m shell -a "\
echo CPUs:\$(nproc) \
Memory_MB: \$(free -m | awk '/^Mem:/ {print \$2}') \
Load: \$(uptime | awk -F'load average: ' '{print \$2}') \
Disk_Usage: \$(df -h / | awk 'NR==2 {print \$5}') \
Mem_Used: \$(free -m | awk '/^Mem:/ {print \$3}') \
Top_Proc: \$(ps -eo comm,%cpu --sort=-%cpu \
Uptime_days: \$(uptime -p | sed 's/up //') \
Top_Proc: \$(ps -eo comm,%cpu --sort=-%cpu | head -n 2 | tail -n 1) \
Zombie_Procs: \$(ps aux | awk '\$8 ~ /Z/ { count++ } END { print count+0 }')" -k -o
---
ansible all -i hosts.txt -m shell -a "\
echo CPUs: \$(nproc) \
Memory_MB: \$(free -m | awk '/^Mem:/ {print \$2}') \
Mem_Used: \$(free -m | awk '/^Mem:/ {print \$3}') \
Swap_Used_MB: \$(free -m | awk '/^Swap:/ {print \$3}') \
Load: \$(uptime | awk -F'load average: ' '{print \$2}') \
Disk_Usage: \$(df -h / | awk 'NR==2 {print \$5}') \
Uptime: \$(uptime -p | sed 's/up //') \
Top_Proc: \$(ps -eo comm,%cpu --sort=-%cpu | head -n 2 | tail -n 1) \
Zombie_Procs: \$(ps aux | awk '\$8 ~ /Z/ { count++ } END { print count+0 }')" -o
---
ansible all -i hosts.txt -m shell -a "\
echo CPUs: \$(nproc) \
Memory_MB: \$(free -m | awk '/^Mem:/ {print \$2}') \
Mem_Used: \$(free -m | awk '/^Mem:/ {print \$3}') \
Swap_Used_MB: \$(free -m | awk '/^Swap:/ {print \$3}') \
Load: \$(uptime | awk -F'load average: ' '{print \$2}') \
Disk_Usage: \$(df -h / | awk 'NR==2 {print \$5}') \
Uptime: \$(uptime -p | sed 's/up //') \
Top_Proc: \$(ps -eo comm,%cpu --sort=-%cpu | head -n 2 | tail -n 1) \
Zombie_Procs: \$(ps aux | awk '\$8 ~ /Z/ { count++ } END { print count+0 }') \
Kernel: \$(uname -r) \
Java_Version: \$(java -version 2>&1 | head -n 1) \
JVM_Opts: \$(ps -eo args | grep '[j]ava' | grep --color=never '\-X')" \
echo THP: \$(cat /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null | tr -d '\n') \
NUMA_Balancing: \$(cat /proc/sys/kernel/numa_balancing 2>/dev/null) \
NTP_Sync: \$(timedatectl 2>/dev/null | grep 'System clock synchronized' | awk '{print \$4}') \
Ulimit_Open_Files: \$(ulimit -n)" -o
ansible all -i hosts.txt -m shell -a "\
echo THP:\$(cat /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null | tr -d '\n') \
NUMA_Balancing:\$(cat /proc/sys/kernel/numa_balancing 2>/dev/null) \
NTP_Sync:\$(timedatectl 2>/dev/null | grep 'System clock synchronized' | awk '{print \$4}') \
Ulimit_Open_Files:\$(ulimit -n)" -o
CPU_Loop:\$(start=\$(date +%s); for i in {1..1000000}; do let j=i*i; done; end=\$(date +%s); echo \$((end - start)) sec)
---
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment