What is Packer ?

Packer is a great tool developped by Hashicorp. It does not need other tools from Hashicorp to operate and allows you to create images.This can be images used in the cloud or on on-premises infrastructures such a VMWare. In this article we will see together how to create an image on AWS and on VMWare with Packer.

Install Packer on CentOS

Install yum-config-manager to manage your repositories :

$ sudo yum install -y yum-utils

Use yum-config-manager to add the official HashiCorp Linux repository :

$ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo

Install packer :

$ sudo yum -y install packer

$ packer
usage: packer [--version] [--help] <command> [<args>]

Available commands are:
    build       build image(s) from template
    fix         fixes templates from old versions of packer
    inspect     see components of a template
    validate    check that a template is valid
    version     Prints the Packer version

Packer is now installed on our local machine.

We will now see together how to build images through examples.

Build an AMI image on AWS

Note : to build this image on AWS, you will need to create an SSH key (see vars).

Basically will use two files to build our image on AWS :

  1. JSON file used by packer to create the image called image.json
  2. Bash script used to configure our image called setup.sh

Here is our image.json file :

{
	  "variables": {
	      "aws_access_key": "{{env `AWS_ACCESS_KEY`}}",
	      "aws_secret_key": "{{env `AWS_SECRET_KEY`}}",
	      "aws_region": "us-east-1"
	  },
	  "builders": [
	      {
	          "type": "amazon-ebs",
	          "access_key": "{{user `aws_access_key`}}",
	          "secret_key": "{{user `aws_secret_key`}}",
	          "region": "{{user `aws_region`}}",
	          "source_ami_filter": {
	              "filters": {
	                  "virtualization-type": "hvm",
	                  "name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*",
	                  "root-device-type": "ebs"
	              },
	              "owners": [
	                  "099720109477"
	              ],
	              "most_recent": true
	          },
	          "instance_type": "t2.micro",
	          "ssh_username": "ubuntu",
	          "ami_name": "learn-packer {{timestamp}}"
	      }
	  ],
	  "provisioners": [
	    {
	        "type": "file",
	        "source": "../tf-packer.pub",
	        "destination": "/tmp/tf-packer.pub"
	      },
	    {
	        "type": "shell",
	        "script": "../scripts/setup.sh"
	    }
	]
	}

Let’s discuss about the 3 differents blocks in Packer json file.

Variables

In this block, we store all variables we need to access to our AWS account. For security reasons, all credentials are passed through “Environement variables”.

Builders

In the builders block, you have all informations related to the instance used to create the image. It can be hardware, image used, ssh user or our image name.

Provisioners

Finally, this block is related to image configuration. In this case we use a bash script (see below) to configure our image as we want.

Here is our setup.sh script :

#!/bin/bash
	set -x
	

	# Install necessary dependencies
	sudo DEBIAN_FRONTEND=noninteractive apt-get -y -o \
         Dpkg::Options::="--force-confdef" \
         -o Dpkg::Options::="--force-confold" dist-upgrade

	sudo apt-get -y -qq install curl wget git \
         vim apt-transport-https ca-certificates

	sudo add-apt-repository ppa:longsleep/golang-backports -y
	sudo apt -y -qq install golang-go
	

	# Setup sudo to allow no-password sudo for "hashicorp" group and
    # adding terraform" user
	sudo groupadd -r hashicorp
	sudo useradd -m -s /bin/bash terraform
	sudo usermod -a -G hashicorp terraform
	sudo cp /etc/sudoers /etc/sudoers.orig
	echo "terraform  ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/terraform
	

	# Installing SSH key
	sudo mkdir -p /home/terraform/.ssh
	sudo chmod 700 /home/terraform/.ssh
	sudo cp /tmp/tf-packer.pub /home/terraform/.ssh/authorized_keys
	sudo chmod 600 /home/terraform/.ssh/authorized_keys
	sudo chown -R terraform /home/terraform/.ssh
	sudo usermod --shell /bin/bash terraform
	

	# Create GOPATH for Terraform user & download the webapp from github
	

	sudo -H -i -u terraform -- env bash << EOF
	whoami
	echo ~terraform
	
	cd /home/terraform
	
	export GOROOT=/usr/lib/go
	export GOPATH=/home/terraform/go
	export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
	go get -d github.com/hashicorp/learn-go-webapp-demo
	EOF

How to build our AMI ?

Run the Packer build command providing your image template file.

$ packer build image.json

==> amazon-ebs: Prevalidating any provided VPC information
==> amazon-ebs: Prevalidating AMI Name: learn-packer 1594419525
    amazon-ebs: Found Image ID: ami-0fcf65d66fe3e1f92
