In this short article I show how to automate machine images creation using Packer.
Packer is a DevOps tool that enables swift creation of identical machine images for multiple platforms, for instance Amazon Machine Images or Google Compute Engine Images from a single configuration file. Packer works remotely and executes tasks in parallel. That is convenient and saves much time.
A machine image is a single static unit that contains a pre-configured operating system and installed software which is used to quickly create new running machines.
Packer has a command-line interface with these commands:
build build image(s) from template
fix fixes templates from old versions of packer
inspect see components of a template
push push a template and supporting files to a Packer build service
validate check that a template is valid
version Prints the Packer version
When we say a template
, what we actually mean is a JSON file that contains definitions of
builders
, provisioners
and optionally some variables
or
post-processors
.
More Packer builders may be specified in a template. We can for instance add a builder to create an AMI, and another to create an identically provisioned VirtualBox image.
The provisioners
section contains a list of Packer provisioners sequentially executed
within a running machine before it is turned into an image. All provisioners are applied to all builders,
unless we override this behavior using only
, or except
parameters of the
provisioner.
Packer offers a wide array of provisioners out of the box. Remote or local shell scripts execution is probably the most common choice, but Ansible, Puppet and Chef provisioners are available too.
Builder template example:
{
"builders": [
{
"type": "amazon-ebs",
"name": "us-east-1",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}",
"region": "us-east-1",
"source_ami": "ami-6d1c2007",
"instance_type": "t2.micro",
"ssh_username": "centos",
"ssh_pty": true,
"ami_name": "consul-instance {{timestamp}}"
}
]
}
Provisioner template example:
{
"provisioners": [
{
"type": "shell",
"script": "scripts/install—container-instance.sh",
"execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E '{{ .Path }}'"
}
]
}
Since Packer uses our cloud provider account(s) to bake the images, we must provide it with credentials. In
case of AWS we need aws_access_key
and aws_secret_key
(note how they are referred to
in the builder definition above) and we can get them from our system as follows:
AWS_ACCESS_KEY=`aws configure get ${PROFILE}.aws_access_key_id`
AWS_SECRET_KEY=`aws configure get ${PROFILE}.aws_secret_access_key`
Obviously we don’t want to store our credentials in the Packer template. Instead we pass them to
packer build
command using -var
parameters:
packer build \
-var "aws_access_key=${AWS_ACCESS_KEY}" \
-var "aws_secret_key=${AWS_SECRET_KEY}" \
template.json
This command starts remote build(s) and we can watch the progress in console. After completion, Packer displays ID and name of each created image (Artifacts in Packer terminology).
Now when we know what Packer is, let’s be clear about what it is not. Packer is not a machine image manager. Packer’s job is done when it creates an image and cleans after itself. In this sense, Packer is probably the most unixy project in the entire HashiCorp arsenal. If we need to view the created images or remove an image, we use tool or interface of the respective platform.
Packer works with perhaps all major platforms out of the box, and is extensible using plugins (written in Go). It is a very useful tool enabling automated and streamlined machine image creation - an important step in many DevOps workflows.