Introduction

AMIs(Amazon Machine Images) are the main building block for creating an instance in aws. You can consider AMI as very much similar to vmware virtual machine images. In simple words, an AMI is the source code to launch an instance in aws.  It is not possible to create an aws instance without specifying an AMI. 

An AMI typically contains the operating system of your choice, along with the required default operating system settings. It is nothing but the root volume/hard drive of a server. You can have additional volumes as well as part of your AMI. 

By default amazon provides a lot of publicly available AMIs. But these are generally vanilla operating systems. For example, an Ubuntu 18.04 AMI will contain just the ubuntu operating system with the default set of packages that canonical provides.  You can create an instance based out of these default AMIs, and then manually install your required application on top of that instance, and create another AMI for your own purpose. 

It is a simple process, which can be manually executed by an operations engineer. However, it’s not a good idea to follow this manual process, when you want to create many AMIs for different applications.  This is where “packer” steps in. 

How Does Packer Work?

Packer is an opensource tool maintained by Hashicorp (The same people who created terraform, consul, vault etc.).As we discussed in the introduction, packer executes the manual process in an automated fashion. Let’s outline the manual process below. 

  1. Create a temporary instance in AWS using any of the freely available AMIs(for example, Ubuntu 18.04)
  2. SSH to that instance, and then configure/install the way you like it
  3. Go to AWS console, and right click on the instance and create an AMI out of the running instance (you can stop it and then take AMI, as that is the recommended way)
  4. Terminate the temporary instance, once you have the AMI ready

The manual process above is exactly what packer does. In the exact same order as described above. 

How To Install Packer?

Installing any hashicorp tool is pretty straight forward. Its as simple as downloading the binary and keeping it in the path.  

Go to the download page for packer: https://www.packer.io/downloads.html, select the download link by right clicking on the 64bit link for appropriate operating system.  Download it to /usr/local/bin/. Once downloaded, extract it, and delete the original zip file. The below shown block does exactly the same. 

#cd /usr/local/bin
#wget https://releases.hashicorp.com/packer/1.5.1/packer_1.5.1_linux_amd64.zip
#unzip packer_1.5.1_linux_amd64.zip
#rm -f packer_1.5.1_linux_amd64.zip

How To Write a Packer Template?

Writing a packer template is simple, as it is written in JSON format. Below is an example of a packer template file. Create a file named “template.json” with the below contents. 

{
"variables": {
    "aws_region": "us-east-1",
    "ami_region": "us-east-1",
    "instance_type": "t2.micro",
    "ami_name": "Nginx_Image",
    "associate_public_ip_address": "true",
    "vpc_id": "vpc-b72324drcc",
    "subnet_id": "subnet-78434dref2",
    "temporary_security_group_source_cidr": "119.235.52.123/32"
},
"builders": [{
    "type": "amazon-ebs",
    "ssh_pty": true,
    "region": "{{user `aws_region`}}",
    "ami_regions": "{{user `ami_region`}}",
    "source_ami_filter": {
        "filters": {
          "name": "amzn-ami-hvm-????.??.?.????????-x86_64-gp2",
          "root-device-type": "ebs",
          "virtualization-type": "hvm"
        },
        "most_recent": true,
        "owners": [
          "amazon"
        ]
        },
"run_volume_tags": [{
      "Name": "Packer-Builder"
    }],
    "temporary_security_group_source_cidrs": "{{user `temporary_security_group_source_cidr`}}",
    "instance_type": "{{user `instance_type`}}",
    "ssh_username": "ec2-user",
    "ami_name": "{{user `ami_name`}}--{{isotime \"01/02/06-15.04.05\"}}",
    "ami_description": "Nginx AMI baked with packer",
    "associate_public_ip_address": "{{user `associate_public_ip_address`}}",
    "vpc_id": "{{user `vpc_id`}}",
    "subnet_id": "{{user `subnet_id`}}"
}],
"provisioners":[
{
        "type": "shell",
        "inline": [
          "sudo yum update -y",
          "sudo yum install nginx -y"
        ]
      }
 ]
}

 

If you see the template file above, you can see three different sections. 

  1. Variables: Which is self explanatory. It’s nothing but a collection of variables you can define, which can be used in the sections beneath in the template. Packer also supports passing of an environment file, so that while execution a new set of variables can be passed. 
  2. Builders: Here is where you specify all the details related to the temporary instance that packer creates. Also it will have some basic details about the AMI (for example, the name of the AMI that you are creating).
    For creating the temporary instance, you need to specify the VPC ID, Subnet id, the username to ssh to the temporary server, the source ip to be whitelisted(to ssh you need to have your source ip whitelisted), the instance type of the temporary instance, and tags.
  3. Provisioners: Packer supports multiple provisioners.  A provisioner’s primary job is to configure the temporary server that packer created with the components and items that you need. In other words, you use provisioners to install and configure your required applications etc before capturing an AMI from the temporary instance. 

