Nitin Nagpal Tech Adventures

AMI Automation using Packer

Packer is yet another awesome tool from HashiCorp to automate your AMI build process. You simply write a json config file in HCL (HashiCorp configuration language) describing all your requirements and Packer will go and launch a server, apply all your changes by running provisioner scripts and give you a final ready to use AMI Id. It also takes care of cleaning up all the resources that it creates on your cloud infrastructure during the build process. From the product’s intro:

Packer is an open source tool for creating identical machine images for multiple platforms from a single source configuration. Packer does not replace configuration management like Chef or Puppet. In fact, when building images, Packer is able to use tools like Chef or Puppet to install software onto the image.

Sample Config:

{
  "variables": {
    "aws_region": "ap-south-1",
    "aws_credentials_profile": "default",
    "aws_instance_type": "t2.nano",
    "ssh_username": "ec2-user",
    "name": "my-base-image",
    "description": "My Base Image",
    "ansible_playbook": "playbook.yml"
  },
  "builders": [{
    "type": "amazon-ebs",
    "region": "{{user `aws_region`}}",
    "profile": "{{user `aws_credentials_profile`}}",
    "instance_type": "{{user `aws_instance_type`}}",
    "ssh_username": "{{user `ssh_username`}}",
    "ami_name": "{{user `name`}}",
    "tags": {
      "Name": "{{user `name`}}",
      "Created": "{{isotime \"2006-01-02\"}}",
      "Packer": true,
      "OS": "AmazonLinux",
      "Release": "Latest",
      "Description": "{{user `description`}}"
    },
    "run_tags": {
      "Name": "packer-instance-{{isotime \"2006-01-02\"}}",
      "Packer": true
    },
    "run_volume_tags": {
      "Name": "packer-instance-{{isotime \"2006-01-02\"}}",
      "Packer": true
    },
    "launch_block_device_mappings": [{
      "device_name": "/dev/xvda",
      "volume_size": 8,
      "volume_type": "standard",
      "delete_on_termination": true
    }],
    "ami_block_device_mappings": [{
      "device_name": "/dev/xvda",
      "volume_size": 8,
      "volume_type": "standard",
      "delete_on_termination": true
    }],
    "source_ami_filter": {
      "filters": {
        "virtualization-type": "hvm",
        "name": "*amzn-ami-hvm-2017.09*",
        "root-device-type": "ebs"
      },
      "most_recent": true
    }
  }],
  "provisioners": [{
      "type": "shell",
      "script": "custom-script.sh"
    },
    {
      "type": "ansible",
      "playbook_file": "{{user `ansible_playbook`}}"
    }
  ]
}

Builders

Builders are responsible for creating machines and generating images from them for various platforms. For example, there are separate builders for EC2, Google Cloud, Docker, VMware, VirtualBox, etc. Packer comes with many builders by default, and can also be extended to add new builders.

"builders": [{
    "type": "amazon-ebs",
    "region": "ap-south-1",
    "instance_type": "t2.micro",
    "ami_name": "ami-11111",
    "ssh_username": "{{user `name`}}"
 }]
"builders": [{
    "type": "googlecompute",
    "zone": "ap-south1-a",
    "machine_type": "f1-micro",
    "image_family": "ubuntu-1604",
    "ssh_username": "{{user `name`}}"
   }]

Provisioners

Provisioners use builtin and third-party software to install and configure the machine image after booting. Provisioners prepare the system for use, so common use cases for provisioners include:

  • installing packages
  • patching the kernel
  • creating users
  • downloading application code

These are just a few examples, and the possibilities for provisioners are endless.

"provisioners": [{
    "type": "shell",
    "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo {{ .Path }} {{user `name`}}",
    "script": "custom-script.sh"
  },
  {
    "type": "ansible",
    "playbook_file": "{{user `ansible_playbook`}}",
    "user": "ec2-user",
    "extra_arguments": "-vv",
    "ansible_env_vars": ["ANSIBLE_HOST_KEY_CHECKING=False"]
  }
]

Building the Image

$  packer build base-ami.json
==> amazon-ebs: Force Deregister flag found, skipping prevalidating AMI Name
    amazon-ebs: Found Image ID: ami-28e8dd48
==> amazon-ebs: Creating temporary keypair: packer_5a23a50e-8a7e-90e8-d7fc-c9ab76a748d1
==> amazon-ebs: Creating temporary security group for this instance: packer_5a23a512-4985-fe84-3bb8-61cecea90b51
==> amazon-ebs: Authorizing access to port 22 from 0.0.0.0/0 in the temporary security group...
==> amazon-ebs: Launching a source AWS instance...
    amazon-ebs: Instance ID: i-0f990949480ce893e
