Automate management of Virtual Machines using Vagrant

Introduction

There are four important Infrastructure as Code tools(or their alternatives) that every DevOps practitioner should be knowledgeable about: Vagrant, Packer,
Terraform, and Ansible. There are other IaC tools that are beneficial as well but a good amount of work can be completed using just the above four tools. In
this post I’ll explain what Vagrant is.

What is Vagrant

Hashicorp says:

Vagrant enables users to create and configure lightweight, reproducible, and portable development environments.

Vagrant is an infrastructure as code(IaC) tool used to automate building and management of virtual machine(VM) environments using different providers like
VMWare, VirtualBox, Hyper-V, libvirt and Docker. What this means is that you can bring up a VM that has everything you need already installed & configured in it
with the help of code written in a declarative configuration file as long as you have Vagrant and your choice of virtualisation software installed(e.g. Virtualbox).

You might want to setup a VM with:

Instead of manually installing all of the packages and tools, you can codify what you need in a configuration file called Vagrantfile, run the command vagrant up and Vagrant will boot up a fully setup VM which you can then work with using VM’s SSH(vagrant ssh) or desktop interface.

Here’s an example of a minimal Vagrantfile to spin-up a VM that has NodeJS build tools pre-installed:

Copy to Clipboard

Here we’re basing our VM on a vagrant box called `generic/ubuntu1804` which is based on Ubuntu 18.04 operating system. There are many such public boxes
available for free at Vagrant Cloud.

How is Vagrant useful? Below are some use-cases.

Spin-up a virtual machine to check if your code works on a clean machine

One of the biggest source of disagreements between a development team and an operations team, or even between two members of the same team, is that the code
“works fine on my machine”. This implies that the problem must be with the machine of the other person, not necessarily with the code itself.

Isn’t it really frustrating when code works just fine on machine A but fails to work on machine B? It could take any number of hours to figure out the reason
for the discrepancy because each machine might have different environments. What’s different on machine B that’s causing the different behaviour? Is it the
incorrect code or is it the new update of the operating system on machine B? Some background process? A different shell? Different version of the build tool?
Stale cache? The list goes on.

Vagrant saves you from the frustrating, time consuming task of trying to figure out if the problem lies with the environment in which the code is running on
machine A/machine B or the code itself. You can create the environment necessary to run the project in a VM, copy the code to that virtual machine(or clone it
from a code repository) and then run it in a clean environment. If it works, the code is fine, if not, the code needs to be fixed. Vagrant can make this process
repeatable on demand effortlessly.

Set-up an isolated environment to develop your project

Are you the kind of developer who tries out many new technologies by installing different tools, libraries & SDKs? Does it frequently mess up your development
machine? Sometimes we can no longer continue development of our main project forcing us to re-install and setup everything from scratch. Conversely, if you
never experiment with new technologies, how will you learn more modern & productive ways to develop software?

Vagrant can help you segregate your main development environment inside a VM while you continue to work with your code editor and project files on the
host(physical) machine. Every change you make to the code on your host machine is synced with VM automatically. This way you can continue to play with new and
experimental technologies on your host machine while your dev environment remains isolated & safe inside a Vagrant managed VM. I prefer doing it with
containers these days but VMs make it more convenient when your project has different parts that need to be tightly integrated.

Set-up your full development environment

You can also set up a full development environment inside a VM, like your preferred shell, editor/IDE, technology stack, git, cloud tools, etc. You can
still keep the applications not related to the development tasks on the host machine, like web browser, email client, chat application, etc. This allows you
to have a consistent development environment inside the VM across your host machines(PC/laptop/office PC), across multiple host operating
systems(Windows/macOS/Linux), across multiple versions of the operating systems as you keep upgrading them(you don’t need to upgrade the OS inside the VM).

As an example, you can let Vagrant setup an Ubuntu LTS version based VM that has the following tools installed & configured: zsh, oh-my-zsh, tmux/tilix, vim/emacs/vscode, Java/NodeJS/React/go, docker, az/gcloud/aws, kubectl, and whatever other tools are needed. You can use it from a Windows, a macOS or a Linux machine, you can replicate it on multiple machines, you can reset it to its initial state whenever something stops working, you can even share it with new team members to get them up to speed in no time. This setup has its advantages and disadvantages, so it’s not for everyone.

Learn DevOps/networking without incurring charges

If you have a decent computer, you can also use Vagrant to spin-up multiple VMs to learn DevOps skills like how Ansible can be used to configure multiple
machines using a uniform config, how to set-up an on-premise Kubernetes cluster, how to deploy an project in a Docker swarm, how to set-up different network
types and setup firewall rules. You can learn the same skills on cloud infrastructure but doing it on VMs running on your local machine is both faster
and more cost effective. Each virtual machine needs very few resources when run in headless mode, so you can run ~3 VMs even on a machine with low
specifications. An alternativclase to do the same would be to use four to seven Raspberry Pi 4 modules in a Turing Pi.

Others

Hashicorp says:

The cost of fixing a bug exponentially increases the closer it gets to production. Vagrant aims to mirror production environments by providing the same operating system, packages, users, and configurations, all while giving users the flexibility to use their favorite editor, IDE, and browser. Vagrant also integrates with your existing configuration management tooling like Ansible, Chef, Docker, Puppet or Salt, so you can use the same scripts to configure Vagrant as production.

You can also use Vagrant to automate the management of VMs if you deploy your applications to production using VMs on on-premise computers or cloud platforms
like AWS.

You can test if your project builds & runs successfully on different operating systems or with different versions of one operating system or in different
versions of a runtime by automating spinning up and tearing down of multiple VMs using Vagrant. A build pipeline can also be used for similar purposes.

Conclusion

[…] As for Vagrant, I’d never heard of it until I got here and it’s pretty much the best thing since sliced bread in my opinion. Good stuff there. […] At one time, I thought of Vagrant as yet another tool in the mix to waste my time. I’m so glad I gave Vagrant a fair chance and that I was wrong to be so cranky about a new tool. I now use Vagrant all the time.

From an email showing Vagrant adoption

There are many other ways in which Vagrant can be used to take some of the pain points out of your development workflow. I’ll show you practical implementation of couple use cases of using Vagrant in future posts. Vagrant is easy to learn, can be immensely useful, and doesn’t really have a one-to-one alternative; hence it’s one of the most essential IaC tools that should be a part of every developer’s toolbox.

References