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 :
- A JSON file used by packer to create the image called image.json
- A 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