Using the Linode Packer Builder to Create Custom Images

Traducciones al Español
Estamos traduciendo nuestros guías y tutoriales al Español. Es posible que usted esté viendo una traducción generada automáticamente. Estamos trabajando con traductores profesionales para verificar las traducciones de nuestro sitio web. Este proyecto es un trabajo en curso.
Create a Linode account to try this guide with a $100 credit.
This credit will be applied to any valid services used during your first 60 days.

Introduction to Packer

Packer is a HashiCorp maintained open source tool that is used to create machine images. A machine image provides the operating system, applications, application configurations, and data files that a virtual machine instance will run once it’s deployed. Packer can be used in conjunction with common configuration management tools like Chef, Puppet, or Ansible to install software to your Linode and include those configurations into your image.

Packer templates store the configuration parameters used for building an image. This standardizes the imaging building process and ensures that everyone using that template file will always create an identical image. For instance, this can help your team maintain an immutable infrastructure within your continuous delivery pipeline.

The Linode Packer Builder

In Packer’s ecosystem, builders are responsible for building a system and generating an image from that system. Packer has multiple different types of builders, with each one being used to create images for a specific platform.

The Linode builder integrates Packer with the Linode platform. This allows Packer to deploy a temporary Linode on your account (using an APIv4 token), configure the system on the Linode according to the parameters in the provided template file, and then create an image based on that Linode. Essentially, this is a convenient way to automatically create Linode Images on your account that can be used for rapidly deploying new Linodes.

Before You Begin

This guide will walk you through the process of installing Packer, creating a template file, building the image, and then deploying that image onto a new Linode. Going further, it will also cover how to use the Ansible tool with Packer. Before you begin, review the following:

  1. Ensure you have access to cURL on your computer.

  2. Generate a Linode API v4 access token with read/write permission for both Linodes and Images. You can follow the Get an Access Token section of the Getting Started with the Linode API guide if you do not already have one.

  3. Optional: Set a variable named TOKEN in your shell environment by running the following command. Replace x with your own API token.

    export TOKEN=x
    
    Note
    Some of the example commands provided in this guide will use this variable. If you do not set this variable, you will need to modify these commands by replacing $TOKEN with your API token.

Installing Packer

The following instructions will install the latest version of Packer on Mac, Ubuntu, or CentOS. For more installation methods, including installing on other operating systems or compiling from source, see Packer’s official documentation and the Download Packer web page.

Mac

To install Packer on Mac, Homebrew will be used. Run the following commands on your terminal:

brew tap hashicorp/tap
brew install hashicorp/tap/packer

Ubuntu

curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install packer

CentOS/RHEL

sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install packer

Verifying the Installation

Verify that Packer was successfully installed by running the command packer --version. This should output the version number for this installation of Packer. For reference, this guide was last tested using version 1.7.2.

Constructing a Template for Packer

Now that Packer is installed, you can make a Packer template. A template is a file that contains the configurations needed to build a machine image. A template can be formatted in JSON or HCL2 (Hashicorp Configuration Language). As of Packer v1.7.0, the HCL2 template format is preferred and, as such, will be used in the examples within this guide.

Note
The steps in this section will incur charges related to deploying a 1GB Linode (Nanode). The Linode will only be deployed for the duration of the time needed to create and snapshot your image and will then be deleted. See our Billing and Payments guide for details about hourly billing.

Creating the Template File

Create a file named example.pkr.hcl. The file can be stored anywhere, though you may want to create a folder called packer in your home directory where you can store all of your template files. Edit this file and type or paste in the following content:

File: ~/packer/example.pkr.hcl
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
variable "linode_api_token" {
  type    = string
  default = ""
}

source "linode" "example" {
  image             = "linode/debian10"
  image_description = "This image was created using Packer."
  image_label       = "packer-debian-10"
  instance_label    = "temp-packer-debian-10"
  instance_type     = "g6-nanode-1"
  linode_token      = "${var.linode_api_token}"
  region            = "us-east"
  ssh_username      = "root"
}

build {
  sources = ["source.linode.example"]
}

Understanding Template Blocks

An HCL2 formatted template file typically contains several blocks. A block can define a variable, specify the exact parameters for a builder plugin, or outline what should happen when the template is built. Here’s a few blocks that will be used most templates:

Variable Block

A variable block contains a single user-defined variable along with any additional parameters or values. There can be multiple variable blocks in a template to define multiple variables. The value of a variable can be used elsewhere in the template through the syntax ${var.variable_name}, where variable_name is the name given to the variable.

