In the previous post in this series, I looked at how to provision an Ubuntu box with Vagrant in a matter of moments. Now that the process is understood and the power of Vagrant has been seen, it’s time to break it down a little bit and show how Vagrant is able to create a fully functioning VM with a simple vagrant up .

Vagrant Project Setup

When deploying a new development environment, the first thing to do is set up an area for all of the project files. In the previous post, vagrant init hashicorp/precise32 actually created the project in whatever directory the terminal was open to. For a bit more organization moving forward, I prefer to create a folder for a given Vagrant environment. The configuration files will be stored here, as will any supporting files. For instance, a www share for an Apache server might exist in this folder. Creating a new project is easy.

mkdir VirtAdmin cd VirtAdmin/ vagrant init

In the desired location, just create a new folder, and from within that folder run vagrant init. This will automatically generate a Vagrantfile! What’s a Vagrantfile, you might ask?

Vagrantfiles

A Vagrantfile is named for the way it appears on the filesystem. It’s literally a file called ‘Vagrantfile’ with no extension. The syntax used within the file is actually Ruby, but don’t let that scare you. I don’t know Ruby from my foot, and I’m getting by just fine. The purpose of a Vagrantfile is to define the machine(s) and provide details about how to configure them. This Vagrantfile is the portion of the project that should be maintained within a version control system (I use Github) to ensure that all systems are configured the same way and that collaborative efforts don’t step on each other’s toes.

When you run Vagrant commands (like vagrant halt ), Vagrant is using the closest Vagrantfile up the tree. Meaning that if my working directory is /Users/James/vagrant-local/Virtadmin/www and I run a Vagrant command, it will find the Vagrantfile that lives at /Users/James/vagrant-local/VirtAdmin/Vagrantfile .

There are four major namespaces within a Vagrantfile where configuration can be done. They are:

config.vm – Configure virtual machine related settings

config.ssh – Configure SSH settings for accessing the VM to do provisioning tasks

config.winrm – Configure WinRM settings for accessing the Windows VM to do provisioning tasks

config.vagrant – Configure host specific details or details about Vagrant itself

Also, rather than the one Vagrantfile in the project directory, the reality is that a series of Vagrantfiles may be loaded. This allows the most specific settings (at the Provider level, for instance) to overwrite generic settings on the Box. The load order (taken from the Vagrant documentation) is show here:

Vagrantfile packaged with the box that is to be used for a given machine. Vagrantfile in your Vagrant home directory (defaults to ~/.vagrant.d ). This lets you specify some defaults for your system user. Vagrantfile from the project directory. This is the Vagrantfile that you’ll be modifying most of the time. Multi-machine overrides if any. Provider-specific overrides, if any.

One other note from the documentation: because the Vagrantfile is actually just Ruby, you can easily use programming constructs like loops. This would allow the configuration of 5 machines in a multi-machine project in a cleaner, more elegant way. The below is an example from the docs.

(1..3).each do |i| config.vm.define "node-#{i}" do |node| node.vm.provision "shell", inline: "echo hello from node #{i}" end end

This will make all three of the machines echo “hello from node ‘n'” in a local shell. Obviously not interesting, but if it was an actual configuration task, this would be quite helpful as opposed to doing this on all three machines.

Vagrant Boxes

Writing a Vagrantfile and base box from scratch may certainly be necessary if the purpose of the deployment is to use as a development environment for a custom enterprise application. The environment would need to be provisioned exactly the way the development team needs, and would likely have plenty of unique customizations to suit their environment. However, writing a Vagrantfile can be a non-trivial task. A shortcut would be nice… Enter boxes!

A Vagrant box is a package of all the information required to go from zero to fully provisioned, and they’re shared publicly and freely in places like the Atlas box catalog and vagrantbox.es. In the first post, the Ubuntu box came from the Atlas catalog! When I ran vagrant init hashicorp/precise32 , it grabbed the box from here! Adding some other kind of box is so easy! If I needed a CentOS box to do some testing with, all that needs to be done is run vagrant box add chef/centos-6.5. In no time, I’ll have a local copy that I can initialize at any time. Or, if I need it right away, this can all be done in one stroke but just using the init command like I did with the Ubuntu machine: vagrant init chef/centos-6.5.

Because boxes support versioning, a development team can iterate as their development environment changes and matures. Most boxes in the wild use semantic versioning. This also comes in handy when boxes need patched or need new dependencies added.

You may be wondering: just what exactly is in the box? Well, boxes are unique to providers (VirtualBox, Fusion, AWS, etc.) so first of all the box contains information specific to the provider. Secondly, it contains metadata about the box.

Provider specific files: in the case of VMware Fusion, this would be a compressed archive of the .vmx, .vmdks, and other files needed to make a virtual machine. Vagrant doesn’t actually do anything with the box files itself – it just hands them off to the provider during initialization.

Metadata: a box also includes a JSON document that describes the box. This includes things like version information, provider information, and checksum data.

That’s it for this section! In Part 4, I’ll be taking a look at networking configuration. Vagrant machines have the option to either use a private “host only” network, or use port forwarding, or gain access directly to the public (could still mean internal LAN) network.