Getting Started with HashiCorp Packer¶
1.0 - Creating Basic Images¶
1.1 - Why Packer?¶
- Packer facilitates the development of immutable infrastructure.
- Cats vs Cattle Analogy
- "Cat" Servers:
- Require a lot of attention and specific configuration
- If server goes down, major problems occur.
- Lots of configuration required to maintain it, as otherwise the workloads supported are totalled.
-
"Cattle" Servers:
- Servers created to serve a specific purpose
- If the server goes down, not as problematic, another "cattle" can just be spun up quickly to replace it with no immediate difference.
-
Summary Comparison:
Cats | Cattle |
---|---|
Must be kept alive at all cost | Expendable |
Lots of manual intervention needed | Work out of the box |
Tough to scale | Easy to scale |
High-stress management | Low-stress |
- Other benefits to immutable infrastructure include:
- Testable infrastructure -> As infrastructure defined as code (Iac)
- Easily reproducible environments
- Infrastructure becomes a "unit of deployment" - deployed alongside the workloads
- Facilitates confidence in changes
Why Packer?¶
- Packer uses images as its packaging mechanism, offering specific packages per platform
- It's cross-platform, capable of running on and creating images for Windows and Linux
- It allows utilization of existing tools to manage and rollout of infrastructure
- Easily integrates with configuration management tools, such as Ansible, Chef, and standard scripting.
Packer Basics¶
- Packer is defined in HCL languages similar to Terraform.
- Images are built via the use of templates, which follow a standard format:
source "type" "name" {
}
build {
sources = ["source.type.name"]
provisioner "type" {
}
post-processor "type" {
}
}
Source
defines the "where" of the configuration build, defining the platform and any specific parameters for the image to be build.Build
defines a unit of execution. Builds refer to 1 or more sources.- Within build, you define any number of
provisioners
to carry out configuration tasks for the images. Post-Processors
are used to "transform" the image after building from one form to another. The most common example for this is "Vagrant".
Sources¶
- Source blocks will always contain:
- The type of source builder e.g.
amazon-ebs
,vmware-iso
- The local name of the build.
- Configuration parameters for thhe build:
- Each build type contains build-specific parameters e.g.:
- For AWS, one needs to provide an
ami_name
, whereasvmware-iso
requires ISO information.
Builds¶
- Combine sources with provisioning/post-processing.
- Multiple sources can be included in a single build, so could output multi-platform images with one run, or just do one particular one depending on the needs.
- This is often beneficial for say different environment builds.
Provisioners¶
- Used to customise the image
- Typically uses scripts (powershell or bash) or configuration management tools like Ansible.
- Provisioners can be configured to run only against particular sources e.g. "fetch ami name" is only applicable to AWS, there's no point running it for a virtualbox build.
Post-Processors¶
- Used to transform build outputs
- Examples include:
- Checksum generation
- Export to an AWS AMI or Vagrant Box
- Multiple post-processors can be used sequentially by usage of the
post_porcessors {}
block
Common Commands¶
packer fmt <template>.pkr.hcl
- Apply standard formattingpacker validate <template>.pkr.hcl
- Syntax validation of configurationpacker build <template>.pkr.hcl
- Build the image- Common options:
-debug
- pause after each provisioner step and post-processor, provides the SSH key for the machine to verify build process during build.-var
- supply a variable to the build in the form<key>=value
-only
- specify only a particular source-on-error
1.2¶
- Randomness can be introduced using Packer's function:
${uuidv4()}
2.0 - Adding Configuration, Secrets, and Multi-Platform Functionality¶
Provisioners¶
- Provisioners are typically used to help configure Packer images to particular needs.
- This is typically achieved by allowing files to be used, or:
- Running configuration management tools (Ansible, Chef, etc)
- Scripts (Powershell, Bash, etc.) on the image being built or the local system
- Manage any files to be used by or extracted from the image being built
Provisioners - File¶
- Used to upload assets, config files, etc. to the image.
- Full directories can be uploaded / downloaded, files included.
- Multiple connector methods available to support this.
- Note:
- The "Packer User" does not have root privileges, it is advised to move any files to a temporary directory before further manipulation in the image, the latter set of operations can be achieved by a script.
- This provisioner cannot change the permissions of the files, a script(s) should be used for this.
Example¶
source "amazon-ebs" "build" {
ssh_username = "ubuntu"
ami_name = "globoticket-${uuidv4()}"
source_ami = "<ami id>"
instance_type = "t3.micro"
}
build {
sources = ["source.amazon-ebs"]
provisioner "file" {
source = "config/nginx.service"
destination = "/tmp/"
}
provisioner "file" {
source = "config/nginx.conf"
destination = "/tmp/"
}
provisioner "file" {
source = "assets"
destination = "/tmp/"
}
}
Provisioners - Script¶
- Allows running of any scripts used to run programs on the image or local system, with many options available for differing OS. Examples include:
- Local Shell
- Run scripts on the local machine, typically used for "prerequisite" actions for other provisioners.
- Remote Shell
- Typically used with Linux machines - runs commands on image.
- Powershell
- Like remote shell, but used to run Powershell scripts.
- Windows Shell
- Used for bash scripts.
-
Windows Restart
- Causes packer to reboot the VM without losing connectivity.
-
Note: When running scripts like this in automation, add the
-x
flag to the shebang, which outputs the commands being ran in standard output. -
Example usage, adding beyond the "file" provisioners previously.
build {
...
provisioner "shell" {
execute_command = "sudo -S env {{.Vars }} {{ .Path }}"
inline = [
"mkdir -p /var/globoticket",
"mv /tmp/nginx.conf /var/globoticket/",
"mv /tmp/nginx.service /etc/systemd/system/nginx.service",
"mv /tmp/assets /var/globoticket"
]
}
provisioner "shell" {
execute_command = "sudo -S env {{.Vars }} {{ .Path }}"
script = "scripts/build_nginx_webapp.sh"
}
...
}
- In the example above:
execute_command
acts as a prerequisite command to enable the image to run the scripts specified. - The substitutions in
{{ }}
are substitutions. .Vars
contains all environment variables needed to pass the command (and any specified as part of the provisioner).Path
contains the path to the script Packer will be running, auto-populated.Inline
allows each command to be listed as an array, separated by commas. This is ok for simple operations, but scripts are better for usage.- When using the
script
parameter, Packer looks to the directory defined, which must be relative to the directory that Packer is being run from (or specify a full path (not recommended)).
Data Sources¶
- Data sources are sources of information that can be referenced by Packer to obtain information to be used in image builds. A common example of this is looking up base AMIs in AWS, or looking up secrets manager
data "amazon-ami" "globoticket" {
filters = {
virtualization_type = "hvm"
name = "ubuntu/images/*ubuntu-focal-20.04-amd64-server-*"
root_device_type = "ebs"
}
owners = ["<numeric id>"]
most_recent = true
}
## Reference the data
source_ami = data.amazon-ami.globoticket.id
data "amazon-secretsmanager" "globoticket-live" {
name = "Globoticket-live"
key = "SECRET_ARTIST_NAME"
}
## Reference the secret in `environment`
provisioner "shell" {
...
environment_vars = ["SECRET_ARTIST_NAME=${data.amazon-secretsmanager.globoticket.value}"]
...
}
Multi-Provider Builds¶
- Packer can allow simultaneous generation of images on different providers, such as:
- VMWare
- Virtualbox
- GCP
-
Docker
-
There are caveats to this:
- Some sources are more complex than others
- Host limitations may become apparent -> may need to run one at a time
-
Outputs can become complex if not managed properly
-
For certain providers, you may wish to install provider-specific utilities. Like VMware guest additions.
- This can be achieved by using another provisioner, and using the
only
parameter to specify it should only run against the specific build
provisioner "shell" {
execute_command = "sudo -S env {{.Vars }} {{ .Path }}"
script = "scripts/virtualbox.sh"
only = ["virtualbox-iso.globoticket]
}
- Unless specified, packer will build all sources in parallel, to specify, use the
-only
flag on the CLI:packer build -only 'type.name' template.pkr.hcl
Post-Processors¶
- Commone examples:
- Compress
- Import to Cloud
- Generate image checksum
- Generate Manifest
-
Create a Vagrant Box
-
For vagrant, one may need to ensure Vagrant is installed to allow the box to be created, steps on this are provided in the documentation.
- If wanting to keep the input artifact, set
keep_input_artifact=true
in the post_processor block.