hashicorp packer - ghdrako/doc_snipets GitHub Wiki

Hashicorp provides Packer to help you create immutable infrastructure by baking configurations directly in your Virtual Machine image, rather than the slow process of creating a Virtual Machine with a generic OS image and then customizing it later. Packer uses a staging Virtual Machine to customize the image. The following is the process that Packer follows while building the custom image:

  1. You start with a packer.json file to define the base image you want to start from and where you want to build the image. You also define the provisioner for building the custom image, such as Ansible, and specify what playbooks to use.
  2. When you run a Packer build, Packer uses the details in the packer.json file to create a build VM from the base image, run the provisioner on it to customize it, turn off the build VM, take a snapshot, and save that as a disk image. It finally saves the image in an image repository.
  3. You can then build the Virtual Machine from the custom image using Terraform or other tools.

Installing Packer

$ wget https://releases.hashicorp.com/packer\
/1.6.6/packer_1.6.6_linux_amd64.zip
$ unzip packer_1.6.6_linux_amd64.zip
$ sudo mv packer /usr/bin
Creating the Apache and MySQL playbooks 287
$ sudo chmod +x /usr/bin/packer
$ packer version

Defining the Packer configuration

Packer allows us to define configuration in JSON as well as HCL files. As JSON is a widely used syntax in Packer's case and HCL is still in beta

The webserver-packer.json file looks like the following:
{
  "variables": {
    "client_id": "{{env `CLIENT_ID`}}",
    "client_secret": "{{env `CLIENT_SECRET`}}",
    "tenant_id": "{{env `TENANT_ID`}}",
    "subscription_id": "{{env `SUBSCRIPTION_ID`}}"
  },
  "builders": [{
    "type": "azure-arm",
    "client_id": "{{user `client_id`}}",
    "client_secret": "{{user `client_secret`}}",
    "tenant_id": "{{user `tenant_id`}}",
    "subscription_id": "{{user `subscription_id`}}",
    "managed_image_resource_group_name": "packer-rg",
    "managed_image_name": "apache-webserver",
    "os_type": "Linux",
    "image_publisher": "Canonical",
    "image_offer": "UbuntuServer",
    "image_sku": "16.04-LTS",
    "azure_tags": {},
    "location": "West Europe",
    "vm_size": "Standard_DS2_v2"
  }],
  "provisioners": [{
    "type": "ansible",
    "playbook_file": "./webserver-playbook.yaml"
  }]
}

The JSON file consists of three sections –

  • variables,
  • builders, and
  • provisioners. The variables section defines a list of user variables that we can use within the builders and provisioners section of the Packer configuration. We've defined four variables – client_id, client_secret, tenant_id, and subscription_id and are sourcing the values of these variables from the environment variables we described in the previous section. That is because we don't want to commit the values of these attributes in source control.

Tip

Always source sensitive data from external variables, such as environment variables, or a secret manager such as Hashicorp's Vault.

The builders section consists of an array of builders. Every builder has a mandatory type attribute that defines the builder's type for the Packer build. Different types of builders have different attributes that help us connect and authenticate with the cloud provider that the builder is associated with. Other attributes define the build Virtual Machine's specification and the base image that the build Virtual Machine will use. It also describes the properties of the custom image we're trying to create.

The provisioners section defines how we're going to customize our Virtual Machine. There are a lot of provisioners that Packer provides. Luckily, Packer provides the Ansible provisioner out of the box. Therefore, we've specified the type as ansible. The Ansible provisioner requires the path to the playbook file, and therefore in the preceding case, we've provided webserver-playbook.yaml.

Tip

You can specify multiple builders in the builders section, each with the same or different types. Similarly, we can have numerous provisioners with the provisioners section. Each will be executed in parallel. So, if you want to build the same configuration for multiple cloud providers, you can specify multiple builders for each cloud provider.

# build the image
$ packer build webserver-packer.json
{
"builders": [{
"type": "azure-arm",
"client_id": "<place-your-client_id-here>",
"client_secret": "<place-your-client_secret-here>",
"tenant_id": "<place-your-tenant-id-here>",
"subscription_id": "<place-your-subscription-here>",
"managed_image_resource_group_name": "CloudNativeAzure-group",
"managed_image_name": "harborImage",
"os_type": "Linux",
"image_publisher": "Canonical",
"image_offer": "UbuntuServer",
"image_sku": "16.04-LTS",
"azure_tags": {
"env": "Production",
"task": "Image deployment"
},
"location": "East US",
"vm_size": "Standard_DS1_v2"
}],
"provisioners": [{
"execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'",
"inline": [
"apt-get update",
"apt-get upgrade -y",
"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
],
"inline_shebang": "/bin/sh -x",
"type": "shell"
},
{
{
"type": "shell",
"inline": [
"wget https://github.com/goharbor/harbor/releases/download/v2.1.3/ \
harbor-online-installer-v2.1.3.tgz -O /tmp/harbor-online-installer-v2.1.3.tgz",
"tar -xvf /tmp/harbor-online-installer-v2.1.3.tgz"
]
},
{
"type": "file",
"source": "{{template_dir}}/harbor.yml",
"destination": "/tmp/harbor"
},
{
"type": "shell",
"inline": [
"sudo chmod 0777 /tmp/harbor/install.sh",
"sudo /tmp/harbor/install.sh"
]
}
]
}