==> amazon-ebs: Waiting for instance (i-0f990949480ce893e) to become ready...
==> amazon-ebs: Waiting for SSH to become available...
==> amazon-ebs: Connected to SSH!
==> amazon-ebs: Provisioning with shell script: user_data.sh
...
...
==> amazon-ebs: Provisioning with Ansible…
...
...
==> amazon-ebs: Creating the AMI: my-base-image
    amazon-ebs: AMI: ami-3e003a5e
==> amazon-ebs: Waiting for AMI to become ready...
==> amazon-ebs: Adding tags to AMI (ami-3e003a5e)...
==> amazon-ebs: Tagging snapshot: snap-0cb6ad8e0b1d322af
==> amazon-ebs: Creating snapshot tags
==> 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-west-1: ami-3e003a5e

It’s a great tool which is must for implementing Infrastructure as Code and goes really good with Terraform

For the above code visit the project on GitHub.

My First Blog!

For a long time, I was planning to start writing Tech Blogs on technologies that I use in my day to day work but I couldn’t find an easy to use tool to set up my blog. I have my portfolio website hosted on S3 which is a simple static HTML5 & CSS3 website.

So to add a blog section to it I came across this amazing tool Jekyll which can be used to generate blog aware static site. It uses markdown format to write blogs and creates the directory structure to replicate blog URLs.

It can be hosted on GitHub pages very easily and there is a support to CNAME the DNS with your personal domain.

Jekyll is a static site generator, an open-source tool for creating simple yet powerful websites of all shapes and sizes. From the project’s readme:

Jekyll is a simple, blog aware, static site generator. It takes a template directory […] and spits out a complete, static website suitable for serving with Apache or your favorite web server. This is also the engine behind GitHub Pages, which you can use to host your project’s page or blog right here from GitHub.

Installation

Setting up the Jekyll website for the first time is as simple as running the below 3 commands

$ gem install jekyll bundler
$ jekyll new my-awesome-site
$ cd my-awesome-site
~/my-awesome-site $ bundle exec jekyll serve
# => Now browse to http://localhost:4000

This will create the below directory structure with your default website

my-awesome-site/
├── 404.html
├── Gemfile
├── Gemfile.lock
├── _config.yml
├── _posts
│   └── 2018-01-20-welcome-to-jekyll.markdown
├── about.md
└── index.md

Configuration

All the configuration related changes are to be made in _config.yml file

# Permalinks
#
# Use of `relative_permalinks` ensures post links from the index work properly.
permalink:           '/:categories/:year/:month/:day/:title'
#relative_permalinks: true

# Setup
title:               Nitin Nagpal
tagline:             'Tech Adventures'
description:         'Tech Blogs on DevOps & more'
url:                 http://blog.nitinnagpal.me
baseurl:             ''
paginate:            5
future:              true

# About/contact
author:
  name:              Nitin Nagpal
  url:               http://nitinnagpal.me
  email:             nitnagpal@gmail.com

github:
  repo:              https://github.com/nitnagpal/nitnagpal.github.io
# Pages
pages:
  Home: '/'
  About: '/about'
  Tags: '/tags'

Layouts

All the templates for your different type of pages will be created under _layouts directory.

_layouts/
├── default.html
├── page.html
└── post.html

Includes

All the things that you want to include in other pages like sidebar, header will go in _includes directory.

_includes/
├── head.html
└── sidebar.html

Pages

Now to create a page (index.html for Homepage) add the below lines to top of the html file so that Jekyll can build the page accordingly. After that simply add all of the HTML you want to design your page.

---
layout: default
title: Home
---
  • layout - the layout from _layouts that this page will use
  • title - the page title

Posts

All the blog posts will go under the _posts folder and the file name should follow a standard format so that Jekyll can extract the date of blog post from name YYYY-MM-DD-blog-name.md

_posts/
├── 2017-12-10-my-first-blog.md
└── 2017-12-17-ami-automation-using-packer.md

Final Result

The final result of all the above will be that Jekyll will create a _site directory with your complete website in a static format and blog urls as sub-directories with html pages

_site/
├── 2017
│   └── 12
│       ├── 10
│       │   └── my-first-blog.html
│       └── 17
│           └── ami-automation-using-packer.html
├── 404.html
├── CNAME
├── LICENSE.md
├── README.md
├── about.html
├── atom.xml
├── feed.xml
├── fonts
│   └── font-awesome
├── index.html
├── js
│   └── custom.js
├── public
│   ├── apple-touch-icon-precomposed.png
│   ├── css
│   ├── favicon.ico
│   └── nitinnagpal.jpg
└── tags.html

Find out more by visiting the project on GitHub.