This example only contains one variable: linode_api_token. The value of this variable is intentionally left blank (default = ""). Instead of setting the variable within the template, it will be set through the command-line when running the packer build command: packer build -var linode_api_token=x example.pkr.hcl, where x is your API token.

Learn more about Packer template variables on the Variables page of Packer’s documentation.

Source Block (the Linode Builder)

A source block defines the parameters for a builder plugin. These sources can then be used within the build block. Multiple sources can be defined in a template, allowing you to potentially create multiple images for multiple platforms. A full list of available builders can be found on the Builders page of Packer’s documentation.

The starting line of each source block will contain the builder plugin to be used as well as the name given to this particular source in the template. For instance, the starting line for the source in this example (source "linode" "example" {) specifies that the Linode builder will be used and this source will be named “example”.

Parameters for the Linode Builder

This example uses the Linode Packer builder as a source. Each of the parameters within the source block are outlined on the Linode Builder page within Packer’s documentation.

  • image: The ID of the “starter” image to use. This can be one of the official Linode images or any private custom images on your account. In this example, we’ll use linode/debian10 to specify the official Linode Debian 10 image. You can view all the images available to you by running the following curl command:

    curl -H "Authorization: Bearer $TOKEN" https://api.linode.com/v4/images
    
  • image_label (optional): The label for the new custom image this template will generate.

  • image_description (optional): A brief description for the new custom image.

  • instance_label (optional): The label for the temporary Linode that Packer will deploy in order to create the new image.

  • instance_type The ID of the instance type for this temporary Linode. This template specifies g6-nanode-1, which is the ID for a 1GB Linode (a Nanode). In most cases, a Nanode should work well for generating images as it comes with 25GB of disk space. You can view all of the Linode types by running the following curl command:

    curl https://api.linode.com/v4/linode/types
    
  • linode_token: The Linode API Personal Access Token that is used to provider Packer with access to your account. This is set to ${var.linode_api_token} in the template since we’re going to use a command-line variable to provide this token rather than saving it directly in the template.

  • region: The ID of the data center. In this template, the ID for the Newark data center is used: us-east. You can view all of the regions by running the following curl command:

    curl https://api.linode.com/v4/regions
    

Build Block

The build block tells Packer what to do when the template is built. This will reference a source and optionally specify a provisioner or post-processor.

Building the Image

After the template file has been saved with your desired parameters, you’re now ready to build the image.

  1. First, validate the template by running the packer validate command below. If you did not set TOKEN as a variable in your shell environment, replace $TOKEN with your own Linode API token. If successful, no errors will be given.

    packer validate -var linode_api_token=$TOKEN example.pkr.hcl
    
    Note
    To learn how to securely store and use your API v4 token, see the Vault section of Packer’s documentation.
  2. Build the image by running the packer build command below. Just like in the last step, if you did not set TOKEN as a variable in your shell environment, replace $TOKEN with your own Linode API token. This process may take a few minutes to complete.

    packer build -var linode_api_token=$TOKEN example.pkr.hcl
    

    The output of this command will outline each process that Packer goes through. Once finished, the last line will provide you with the ID for the new custom image.

Deploying a Linode with the New Image

Once the Packer build process completes, a new Custom Image will appear on your account. This image can be deployed a few ways:

  • Cloud Manager: Use the Cloud Manager to deploy a Custom Image by following the Deploy an Image to a New Compute Instance guide.

  • Linode CLI: Use the Linode CLI through the command-line by following the Using the Linode CLI guide. The Linode Instances section of that guide will provide example commands. The command below will deploy a new Linode in the Newark data center. Replace mypassword with the root password you’d like to use and linode/debian10 with the ID of your new image.

    linode-cli linodes create --root_pass mypassword --region us-east --image linode/debian10
    
  • Linode APIv4: Use the Linode API to programmatically create a new Linode by reviewing the documentation outlined under API > Linode Instances > Linode Create. The following example curl command will deploy a 1GB Linode (Nanode) to the Newark data center. Ensure you replace any necessary parameters, including replacing linode/debain10 with your Custom Image’s ID and assigning your own root_pass and label.

    curl -H "Content-Type: application/json" \
        -H "Authorization: Bearer $TOKEN" \
        -X POST -d '{
          "image": "private/7550080",
          "root_pass": "aComplexP@ssword",
          "booted": true,
          "label": "my-example-label",
          "type": "g6-nanode-1",
          "region": "us-east"
        }' \
        https://api.linode.com/v4/linode/instances
    

