Custom Image Creation

This recipe shows how a custom image can be created using the Packer tool.

Packer

Packer is a cloud agnostic tool to automate image builds.

For more details see Packer

Packer allows you to create a coded (documented) image creation template. You can use it to reproducibly create images, evolve their configuration and commit this all into a version control system. In the mid-term it will make your image creation faster and more reliable.

Here we’re just going to cover a basic example by following the steps below.

  • Install Packer

  • Configure the openstack environment (command line)

  • Create a Packer template file

  • Run the build command

Install Packer

Packer is available for numerous operating system releases out of the box.

For more info check: Packer Download

Configure Openstack environment

To configure your environment simply source the rc file from your openstack account

. openrc.sh

Packer can take advantage of the environment variables which this creates.

Note: For the mpcdf release you will need to remember that packer uses OS_TENANT_NAME or OS_TENANT_ID rather than OS_PROJECT_NAME and OS_PROJECT_ID. This means you will need to explicitly set the tenant as we do in the config file below or modify the openrc.sh file to export both *_PROJECT_* and *_TENANT_*.

Once you have the environment setup you can use the openstack command line tool to interact with openstack and determine configuration variables etc for the Temmplate.

Create a Temmplate

The template file is a simple text config file in json or HCL.

In the example below we will use the HCL format.

The definition of the template will use information that can be gained from the OS_* environment vars or via quieries to openstack itself.

We will look at the two main blocks needed to define a tenplate file. The source block and the build block.

Source Block

The source block contains openstack related information which may be static (related to the openstack instance and project) or more dynamic (related to the base image on which to build etc).

For instance:

SOURCE_ID=`openstack image list -f json | jq -r '.[] | select(.Name == "Ubuntu 20.04") | .ID'`
FLAVOR_ID=`openstack flavor list -f json | jq -r '.[] | select(.Name == "mpcdf.small") | .ID'`
NETWORK_ID=`openstack network list -f json | jq -r '.[] | select(.Name == "cloud-local-1") | .ID'`

And the openstack environmental variables:

OS_REGION_NAME
OS_PROJECT_DOMAIN_ID
OS_INTERFACE
OS_AUTH_URL
OS_USERNAME
OS_PROJECT_ID
OS_USER_DOMAIN_NAME
OS_TENANT_NAME
OS_PASSWORD
OS_IDENTITY_API_VERSION

Some of these are read form the environment variables by packer (many are explicitly defined in the config below but omit the OS_PASSWORD to ensure this isn’t recorded plain text in a file).

For more info see: Packer Openstack Builder

Example (with some redaction):

source "openstack" "autogenerated_1" {
  flavor            = "1011"
  identity_endpoint = "https://rdocloud.mpcdf.mpg.de:13000"
  image_name        = "CustomImage"
  networks          = ["***-***-***-***"]
  region            = "regionOne"
  source_image      = "c42847a8-3456-43d4-82bb-5ad05f402d7a"
  ssh_ip_version    = "4"
  ssh_username      = "root"
  tenant_id         = "**************"
  username          = "**********"
}

The entries in the above template are relatively self explaining and some can actually be omitted but are included here to help you get a better insight into the process. See the packer docs for more info about which variables can read from the OS_ environment: Packer Openstack Builder

Build Block

The build block defines which source should be used for the build process and then how to provision the image.

Provisioning is the step where the virtual machine is configured. Tasks such as software installation and adaption of config files are undertaken during the image provisioning step.

There are several different types of provisioners, here we will show an ansible and shell provisioner

Ansible Example:


build {
  sources = ["source.openstack.autogenerated_1"]

  provisioner "ansible" {
    playbook_file = "provision-template.yml"
  }

}

provision-template.yml is a standard ansible playbook.

Shell Example:

build {
  sources = ["source.openstack.autogenerated_1"]

  provisioner "shell" {  
    script       = "script.sh"  
  }

}

script.sh is a simple shell script which will be uploaded and executed on the machine being provisioned. You can also supply an array or several scripts to be executed or define explicit commands to be executed via the inline option.

Run the Build

To run the build process sinply call packer build with the template file

packer build mytemplate.pkr.hcl

This will create a temporary instance based on the base image and provision it using the provisioner you defined. In a final step the new image will be created using the image name you defined in the template, in this case “CustomImage”.

You can check the image after creation using:

openstack image list