Packer supports almost all modern configuration management tools as provisioners. Below mentioned are some of them.  Do not forget the fact that bash shell itself is a provisioner (although not mentioned below). In our example template above, we are using bash shell as our provisioner. 

  1. Puppet
  2. Ansible
  3. Chef
  4. Salt

How To Execute a Packer Template?

Executing a packer template is a single command away. Below shown is an example packer build command, using the template.json file we created earlier. 

Note:  The below command assumes that you have proper aws credentials configured. Typically this involves installing “awscli” utility and having an access_key & secret_key stored inside ~/.aws/credentials file.  Refer: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html

#packer build template.json

An example output of the above command is shown below. 

amazon-ebs: output will be in this color.

==> amazon-ebs: Prevalidating any provided VPC information
==> amazon-ebs: Prevalidating AMI Name: Nginx_Image--01/25/20-05.03.59
    amazon-ebs: Found Image ID: ami-00eb20669e0990cb4
==> amazon-ebs: Creating temporary keypair: packer_5e2bcc3f-af9f-fd72-f7d0-215796a09a9b
==> amazon-ebs: Creating temporary security group for this instance: packer_5e2bcc47-d1b1-a559-6fa3-2bd1437b7a1b
==> amazon-ebs: Authorizing access to port 22 from [119.235.52.123/32] in the temporary security groups...
==> amazon-ebs: Launching a source AWS instance...
==> amazon-ebs: Adding tags to source instance
    amazon-ebs: Adding tag: "Name": "Packer Builder"
    amazon-ebs: Adding tag: "Name": "Packer-Builder"
    amazon-ebs: Instance ID: i-04eec66c069005dba
==> amazon-ebs: Waiting for instance (i-04eec66c069005dba) to become ready...
==> amazon-ebs: Using ssh communicator to connect: 174.129.53.59
==> amazon-ebs: Waiting for SSH to become available...
==> amazon-ebs: Connected to SSH!
==> amazon-ebs: Provisioning with shell script: /var/folders/4x/fn3wj_hs1y17d1tvj8p50nyc0000gn/T/packer-shell262606561

As you can see from the logs above, it first creates the temporary instance and then does SSH and then uses the provisioner. In our case, shell script is the provisioner. Once the packer finishes the provisioner execution, it prints the below message before finishing its job.    

amazon-ebs: Complete!
==> amazon-ebs: Stopping the source instance...
    amazon-ebs: Stopping instance
==> amazon-ebs: Waiting for the instance to stop...
==> amazon-ebs: Creating AMI Nginx_Image--01/25/20-05.03.59 from instance i-04eec66c069005dba
    amazon-ebs: AMI: ami-095c7d0056fa4e25e
==> amazon-ebs: Waiting for AMI to become ready...
==> amazon-ebs: Modifying attributes on AMI (ami-095c7d0056fa4e25e)...
    amazon-ebs: Modifying: description
==> amazon-ebs: Modifying attributes on snapshot (snap-00393a001c291b308)...
==> amazon-ebs: Terminating the source AWS instance...
==> amazon-ebs: Cleaning up any extra volumes...
==> amazon-ebs: No volumes to clean up, skipping
==> amazon-ebs: Deleting temporary security group...
==> amazon-ebs: Deleting temporary keypair...
Build 'amazon-ebs' finished.

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:
us-east-1: ami-095c74e25e

We can clearly see that packer properly terminates the instance after capturing the AMI. One important thing to note here is that you can combine multiple provisioners in a single packer template. 

For example, just after executing the shell provisioner, you can also have an ansible role to be executed, and puppet module to be called and so on. If you notice the template, the provisioners keyword in the template is a “list”.

To keep the AMI name unique we have appended time stamp to the AMI name. Apart from this, you can have an application version appended to the AMI name as well. 

 

Dascase provides critical DevOps-as-a-Service, Infrastructure as a Code, Cloud Migrations, Infrastructure solutions and Digital transformation to high growth companies looking for expert support with DevOps, Kubernetes, cloud security, cloud infrastructure, and CI/CD pipelines. Our managed and consulting services are a more cost-effective option than hiring in-house, and we scale as your team and company grow. Check out some of the use cases, learn how we work with clients, and read more about our Service offering.

Scroll to Top