==> amazon-ebs: Creating temporary keypair: packer_5f08e945-d87e-c0e0-9c06-127cb40d51f4
==> amazon-ebs: Creating temporary security group for this instance: packer_5f08e947-d29d-8982-903a-f1532932d397
==> amazon-ebs: Authorizing access to port 22 from [0.0.0.0/0] 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: Instance ID: i-0208315d85b166c4b
==> amazon-ebs: Waiting for instance (i-0208315d85b166c4b) to become ready...
==> amazon-ebs: Using ssh communicator to connect: 54.81.101.149
==> amazon-ebs: Waiting for SSH to become available...
==> amazon-ebs: Connected to SSH!
==> amazon-ebs: Uploading ../tf-packer.pub => /tmp/tf-packer.pub
newkey.pub 574 B / 574 B [===============================================] 100.00% 0s
==> amazon-ebs: Provisioning with shell script: ../scripts/setup.sh
    amazon-ebs: Reading package lists...
    amazon-ebs: Building dependency tree...
    amazon-ebs: Reading state information...
==> amazon-ebs: Stopping the source instance...
    amazon-ebs: Stopping instance
==> amazon-ebs: Waiting for the instance to stop...
==> amazon-ebs: Creating AMI learn-packer 1594419525 from instance i-0208315d85b166c4b
    amazon-ebs: AMI: ami-06cb92a18a30fec16
==> amazon-ebs: Waiting for AMI to become ready...
==> 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-06cb92a18a30fec16

That’s it, you can use this image now, with Terraform for example, to create aws instances !

How to build a CentOS image on VMWare ?

Introduction

In this second example, you will need to download a recent CentOS ISO to create our template on VMWare. You can download it through this link : here.

We will have 2 files, one for the packer image (centos-vsphere.json file), one for a kickstart file (ks.cfg.)

So in this example, we won’t use an shell script but a kickstart file.

You can access to these file with the following link. We will just see them in details in the following part of my article.

Centos-vsphere.json

Just as with the AMI, the variables block refers to all variables to connect to the VMWare Vsphere and variables used in other blocks :

"variables": {
  "vsphere-server": "kopi-vsphere-01",
  "vsphere-user": "administrator@kopicloud.local",
  "vsphere-password": "Ins3cur3P@ssw0rd",
  "vsphere-datacenter": "KopiCloud",
  "vsphere-cluster": "Kopi-Cluster",
  "vsphere-network": "VM Network",
  "vsphere-datastore": "Kopi-Datastore",
  "vsphere-folder": "Templates",
  "vm-name": "CentOS7-Template",
  "vm-cpu-num": "1",
  "vm-mem-size": "1024",
  "vm-disk-size": "25600",
  "iso_url": "[Kopi-Datastore] ISO-Linux/CentOS-7-x86_64.iso" 
},

The builders block is virtual machine settings. The convert_to_template is used to convert the VMware Virtual Machine to VMware Template at the end of the build.

"convert_to_template": "true",
"vm_name": "{{user `vm-name`}}",
"notes": "Build via Packer",
"guest_os_type": "centos7_64Guest",
"CPUs": "{{user `vm-cpu-num`}}",
"RAM": "{{user `vm-mem-size`}}",
"RAM_reserve_all": false,      
"disk_controller_type": "pvscsi",      
"disk_size": "{{user `vm-disk-size`}}",
"disk_thin_provisioned": true,      
"network_card": "vmxnet3",

The final part of the Builders section is used to configure ISO images and floppy disks. Here, we will define the ISO image: the CentOS iso file specified in the variables section. Also, we will create a floppy and copy the ks.cfg file inside it.

Then, we use the boot_command command to request CentOS to load the kickstart file from the floppy disk.

"iso_paths": ["{{user `iso_url`}}"],
"floppy_files": ["ks.cfg"],
"boot_command": [        
  "<esc><wait>",        
  "linux ks=hd:fd0:/ks.cfg<enter>"      
]

The provisioner executes commands on the virtual machine using the shell. We use the shell to install cloud-init, that we will use to configure the OS from Terraform.

"provisioners": [
  {
    "type": "shell",
    "inline": [        
      "sudo yum install -y cloud-init"      
    ]    
  }  
]

To deploy it, you can use the same packer command we have seen previously.

How to deploy our images ?

Now that you have your image created, you can use Terraform or Ansible to deploy your infrastructure. Personnally, I like to deploy VMs, security groups, external IPs and all infrastructure components using Terraform and then manage VMs configurations through Ansible. Check my previous articles for more information ! 🙂

Sources

hashicorp.op

github – guillermo-musumeci

github – learn-terraform-provider

Leave a Comment

Your email address will not be published. Required fields are marked *