Simply put, Vagrant is software which provides orchestration for a number of virtualization technologies. It's open source, and created by HashiCorp. Vagrant allows for defining the properties of a virtual machine such as memory, CPUs, name, provisioning, etc, and allows for the quick creation and access of that virtual machine.
Vagrant is an incredibly powerful tool in both development and operations. Whether its for a shared development environment of a legacy application or testing server configuration management tools across varying operating system, or development of anything in between, adding Vagrant to your workflow is incredibly useful.
Key Terms
Vagrant has a few key terms to keep in mind during an introduction.
- Vagrantfile
-
Configuration file defining the machine(s) to create
-
Provider
-
Virtualization Platform (libvirt, VirtualBox, docker, etc)
-
Box
-
Base image (Operating system + configuration to use)
-
Provisioner
-
Additional configuration management to be completed after orchestration is complete
-
Plugin
- extends the functionality of Vagrant
Vagrantfile
The Vagrantfile is a ruby configuration file that defines the machine or machines for orchestration. A default Vagrant file will be created in the working directory on running vagrant init
. Exact configuration options vary by provider, meaning some configuration specifics may be available to libvirt but not VirtualBox.
Providers
A Vagrant Provider is the virtualization platform that Vagrant interacts with. Fedora defaults to libvirt, but this can instead be VirtualBox, docker, even a cloud provider such as Rackspace or AWS. This can be defined at the command line or more conveniently via an environment variable.
Box
A box is the image to use with Vagrant. This is defined as the config.vm.box
to set the base image of the machine to be created. Boxes can be custom created and stored in an artifact repository, or can be found and retrieved directly from Vagrant. A box can have default parameters, such as the number of CPUs and memory, that can be overriden via the Vagrantfile.
Once the Vagrantfile is defined and vagrant up
is run for the first time, the box is pulled from a remote respository. Once the box is pulled its cached locally, meaning that there isn't a large download each time a box is spun up. The default configuration is also to include checks to determine if a box has an update, which can be updated with a separate command (vagrant box update
).
Provisioner
A provisioner is a method for hooking in further configuration management after the virtual machine orchestrated by Vagrant is up and accessible. This can be as simple as running a few bash commands, to as complex as running an ansible playbook to connect to an existing salt master. The big configuration managment methods are supported (Ansible, Salt, Chef, Puppet...) so its easy to set up the virtual machine exactly how you want or even test new or existing playbooks/manifests/state files to run on your existing infrastructure.
Plugin
Plugins (as expected) are ways to increase the functionality and features of Vagrant. These include interactions with cloud providers, methods for creating custom boxes, and an method for creating your own needed functionality through custom plugins.
Using Vagrant
Vagrant configurations can be as complex as your imagination can create, but getting started is super easy. For this example I'm going to go over how to set up a quick Jenkins instance I used to test Jenkins Job Builder files.
Installation
For a simple example all you need is Vagrant and Virtualbox. Follow the installation instructions for your Operating System as this obviously varies.
Environment Variable configuration
I use Fedora which defaults Vagrant to using the libvirt
provider. This is all well and good, but to ensure cross platformness of my Vagrantfiles, I prefer to use VirtualBox. This can be overridden by setting the VAGRANT_DEFAULT_PROVIDER
environment variable to virtualbox
. If you're not familiar, this can be done via export VAGRANT_DEFAULT_PROVIDER=virtualbox
.
Vagrantfile Configuration
In a new and empty directy, create a generic new Vagrantfile with:
vagrant init
This creates a well commented vagrantfile that I'd recommend taking a few minutes taking a look at. We'll need to modify this just a bit. The comments can be left in, but in the end we need the following lines:
```Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.network "forwarded_port", guest: 8080, host: 8080 config.vm.provider "virtualbox" do |vb| vb.memory = "2048" vb.cpus = 2 end config.vm.provision "shell", inline: <<-SHELL sudo yum install -y wget sudo yum update -y sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo sudo rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key sudo yum install -y jenkins sudo yum install -y java-1.8.0-openjdk sudo yum install -y vim git ansible python-requests unzip sudo systemctl start jenkins sleep 300 cat /var/lib/jenkins/secrets/initialAdminPassword SHELL end
#### Basic config
One nice thing about this configuration is that it is very human readable. We're using the basic CentOS7 image available for our base and making port 8080 on the virtual machine accessible on the localhost through port forwarding. Next we set the configuration to be two CPUs and 2GB of memory.
#### Provisioning
To provision the virtual machine, this just just using bash commands. Theres a lot of benefits to using server configuration management such as Ansible for this, but for this simple example we're just running a handful of commands specific to RHEL/CentOS 7.
The provision step updates all packages via yum, installs epel-release to allow the installation of other packages (git, ansible), installs java, then installs and enables the latest official Jenkins LTS. Finally, it starts Jenkins and outputs the username and password of the admin user so we can log into Jenkins.
### Running the example
Spin it up with `vagrant up`. Not including all output for brevity:
```$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos/7'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'centos/7' is up to date...
...
==> default: python2-jmespath.noarch 0:0.9.0-3.el7
==> default: python2-pyasn1.noarch 0:0.1.9-7.el7
==> default: sshpass.x86_64 0:1.06-2.el7
==> default: vim-common.x86_64 2:7.4.160-4.el7
==> default: vim-filesystem.x86_64 2:7.4.160-4.el7
==> default: Complete!
==> default: 3297c139343744c9a05c235e6d73762e
The virtual machine can be accessed via vagrant ssh
. Vagrant takes care of the host and port forwarding.
```$ vagrant ssh [vagrant@localhost ~]$ hostname localhost.localdomain
Ideally, you wouldn't **need** to log into the virtual machine and make changes, but its always handy to be able to troubleshoot or add further configuration. If you find yourself needing to log in to virtual machine, it is probably something that should be added to the provisioner step in the Vagrantfile for the next time you spin it up.
Access the Jenkins instance by opening your browser to `http://localhost:8080`. Setup and configuration of Jenkins is beyond the scope of this post.
If you're all done with the virtual machine, or at some point changes were made to the point that you need to start fresh, this virtual machine can be completely removed via `vagrant destroy`.
```$ vagrant destroy
default: Are you sure you want to destroy the 'default' VM? [y/N] y
==> default: Forcing shutdown of VM...
==> default: Destroying VM and associated drives...
Overview
Vagrant is an easy and programmatic way to spin up a local virtual machine for review or test software, configuration management, or just toying around with a different operating system. Thanks to the fairly shallow learning curve in getting started with Vagrant, it is a great way to add to your productivity in a wide range of development or operations tasks.