Build an image
With Packer installed, it is time to build your first image. For speed, simplicity's sake, and because it's free to download, you will build a Docker container. You can download Docker here if you don't have it installed.
Write Packer template
A Packer template is a configuration file that defines the image you want to build and how to build it. Packer templates use the Hashicorp Configuration Language (HCL).
Create a new directory named packer_tutorial
. This directory will contain your Packer template for this tutorial.
$ mkdir packer_tutorial
Navigate into the directory.
$ cd packer_tutorial
Create a file docker-ubuntu.pkr.hcl
.
$ touch docker-ubuntu.pkr.hcl
Add the following HCL block to it and save the file.
packer { required_plugins { docker = { version = ">= 1.0.8" source = "github.com/hashicorp/docker" } }} source "docker" "ubuntu" { image = "ubuntu:jammy" commit = true} build { name = "learn-packer" sources = [ "source.docker.ubuntu" ]}
This is a complete Packer template that you will use to build an Ubuntu Docker image. In the following sections, you will review each block of this template in more detail.
Packer Block
The packer {}
block contains Packer settings, including specifying a required Packer version.
In addition, you will find required_plugins
block in the Packer block, which specifies all the plugins required by the template to build your image. Even though Packer is packaged into a single binary, it depends on plugins for much of its functionality. Some of these plugins, like the Docker Builder which you will to use, are built, maintained, and distributed by HashiCorp — but anyone can write and use plugins.
Each plugin block contains a version and source
attribute. Packer will use these attributes to download the appropriate plugin(s).
- The
source
attribute is only necessary when requiring a plugin outside the HashiCorp domain. You can find the full list of HashiCorp and community maintained builders plugins in the Packer Builders documentation page. - The version attribute is optional, but we recommend using it to constrain the plugin version so that Packer does not install a version of the plugin that does not work with your template. If you do not specify a plugin version, Packer will automatically download the most recent version during initialization.
In the example template above, Packer will use the Packer Docker builder plugin that is greater than or equal to version 1.0.8
.
Source block
The source
block configures a specific builder plugin, which is then invoked by a build
block. Source blocks use builders and communicators to define what kind of virtualization to use, how to launch the image you want to provision, and how to connect to it. Builders and communicators are bundled together and configured side-by-side in a source block. A source
can be reused across multiple builds, and you can use multiple source
s in a single build. A builder plugin is a component of Packer that is responsible for creating a machine and turning that machine into an image.
A source block has two important labels: a builder type and a name. These two labels together will allow us to uniquely reference sources later on when we define build runs.
In the example template, the builder type is docker
and the name is ubuntu
.
source "docker" "ubuntu" { image = "ubuntu:jammy" commit = true}
Each builder has its own unique set of configuration attributes. The Docker builder starts a Docker container, runs provisioners within this container, then exports the container for reuse or commits the image.
In the example template, the Docker builder configuration creates a new Docker image using ubuntu:jammy
as the base image, then commits the container to an image.
Tip
The example template does not configure any communicators, because the Docker builder is a special case where Packer can't use a typical ssh
or winrm
connection. To see an example of configuring a communicator, refer to the AWS EBS example.
The Build Block
The build
block defines what Packer should do with the Docker container after it launches.
In the example template, the build block references the Docker image defined by the source block above (source.docker.ubuntu
).
build { name = "learn-packer" sources = [ "source.docker.ubuntu" ]}
Tip
In later tutorials, you will add the provision
block (define additional provisioning steps) and post-process
block (define what do to with the build artifacts) to the build block.
Initialize Packer configuration
Initialize your Packer configuration.
$ packer init .
Packer will download the plugin you've defined above. In this case, Packer will download the Packer Docker plugin that is greater than or equal to version 1.0.8
.
You can run packer init
as many times as you'd like. If you have already have the plugins you need, Packer will exit without an output.
Packer has now downloaded and installed the Docker plugin. It is ready to build the Docker image!
Format and validate your Packer template
We recommend using consistent formatting in all of your template files. The packer fmt
command updates templates in the current directory for readability and consistency.
Format your template. Packer will print out the names of the files it modified, if any. In this case, your template file was already formatted correctly, so Packer won't return any file names.
$ packer fmt .
You can also make sure your configuration is syntactically valid and internally consistent by using the packer validate
command.
Validate your template. If Packer detects any invalid configuration, Packer will print out the file name, the error type and line number of the invalid configuration. The example configuration provided above is valid, so Packer will return nothing.
$ packer validate .
Build Packer image
Build the image with the packer build
command. Packer will print output similar to what is shown below.
$ packer build docker-ubuntu.pkr.hcllearn-packer.docker.ubuntu: output will be in this color. ==> learn-packer.docker.ubuntu: Creating a temporary directory for sharing data...==> learn-packer.docker.ubuntu: Pulling Docker image: ubuntu:jammy learn-packer.docker.ubuntu: jammy: Pulling from library/ubuntu learn-packer.docker.ubuntu: 4007a89234b4: Pulling fs layer## ... learn-packer.docker.ubuntu: Digest: sha256:bb84bbf2ff36d46acaf0bb0c6bcb33dae64cd93cba8652d74c9aaf438fada438 learn-packer.docker.ubuntu: Status: Downloaded newer image for ubuntu:jammy learn-packer.docker.ubuntu: docker.io/library/ubuntu:jammy==> learn-packer.docker.ubuntu: Starting docker container... learn-packer.docker.ubuntu: Run command: docker run -v /Users/youruser/.packer.d/tmp819964324:/packer-files -d -i -t --entrypoint=/bin/sh -- ubuntu:jammy learn-packer.docker.ubuntu: Container ID: 6e18a8066f5f9b758a35a257bb7a08a2e06b5b257d51d3599f15466ccab74691==> learn-packer.docker.ubuntu: Using docker communicator to connect: 172.17.0.2==> learn-packer.docker.ubuntu: Committing the container learn-packer.docker.ubuntu: Image ID: sha256:13d5a06b9ad37668669ecef6b8ddcaa81c0e6bcacc140f6e954268e8181ad140==> learn-packer.docker.ubuntu: Killing the container: 6e18a8066f5f9b758a35a257bb7a08a2e06b5b257d51d3599f15466ccab74691Build 'learn-packer.docker.ubuntu' finished after 32 seconds 28 milliseconds. ==> Wait completed after 32 seconds 29 milliseconds ==> Builds finished. The artifacts of successful builds are:--> learn-packer.docker.ubuntu: Imported Docker image: sha256:13d5a06b9ad37668669ecef6b8ddcaa81c0e6bcacc140f6e954268e8181ad140
List all the Docker images to confirm that Packer successfully built your Docker image.
$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZE<none> <none> 13d5a06b9ad3 About a minute ago 134MB
Congratulations — you built your first image using Packer!
Managing the Image
Packer only builds images. It does not attempt to manage them in any way. After they're built, it is up to you to launch or destroy them as you see fit.
You can delete your Docker image by running the following command. Replace IMAGE_ID
with the image ID return by the Packer output.
$ docker rmi IMAGE_ID
Next steps
In this tutorial, you launched a Docker container and saved it as an image. Although you essentially repackaged an existing image, you should now have a general idea of how Packer works, what templates are and how to validate and build templates into machine images. Now that you have create your first Packer image, continue to the next tutorial to install and configure software into your image with provisioners.
Refer to the following resources for additional details on the concepts covered in this tutorial:
- Read more about the Docker builder block.
- Learn more about the
packer init
andpacker build
commands. - For more information about Packer-specific HCL blocks, refer to the Packer HCL documentation page.