A quick intro to
vagrant,
packer, and
boxcutter, which are all projects under the
Hashicorp umbrella. From reading the project websites it's not immediately obvious how all of these relate and compliment each other, they are all vm-related after all. I'll introduce each and then give an end-to-end example.
Vagrant
Firstly vagrant: it allows you to specify your build environment in a way that is completely repeatable by anyone in the world. No more "builds fine for me". You can test in exactly the same environment, with the same versions of dependencies, installed by the same scripts.
So what's special about that? Why not just have a base vm and an install script that sets everything up? For a long time we essentially did that but a bit worse: we had a server with a set of build VMs on it. Everyone who wanted to use the VMs would add their ssh key, and copy a giant ssh_config which set up all the port forwards needed to talk to the VMs and build the project. We never quite got around to automating the dependency installation, so whenever a dependency needed updating we'd need to ssh in and upgrade each VM manually. The server lived in one timezone, but developers were also on the other side of the world so copying chunks of data over for building was slow and timeouts were fairly common.
With vagrant we saw the following advantages:
- Bringing up a new vm is fast, so there's no need to keep them around and potentially contaminate your new build with previous build products. If you always provision a new vm to build then maintaining dependencies is as simple as updating the provisioning scripts.
- Port conflicts for multiple VMs are managed automatically, no need for the big ssh_config
- Builds are performed on local VMs, so no waiting on network copies to servers on the other side of the world. Shared folders provide a simple way to move data in and out of the VM.
- Testing additional operating systems and architectures is simple thanks to the atlas catalog of vagrant boxes.
- Updating dependencies across multiple architectures and operating systems is as simple as modifying the provisioning script. While this isn't technically an advantage of vagrant, it encourages this kind of automation.
- Instead of describing how to set up a build environment in pages of documentation, you can add a Vagrantfile and some scripts to your project and reduce all that documentation to a single command like 'make'.
- We don't need to store the virtual machines ourselves, just upload a base build box to the catalog. When people build our project vagrant will fetch the VM and check the hash of the downloaded VM matches what we specify in our Vagrantfile (or at least it will soon).
OK that sounds good, but I want to make my own base VMs and it's tedious. Enter packer.
Packer
So vagrant helps you automate provisioning and running a build environment working from a base VM. Packer helps you with building the base VM in the first place which
opens up a lot of possibilities for testing and automation. For the purposes of this post, packer is just going to help us avoid the tedious GUI click fest of installing a windows build VM and getting it into a usable state.
Since packer basically eats a JSON config and turns it into a virtual machine, you expect to find an example of a packer config to follow right? You're not the first person to want a Windows 7 VM. Enter boxcutter.
Boxcutter
First install vagrant, packer, and whatever virtualisation software you plan to use (e.g. virtualbox or vmware: make sure you have the latest version) then get the boxcutter repo:
$ git clone https://github.com/boxcutter/windows.git boxcutter-windows
Then get a windows VM (this took around 45min for me, but is a spectacularly less work than
doing it yourself):
$ make virtualbox/eval-win7x64-enterprise
The boxcutter scripts include all the tricks for minimising disk usage (doing a defrag, zero-ing free disk space etc.), so this is about as small as it can possibly be:
$ ls -lh box/virtualbox/eval-win7x64-enterprise-nocm-1.0.4.box
-rw-r----- 1 user group 3.2G Apr 13 14:31 box/virtualbox/eval-win7x64-enterprise-nocm-1.0.4.box
Add it into vagrant:
$ vagrant box add box/virtualbox/eval-win7x64-enterprise-nocm-1.0.4.box --name eval-win7x64
Reference it in your Vagrantfile something like this:
config.vm.define "eval-win7x64" do |box|
box.vm.box = "eval-win7x64"
box.vm.guest = :windows
box.vm.communicator = "winrm"
end
Fire it up:
$ vagrant up eval-win7x64
Log in:
$ vagrant ssh eval-win7x64
Last login: Mon Apr 13 14:19:31 2015 from 10.0.2.2
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Users\vagrant>
Troubleshooting
Note if you see errors like these:
==> virtualbox-iso: Provisioning with shell script: script/vagrant.bat
virtualbox-iso: 'C:/Windows/Temp/script.bat' is not recognized as an internal or external command,
virtualbox-iso:
virtualbox-iso: operable program or batch file.
==> virtualbox-iso: Unregistering and deleting virtual machine...
==> virtualbox-iso: Deleting output directory...
Build 'virtualbox-iso' errored: Script exited with non-zero exit status: 1
or
==> virtualbox-iso: Error detaching ISO: VBoxManage error: VBoxManage: error: Assertion failed: [SUCCEEDED(rc)] at '/build/buildd/virtualbox-4.3.10-dfsg/src/VBox/Main/src-server/MachineImpl.cpp' (10875) in nsresult Machine::saveStorageControllers(settings::Storage&).
==> virtualbox-iso: VBoxManage: error: COM RC = E_ACCESSDENIED (0x80070005).
==> virtualbox-iso: VBoxManage: error: Please contact the product vendor!
==> virtualbox-iso: VBoxManage: error: Details: code NS_ERROR_FAILURE (0x80004005), component SessionMachine, interface IMachine, callee nsISupports
==> virtualbox-iso: VBoxManage: error: Context: "SaveSettings()" at line 888 of file VBoxManageStorageController.cpp
==> virtualbox-iso: Unregistering and deleting virtual machine...
==> virtualbox-iso: Deleting output directory...
Build 'virtualbox-iso' errored: Error detaching ISO: VBoxManage error: VBoxManage: error: Assertion failed: [SUCCEEDED(rc)] at '/build/buildd/virtualbox-4.3.10-dfsg/src/VBox/Main/src-server/MachineImpl.cpp' (10875) in nsresult Machine::saveStorageControllers(settings::Storage&).
VBoxManage: error: COM RC = E_ACCESSDENIED (0x80070005).
VBoxManage: error: Please contact the product vendor!
VBoxManage: error: Details: code NS_ERROR_FAILURE (0x80004005), component SessionMachine, interface IMachine, callee nsISupports
VBoxManage: error: Context: "SaveSettings()" at line 888 of file VBoxManageStorageController.cpp
Then you need to upgrade your virtualbox. I saw these when using the virtualbox shipped with ubuntu.