Vagrant with Packer on VirtualBox
02 Apr 2014
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.
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 goinstalled 1.2.1
- Packer. Doing
brew tap homebrew/binaryand
brew install packerinstalled 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.
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
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
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
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.
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.