Creating Operating System Images with Packer

Infrastructure as code (IaC) has become an important requirement for the life cycle management of software applications, mainly for those who are deploying, maintaining, and supporting the infrastructure where applications will be installed. High availability and faster deployments are very important principles for every company, and they have traditionally been provided with the help of configuration management tools that allow teams to automate and control infrastructure configuration. But what if we could improve this process by automating the operating system (OS) installation and configuration and then creating a preconfigured image that could be reused at any time? Well, that is what Packer can do for us.

What is an Operating System Image?

An operating system (OS) image is a static template that contains a preconfigured OS with applications and specific settings. This template can be cloned to create new running machines that are already set up with everything you need.

What is Packer?

Packer is a binary program written in Golang (or “Go”) that can automate the process of creating OS images for multiple platforms. This tool uses a JSON template with OS-configuration data to create an OS image. 

Currently, Packer supports the creation of OS images for various platforms like Amazon Web Services, Google Cloud Platform, Azure, Digital Ocean, and VMware, among others.

Platforms supported by Packer for the creation of OS images

Packer Template

Packer runs a simple JSON file containing different sections that each perform a specific task to install and configure the operating system image. The Packer template sections are the following:

• Variables: Packer allows for variables to be defined for settings that might change every time a new image is built. Variables can be passed as arguments into Packer, exported as environment variables, or even obtained from Ansible Vault.

• Builders: This section is responsible for creating machines and generating images from them for various platforms. Here you can define multiple builders (AWS, Google, Azure, etc.) and also customize the specific settings available for them such as storage, CPU, RAM, OS type, region, machine type, etc.

• Provisioners: The provisioners contain an array of all the tools that Packer needs to use to configure software within running machines prior to turning them into machine images. Multiple provisioners are supported like Ansible, Puppet, Shell, PowerShell, etc.

• Post-Processors: These are used to define actions that will be performed on the image once it has been created by the builders. Examples include compressing files, uploading artifacts, and importing images to other platforms, among others.

Creating an Operating System Image with Packer

1. Create a Packer template.

2. Add the Amazon Web Services and Google Cloud Platform builders.

        a. The CentOS 7 vanilla image will be the source image to use.

3. Provide the OS with a shell script to install and start the Nginx service.

4. Run Packer and build the images.

Prerequisites

The Packer template that we are creating supports two builders: Amazon Web Services and Google Cloud Platform. If you want to create an image for both of them, you must follow the prerequisites for each builder. For both options, however, you will need to follow the general prerequisites below. 

1. Create a new folder to store the Packer binary and Packer template in.

2. Download the Packer binary.

3. Create the Packer template (copy and paste).

        a. Choose whatever name you want for the template (e.g. template.json).

{
 "variables": {
   "name": "{{env `IMAGE_NAME`}}",
   "subnet_id": "{{env `AWS_SUBNET_ID`}}",
   "access_key": "{{env `AWS_ACCESS_KEY`}}",
   "secret_key": "{{env `AWS_SECRET_KEY`}}",
   "project_id": "{{env `GCP_PROJECT_ID`}}",
   "account_file": "{{env `GCP_SERVICE_ACCOUNT`}}"
 },
 "sensitive-variables": ["access_key", "secret_key", "account_file"],
 "builders": [
   {
     "name": "aws",
     "type": "amazon-ebs",
     "access_key": "{{user `access_key`}}",
     "secret_key":"{{user `secret_key`}}",
     "ami_name": "{{user `name`}}",
     "region": "us-east-1",
     "instance_type": "t2.micro",
     "ssh_username": "centos",
     "subnet_id": "{{user `subnet_id`}}",
     "run_tags": {
       "Name": "{{user `name`}}"
     },
     "tags": {
       "OS_Version": "CentOS 7 (x86_64)",
       "Base_AMI_Name": "{{ .SourceAMIName }}"
     },
     "source_ami_filter": {
       "filters": {
         "owner-alias": "aws-marketplace",
         "product-code": "aw0evgkw8e5c1q413zgy5pjce",
         "virtualization-type": "hvm"
       },
       "owners": ["aws-marketplace"],
       "most_recent": true
     }
   },
   {
     "name": "gcp",
     "type": "googlecompute",
     "instance_name": "{{user `name`}}",
     "image_name": "{{user `name`}}",
     "account_file": "{{user `account_file`}}",
     "project_id": "{{user `project_id`}}",
     "source_image_family": "centos-7",
     "ssh_username": "packer",
     "zone": "us-east1-b",
     "machine_type": "n1-standard-1"
   }
 ],
 "provisioners": [
     {
     	"type": "shell",
     	"inline": "sudo yum install -y epel-release"
     },
     {
       "type": "shell",
       "inline": ["sudo yum install -y nginx","sudo systemctl enable nginx", "sudo systemctl start nginx"]
      }]
 }
