Building images for Vagrant with Packer

In this how-to I'll show how to build Debian and Ubuntu ISO images with Packer.

TL;DR

Packer is a tool for creating server images for platforms such as Amazon AWS, OpenStack, VMware, VirtualBox, Docker, and more — all from a single source configuration.

Here I go through the following steps:

  • Setup Packer and Vargant locally with VirtualBox
  • Download and run a pre-defined ISO base box
  • Define an own Debian ISO base box with Packer
  • Build Jessie 8.3 locally
  • I'll show how to use Atlas to build and store the image in a next how-to.

Setup Vagrant and Packer with VirtualBox

If you are not familiar with Vagrant and Packer, then some reading first:

Install Vagrant

The recommended way of installing Vagrant is via packages. Simply go to Vagrant download page for the available downloads for the latest version of Vagrant. Download and install the proper package for your operating system and architecture, that's it.

Vagrant 1.0.x had the option to be installed as a RubyGem. This installation method is no longer supported, remove those gems upfront. Also, some operating system distributions include a vagrant package in their upstream package repos. Typically these packages are outdated versions of Vagrant.

The installer will automatically add vagrant to your system path so that it is available in terminals. Do not expect any application, Vagrant is a server-side tool.

Do not forget logging out and logging back in to your system.

The user data for Vagrant is filed in the directory from which vagrant was used and is stored in a hidden directory named .vagrant.d. You can check the version of vagrant you have by typing vagrant -v

Install Packer

Packer is also distributed as a binary package for all supported platforms and architectures.

To install packer, go to Packer download page, find the appropriate package for your system and download it.

Packer is packaged as a "zip" file, so unzip the downloaded package into a directory where Packer will be installed. I use /usr/local/packer. After unzipping the package, the directory will contain a single binary program called packer.

The final step to installation is add Packer to PATH.

You need to add /usr/local/packer to your ~/.profile file:

echo "export PATH=$PATH:/usr/local/packer" >> ~/.profile

Depending on what you're doing, you also may want to symlink to binaries:

cd /usr/bin
sudo ln -s /usr/local/packer packer

After installing Packer, verify the installation by opening a new command prompt and simply type packer. Expected result is like this:

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
push        push a template and supporting files to a Packer build service
validate    check that a template is valid
version     Prints the Packer version

Install VirtualBox

This is well documented on the Internet, I won't cover it here.

Download and run a pre-defined ISO base box

First I'll just download an already prepared ISO base box from Vagrant repository.

Use a Vagrant base box

There are official Ubuntu and Debian vagrant boxes (the “base boxes”) at Atlas repo, its former name was Vagrant Cloud.

If you don't want to build a customized box, and trust the others, you can use those boxes instead of building boxes yourself using Packer.

64 bit Debian server boxes

Ubuntu Official boxes

Some of my boxes

  • Vanilla Ubuntu 15.10 server box for 64-bit architecture doka/wily64
  • Vanilla Ubuntu 15.04 server box for 64-bit architecture doka/vivid64
  • Vanilla Ubuntu 15.10 desktop box for 64-bit architecture doka/wily64-desktop
  • Vanilla Debian Jessie 8.3 server box for 64-bit architecture doka/jessie64

For example, most simple way with Vagrant is to use Vagrantfile like below, using my Debian Jessie box.

mkdir ~/jessie64
cd ~/jessie64
vagrant init doka/jessie64;
vagrant up
vagrant ssh

In few minutes, you'll be logged in into my Jessie box.

Have fun with it!

Define an own Debian ISO base box with Packer

Here I document how I configured my Debian Jessie box you can find at Atlas.

Since Packer is driven by template files, so the first thing we need to do is create a Packer template. These are JSON documents that let Packer know what you want built.

Let's prepare the JSON template for Packer. There can be few discrete sections in each template, and let’s focus on the most simple one, with only one VirtualBox builder.

Its basic structure is as follows:

jessie64.json:
{
    "variables": {},
    "builders": [],
    "provisioners": [],
    "post-processors": []
}

In the variables we'll define variables for the template. Not really surprising, right? The builders can have all the platforms supported by Packer, but I'll use here only VirtualBox. Special - and complex - sections are boot and preseed. For provisioners, I use the most simple one: shell. The post-processor will be only Vagrant. And please also note:

JSON doesn't support comments!

The variables for Debian Jessie 8.3, and for VirtualBox Guest Additions 5.0.16, are looking like these:

"variables": {
    "ISO_URL":        "http://cdimage.debian.org/cdimage/release/8.3.0/amd64/iso-cd/debian-8.3.0-amd64-netinst.iso",
    "ISO_SUM":        "dd25bcdde3c6ea5703cc0f313cde621b13d42ff7d252e2538a11663c93bf8654",
    "VBOX_VERSION":   "5.0.16",
    "VM_DISK_SIZE":   "5120",
    "VM_TYPE":        "Debian_64",
    "VM_NAME":        "jessie64"
},

