Most commonly, Vagrant’s Vagrantfile describes only a single VM. That’s fine, but most environments separate functionality to different servers (e.g., database and web app). For this reason, Vagrantfiles can be set up for multi-VM arrangements.
However, I’m going to describe a different use case for multiple VMs.
Testing Multiple Distributions
As a cookbook developer for a variety of platforms and platform versions, I have to ensure changes do not break existing functionality across supported platforms - namely current releases of Ubuntu and CentOS (and their parents, Debian and RHEL).
Enter Vagrant’s Multi-VM Vagrantfile.
While I posted recently about testing with VMware Fusion, I am using Vagrant more. Primarily because Opscode uses Vagrant internally - Seth Chisamore built a multi-VM Vagrantfile for bringing up our full stack. The specifics in his Vagrantfile are tuned to that particular use case which is different than mine. However, there are similar patterns that I adapted.
The Vagrantfile configures four virtual machines:
- CentOS 5.7 and 6.2
- Ubuntu 10.04 and 11.10
I built my VMs with veewee templates that install Chef via the omnibus built chef-full package. That way I have a consistent installation that reflects what Opscode will ship as the easiest and best supported way to install Chef.
Part of the magic of this configuration is that I’m going to reuse my knife configuration. The Vagrantfile itself goes into my cookbook testing Chef Repository.
1 2 3 4 5 6
Next, I’m going to describe data about the virtual machines that I’m
going to run. This is a hash of named VMs,
lucid, etc. I
assign their hostname, and give them a host only IP address. I also
set an initial run list, since Vagrant will (noisily) complain if the
run list is empty in a Chef provisioner.
Note I have a
base role as a holder, the actual relevant things
are in the
base_debian roles. The details really
don’t matter, though.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
Next, I’m going to set up the
Vagrant::Config object as “global”
configuration for all the VMs, and then iterate over each of the VMs
1 2 3 4 5
I disable the shared folder, since I’m going to use a Chef Server, and my recipes will download what they need from remote repositories, not my local system.
Set up some basic configuration for the box. Modify this to suit your
environment. This section is on a per-VM basis. If particular tunables
were required, I’d create additional config in the
hash above, and use those values here.
name will be a symbol, but only in some contexts of
1 2 3 4
Now I set up the Chef provisioner. Again, I’m using Chef with a Server
(Opscode Hosted Chef, of course). I
validation_client_name settings from
The nodes’ names will be
NAME-cookbook-test, rather than their FQDN.
I use this with a rake task that nukes them all from orbit
1 2 3 4 5 6
The run list is going to be combined from the run lists defined from
cookbook_testers hash above, and a shell environment variable,
CHEF_RUN_LIST, which is simply a comma-separated list of run list
items, similar to that used by
1 2 3
To use the Vagrantfile, I export the shell variable with the
role(s)/recipe(s) I am testing, then run
Vagrant will bring up each VM one at a time, going through the full
cycle of provisioning. If there’s an unhandled exception that causes
Chef to exit, then Vagrant also halts execution. If
vagrant up is
rerun, then Vagrant continues to the next VM. To reprovision a
failed VM, it can be specified:
Without the VM name, vagrant would reprovision all the VMs. Likewise,
vagrant ssh NAME can be used to open an SSH connection to the named
VM. This is useful to reprovision a VM that failed early, while
Vagrant is continuing on with the others.
The Vagrantfile is split up in the earlier section, but you can see the full thing below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51