Getting started with Packer in 2024

Michael Nikitochkin - Apr 15 - - Dev Community

Photo by Marek Okon on Unsplash

TL;DR Looking to build Ubuntu image for qemu on Fedora or Macos check my solution in my repo for packer experiments 1.

I've revisited my notes on automating the creation of virtual machines on local systems, this time using Fedora Linux to build an Ubuntu 22.04 image. Previously, I documented my process on Medium 2, but since then, there have been significant changes. Different operating systems often require different automation tools, and even within the same OS, different versions may necessitate distinct approaches. In this article, I'll delve into the usage of cloud-init for non-cloud installations and the transition from JSON to HCL file format in Packer3.

Install Packer

To get started, we need to install the Packer command-line tool. Follow these steps based on your operating system:

Fedora:



$ sudo dnf install -y dnf-plugins-core
$ sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/fedora/hashicorp.repo
$ sudo dnf -y install packer


Enter fullscreen mode Exit fullscreen mode

For Fedora, we first install dnf-plugins-core, add the package repository, and then install Packer using dnf.

MacOS:



$ brew install packer


Enter fullscreen mode Exit fullscreen mode

For MacOS users, Packer can be easily installed via Homebrew with a single command.

After installation, you can verify that packer is installed correctly by running the following command:



$ packer --version
1.9.4


Enter fullscreen mode Exit fullscreen mode

You can find more information about installation process on the Packer homepage 4.

Setup Qemu Plugin

packer is modular and consists of different plugins, making it highly extendable and allowing users to install only the modules they need. For our purposes, we'll be utilizing the qemu virtual machine manager. To install the Qemu plugin, you have two options:

Option 1: Command Line Installation

Use the following command:



$ packer plugins install github.com/hashicorp/qemu


Enter fullscreen mode Exit fullscreen mode

Option 2: Build Configuration File

Alternatively, you can specify the plugin in your build configuration file. Add the following block to your configuration file:



