by Dan Pilone
I recently had the opportunity to talk at the DC Ruby User’s Group about how we use Vagrant in our web development projects. My slides are available here. The short version is that Vagrant provides a virtualized development environment. This doesn’t mean you have to write everything using vi or Emacs in a shell (though I won’t judge you if you do that… some of my best friends use emacs…). Vagrant transparently mounts directories into the VM so you can use your normal editor/IDE on your machine to develop but run rails server or whatever inside the VM.
In addition to mounting a shared filesystem, Vagrant makes port forwarding trivial. When you first create a Vagrant instance it will place a sample Vagrantfile in the directory for you. In this file you can configure what directories to mount where, what ports to forward from your development machine into the VM, and lots more. By setting up a port forward for port 3000, for example, you can use Chrome, Firefox, IE (bazinga…) on your development machine to hit your rails app running inside the Vagrant VM.
Vagrant does all of this magic by leveraging Oracle’s VirtualBox. Vagrant has the concept of base boxes – basically minimal installs of an OS that are used as the initial setup for a new VM. There are lots of Linux VMs available here and it’s pretty straightforward to create your own. Once you have your base box selected, you can simply run the vagrant init command to have Vagrant create a Vagrantfile for you. Tweak that if so desired, then run vagrant up to have vagrant create a new VM from your base image, register it with VirtualBox, then launch it headlessly. Once the VM is up, you can use vagrant ssh to connect to it and your project data is available in /vagrant. When you’re done, you can vagrant halt to stop the VM or vagrant destroy to remove it entirely. A simple vagrant up brings it all back again.
Why we’re all here…
So while VMs are cool and all that, you might be wondering why this is worth a blog post. Well, things like RVM and bundler do great things for keeping Ruby versions separate and project gems in isolation, but they do nothing to help with application dependencies like MySQL, Redis, Passenger, Apache, PostgreSQL, Oracle, ImageMagick, Qt (for WebKit), etc., etc. A developer’s box kinda becomes this sketchy place where you just really want to wash your hands after using it. Vagrant provides a great way to keep all of that nicely contained, per project, and away from your precious development machine. But even that’s not the totally awesome part…
Vagrant has built in support for machine provisioning. It supports both Chef and Puppet as provisioning solutions. By simply enabling a provisioner in your Vagrantfile, when Vagrant boots your box it will kick off your provisioner of choice. This means that with a little effort (ok, if you’re new to puppet, a lot of effort…) you can codify what your development environment needs to look like for a specific project and Vagrant can build it for you with a simple vagrant up. By taking the time to put together provisioning information, you can now CM your development needs along with the rest of your project artifacts and someone that needs to work on the project (someone else on your team or even the customer when you transition everything back to them) can simply use Vagrant to put together all of the dependencies necessary for development and deployment. If you do it right, you can even leverage your provisioning information to provision your operational or test systems – no more hunting down weird dependency problems or debugging something only to find that the wrong version of MySQL, Passenger, imagemagick, berkeleydb,
Now if you really want to push Vagrant to the limits, it supports the concept of multi-VMs. One of our clients has a multi-tiered, load balanced, highly available, clustered application that we write and maintain. This application uses a three node Oracle RAC and a three node Elastic Search cluster to index and search over a hundred million pieces of geospatial information. Fault tolerance is essential. By using multi-VM support in Vagrant you can trivially configure multiple VMs in your Vagrantfile and, with a single vagrant up command, bring up an entire stack of clustered nodes. You can configure the nodes to all be on a specific subnet (or multiple subnets if so desired) and they can see each other as expected. From here you can build multinode elastic clusters, RAC instances, etc. You can test load balancing with tools like HAProxy, fault tolerance and failover, cluster balancing on node loss, etc. Obviously performance isn’t going to be what it would on real hardware, but you can, in a development environment, mimic what a real operational configuration would be like and how the system would really respond to failures. Being able to test this without needing stacks of machines or manually managing VMs is just plain awesome.
Vagrant is an important tool in our toolbox. If you want to know more about how we build and test scalable, fault tolerant systems for our clients or want this kind team working on your projects, drop us a note at firstname.lastname@example.org – we’d love to talk to you. — Dan