Packer: Building images on Google Cloud
Tags: gcp, googlecloud, packer, hashitools
Published: 2020-09-08 00:29:57

Installing the software we need to run our website or tools, can take some time to complete. This isn't ideal when our deployments are time sensitive, like when we need to scale up the web server pool to handle that sudden interest in your latest line of fancy socks.

We can solve this issue by pre-installing the software we need and setting the configuration options, which we can then turn in to an image we can deploy over and over again.

To make this entire process quicker, repeatable and something we can easily template - We'll use packer

## What is Packer?

Packer is a Hashicorp tool that can be used to ease the creation of system images on cloud platforms or for other local tools like Vagrant ( another Hashicorp tool), Docker and VirtualBox.

## Where can I get it ?

You can download the HashiCorp Packer tool from the [download page on the packer website](https://www.packer.io/downloads)

There's also a handy guide on the [hashicorp learning portal](https://learn.hashicorp.com/) for [how to install it on your operating system](https://learn.hashicorp.com/tutorials/packer/getting-started-install)

## How can I use it with the Google Cloud Platform?

You'll need to create a "builder" configuration in your packer file.
Here's a basic example:

```json
{
  "builders": [
    {
      "type": "googlecompute",
      "project_id": "my project",
      "zone": "us-central1-a",
      "source_image_family": "debian-10",
      "ssh_username": "packer",
      "image_name": "packer-{{timestamp}}",
      "image_family": "socks-web"
    }
  ]
}

```

The options we have set above do the following things:

`type`
  is the type of builder we're using. Here its `googlecompute` because we're talking to GCP

`project_id`
  is the project where the final image will be stored

`zone`
  Will be the zone we build our image in

`source_image_family`
  This is the group of images where our base image will be taken from

`ssh_username`
  The ssh username we'll use to talk to the instance that will create our image

`image_name`
  The name of the image we will create.
The image name needs to be unique within our project so we're using the packer variable `timestamp` to do that.

`image_family`
  The name of our image family. This is optional but is useful later on.


## What is this `image_family` thing ?

Images can be stored in groups referred to as image families. When building instances we can ask GCP to get our image from our family which will return the latest image that was added to it.

If something is found to be wrong with a new image we can [tag it as deprecated](https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#setting_families) and the family will offer up a previous version next time we deploy an instance.

More info on image families can be found in [the GCP documentation](https://cloud.google.com/compute/docs/images#image_families)


## How do I install that web server in my image?

You'll need to add a [provisioner](https://www.packer.io/docs/provisioners) or two, to your packer config file.

Provisioner's are usually shell scripts or file uploads but could be a puppet, ansible, salt or chef operation. 

Here's a complete example of our packer file including our new provisioners:

```json
{
  "builders": [
    {
      "type": "googlecompute",
      "project_id": "my project",
      "zone": "us-central1-a",
      "source_image_family": "debian-10",
      "ssh_username": "packer",
      "image_name": "packer-{{timestamp}}",
      "image_family": "socks-web"
    }
  ],
  "provisioners": [                                                           
      {                                                                         
      "type": "file",                                                           
      "source": "./index.html",                                           
      "destination": "/tmp/index.html"                                            
      },
      {                                                                         
        "type": "shell",                                                        
        "inline": [                                                             
            "sudo apt-get update -qq",
            "sudo apt-get upgrade -qq",                                     
            "sudo apt-get install -qq apache2",
            "sudo cp /tmp/index.html /var/www/html/index.html",
         ]                                                                      
    }]
}
```

Here we copy our `index.html` file to `/tmp`, Update the cache for apt, perform a system upgrade to make sure we have all the updates installed then install apache and finally copy our index page in to place.


## How do I build that image ?

You can now run the following command from the directory where you've saved the packer file:

```shell
packer build example.json
```

Packer will now go away and build that image for you and will store the resulting image as `packer-xxxxxxxxx` ( where `xxxxxxxxx` is a timestamped value ) The resulting image will be linked to the image family `socks-web` which you will then be able to reference in your terraform template for your project.

 :tada: Tada!


## Are there other things to know ?

Yes, there are a couple of things:

#### Networking

The example above assumes the project you are using contains a default networking setup with subnetworks available in your chosen zone. If these things aren't available the build process will fail and you'll need to add a `network` OR `subnetwork` option to the builder config which lines up with your chosen zone.

It also assumes that the default firewall rules are in place so packer can ssh to the instance it creates.
If you don't have the needed firewall rules to allow access your build process will time out while waiting to connect.

#### `machine_type` has been omitted from the example

It will default to `n1-standard-1` but you may need to increase its size depending on what you're doing. A larger machine type will help it run faster.

#### Cost

There is a cost for storing your images.
At time of writing its about $0.050 for US and EU multi region storage but it can be as high as $0.075 is specific regions.

There will also be a cost for building the image.
This will vary based on the image size you use and where you choose to build it.
At time of writing the cost of the the given example should be in the region of $0.04749975 Per hour

Check the [pricing page](https://cloud.google.com/compute/all-pricing#machine_image) for more info.

To quote the pricing pages:

*If you pay in a currency other than USD, the prices listed in your currency on [Cloud Platform SKUs](https://cloud.google.com/skus/) apply.*

:exclamation:  **Make sure you check the cost before you run up a big bill** :exclamation: 


#### Cleaning up

If you don't want that image you've just created you can head over to [images](https://console.cloud.google.com/compute/images) in the compute section of the console and delete the image you no longer want. 

*Note:* You cannot delete public images from your project - They are free anyway :smiley: