Getting started with Packer on IBM Cloud
Build a custom IBM Cloud VPC image using Packer
Overview
In this guide I will introduce you to Packer, it’s main components, and then how to use it on the IBM Cloud to build “golden images”. If you are already familiar with Packer as a concept and just want to get your hands on the code and test yourself, skip ahead to the Creating Custom Images on IBM Cloud section.
Packer Overview
Packer is an open-source tool from Hashicorp that helps you automate the process creating machine images. In this context, a machine image is a piece of immutible infrastructure that contains a preconfigured operating system and installed software/applications. Packer allows you to build images across multiple cloud and compute providers and on-prem infrastructure.
Mutable vs Immutable infrastructure
In traditional deployment scenarios the workflow looks like this:
Our developers write some code, our ops team deploys the needed infrastructure, and then has to:
- Patch the kernel
- Add system packages or third party binaries
- Add users and set up group-level access
- Copy application code to the server
While this is not necessarily a big deal for smaller deployments or in-house projects, larger deployments tend to suffer from both configuration drift, as well as outdated/vulnerable packages. Tools like Ansible, Salt, Chef, etc can fill the gap here, but now you are introducing another layer, possibly more overhead, and you need good centralized code for all of the possible tasks you want to run against your infrastructure.
With Packer we can swap those last 2 actions so that the machines we provision are up-to-date, pre-baked, and ready for production.
The Packer Template
A Packer template is split in to many configuration blocks
that determine how the machine image is created.
Packer block
The Packer block is used to control the behavior of Packer itself. In most cases this is you would set version constraints or declare all of the plugins required for the machine image to be created.
For IBM Cloud our Packer block would look like this:
packer {
required_plugins {
ibmcloud = {
version = ">=v3.0.3"
source = "github.com/IBM/ibmcloud"
}
}
}
As of this post, the latest version is
v3.0.3
. Please check the Github repo for the latest available version.
Variables
The Variables block allows you to define variables within your Packer configuration. These can be used within build sources and provisioners.
You can pass variables to the build process in several ways:
- Environment Variables
- Manually configured in the packer file
- Via the CLI with the
-var
and-var-file
options
For IBM Cloud, the two main variables we would be setting are the API Key used to provision the infrastructure, and the VPC Region where the image will be created and stored. In this example I am using one Environment Variable and one Manually Configured variable type:
variable "ibm_api_key" {
type = string
default = "${env("IBMCLOUD_API_KEY")}"
}
variable "ibm_region" {
type = string
default = "ca-tor"
}
Source Blocks
The Source block defines an image builder plugin. The first label — ibmcloud-vpc
here — is the builder plugin used to create our machine image. It instructs the builder which plugin to use for our image creation (aka IBM Cloud).
source "ibmcloud-vpc" "ubuntu_base_image" {
api_key = "${var.ibm_api_key}"
region = "${var.ibm_region}"
subnet_id = "02q7-xxxx-2e8b-xxx-xxx-xxxxxx"
resource_group_id = "6b6xxxx784e62xxxx4b6be"
security_group_id = "r038-xxxx-6bb1-xxxx-8016-xxxx"
vsi_base_image_id = "r038-xxxx-4449-xxxx-b64f-xxxx"
vsi_profile = "cx2-2x4"
vsi_interface = "public"
vsi_user_data_file = "init.yml"
image_name = "rt-ubuntu-${local.timestamp}"
communicator = "ssh"
ssh_username = "root"
ssh_port = 22
ssh_timeout = "15m"
timeout = "30m"
}
Build Blocks
The Build block defines what builders are started, how to provision them and if necessary what to do with their artifacts using post-process. In this example I am creating 3 golden images of different OS types:
build {
name = "vpc-packer-builder"
sources = [
"source.ibmcloud-vpc.ubuntu_base_image",
"source.ibmcloud-vpc.rocky_base_image",
"source.ibmcloud-vpc.debian_base_image"
]
}
Provisioner Blocks
Provisioners use builtin and third-party software to install and configure the machine image after booting (configure before deploy). While there are a number of provisioners
supported by Packer, the most common are: File, Shell, Ansible, and PowerShell.
provisioner "file" {
source = "./hashi.sh"
destination = "/opt/hashi.sh"
}
provisioner "shell" {
execute_command = "{{.Vars}} bash '{{.Path}}'"
environment_vars = [
"REGION=${var.ibm_region}",
"LOGDNA_KEY=${var.logging_key}"
]
inline = [
"chmod +x /opt/hashi.sh",
"/opt/hashi.sh"
]
}
provisioner "ansible" {
playbook_file = "./monitoring.yml"
extra_arguments = ["--extra-vars", "packer_template=${var.logging_key}", "--extra-vars", "region=${var.ibm_region}"]
}
Creating Custom Images on IBM Cloud
IBM Cloud Packer Build Flow
When we initiate a packer build
several things happen:
- A new temporary SSH Key is created and added to the VPC Region.
- A new compute instance is created with the temporary SSH key injected.
- All declared provisioners are run. Ex: shell scripts, ansible playbooks, etc.
- The compute instance is rebooted and then stopped.
- An image is taken from the stopped compute instance.
- All declared post-processors are executed.
- Once the image template is marked as
available
, the compute instance and SSH keys that were created are deleted.
Clone Example Code Repository
I’ve created a Github repository that has some basic examples. If you want to test a really simple example you can clone the repo and go in to the starter
directory.
git clone https://github.com/cloud-design-dev/ibmcloud-vpc-packer.git
cd ibmcloud-vpc-packer/starter
After you’ve cloned the repository and changed in to the correct directory, you will need to copy the example.pkrvars
to .pkvars.hcl
and update the file with the required variables.
With those set, we can initialize and validate the template by running the following commands:
packer init starter.pkr.hcl
packer validate -var-file=.pkrvars.hcl starter.pkr.hcl
If the validation step succeeds, you are ready to build the image with the build
command:
packer build -var-file=.pkrvars.hcl starter.pkr.hcl