# ubuntu.pkr.hcl
packer {
  required_plugins {
    qemu = {
      version = "~> 1"
      source  = "github.com/hashicorp/qemu"
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

After adding the block, run the following command:



$ packer init


Enter fullscreen mode Exit fullscreen mode

By following either of these methods, you'll ensure that the Qemu plugin is properly set up for Packer environment.

Build

Before proceeding with the build, let's configure the Qemu source block in the configuration file. You can find more detailed information about the plugin on the official documentation page 5. While the provided example may not work out of the box, it serves as a useful starting point for customization.



# ubuntu.pkr.hcl

source "qemu" "ubuntu" {
  # Configuration options for building the image
}


Enter fullscreen mode Exit fullscreen mode

In this block, you'll specify the necessary configuration options for building your image. This will include details such as the source image, provisioning steps, and any other customizations required for your setup.

Using this structure, you can tailor the configuration to your specific needs and ensure that the build process aligns with your requirements. Once configured, you'll be ready to initiate the build process and generate your virtual machine image.

Install Image

Before proceeding with the build, we need to obtain the Ubuntu ISO image url. For simplicity, we'll focus on a basic Ubuntu image that can serve as a test environment for building applications. You can find the desired image on the release page 6.

NOTE: Additionally, Ubuntu provides cloud images that are convenient for use with virtual machines straightaway. These can be found at 7.

To specify the installation image in the source block of the configuration, add the following attributes:



# ubuntu.pkr.hcl

iso_url      = "https://releases.ubuntu.com/22.04.4/ubuntu-22.04.4-live-server-amd64.iso"
iso_checksum = "file:https://releases.ubuntu.com/22.04.4/SHA256SUMS"


Enter fullscreen mode Exit fullscreen mode

The iso_url attribute points to the direct download link for the Ubuntu install image, while iso_checksum ensures the integrity of the downloaded file by automatically fetching the checksum from the provided URL. Alternatively, you can download the checksum locally and include it in your repository.

With these attributes added, you're ready to proceed with the configuration of your build process.

VM Settings

To configure the virtual machine settings and specify the output image name, include the following settings in the configuration:



# ubuntu.pkr.hcl

vm_name          = "ubuntu.qcow2"
format           = "qcow2"
output_directory = "output"


Enter fullscreen mode Exit fullscreen mode

In this configuration:

  • vm_name: Specifies the name of the output image file for the virtual machine.
  • format: Specifies the format of the output image file, in this case, qcow2.
  • output_directory: Specifies the directory where the output image will be saved.

These settings ensure that your virtual machine is configured properly and that the resulting image is generated according to your specifications.

Boot Configuration

To ensure a smooth boot process and proper provisioning of the virtual machine, we need to configure the boot settings and set up access to the image after it's provisioned.

First, define a variable for the default user password in your configuration:



# ubuntu.pkr.hcl

variable "password" {
  type    = string
  default = "ubuntu"
}


Enter fullscreen mode Exit fullscreen mode

For more details about variables, refer to the 8.

Next, provide the boot settings and access configuration:



# ubuntu.pkr.hcl

boot_wait         = "3s"
boot_key_interval = "150ms"
http_directory    = "http"

ssh_username      = "ubuntu"
ssh_password      = var.password
ssh_timeout       = "30m"
shutdown_command  = "echo '${var.password}' | sudo -S shutdown -P now"


Enter fullscreen mode Exit fullscreen mode

Explanation of boot settings:

  • boot_wait: Specifies the duration to wait before starting the type boot_commands.
  • boot_key_interval: Specifies the interval between each key sends to the qemu process via VNC.
  • http_directory: Specifies the directory where the HTTP server for cloud-init configuration files will be hosted.

Additionally, Ubuntu uses Cloud Init for auto-installation. To make it work with Packer, create the required files:



mkdir http
touch http/user-data
touch http/meta-data


Enter fullscreen mode Exit fullscreen mode

Ensure you have both http/user-data and http/meta-data files for cloud-init configuration. packer will spin off a web server to serve these files via HTTP.

To configure the boot commands, follow these steps:

  1. Identify the boot command based on Grub settings in the install image and settings extracted from the Cloud Init site.
  2. Convert the identified boot command to the boot_commands format.

Example of boot commands:



# ubuntu.pkr.hcl

boot_command = [
  "c<wait>linux /casper/vmlinuz --- autoinstall 'ds=nocloud;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/'<enter><wait>",
  "initrd /casper/initrd<enter><wait><wait>",
  "boot<enter><wait>"
]


Enter fullscreen mode Exit fullscreen mode

Explanation of boot commands:

  • c: Key press for Grub to enter the command line to input boot commands.
  • <wait>: Configurable delay to ensure the virtual machine is ready for inputting new key presses.
  • linux /casper/vmlinuz: Specifies the Linux kernel to boot.
  • autoinstall 'ds=nocloud;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/': Configures the auto-installation parameters using cloud-init.

These configurations and boot commands should facilitate the smooth provisioning of your Ubuntu virtual machine image.

If you encounter any issues during the provisioning process, consider the following troubleshooting tips:

  • After the install image boots, inspect the dmesg output to verify the presence of the expected command text.
  • Alternatively, check /proc/cmdline to ensure that the kernel attributes are provided correctly.

CloudInit Configuration

Learning the basics of cloud-init configuration during auto-installation can be instrumental in streamlining your provisioning process.
Refer to the following resources for more insights:

  • Autoinstall Quickstart 9
  • Creating Autoinstall Configuration 10
  • nocloud DataSource 11
  • Running Cloud-init Locally 12
  • Cloud-init Examples 13

Now, let's delve into the configuration example http/user-data:



#cloud-config
autoinstall:
  version: 1
  identity:
    hostname: ubuntu-server
    # Generate with `mkpasswd -m sha-512`
    password: '$6$9xa2L/Jtd/BUMqh4$trKWwbPGdJDpU.R8UrEW9eXDi.joNBkVyS2XLqbetw2Hp7uUPEJVbwn7Jq2Ddt0P0wWiM1B9lbZ8dff5WlYpF0'
    username: ubuntu

  ssh:
    install-server: true
    allow-pw: yes

  locale: en_US

  keyboard:
    layout: us

  storage:
    layout:
      name: lvm

  snaps:
    - name: juju
    - name: lxd

  packages:
    - net-tools


Enter fullscreen mode Exit fullscreen mode

In this YAML configuration:

  • autoinstall section contains the auto-installation parameters.
  • identity defines the hostname, username, and hashed password for the default user. It is required be the same as for packer ssh configs.
  • ssh configuration enables SSH server installation and allows password authentication. It allows automaticaly to download Public SSH keys from Github.
  • locale and keyboard configurations specify system locale and keyboard layout, respectively.
  • storage configuration sets the storage layout to LVM.
  • snaps section installs specified snap packages.
  • packages installs additional packages such as net-tools.

For generating the hashed password, you can use the command:



$ mkpasswd -m sha-512


Enter fullscreen mode Exit fullscreen mode

If the auto-installation proceeds successfully, you shouldn't encounter any interactive prompts. However, if issues arise, you can troubleshoot using the following logs:

  • /var/log/installer/autoinstall-user-data: Autoinstall log.
  • /var/log/cloud-init-output.log: Cloud-init output log.

CloudInit Success

Collect Everything

To organize everything into a single place, let's organise files like this:



$ tree 
├── http
│   ├── meta-data
│   └── user-data
└── ubuntu.pkr.hcl


Enter fullscreen mode Exit fullscreen mode

In the ubuntu.pkr.hcl file, include all configurations and settings:



# ubuntu.pkr.hcl

packer {
  required_plugins {
    qemu = {
      version = "~> 1"
      source  = "github.com/hashicorp/qemu"
    }
  }
}

variable "password" {
  type    = string
  default = "ubuntu"
}

source "qemu" "ubuntu" {
  iso_url      = "https://releases.ubuntu.com/22.04.4/ubuntu-22.04.4-live-server-amd64.iso"
  iso_checksum = "file:https://releases.ubuntu.com/22.04.4/SHA256SUMS"

  vm_name             = "ubuntu.qcow2"
  format              = "qcow2"
  output_directory    = "output"
  shutdown_command    = "echo '${var.password}' | sudo -S shutdown -P now"

  # Comment next line on MacOS
  accelerator         = "kvm"
  cpus                = 4
  memory              = 4096
  use_default_display = true
  # To not run Qemu window. For Debuging set the value to `false`
  headless            = true

  http_directory = "http"
  ssh_username   = "ubuntu"
  ssh_password   = var.password
  ssh_timeout    = "20m"

  boot_wait = "3s"
  boot_command = [
    "c<wait>linux /casper/vmlinuz --- autoinstall 'ds=nocloud;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/'<enter><wait>",
    "initrd /casper/initrd<enter><wait><wait>",
    "boot<enter><wait>"
  ]
}

build {
  name    = "ubuntu"
  sources = ["source.qemu.ubuntu-2204"]
}


Enter fullscreen mode Exit fullscreen mode

Once everything is set up, initiate the build process with the following command:



$ PACKER_LOG=1 packer build ubuntu.pkr.hcl

...
==> qemu.ubuntu: Connected to SSH!
==> qemu.ubuntu: Gracefully halting virtual machine...
==> qemu.ubuntu: Converting hard drive...
Build 'qemu.ubuntu' finished after 30 minutes 56 seconds.


Enter fullscreen mode Exit fullscreen mode

This command will start building your Ubuntu virtual machine image according to the configuration.

Test

After 10-20 minutes, the build should be finished, resulting in the file output/ubuntu. This file is our image. Let's verify that it works by running it with qemu:



$ qemu-system-x86_64 -name ubuntu-server \
    -drive file=output/ubuntu.qcow2,if=virtio,cache=writeback,discard=ignore,format=qcow2 \
    -machine type=pc,accel=kvm  \
    -m 4096M \
    -smp 4 \
    -device virtio-net,netdev=user.0  \
    -netdev user,id=user.0,hostfwd=tcp::4141-:22


Enter fullscreen mode Exit fullscreen mode

This command boots the newly created image. It should also listen on local port 4141 to allow access to the machine via SSH:



$ ssh ubuntu@localhost -p 4141

...
ubuntu@localhost's password:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-102-generic x86_64)

ubuntu@ubuntu-server:~$ 


Enter fullscreen mode Exit fullscreen mode

You can use this SSH command to verify that you can connect to the Ubuntu machine running.

Conclusion

Combining and configuring everything for building virtual machine images with Packer can be quite challenging, especially for someone who is not a system administrator. It often requires a lot of experimentation and reading through documentation.

I hope you find these instructions useful for your own projects. If you're interested, you can find more of my experiments with Packer in my Git repository 1.

Feel free to explore and adapt these configurations to your own needs. Happy building!

That’s all folks!

References


  1. Codeberg: miry/samples > Packer Experiment 

  2. Michael Nikitochkin: How to automate building local virtual machines with Packer 

  3. Automate image builds with Packer 

  4. Tutorial: Install Packer 

  5. Packer: QEMU plugin 

  6. Ubuntu 22.04 release page 

  7. Ubuntu Cloud Image 23.10 release page 

  8. Packer HCL Variables 

  9. Autoinstall Quickstart 

  10. Creating Autoinstall Configuration 

  11. nocloud DataSource 

  12. Running Cloud-init Locally 

  13. Cloud-init Examples 

. . . . . . . . . . . . .