Cloud Image Usage
Enterprise Linux distributions (AlmaLinux, Rocky Linux) provide pre-built cloud images distributed in qcow2 format. These images are small in size, boot quickly, come with Cloud-Init pre-installed, and are well-suited for rapid VM deployment on KVM/libvirt, OpenStack, or other virtualization platforms.
Downloading Official Cloud Images
Section titled “Downloading Official Cloud Images”AlmaLinux Cloud Images
Section titled “AlmaLinux Cloud Images”# AlmaLinux 9 GenericCloud imagewget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2
# AlmaLinux 8 GenericCloud imagewget https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-latest.x86_64.qcow2
# Verify download integritywget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/CHECKSUMsha256sum -c CHECKSUM --ignore-missingRocky Linux Cloud Images
Section titled “Rocky Linux Cloud Images”# Rocky Linux 9 GenericCloud imagewget https://download.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2
# Rocky Linux 8 GenericCloud imagewget https://download.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-GenericCloud.latest.x86_64.qcow2
# Verify checksumswget https://download.rockylinux.org/pub/rocky/9/images/x86_64/CHECKSUMsha256sum -c CHECKSUM --ignore-missingViewing Image Information
Section titled “Viewing Image Information”# Install the qemu-img toolsudo dnf install -y qemu-img
# View image informationqemu-img info AlmaLinux-9-GenericCloud-latest.x86_64.qcow2Example output:
image: AlmaLinux-9-GenericCloud-latest.x86_64.qcow2file format: qcow2virtual size: 10 GiB (10737418240 bytes)disk size: 582 MiBcluster_size: 65536Format specific information: compat: 1.1 compression type: zlib lazy refcounts: falseCustomizing Images with virt-customize
Section titled “Customizing Images with virt-customize”virt-customize is a tool provided by libguestfs-tools that can modify disk images without booting a virtual machine.
Installing the Tools
Section titled “Installing the Tools”sudo dnf install -y libguestfs-toolsBasic Customization Operations
Section titled “Basic Customization Operations”# Make a copy of the image for customization (preserve the original)cp AlmaLinux-9-GenericCloud-latest.x86_64.qcow2 almalinux9-custom.qcow2
# Set root passwordsudo virt-customize -a almalinux9-custom.qcow2 \ --root-password password:YourSecurePassword123
# Install packagessudo virt-customize -a almalinux9-custom.qcow2 \ --install vim-enhanced,htop,tmux,git,wget,curl
# Enable servicessudo virt-customize -a almalinux9-custom.qcow2 \ --run-command 'systemctl enable firewalld'
# Write filessudo virt-customize -a almalinux9-custom.qcow2 \ --write '/etc/motd:Welcome to Custom AlmaLinux 9 Image'
# Upload a local file into the imagesudo virt-customize -a almalinux9-custom.qcow2 \ --upload /path/to/local/sshd_config:/etc/ssh/sshd_config
# Set hostnamesudo virt-customize -a almalinux9-custom.qcow2 \ --hostname custom-server
# Set timezonesudo virt-customize -a almalinux9-custom.qcow2 \ --timezone Asia/Shanghai
# Run custom scriptssudo virt-customize -a almalinux9-custom.qcow2 \ --run-command 'echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.d/99-custom.conf'
# Set SELinux relabel (recommended after modifying files)sudo virt-customize -a almalinux9-custom.qcow2 \ --selinux-relabelComplete Image Customization Workflow
Section titled “Complete Image Customization Workflow”#!/bin/bash# customize-image.sh - Batch customize cloud images
SOURCE_IMAGE="AlmaLinux-9-GenericCloud-latest.x86_64.qcow2"CUSTOM_IMAGE="almalinux9-production.qcow2"
# Copy the original imagecp "${SOURCE_IMAGE}" "${CUSTOM_IMAGE}"
# Expand disk to 30GBqemu-img resize "${CUSTOM_IMAGE}" 30G
# Perform customizationsudo virt-customize -a "${CUSTOM_IMAGE}" \ --root-password password:TempPassword123 \ --timezone Asia/Shanghai \ --install epel-release \ --install vim-enhanced,htop,tmux,git,wget,curl,net-tools,bind-utils \ --install firewalld,fail2ban,chrony \ --run-command 'systemctl enable firewalld' \ --run-command 'systemctl enable fail2ban' \ --run-command 'systemctl enable chronyd' \ --write '/etc/sysctl.d/99-custom.conf:net.core.somaxconn = 65535net.ipv4.tcp_max_syn_backlog = 65535vm.swappiness = 10' \ --run-command 'echo "PermitRootLogin prohibit-password" >> /etc/ssh/sshd_config.d/99-custom.conf' \ --selinux-relabel
echo "Image customization complete: ${CUSTOM_IMAGE}"qemu-img info "${CUSTOM_IMAGE}"Cleaning Images with virt-sysprep
Section titled “Cleaning Images with virt-sysprep”Before distributing a custom image, you should clean out instance-specific information:
# Clean the image (removes SSH keys, machine IDs, logs, etc.)sudo virt-sysprep -a almalinux9-custom.qcow2
# Specify which cleanup operations to performsudo virt-sysprep -a almalinux9-custom.qcow2 \ --operations defaults,-ssh-userdir
# List all available cleanup operationssudo virt-sysprep --list-operationsCreating Cloud Instances with virt-install
Section titled “Creating Cloud Instances with virt-install”Installing Virtualization Tools
Section titled “Installing Virtualization Tools”# Install KVM and management toolssudo dnf install -y @virtualizationsudo systemctl enable --now libvirtd
# Verify virtualization supportvirt-host-validatePreparing Cloud-Init Configuration
Section titled “Preparing Cloud-Init Configuration”# Create a working directorymkdir -p /var/lib/libvirt/cloud-init
# Create meta-datacat > /var/lib/libvirt/cloud-init/meta-data << 'EOF'instance-id: vm-001local-hostname: web-server-01EOF
# Create user-datacat > /var/lib/libvirt/cloud-init/user-data << 'EOF'#cloud-config
hostname: web-server-01fqdn: web-server-01.example.com
users: - default - name: admin groups: wheel sudo: ALL=(ALL) NOPASSWD:ALL shell: /bin/bash ssh_authorized_keys: - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... admin@workstation
package_update: truepackages: - vim-enhanced - htop - nginx
runcmd: - systemctl enable --now nginxEOF
# Create network-config (optional, for static IP configuration)cat > /var/lib/libvirt/cloud-init/network-config << 'EOF'version: 2ethernets: eth0: dhcp4: trueEOFGenerating the Cloud-Init ISO
Section titled “Generating the Cloud-Init ISO”# Install genisoimagesudo dnf install -y genisoimage
# Generate the seed ISOgenisoimage -output /var/lib/libvirt/cloud-init/seed.iso \ -volid cidata \ -joliet -rock \ /var/lib/libvirt/cloud-init/user-data \ /var/lib/libvirt/cloud-init/meta-data \ /var/lib/libvirt/cloud-init/network-configCreating a Virtual Machine
Section titled “Creating a Virtual Machine”# Prepare the disk image (copy cloud image as the VM disk)VM_NAME="web-server-01"IMAGE_DIR="/var/lib/libvirt/images"
sudo cp almalinux9-custom.qcow2 "${IMAGE_DIR}/${VM_NAME}.qcow2"
# Resize the disksudo qemu-img resize "${IMAGE_DIR}/${VM_NAME}.qcow2" 30G
# Create the VM with virt-installsudo virt-install \ --name "${VM_NAME}" \ --memory 2048 \ --vcpus 2 \ --disk "${IMAGE_DIR}/${VM_NAME}.qcow2",format=qcow2 \ --disk /var/lib/libvirt/cloud-init/seed.iso,device=cdrom \ --os-variant almalinux9 \ --network bridge=virbr0,model=virtio \ --graphics none \ --console pty,target_type=serial \ --import \ --noautoconsole
# Check VM statussudo virsh list --all
# View VM IP addresssudo virsh domifaddr "${VM_NAME}"Batch Creating Virtual Machines
Section titled “Batch Creating Virtual Machines”#!/bin/bash# create-vms.sh - Batch create virtual machines
BASE_IMAGE="/var/lib/libvirt/images/almalinux9-custom.qcow2"IMAGE_DIR="/var/lib/libvirt/images"CI_DIR="/var/lib/libvirt/cloud-init"SSH_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... admin@workstation"
VMS=("web-01" "web-02" "db-01")MEMORY=2048VCPUS=2DISK_SIZE="30G"
for VM_NAME in "${VMS[@]}"; do echo "=== Creating VM: ${VM_NAME} ==="
# Prepare the disk sudo cp "${BASE_IMAGE}" "${IMAGE_DIR}/${VM_NAME}.qcow2" sudo qemu-img resize "${IMAGE_DIR}/${VM_NAME}.qcow2" "${DISK_SIZE}"
# Generate cloud-init configuration WORK_DIR="${CI_DIR}/${VM_NAME}" mkdir -p "${WORK_DIR}"
cat > "${WORK_DIR}/meta-data" << METAEOFinstance-id: ${VM_NAME}local-hostname: ${VM_NAME}METAEOF
cat > "${WORK_DIR}/user-data" << USEREOF#cloud-confighostname: ${VM_NAME}users: - default - name: admin groups: wheel sudo: ALL=(ALL) NOPASSWD:ALL shell: /bin/bash ssh_authorized_keys: - ${SSH_KEY}package_update: truepackages: - vim-enhanced - htopUSEREOF
# Generate ISO genisoimage -output "${WORK_DIR}/seed.iso" \ -volid cidata -joliet -rock \ "${WORK_DIR}/user-data" \ "${WORK_DIR}/meta-data"
# Create the VM sudo virt-install \ --name "${VM_NAME}" \ --memory ${MEMORY} \ --vcpus ${VCPUS} \ --disk "${IMAGE_DIR}/${VM_NAME}.qcow2",format=qcow2 \ --disk "${WORK_DIR}/seed.iso",device=cdrom \ --os-variant almalinux9 \ --network bridge=virbr0,model=virtio \ --graphics none \ --console pty,target_type=serial \ --import \ --noautoconsole
echo "${VM_NAME} created successfully"done
echo ""echo "=== All VM Status ==="sudo virsh list --allResizing Images
Section titled “Resizing Images”Expanding Disk Capacity
Section titled “Expanding Disk Capacity”# View current sizeqemu-img info /var/lib/libvirt/images/web-server-01.qcow2
# Expand to 50GB (VM must be shut down)sudo virsh shutdown web-server-01sudo qemu-img resize /var/lib/libvirt/images/web-server-01.qcow2 50Gsudo virsh start web-server-01After the VM boots, you still need to expand the partition and filesystem:
# Execute the following after logging into the VM# View disk layoutlsblk
# If using LVM (typical for cloud images)sudo growpart /dev/vda 4 # Expand the partition (partition number may vary)sudo pvresize /dev/vda4 # Expand the PVsudo lvextend -l +100%FREE /dev/mapper/almalinux-root # Expand the LVsudo xfs_growfs / # Expand the XFS filesystem# Or: sudo resize2fs /dev/mapper/almalinux-root # For ext4
# If not using LVM (simple partitioning)sudo growpart /dev/vda 1sudo xfs_growfs /Compressing Images
Section titled “Compressing Images”# Shut down the VMsudo virsh shutdown web-server-01
# Compress the qcow2 image (remove sparse space)sudo qemu-img convert -O qcow2 -c \ /var/lib/libvirt/images/web-server-01.qcow2 \ /var/lib/libvirt/images/web-server-01-compressed.qcow2
# Use virt-sparsify for more thorough compressionsudo virt-sparsify --in-place /var/lib/libvirt/images/web-server-01.qcow2Format Conversion
Section titled “Format Conversion”# qcow2 to raw formatqemu-img convert -f qcow2 -O raw image.qcow2 image.raw
# raw to qcow2 formatqemu-img convert -f raw -O qcow2 image.raw image.qcow2
# qcow2 to vmdk (VMware format)qemu-img convert -f qcow2 -O vmdk image.qcow2 image.vmdk
# qcow2 to vdi (VirtualBox format)qemu-img convert -f qcow2 -O vdi image.qcow2 image.vdiCommon VM Management Commands
Section titled “Common VM Management Commands”# List all virtual machinessudo virsh list --all
# Start/Shutdown/Rebootsudo virsh start web-server-01sudo virsh shutdown web-server-01sudo virsh reboot web-server-01
# Force shutdownsudo virsh destroy web-server-01
# Connect to the VM consolesudo virsh console web-server-01# Exit console: Ctrl+]
# View VM informationsudo virsh dominfo web-server-01sudo virsh domifaddr web-server-01
# Create a snapshotsudo virsh snapshot-create-as web-server-01 \ --name "before-upgrade" \ --description "Snapshot before upgrade"
# List snapshotssudo virsh snapshot-list web-server-01
# Revert to a snapshotsudo virsh snapshot-revert web-server-01 before-upgrade
# Delete a VM (keep disks)sudo virsh undefine web-server-01
# Delete a VM (remove disks as well)sudo virsh undefine web-server-01 --remove-all-storageUsing official cloud images with Cloud-Init and virt-install allows you to rapidly create standardized VM instances in a local KVM environment. This experience is very similar to creating ECS instances on a public cloud, making it an ideal solution for building private clouds and development/testing environments.