Vagrant Use Case
I work on a MacBook. I love my MacBook. But I don’t love my software and the tools it relies on to behave differently when I’m using them, than they do when deployed to staging or production. It doesn’t happen often, but when it does, it can be very frustrating. For this and other reasons, I have found Vagrant to be a useful tool to have in my development toolbox. I use it everyday.
This is not meant to be a “How To”. In my opinion, the documentation does a nice job explaining how to use the tool. Instead, I want to explain how and why I use it, as well as some of the trade-offs I have made to get the benefits.
What Is It?
The intro page on the Vagrant site offers a good explanation. That said, I view it as a command line tool that is used to interface with a headless virtual machine (eg. VirtualBox, VMWare, etc.) that runs on your laptop or desktop. It is used to build, manage, and destroy virtual machines that are meant to replicate production environments for development.
Now, I know what you’re thinking because I thought the same thing when it was described to me. Why do I need a virtual machine and why learn a command line tool to manage it when it comes with a perfectly good GUI? I will attempt to answer these questions now.
The first, and in my opinion most important, reason I use a VM is to run my development environment as similar as I can to my production environment. I was happy to learn (after I decided this was important to me) that these sentiments are echoed in the tenth factor of The Twelve Factor App. It’s a good read if you’re not familiar with it.
I remember when I attempted to switch from RVM’s gemsets to project local
vendor bundles to manage all of my projects’ gems. I happened to be using
Vagrant at the time and I quickly found out that when I ran
bundle install on
my host MacOS, I got different gems than when I installed them on my virtual
CentOS machine. The MacOS gems cannot run on the virtual machine and vice
versa. The differences between the two sets of gems are in the compilation and
are likely trivial, I know. But the fact that they are different made a
significant impact on me.
Provisioning is key feature that Vagrant provides. It’s a fancy way of
saying automated setup of the virtual machine. Vagrant allows several methods
for provisioning a new VM. I
chose to write a set of bash scripts that Vagrant uses to setup my VM the
first time it is brought up. The scripts do take some time to write but it is
well worth the effort. They allow me to destroy a littered VM and bring up a
brand new one, complete with a necessarily old version of MySQL, RVM with the
JRuby and MRI versions I need, a few global gems, all of my dotfiles, the Nix
package manager, Supervisord, and all of the other dependencies our
application has with a single
vagrant up command.
Because creating a brand new VM with all of the software and tools I need is so easy, I frequently destroy it and start fresh. Why not? If I ever find myself wondering if a hosed up environment is causing trouble, it takes about 5-10 minutes to have a brand new one and resolve the concern. Awesome.
Once a Vagrant machine is created and tailored to your project’s needs, it can be easily shared with the rest of the development team. This can be accomplished in two ways. If you have a thorough provisioning process you can share the Vagrantfile along with any scripts needed to complete the provisioning. I accomplish this via a git repository. The other method is to create the VM manually then turn it into a Vagrant box. To share the box you can upload it to Vagrant Cloud or pass the large file around on an external hard drive.
Snapshots have been around on virtual machines for years. Vagrant grants easy access to them with a few simple commands. I use snapshots as sort of a test bed to determine what should go into my provisioning scripts. I’ll make a snapshot when I’ve made some changes to the VM that I think are important. If I call up the snapshot with any regularity, it’s time to update the provisioning scripts.
Another benefit I get out of using a VM is having a sandboxed environment. I use Homebrew on my Mac to get packages I need. My team uses Nix to package all of our services and dependencies. If you’ve ever tried to use two different package management systems on one machine you’ll know how difficult it can be. I had heard about the peril it created but I decided to learn the hard way. (Spoiler: SSL certificate management is a pain in the butt.) Now I keep Nix on the VM, Homebrew safely alone on the host OS, and everything works great.
Synced folders are used in Vagrant to keep host machine and virtual machine folders, well, in sync. They’re easy to setup and allow me to continue writing all of my code in IntelliJ, Atom, or whatever my current editor of choice is on MacOS and immediately run the changes in the virtualized CentOS environment. I admit, this isn’t so much a “pro” as it is a non-con, but I thought it was worth mentioning here.
The final item in my “pros” list is simple and subjective. I like the console. The list of commands needed to interact with Vagrant on a daily basis is short and intuitive. From firing up the VM to ssh’ing onto it then bringing it down - no GUI necessary.
Alas, the path to developing atop a virtual environment is not all sunshine and rainbows. While there are many benefits to running all of your code on a VM, those benefits come with some costs. Here they are.
In my opinion, the biggest cost to running with Vagrant is slower runtimes. My applications typically take 10-20 seconds longer to load in the VM than when run from the host OS. Requests from my host OS browser to the server running on the VM also take a few seconds longer to execute. The added time may not sound like much but it is absolutely noticeable.
My understanding is that this performance penalty stems from using synced folders. So if you can live without them (ie. edit code and perform all of your Git duties solely within the VM), then you may not encounter the penalty. Because the synced folders feature is fairly high on my “need-to-have” list, I’m not willing to make the trade.
The second challenge introduced by virtual machines is shared resources. CPU cycles, RAM, and disk space are all consumed by the overhead of running another operating system in parallel with the one hosting the VM. Disk space isn’t usually a problem but I’ve seen people struggle with it. Memory and CPU bottle necks seem to be a bit more common.
The last item on my “cons” list is subjective but absolutely warrants recognition. As I mentioned earlier in this post, I tried switching from RVM’s gemsets to bundler managed project local gem directories. One implementation of this can be found in Stefan Wrobel’s blog post, Bundle Everything. Doing this with synced folders absolutely destroyed the performance of my projects. It was unusable. If this is your required workflow, Vagrant is probably not for you. On the other hand, if you’ve found a way to make this workable, please let me know in the form below.
When I tally up the scores, the pros outweigh the cons for me. That said, it is not a landslide victory. Many of the calculations on the scales are subjective. I have enough CPU, RAM, and disk space that the downsides aren’t that bad. Having a similar environment to production and having the ability to renew my environment on a whim, make up for the performance losses I incur. I hope I’ve given you enough information to decide for yourself whether or not Vagrant might be worth putting into your toolkit.