Going Further with Ansible

Packer is extremely powerful and customizable tool for creating images. The first template outlined in this guide is a minimalist example and doesn’t showcase the true potential of Packer. To take things further, this section will cover integrating Packer with Ansible. Ansible is one of many different options available for customizing an image in Packer.

Ansible is an automation tool for server provisioning, configuration, and management. Before continuing, follow the Getting Started With Ansible - Basic Installation and Setup guide to install Ansible and familiarize yourself with basic Ansible concepts.

Creating the Ansible Playbook

An Ansible playbook outlines the tasks and scripts to be run when provisioning a server. You will create a playbook that adds a limited user account on the Linode before Packer creates the final image.

  1. Use the mkpasswd utility (available on many Linux systems) to generate a hashed password. This will be used when configuring Ansible’s user module for your limited user account.

    mkpasswd --method=sha-512
    

    You will be prompted to enter a plain-text password and the utility will return a hash of the password.

  2. Create the playbook file with the following content. Replace username with the username you’d like to add and replace password with the password hash generated in the previous step.

    File: ~/packer/limited_user_account.yml
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    ---
    - hosts: all
      remote_user: root
      vars:
        NORMAL_USER_NAME: 'username'
      tasks:
        - name: "Create a secondary, non-root user"
          user: name={{ NORMAL_USER_NAME }}
                password='$6$eebkauNy4h$peyyL1MTN7F4JKG44R27TTmbXlloDUsjPir/ATJue2bL0u8FBk0VuUvrpsMq6rSSOCm8VSip0QHN8bDaD/M/k/'
                shell=/bin/bash
        - name: Add remote authorized key to allow future passwordless logins
          authorized_key: user={{ NORMAL_USER_NAME }} key="{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
        - name: Add normal user to sudoers
          lineinfile: dest=/etc/sudoers
                      regexp="{{ NORMAL_USER_NAME }} ALL"
                      line="{{ NORMAL_USER_NAME }}"

    This playbook will also add the public SSH key stored on your local computer. If the public key you’d like to use is stored in a location other than ~/.ssh/id_rsa.pub, you can update that value. Finally, the playbook adds the new system user to the sudoers file.

Modifying or Creating a New Template File

Edit your existing template file or create a new template file with the following content. Specifically, you’ll add a provisioner block within the build block, setting ansible as the type of provisioner and providing the location of the playbook file you created.

File: ~/packer/ansible-example.pkr.hcl
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
variable "linode_api_token" {
  type    = string
  default = ""
}

source "linode" "ansible-example" {
  image             = "linode/debian10"
  image_description = "This image was created using Packer."
  image_label       = "packer-advanced-debian-10"
  instance_label    = "temp-packer-debian-10"
  instance_type     = "g6-nanode-1"
  linode_token      = "${var.linode_api_token}"
  region            = "us-east"
  ssh_username      = "root"
}

build {
  sources = ["source.linode.ansible-example"]

  provisioner "ansible" {
    playbook_file = "./limited_user_account.yml"
  }
}

Understanding the Provisioner Block

A provisioner allows you to further configure your system by completing common system administration tasks, like adding users, installing and configuring software, and more. The example uses Packer’s built-in Ansible provider and executes the tasks defined in the local limited_user_account.yml playbook. This means your Linode image will also contain anything executed by the playbook. Packer supports several other provisioners, like Chef, Salt, and shell scripts. Learn more about Packer provisioners on the Provisioner page of Packer’s documentation.

Building and Deploying the Image

Follow the previous sections for building the image and deploying the image. Once a new Linode is deployed using the newly created image, you should be able to log in to that Linode over ssh by running the following command. Replace username with the username you specified in the Ansible playbook and replace 192.0.2.0 with the IPv4 address of your new Linode.

ssh username@192.0.2.0

Next Steps

If you’d like to learn how to use Terraform to deploy Linodes using your Packer created image, you can follow our Terraform guides to get started:

This page was originally published on


Your Feedback Is Important

Let us know if this guide made it easy to get the answer you needed.


Join the conversation.
Read other comments or post your own below. Comments must be respectful, constructive, and relevant to the topic of the guide. Do not post external links or advertisements. Before posting, consider if your comment would be better addressed by contacting our Support team or asking on our Community Site.