Automate image building with packer

Packer, crafted by HashiCorp, offers an efficient, reusable, and reproducible approach to automate image creation.

Continuous Deployment (CD) and Infrastructure as a Service (IaaS) offer a plethora of handy tools for seamless updates, rollbacks, and overall infrastructure management. The cornerstone of a well-maintained infrastructure lies in the ability to restart it entirely from scratch. This entails having a straightforward and reproducible method to reconfigure your virtual machines, precisely what Packer facilitates.

Basic requirement

You need to configure your OpenStack CLI as described in the dedicated section. The examples provided are based on this configuration, so if you have any specific configurations, you'll need to adjust accordingly.

Packer utilizes SSH to connect to the virtual machine, so you should run Packer in a network where SSH is open from your client.

Official documentation

Official documentation can be found on packer documentation website.

Installation

On Mac OS

$ brew install packer
$ mkdir -p ~/packer/example
$ cd ~/packer/example
$

On ubuntu 24.04

$ sudo apt install packer
$ mkdir -p ~/packer/example
$ cd ~/packer/example
$

Quick start

Cloud@VirtualData provide a basic packer skeleton to simplify packer usage. In "Advanced usage" section will we go further. You can simply fork the gitlab repository and clone your fork.

The basic skeleton provide you some key features :

  • multiple source choice : allow you to choose which OS and cloud you want to use. It's especially useful if you want to have a federated services on multiple cloud infrastructure
  • formatted image name : each images will be formatted following the schema os-name-flavor-timestamp
  • common [pre|post]configure scripts : some configuration must be done on every image you want to have, this could be configuring ntp, installing monitoring packages, ...

Skeleton hierarchy

.pkr.hcl is the packer file extension. It uses hcl format.

The files directory contains all file that will be uploaded on images during creation. common directory will be uploaded for any kind of images, flavor directory will only be uploaded when creating a specific flavor image.

The scripts directory contains all scripts that will be run through on virtual machine during creation. The same-idea than files is applied here for subdirectory.

Finally, variables will contain packer variables declaration for services. Per example, operating system name, flavor, ...

$ tree -A
.
├── build.pkr.hcl
├── files
│   ├── common
│   │   └── empty
│   ├── core
│   │   └── empty
│   └── service-1
│       ├── empty
│       └── service.cfg
├── scripts
│   ├── common
│   │   ├── empty
│   │   ├── post-configure.sh
│   │   └── pre-configure.sh
│   └── profiles
│       ├── core.sh
│       ├── empty
│       └── service-1.sh
├── sources.pkr.hcl
├── variables
│   ├── empty
│   └── service-1.pkr.hcl
└── variables.pkr.hcl

Using skeleton

As example, we will create a web server image with our skeleton. The server will be based on alma-9x OS and the flavor will be web.

Note

packer use a standard user to log into the virtual machine during installation. This mean that you can't directly put a file in /etc and you need to prefix all command which need root access with sudo -E in your scripts.

files/flavor

You need to create a flavor directory the a empty file. empty file is just needed to push on git server the directory. If you don't want to use git (which is not recommended), you don't need to create empty file.

All file will be uploaded on /tmp/common and /tmp/flavor. You can manipulate them with flavor.sh script.

$ mkdir -p ./files/web
$ touch ./files/web/empty
$

scripts/profiles/flavor.sh

You need to create a flavor.sh script that will be run on the virtual machine.

$ cat > ./scripts/profiles/web.sh <<EOF
#!/bin/sh
# WARNING : packer use a standard user account to connect to the virtual machine
# You need to prefix all your command with `sudo -E`

sudo -E dnf -y install nginx
EOF

variables/service.pkr.hcl

This file will be used to overwrite default value of variables flavor and operating-system. As the default operating-system is alma-9x we don't need to overwrite this value.

$ cat > variables/alma-9x-web.pkr.hcl << EOF
flavor = "web"
EOF

Build a specific service

By default, alma-9x-core image will be generated. To create another image based on different operating system or flavor, you have to create your own file directory, scripts and variables/flavor.pkr.hcl. Per example, to generate a alma-9x-web images you should build with the following command.

$ packer init .
$ packer build --var-file variables/alma-9x-web.pkr.hcl .
[...]
$ openstack image list \
  --os-cloud virtualdata
+--------------------------------------+--------------------------+--------+
| ad15a094-2dab-4024-b37d-e5b38858df72 | alma-9x-web-202404301725 | active |
+--------------------------------------+--------------------------+--------+

Advanced usage

Define openstack plugins for packer

Packer offers an OpenStack plugin for interacting with OpenStack-based clouds as Cloud@VirtualData.

$ cat > ~/packer/example/config.pkr.hcl << EOF
packer {
  required_plugins {
    openstack = {
      version = ">= 1.1.1"
      source  = "github.com/hashicorp/openstack"
    }
  }
}
EOF

Define sources

The initial step with Packer involves defining the sources for your build. The source section encompasses:

  • image_name: the name of the generated image
  • ssh_username: the default username utilized by the distribution
  • source_image: the ID of the GenericCloud image on the cloud
  • flavor: the flavor employed for building the image
  • cloud: the --os-cloud value within your clouds.yaml file
  • networks: a list of network IDs

The provided source section will utilize an Almalinux 9 Generic Cloud as the source image on Cloud@VirtualData, resulting in the creation of an image named "alma-9x-my-image."

$ cat >> ~/packer/example/main.pkr.hcl << EOF
source "openstack" "alma-9x" {
  image_name = "alma-9x-my-image"
  ssh_username = "almalinux"
  source_image = "901c99e8-2f6a-4cfa-879d-a48144322fbc"
  flavor = "m1.small"
  cloud = "virtualdata"
  networks = [
    "39aa90ca-163b-4630-9671-9439fefe516f"
  ]
}
EOF

Building procedure

The build section define the list of action that will be done to generate your image.

Sources

By default, you only need a sources section. With the following build section, Packer will only create a new image without performing any additional actions, which is essentially useless.

$ cat > ~/packer/example/main.pkr.hcl << EOF
build { # Be careful, build section is close on "Run building" section
  sources = ["source.openstack.alma-9x"]
EOF

Provisioner

Provisioners are plugins that can be used to configure VMs. In this documentation, we will explore two provisioners: file and shell.

file provisioner

The File provisioner will copy a local file to the VM in a specific directory. With the following section, we will create a file called myfile.cfg on your local machine and place it in the /etc directory on the VM.

$ cat > ~/packer/example/myfile.cfg << EOF
[section]
key = value
EOF
$ cat >> ~/packer/example/main.pkr.hcl <<EOF
  provisioner "file" {
    destination = "/etc/"
    source      = "./myfile.cfg"
  }
EOF
shell provisioner

The Shell provisioner will run a command on the VM after it is up. This section will create a script called configure.sh that will create a /etc/myscript.cfg file on the VM.

$ cat > ~/packer/example/configure.sh << EOF
#!/bin/sh
sudo sh -c 'hostname > /etc/myscript.cfg'
EOF
$ cat >> ~/packer/example/main.pkr.hcl <<EOF
  provisioner "shell" {
    script = "configure.sh"
  }
EOF

Run building

To build a new image called alma-9x-my-image, you just need to start packer build as follows:

Warning

Be careful, as Packer logs into the machine as a standard user, you don't have direct root access. In your scripts, you must prefix all commands with sudo to allow the script to be run as root.

$ cat >> ~/packer/example/main.pkr.hcl <<EOF
} # We close build section
EOF
$ packer build .
[...]