Amazon Web Services Prerequisites

1. AWS account.

2. AWS role with the minimal permission set to use Packer.

        a. Copy this JSON file and create a new AWS role.

{
"Version": "2012-10-17",
"Statement": [{
    "Effect": "Allow",
    "Action" : [
      "ec2:AttachVolume",
      "ec2:AuthorizeSecurityGroupIngress",
      "ec2:CopyImage",
      "ec2:CreateImage",
      "ec2:CreateKeypair",
      "ec2:CreateSecurityGroup",
      "ec2:CreateSnapshot",
      "ec2:CreateTags",
      "ec2:CreateVolume",
      "ec2:DeleteKeyPair",
      "ec2:DeleteSecurityGroup",
      "ec2:DeleteSnapshot",
      "ec2:DeleteVolume",
      "ec2:DeregisterImage",
      "ec2:DescribeImageAttribute",
      "ec2:DescribeImages",
      "ec2:DescribeInstances",
      "ec2:DescribeInstanceStatus",
      "ec2:DescribeRegions",
      "ec2:DescribeSecurityGroups",
      "ec2:DescribeSnapshots",
      "ec2:DescribeSubnets",
      "ec2:DescribeTags",
      "ec2:DescribeVolumes",
      "ec2:DetachVolume",
      "ec2:GetPasswordData",
      "ec2:ModifyImageAttribute",
      "ec2:ModifyInstanceAttribute",
      "ec2:ModifySnapshotAttribute",
      "ec2:RegisterImage",
      "ec2:RunInstances",
      "ec2:StopInstances",
      "ec2:TerminateInstances",
      "ec2:RequestSpotInstances",
      "ec2:CancelSpotInstanceRequests",
      "ec2:DescribeSpotInstanceRequests",
      "ec2:DescribeSpotPriceHistory"
    ],
    "Resource" : "*"
}]
}

 

3. AWS user with programmatic access. 

        a. Assign the AWS role created above.

4. AWS subnet with the “auto-assign public IPv4” address setting enabled.

        a. This subnet must have Internet access.

Google Cloud Platform Prerequisites

1. GCP account.

2. Follow the steps to create a service account with the required permissions to run Packer.

Creating the Operating System Image

As we mentioned previously, we will pass some environment variables to Packer. They are mainly authentication variables for each builder.

General Environment Variables
# Name that the generated image will have
export IMAGE_NAME=""

Amazon Web Services Environment Variables
# Subnet ID with the auto-assign public IPv4 address setting enabled
export AWS_SUBNET_ID=""
export AWS_ACCESS_KEY=""
export AWS_SECRET_KEY=""
Google Cloud Platform Environment Variables
export GCP_PROJECT_ID=""
# Service account JSON file
export GCP_SERVICE_ACCOUNT=""

Run Packer and Build Image

The Packer “build” command will take the JSON template and use it to execute every specified builder to create a new OS instance. Then, the provisioners will be executed to configure the OS instance. Finally, the post-processors will run after Packer has created the OS image. 

When we run Packer, parallel builds are supported, so you can create the images for AWS and GCP at the same time. You have the option to change this behavior, though, by specifying which builder to run.

# Run packer (all builders)
./packer build centos-7-nginx.json

# Run packer (AWS only)
./packer build -only='aws' centos-7-nginx.json

# Run packer (GCP only)
./packer build -only='gcp' centos-7-nginx.json
Verify Image Creation

Once Packer has completed execution, you should be able to see and use the new operating system image in the Cloud Provider Image section. Now if you need to deploy a web application, you can clone the image that we have just created, avoiding the manual process of creating and configuring the OS from scratch.

Amazon Web Services

1. Go to the AWS console.

2. Select “Services” and then “EC2.”

3. Under the “Images” section, select “AMIs.”

Google Cloud Platform

1. Go to the Google Cloud Platform console.

2. Select “Compute Engine” and then “Images.”

Conclusion

Packer is an open-source tool that can integrate existing configuration management tools to provide a fully-automated solution for building and configuring reusable operating systems. Packer achieves this by converting these preconfigured operating systems into images containing everything you need to build infrastructure environments within minutes, with no extra configuration. In sum, Packer makes it easy for infrastructure administrators to establish the principles of high availability, disaster recovery, and fast deployment in each project. 

 

 

Subscribe to our Blog

Eduardo Vega Valerio
Eduardo Vega Valerio
Eduardo Vega is a DevOps Software Engineer at Gorilla Logic. He enjoys reading about Linux, working with programming languages, and spending time at the beach.

Deliver off-the-chart results.

WordPress Video Lightbox Plugin