Create custom infrastructure images with Packer

There are a number of ways to deploy custom applications on Triton. We've talked a lot about how easy it is to dockerize applications. Triton provides multiple hardware virtual machine (HVM) and infrastructure images (just run triton images to see what we offer) to meet all of your various application needs.

Although those images can be deployed into containers which can be customized individually, that extra work can be cumbersome and difficult to replicate. With Packer by Hashicorp, it's quick and easy to automate the customization of a Triton image which you can easily deploy into multiple instances and update as needed.

After installing Packer, we'll go over the three steps to create the configuration file, build an image, and deploy the container on Triton.

If you haven't already, be sure to sign up for Triton and set up the triton CLI tool.

You can also sign up and watch our on-demand webinar to see this process in action.

What is Packer?

Packer by Hashicorp

Packer makes it simple to automate the creation of any type of machine image from a single configuration file. With the help of scripts to install and configure software within your image, Packer allows you to run multiple containers without needing to tweak each individual one.

Packer is platform-agnostic. That means if you've got existing Packer templates which build images for other cloud providers, it's easy to adapt them to build those same images on Triton.

There are a number of terms to know for Packer, but here are the highlights:

  • Template: templates are configuration files for Packer images.
  • Builders: builders create machine images for individual platforms. Triton is a builder, using CloudAPI to create the image. The builder launches a temporary VM based on the template, runs any provisioning necessary, creates a reusable image, and then destroys the VM. This builder does not manage images; you must use or delete it with CloudAPI outside of Packer.
  • Provisioners: provisioners install and configure software within a running machine prior to the machine becoming a static image. They perform the work which customizes Triton images to contain software including installing packages, creating users, and downloading application code.
  • Artifacts: artifacts are the result of a single build, including a set of IDs or files which represent the final machine image. Every builder produces a single artifact. For the Triton builder, the artifact is the new image ID.

In an upcoming blog post, we'll talk about how to take your Packer images and add application infrastructure with Terraform to further automate deployments.

Installing Packer

Before you can create an image with Packer, you must install it. You have a few options:

The unzipped, downloaded file should live in Unix systems in the ~/packer directory or /usr/local/packer. For Windows, the location of the downloaded file doesn't matter.

Afterwards, you must set the PATH for your system.

Set PATH for macOS or Linux

Edit your bash profile (which may look like .bash_profile or .bashrc) to add the PATH and other environment variables, just as you've done for CloudAPI environment variables.

Add the following content:

export PATH=$PATH:/usr/local/packer

If you've installed Packer in a different directory, you must modify /usr/local/packer to reflect the correct information.

Set PATH for Windows

You can set the PATH by going to the Control Panel -> System -> Advanced System Settings. Under Environment Variables, scroll until you find PATH. Edit accordingly and be sure to include a semicolon at the end of any previously set paths. For example:


Create the template

When deciding what application to use for this post, I decided to find a very simple web-based project. Additionally, I hoped it would be weird and fun, but you can be the judge of that.

We'll be creating a template for an application that I'll dub the Cat Randomizer, an HTML+JS application by Bryce Osterhaus. If you like cat GIFs and random meows, you're in luck. Otherwise, you may want to keep your sound off once you've launched your application.

Before adding Packer, you'll need to either download and unzip the Cat Randomizer or fork the application on GitHub.

We'll also need to add a simple shell script which will modify the file structure of the Nginx image we'll use as our base.

Adding the shell script

Get into the directory of your local Cat Randomizer application. You can use whatever text-editor you're most comfortable with to create and edit the shell script and the following configuration files. Let's call our script

$ cd ~/cat-randomizer/$ touch

Our shell script is going to create the directories which match our application, for CSS and resources.

mkdir -p /usr/share/nginx/html/cssmkdir -p /usr/share/nginx/html/resources

That's it. Those two lines to create the necessary directories. We'll talk more about why this is important in our later step on provisioning.

Adding a Packer configuration file

The configuration file will determine the type of image we're building, written in JSON. Let's call ours cat-config.json.

$ touch cat-config.json

The contents of our file will begin with Triton environment variables for your account, followed by the builders and provisioners. If you're brazen, skip the walk-through and check out the finalized configuration file.

Variables are particularly useful when it comes to sensitive information, such as your account login and SSH key fingerprint. These are the same variables used for CloudAPI and other Triton tools. To access these variables, it's important to set the Triton environment before building your image.

