Use Ansible and Packer to create a Docker image
To create a Docker image, you usually use Docker CLI with the docker build
command. One of the advantages is that this command creates one layer per line of the Dockerfile.
However, when you create several Docker images with common code, it may be appropriate to use the Ansible role concept.
Packer helps us to integrate Ansible into the process of building a Docker image. It is then possible to share common parts of Docker images via Ansible roles. But the disadvantage is that you no longer benefit from Docker’s layer-based caching mechanism.
Install Packer on MacOS
Install the repository of the packages Homebrew, tap
:
brew tap hashicorp/tap
The output is similar to:
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 3 taps (homebrew/core, homebrew/cask and homebrew/services).
==> New Formulae
fleet-cli
==> Updated Formulae
Updated 249 formulae.
==> Updated Casks
ajour garagesale postbox trilium-notes
android-studio gns3 proclaim unity-android-support-for-editor
anydesk insync pycharm-ce-with-anaconda-plugin unity-ios-support-for-editor
appstudio macupdater pycharm-with-anaconda-plugin unity-lumin-support-for-editor
badlion-client milanote razorsql unity-webgl-support-for-editor
box-drive minizincide stella unity-windows-support-for-editor
camo-studio musescore streamlabs-obs waterfox-classic
chromedriver neofinder sunloginclient webcatalog
cleanshot nordvpn-teams telegram-desktop wire
cmake obyte tempo workflowy
disk-drill origami-studio tinderbox xmplify
fsnotes osxfuse transmission
==> Tapping hashicorp/tap
Cloning into '/usr/local/Homebrew/Library/Taps/hashicorp/homebrew-tap'...
remote: Enumerating objects: 123, done.
remote: Counting objects: 100% (123/123), done.
remote: Compressing objects: 100% (99/99), done.
remote: Total 532 (delta 54), reused 70 (delta 24), pack-reused 409
Receiving objects: 100% (532/532), 107.08 KiB | 275.00 KiB/s, done.
Resolving deltas: 100% (221/221), done.
Tapped 6 formulae (42 files, 181.9KB).
Install Packer:
brew install hashicorp/tap/packer
The output is:
==> Installing packer from hashicorp/tap
==> Downloading https://releases.hashicorp.com/packer/1.6.4/packer_1.6.4_darwin_amd64.zip
######################################################################## 100.0%
packer --version🍺 /usr/local/Cellar/packer/1.6.4: 3 files, 140.9MB, built in 8 seconds
Verify the installation:
packer --version
The output is:
1.6.4
Create the configuration file of Packer
Let’s create the configuration file of Packer, ubuntu.json
, with this content:
{
"builders": [
{
"name": "docker",
"type": "docker",
"image": "ubuntu:16.04",
"commit": true
}
],
"provisioners": [
{
"type": "shell",
"script": "install_python.sh"
},
{
"type": "ansible",
"playbook_file": "playbook.yaml"
}
],
"post-processors": [
{
"type": "docker-tag",
"repository": "my-image",
"tag": "1.0"
}
]
}
Create the script install_python.sh
to install Python :
#!/bin/bashapt-get update
apt install -y python
Create the Ansible playbook playbook.yaml
:
---
- hosts: all
tasks:
- name: message
debug: msg="Container {{ inventory_hostname }}"
- name: install
package: name=git state=latest
Launch Packer to generate the image:
packer build ubuntu.json
The output is:
docker: output will be in this color.==> docker: Creating a temporary directory for sharing data...
==> docker: Pulling Docker image: ubuntu:16.04
docker: 16.04: Pulling from library/ubuntu
docker: Digest: sha256:185fec2d6dbe9165f35e4a1136b4cf09363b328d4f850695393ca191aa1475fd
docker: Status: Image is up to date for ubuntu:16.04
docker: docker.io/library/ubuntu:16.04
==> docker: Starting docker container...
...
==> docker: Committing the container
docker: Image ID: sha256:d43b3a7f77a427879b44f6d86720a63c5dbf5834175fa2a2b6ea628f87c14d33
==> docker: Killing the container: 1fdf6787d2cb456ccd9cb3c5d8ae65383bce83bff7c27861af24ed766bcdf882
==> docker: Running post-processor: docker-tag
==> docker (docker-tag): Deprecation warning: "tag" option has been replaced with "tags". In future versions of Packer, this configuration may not work. Please call `packer fix` on your template to update.
docker (docker-tag): Tagging image: sha256:d43b3a7f77a427879b44f6d86720a63c5dbf5834175fa2a2b6ea628f87c14d33
docker (docker-tag): Repository: my-image:1.0
Build 'docker' finished after 1 minute 23 seconds.==> Wait completed after 1 minute 23 seconds==> Builds finished. The artifacts of successful builds are:
--> docker: Imported Docker image: sha256:d43b3a7f77a427879b44f6d86720a63c5dbf5834175fa2a2b6ea628f87c14d33
--> docker:
Let’s verify that the original image ubuntu:16.04
doesn’t include the git
command:
docker run -it ubuntu:16.04 sh -c "git"
The output is:
sh: 1: git: not found
Let’s verify that the new image includes the git
command:
docker run -it my-image:1.0 -c "git --version"
The output is:
git version 2.7.4
The new way to create Packer configuration file
A new way to create configuration files is to use HCL. HCL is already used for example by Terraform.
These files must have the extension .pkr.hcl
. Here is the equivalent of our ubuntu.json
file in HCL :
variable "tag" {
type = string
}source "docker" "ubuntu" {
commit = true
image = "ubuntu:16.04"
}build {
sources = ["source.docker.ubuntu"] provisioner "shell" {
script = "install_python.sh"
}
provisioner "ansible" {
playbook_file = "playbook.yaml"
}
post-processor "docker-tag" {
repository = "my-image"
tag = [ var.tag ]
}
}
We have also made an improvement by setting the Docker image tag as a parameter. The value of this tag is now provided as a parameter when calling the packer :
packer build -var="tag=1.0.0" packer.pkr.hcl
Fortunately, to help you switch from JSON format to HCL format, Packer provides a converter that you can use like this :
packer hcl2_upgrade ubuntu.json
Packer then generates a new file corresponding to the conversion of the JSON file passed to the :
Successfully created ubuntu.json.pkr.hcl
Rename this file:
mv ubuntu.json.pkr.hcl ubuntu.pkr.hcl
Then you get the file ubuntu.pkr.hcl
.

My book DevOps Patterns Guide on Amazon
https://www.amazon.com/DevOps-Patterns-Guide-Bruno-Delb-ebook/dp/B08QSMX9LC
More articles on my blog http://www.DevOpsTestLab.com.
My DevOpsTestLab Youtube channel.
My LinkedIn profile: https://fr.linkedin.com/in/brunodelb