< back home
Crated on ; Last modified on

~ packer

table of contents

what is packer?

Packer is a tool to create machine images for multiple platforms from a single template.

packer file structure

Packer uses configuration files to create the images, these are called templates. They are coded in Hashicorp Configuration Language (hcl).

We will follow the documentation official tutorial on how to build an image for this section. So go there for any clarifications.

packer block

You will have a packer {} block. Here you will have the setting for packer, and you will add the plugins you need, например:


packer {
  required_plugins {
    amazon = {
      version = ">= 1.2.8"
      source  = "github.com/hashicorp/amazon"
    }
  }
}

You need to specify the source of the plugin; which is where packer will look to download the plugin. version is optional but nice to have.

source block

This is the blueprint for the machine we want to build. Here we add all the infrastructure information we need for it.

After the source keyword, we specify what type builder plugin we are going to use. A builder plugin is a packer component that will create the machine and make it into an image.


source "amazon-ebs" "ubuntu" {
  ami_name      = "learn-packer-linux-aws"
  instance_type = "t2.micro"
  region        = "us-west-2"
  source_ami_filter {
    filters = {
      name                = "ubuntu/images/*ubuntu-jammy-22.04-amd64-server-*"
      root-device-type    = "ebs"
      virtualization-type = "hvm"
    }
    most_recent = true
    owners      = ["099720109477"]
  }
  ssh_username = "ubuntu"
}

Here we are using the amazon-ebs builder plugin. This will basically launch an ec2 instance from a source AMI, provision the running machine, and then make an AMI out of that. We are naming it ubuntu

Each builder has its attributes. In this case amazon-ebs is going to launch a t2.micro in us-west-2 using an ubuntu-jammy based image. Then it will create a new AMI image named learn-packer-linux-aws.

build block

Finally we have the build block. Here is where later we will add stuff to provision the image with.


build {
  sources = [
    "source.amazon-ebs.ubuntu"
  ]
}

You can see the full .pkr.hcl file here.

packer cli

Assuming you are inside the directory where you have the packer configuration file

There is no subcommand for deleting an image. You need to deregister (delete) the AMI from AWS directly, when you do it be sure to also delete the snapshot that was created.

provision images

In the above example we just repackage an image that already exists. Packer becomes useful when we can configure that image to our liking, installing different packages, setting up files, and then turning that into an image we can reuse across a fleet of servers.

Packer can do this provisioning automatically, so you can setup a pipeline that builds the image every time there is a trigger.

add provisioner to template

Continuing from the above example where we are using amazon-ebs, we can review that we are using the ssh communicator. Meaning that we will communicate to the ec2 instance via ssh. We are specifying a username (ssh_username = "ubuntu"), which means for provisioning packer will use a temporary ssh credentials.

Inside the build block we can add what we want to configure on the ec2 instance, by using a provisioner. We use the shell type which basically let us do whatever we want with a shell.


provisioner "shell" {
  environment_vars = [
    "FOO=hello world",
  ]
  inline = [
    "echo Installing Kubeadm",
    "sudo apt-get update",
    "sudo apt-get install -y kubeadm",
    "echo \"FOO is $FOO\" > example.txt",
  ]
}

You can have multiple of these. So your build block would look something like:


build {
  name = "learn-packer"
  sources = [
    "source.amazon-ebs.ubuntu"
  ]
  provisioner "shell" {
    environment_vars = [
      "FOO=hello world",
    ]
    inline = [
      "echo Installing Git",
      "sudo apt-get update",
      "sudo apt-get install -y git",
      "echo \"FOO is $FOO\" > example.txt",
    ]
  }
  provisioner "shell" {
    inline = [
      "echo \"Hello there my friend\"",
    ]
  }
}

Then you go ahead and packer build it and you are good to go.

variables

You can add variables to your templates in order to not modify stuff directly on the template every time you run it, say, modify a name, a version or whatever.

There are two types

Do notice that the way you define them is different, for the input each has its own block, and the type defined. For the local they are all inside a block.

You call them like you were using some type of fstrings.


"${var.ami_prefix}-${local.timestamp}"

build multiple images in parallel

You just need to create another source block, and add the name to the sources array in your build block.


build {
  name    = "learn-packer"
  sources = [
-    "source.amazon-ebs.ubuntu"
+    "source.amazon-ebs.ubuntu",
+    "source.amazon-ebs.ubuntu-focal"
  ]
  ## ...
}

~ table of contents

↑ go to the top