Portrait of Martijn

Vagrant with Packer on VirtualBox

02 Apr 2014

This post is about using Packer with Vagrant under VirtualBox.

Introduction

I typically use Linux server virtualisation technologies, but I often work with other developers who just want to have an easy deployable virtual machine on their MacBooks. For that use-case, Vagrant under virtualBox is a great option, but then the question becomes how to package virtual machine images. You can do it by hand, you can script it with VBoxManage and ssh, use the usual Chef/Puppet/Salt/Ansible/Fabric provisioning, and there are packaging mechanisms like VeeWee, and Packer.

Packer is "a tool for creating identical machine images for multiple platforms from a single source configuration". It's a project from Mitchell Hashimoto (author of Vagrant), and according to the github commit history has been going for about a year, but is stiill pre 1.0. What's appealing about it to me is that it targets multiple platforms (Amazon EC2 AMIs, VirtualBox, Docker, Google Compute Engine, OpenStack, VMWare, and Qemu) and that it's written in Go and self-contained (so no Ruby/RVM gem dependency chain maintenance nightmare). It is distributed under the MPL2.

So in the VirtualBox case, what does it actually do? It parses a json config file, uses VBoxManage to create a virtual machine, installs the OS (by typing key codes at the console, and providing a preseed file over HTTP, installs your applications, and produces a Vagrant "box" that you can import and run. That's really nice. I've done some of that scripting (VBoxManage with Fabric) before, but this is far more complete and re-usable.

The Packer Docs are very good, and there is an introduction that uses EC2 as an example. I found this blog post "Building Vagrant Machines with Packer" which is more a quick-start overview with an example that installs Redis.

Then I discovered ffuenf, a set of Packer configurations for specific OS versions, under an Apache 2.0 License, with prepared boxes ready for download. It includes configuration of Docker. For my particular project I wanted an Ubuntu install with Java and Docker, so this was rather close.

I'll go through how to create and use the virtual image from the ffuenf example, then modify it for my needs.

Pre-requisites

I'm using a MacBook Pro for development. You need:

  • VirtualBox. I used 4.3.10 for OS X
  • Vagrant. I used 1.4.3
  • Go. Doing brew install hg; brew install go installed 1.2.1
  • Packer. Doing brew tap homebrew/binary and brew install packer installed 0.5.2

I recommend you also use Homebrew, and I expect you may need the OSX command line developer tools (see How to Install Command Line Tools in OS X Mavericks Without Xcode).

The Packer build

Get the ffuenf/vagrant-boxes repo. If you don't have git you can probably use the "Download Zip" button; or just install with brew install git.

git clone https://github.com/ffuenf/vagrant-boxes

We'll build the latest ubuntu image, just for VirtualBox:

cd vagrant-boxes/ubuntu-13.10-server-amd64
packer build -only=virtualbox ubuntu-13.10-server-amd64_lxc-docker.json
# this takes a while
ls ubuntu-13.10-server-amd64_lxc-docker_virtualbox.box

to use that box, we'll add it to Vagrant:

vagrant box add ubuntu-13.10-server-amd64_lxc-docker ubuntu-13.10-server-amd64_lxc-docker_virtualbox.box
mkdir test
cd test
vagrant init
sed -i '' 's/config.vm.box = "base"/config.vm.box = "ubuntu-13.10-server-amd64_lxc-docker"/' Vagrantfile
vagrant up
vagrant ssh
docker --version

which should show:

Welcome to Ubuntu 13.10 (GNU/Linux 3.11.0-19-generic x86_64)

 * Documentation:  https://help.ubuntu.com/
Last login: Tue Apr  1 21:24:38 2014 from 10.0.2.2
vagrant@ubuntu-13:~$ docker --version
Docker version 0.9.1, build 3600720

What is particulary handy is that your current directory (test in our case) is available from within the VM, in /vagrant/, so you can easily copy files in and out.

Beyond this, you can do the usual vagrant operations such as:

vagrant suspend
vagrant resume
vagrant destroy

You can modify the Vagrantfile to your tastes, for example uncomment the config.vm.provider :virtualbox section to run with the GUI rather than headless, and to increase RAM.

Troubleshooting

If things go wrong, try running packer with -debug or set export PACKER_LOG=1 to get full logging.

One particular issue I ran into was that Packer hung with an error Waiting for VM to boot. This can take a few minutes. This turned out to be because I had modified the json config file to not configure chef/puppet, which inadvertently removed the installation of curl, which scripts/vagrant.sh uses to get the ssh key it uses to connect to the VM. Google searches found several people with unrelated network-level DHCP problems, but the PACKER_LOG output, and logging in through the VirtualBox GUI soon showed the real problem. To fix this I simply added a apt-get -y install curl to the start of scripts/vagrant.sh.

Customisation

For my purposes I wanted to install Java, and remove Chef.

The json file has a list of "provisioners", which manipulate the virtual machine during installation. In our case that includes a list of shell scripts (located in ./scripts/). To remove Chef, I just removed the scripts/chef.sh line. To add Java, I added a scripts/java.sh line and created that file with:

#!/bin/bash -eux

apt-get install -y git-core curl wget python-yaml build-essential libssl-dev
locale-gen en_US.UTF-8
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

apt-get -y install python-software-properties software-properties-common
hash -r
add-apt-repository ppa:webupd8team/java
apt-get update && apt-get -y upgrade

echo oracle-java7-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections

sudo apt-get -y install oracle-java7-installer

update-alternatives --display java

apt-get -y install oracle-java7-set-default && apt-get clean

For further customisation I want to install my application. I'll copy the installation files with the File provisioner, and add a script to install that.

Conclusion

I've been really impressed with this setup; VirtualBox + Vagrant + Packer make a great combination, and there is good documentation and various examples. I look forward to seeing how this works out in practice.

A big thank you to Mitchell Hashimoto at HashiCorp for Vagrant and Packer, Florian Motlik at Codeship for his blog post, Achim Rosenhagen at ffuenf for his configuration, and Open Source developers everywhere for their contributions.