The builders are setting SSH and VirtualBox parameters for Vagrant. Next to this, the boot_command is key here. It instructs the server box to bypass the graphical installer, drop to the boot prompt, type some configuration directives, and fetch a pre-configuration file (jessie-preseed.cfg) from a local web server Packer has spun-up.

The boot command sequence will vary from OS to OS and flavors, and this is a common source of failed installs at new OS versions.

"builders": [
{
  "type": "virtualbox-iso",
  "boot_command": [
    "<esc> <wait>",
    "auto url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/jessie-preseed.cfg <wait>",
    "hostname={{user `VM_NAME`}} ",
    "<enter><wait>"
  ],
  "headless": true,
  "boot_wait": "10s",
  "disk_size": "{{ user `VM_DISK_SIZE` }}",
  "guest_os_type": "{{ user `VM_TYPE` }}",
  "http_directory": "http",
  "iso_checksum": "{{ user `ISO_SUM` }}",
  "iso_checksum_type": "sha256",
  "iso_url": "{{ user `ISO_URL` }}",
  "output_directory": "output-{{ user `VM_NAME` }}-{{isotime \"2006-01-02\"}}",
  "ssh_username": "vagrant",
  "ssh_password": "vagrant",
  "ssh_port": 22,
  "ssh_wait_timeout": "10000s",
  "shutdown_command": "echo '/sbin/halt -h -p' > shutdown.sh; echo 'vagrant'|sudo -S bash 'shutdown.sh'",
  "guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso",
  "virtualbox_version_file": ".vbox_version",
  "vboxmanage": [
    ["modifyvm", "{{.Name}}", "--memory", "1024"]
  ]
}
],

The preseed file is located at the HTTP directory, as defined above.

There are lots of options and solutions out there, what and how to put into preseed. I'm using here a minimum version, for the sake of simplicity, and I put as much as possible rather into provisioners scripts.

My minimum in preseed is about language and keyboard settings, network config, setting a mirror and the account on the box (no root, just the vagrant user in my version), clock and timezone setting, partitioning, minimum set of packages, and the boot loader config.

At the end, the vagrant user must have sudo rights, in order to run the remaining setup process with root privileges.

The unattended install process is well documented, start at Debian for more readings.

The provisioners: I'm using base shell scripts here, also for the sake of simplicity. More sophisticated setup with Chef, Puppet or other tools are out there as well.

So my scripts (also on Github):

"provisioners": [
{
  "type": "shell",
  "scripts": [
    "scripts/base.sh",
    "scripts/virtualbox.sh",
    "scripts/vagrant.sh",
    "scripts/dep.sh",
    "scripts/cleanup.sh",
    "scripts/zerodisk.sh"
  ],
  "override": {
    "virtualbox-iso": {
      "execute_command": "echo 'vagrant'|sudo -S bash '{{.Path}}' '{{ user `VBOX_VERSION` }}' '{{ user `VM_TYPE` }}' "
    }
  }
}
],

The sequence of the scripts are also the order how they are executed. Feel free to fork, modify, add and rework these scripts, this is where you can taylor your base box how you want.

My scripts are rather simple, since this is a base box for Jessie, not a special purpose server.

You can extend these as you wish!

The post-processor is also very simple one. I go for a compressed disk size, and defining the output location, where you'll find this box after a successful build.

"post-processors": [
[{
  "type": "vagrant",
  "compression_level": 9,
  "output": "builds/{{.Provider}}/{{ user `VM_NAME` }}-{{isotime \"2006-01-02\"}}.box"
}]
]

Build Jessie 8.3 locally

So I'll have all configuration together, let's build now the box locally.

We'll build my Debian Jessie box, what you can also find on Atlas Repo.

First, clone my repo, with the files explained above, and with some other for a few Ubuntu desktop and server flavors.

git clone https://github.com/doka/vagrant-packer-boxes
cd vagrant-packer-boxes

Then set the packer cache directory as variably in your .profile. It will ensure all of the ISO's downloaded reside in a single location instead of saving them in each folder. This set is optional, but worth to set if you want to regularly download ISO files.

export PACKER_CACHE_DIR="~/vagrant-packer-boxes/cache"

Next, you might want to edit the jessie64.json file. You'll find the preseed file jessie-preseed.cfg at vagrant-packer-boxes/http/, and the provisioning scripts are in vagrant-packer-boxes/scripts/

If everything is ready, then simply run:

packer build jessie64.json

and watch how the magic goes on!

Switch on your VirtualBox, you can follow there the booting and unattended install process (or trace error down).

In your terminal, you'll see the logs from Packer.

It will take a while, be patient!

When it ends, you'll find your Jessie base box at vagrant-packer-boxes/builds/virtualbox/.

Use vagrant to add your box to Vagrant and spin it up as usual:

vagrant box add jessie64 ~/vagrant-packer-boxes/builds/virtualbox/jessie64-2016-04-10.box
vagrant init jessie64
vagrant up
vagrant ssh

Have fun and give feedback how it works!