Packer use cases and samples are kept in this GitHub Repo.
Photo by cottonbro from Pexels
- Introduction
- Installing Packer
- Create your first Packer file
- Packer Parallel Build
- Packer Post-Processors
- Packer with AWS
- Creating Vagrant Boxes from AMI
Introduction
Packer is an open source tool for creating identical machine images for multiple platforms from a single source configuration.
Installing Packer
Download and install packer.
# packer on linux
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install packer
Create your first Packer file
Sample docker-ubuntu.pkr.hcl
packer {
required_plugins {
docker = {
version = ">= 0.0.7"
source = "github.com/hashicorp/docker"
}
}
}
source "docker" "ubuntu" {
image = "ubuntu:xenial"
commit = true
}
build {
name = "learn-packer"
sources = [
"source.docker.ubuntu"
]
}
Init and Build Packer
# init packer
$ packer init .
# format packer files
$ packer fmt .
# validate packer files
$ packer validate .
# build
$ packer build .
# or
$ packer build docker-ubuntu.pkr.hcl
Adding provisioner
build {
name = "learn-packer"
sources = [
"source.docker.ubuntu"
]
provisioner "shell" {
environment_vars = [
"FOO=hello world",
]
inline = [
"echo Adding file to Docker Container",
"echo \"FOO is $FOO\" > example.txt",
]
}
provisioner "shell" {
inline = ["echo This provisioner runs last"]
}
}
Variables
variable "docker_image" {
type = string
default = "ubuntu:xenial"
}
$ touch example.pkrvars.hcl
docker_image = "ubuntu:bionic"
# build with varible file
$ packer build --var-file=example.pkrvars.hcl docker-ubuntu.pkr.hcl
Packer will automatically load any variable file that matches the name *.auto.pkrvars.hcl
, without the need to pass the file via the command line.
Build image with command line flag
$ packer build --var docker_image=ubuntu:groovy .
Handling Sensitive Data
local "secret_key" {
key = "${var.secret_key}"
sensitive = true
}
Packer Parallel Build
Add multiple sources
source "docker" "ubuntu" {
image = var.docker_image
commit = true
}
source "docker" "ubuntu-bionic" {
image = "ubuntu:bionic"
commit = true
}
Add multiple sources in build
build {
name = "learn-packer"
sources = [
"source.docker.ubuntu",
"source.docker.ubuntu-bionic",
]
.
.
Packer Post-Processors
While provisioners are run against an instance while it is running, post-processors run only after Packer saves the instance as an image.
eg:
- compress artifacts
- upload artifacts
- create some files
Adding a docker tag using post-processor
Post
build {
name = "learn-packer"
.
.
post-processor "docker-tag" {
repository = "learn-packer"
tags = ["ubuntu-xenial", "packer-rocks"]
only = ["docker.ubuntu"]
}
post-processor "docker-tag" {
repository = "learn-packer"
tags = ["ubuntu-bionic", "packer-rocks"]
only = ["docker.ubuntu-bionic"]
}
Sequential post-processing steps
- Use
post-processors
instead ofpost-processor
- Use the post-processors (note the pluralization) block to create post-processing pipelines where the output of one post-processor becomes the input to another post-processor.
post-processors {
post-processor "docker-import" {
repository = "swampdragons/testpush"
tag = "0.7"
}
post-processor "docker-push" {}
}
Packer with AWS
Manage AWS Credentials for Packer
Configure environment variables.
$ export AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY
$ export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_KEY
Important: Managing the AMIs created by Packer
- Packer only builds images, make sure you cleanup if AMI is not needed anymore
- Remove the AMI by first deregistering it on the AWS AMI management page.
- Remember to delete the associated snapshot on the AWS snapshot management page.
Add provisioner to template
Using provisioners allows you to completely automate modifications to your image. You can use,
- shell scripts
- file uploads
- tools like Chef or Puppet
Inside the build block,
provisioner "shell" {
environment_vars = [
"FOO=hello world",
]
inline = [
"echo Installing Redis",
"sleep 30",
"sudo apt-get update",
"sudo apt-get install -y redis-server",
"echo \"FOO is $FOO\" > example.txt",
]
}
Using Variable to Manage AMI Name
Remember to clean up exising AMI or use different AMI name when you create as AMI name should be unique in AWS.
Automate the AMI naming using local variable and timestamp.
variable "ami_prefix" {
type = string
default = "learn-packer-linux-aws-redis"
}
locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
}
and
source "amazon-ebs" "ubuntu" {
ami_name = "${var.ami_prefix}-${local.timestamp}"
## ...
}
Creating Vagrant Boxes from AMI
Add vagrant post-processor
build {
name = "learn-packer"
sources = [
"source.amazon-ebs.ubuntu",
"source.amazon-ebs.ubuntu-focal"
]
.
.
provisioner "shell" {
inline = ["echo This provisioner runs last"]
}
post-processors {
post-processor "vagrant" {}
post-processor "compress" {}
}
}