{  "variables": {      "triton_url": "{{env `SDC_URL`}}",      "triton_account": "{{env `SDC_ACCOUNT`}}",      "triton_key_id": "{{env `SDC_KEY_ID`}}"  },

We'll be referring to those variables within our builder. As defined above, the Triton builder will build an image with the help of CloudAPI. In our builder, we will define the keys to accessing Triton (those above variables), SSH access, the base image on which to build our application, details about our new image.

NOTE: If you haven't set those environment variables with CloudAPI, you can replace the values with the corresponding information. For example:

{  "variables": {      "triton_url": "",      "triton_account": "jill",      "triton_key_id": "2e:c9:f9:89:ec:78:04:5d:ff:fd:74:88:f3:a5:18:a5"  },

Since the Cat Randomizer is a web-based application, I've chosen Nginx as a base with the smallest (and cheapest) available package, g4-highcpu-128M. For all available images, run triton images.

To get the "source_machine_image" (i.e. the image ID), execute triton image get with the image name.

Our custom image will be called cat_randomizer.

  "builders": [      {        "type": "triton",        "triton_url": "{{user `triton_url`}}",        "triton_account": "{{user `triton_account`}}",        "triton_key_id": "{{user `triton_key_id`}}",        "ssh_username": "root",        "ssh_private_key_file": "/Users//.ssh/id_rsa",        "source_machine_name": "nginx-1",        "source_machine_package": "g4-highcpu-128M",        "source_machine_image": "88cf77e4-1958-11e7-bda8-777cc817ade5",        "image_name": "cats_randomizer_packer",        "image_version": "1.0.0"      }  ],

Note: For your SSH key to be usable, it must be available through the ssh-agent.

Once our builder is in place, we can add provisioners to customize it. We'll be using two types of provisioners: shell and file.

The shell provisioner will call to the shell script, which ensures that the Nginx image matches the setup of our local application as to not break it.

The file provisioners upload all of our necessary application materials. This can be either a single file, such as index.html, or an entire directory, such as css/.

  "provisioners": [      {        "type": "shell",        "script": ""      },      {        "type": "file",        "source": "index.html",        "destination": "/usr/share/nginx/html/"      },      {        "type": "file",        "source": "container.json",        "destination": "/usr/share/nginx/html/"      },      {        "type": "file",        "source": "css/",        "destination": "/usr/share/nginx/html/css/"      },      {        "type": "file",        "source": "resources/",        "destination": "/usr/share/nginx/html/resources/"      }  ]}

All together this creates a single JSON file to build your Packer image.

Full Packer configuration file

The full Packer configuration file, cat-config.json, contains the following:

{  "variables": {      "triton_url": "{{env `SDC_URL`}}",      "triton_account": "{{env `SDC_ACCOUNT`}}",      "triton_key_id": "{{env `SDC_KEY_ID`}}"  },  "builders": [      {        "type": "triton",        "triton_url": "{{user `triton_url`}}",        "triton_account": "{{user `triton_account`}}",        "triton_key_id": "{{user `triton_key_id`}}",        "ssh_username": "root",        "ssh_private_key_file": "/Users//.ssh/id_rsa",        "source_machine_name": "nginx-1",        "source_machine_package": "g4-highcpu-128M",        "source_machine_image": "88cf77e4-1958-11e7-bda8-777cc817ade5",        "image_name": "cats_randomizer_packer",        "image_version": "1.0.0"      }  ],  "provisioners": [      {        "type": "shell",        "script": ""      },      {        "type": "file",        "source": "index.html",        "destination": "/usr/share/nginx/html/"      },      {        "type": "file",        "source": "container.json",        "destination": "/usr/share/nginx/html/"      },      {        "type": "file",        "source": "css/",        "destination": "/usr/share/nginx/html/css/"      },      {        "type": "file",        "source": "resources/",        "destination": "/usr/share/nginx/html/resources/"      }  ]}

Build the image

Once the template is completed, you're ready to build the image. But before we build it, it's important to validate the template and ensure that the JSON syntax and configuration values are correct.

First get into your default Triton environment:

eval "$(triton env)"

Then run packer validate with the name of the Packer template:

$ packer validate cat-config.jsonTemplate validated successfully.

Note: If the template was not successful, that means there is an error in your configuration file. The validate command should tell you where to find the error.

If you receive the success notice, you can move on to build the Cat Randomizer image. To do so, we'll be executing packer build with the name of our template file:

$ packer build cat-config.jsontriton output will be in this color.==> triton: Creating source machine...==> triton: Waiting for source machine to become available...==> triton: Waiting for SSH to become available...==> triton: Connected to SSH!==> triton: Provisioning with shell script:> triton: Uploading index.html => /usr/share/nginx/html/==> triton: Uploading container.json => /usr/share/nginx/html/==> triton: Uploading css/ => /usr/share/nginx/html/css/==> triton: Uploading resources/ => /usr/share/nginx/html/resources/==> triton: Stopping source machine (794f88bb-8198-cbde-e94a-c87e9730eae8)...==> triton: Waiting for source machine to stop (794f88bb-8198-cbde-e94a-c87e9730eae8)...==> triton: Creating image from source machine...==> triton: Waiting for image to become available...==> triton: Deleting source machine...==> triton: Waiting for source machine to be deleted...Build 'triton' finished.==> Builds finished. The artifacts of successful builds are:--> triton: Image was created: fc54ddf8-d0f4-478d-a2af-5a03776453eb

At the end of the output, you'll see a list of the artifacts from your build. For us, that's a single Triton image with the ID fc54ddf8-d0f4-478d-a2af-5a03776453eb. Check out your newly created image within the list of Triton images:

$ triton images SHORTID   NAME                      VERSION     FLAGS  OS       TYPE         PUBDATE25483bdc  nginx-1                   20160413    P      linux    lx-dataset   2016-04-13[...]fc54ddf8  cats_randomizer_packer    1.0.0       I      linux    lx-dataset   2017-10-23

The cats_randomizer_packer image is now available for you to deploy a container.

Deploy the container

After the image has been built on Triton, you can quickly spin up a new container. Once again, you should be in the Triton profile where the image was deployed:

eval "$(triton env)"

To spin up a new container with our Packer image, we'll be using triton instance create. For more on how to use that command, read our documentation on instance creation.

$ triton instance create -w --name=cats-randomizer cats_randomizer_packer g4-highcpu-128MCreating instance cats-randomizer (3cd96488-079f-639f-927a-df624ef1a79d, cats_randomizer_packer@1.0.0)Created instance cats-randomizer (3cd96488-079f-639f-927a-df624ef1a79d) in 38s

Let's check out our new application and make sure everything is running as expected. To do so, we'll need the IP address:

$ triton inst ip 3cd96488165.225.173.157

You can check out my live application at

Watch the webinar

Want to see this image creation in action? Sign up to watch our on-demand webinar for creating custom images with Packer.

Post written by Alexandra White