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" {
}
}
Sourcedefines the "where" of the configuration build, defining the platform and any specific parameters for the image to be build.Builddefines a unit of execution. Builds refer to 1 or more sources.- Within build, you define any number of
provisionersto carry out configuration tasks for the images. Post-Processorsare 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-isorequires 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
-xflag 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_commandacts as a prerequisite command to enable the image to run the scripts specified. - The substitutions in
{{ }}are substitutions. .Varscontains all environment variables needed to pass the command (and any specified as part of the provisioner).Pathcontains the path to the script Packer will be running, auto-populated.Inlineallows 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
scriptparameter, 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
onlyparameter 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
-onlyflag 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=truein the post_processor block.