Episode #42 - Crash Course on Vagrant (revised)

Loading the player...

About Episode - Duration: 22 minutes, Published: Dec 16, 2014

In this episode, I wanted to give you a crash course on what Vagrant is, along with how I use it. Vagrant is my go to tool for launching virtual test environments, and once you start to learn how to use it, you will wonder how you ever lived without it.

Download: mp4 or webm

Get notified about future content via the mailing list, follow @jweissig_ on Twitter for episode updates, or use the RSS feed.

Links, Code, and Transcript


In this episode, I wanted to give you a crash course on what Vagrant is, along with how I use it. Vagrant is my go to tool for launching virtual test environments, and once you start to learn how to use it, you will wonder how you ever lived without it.

Back in episode #4, we briefly covered Vagrant at a fairly high level, so I thought it might be useful to dive a little deeper through a revised episode, and show you how I have been using Vagrant for the past year and a half. My take on Vagrant, is that it acts as a type of wrapper around virtualization software, which greatly speeds up many of the tasks associated with setting up, tearing down, and sharing virtual machines. Vagrant is great for creating disposable development environment, and I heavily use it to create sandboxes for episode ideas, where I can play around with an operating system, and then delete it afterwards. I thought it might be worthwhile to go over some of my use cases, so that you can get an idea of what you might want to use it for. Say for example, that if I am going to deploy a new service, I might spin up a couple Vagrant virtual machines, or boxes, on the target operating system, to learn how this service is going to operate in real life. The idea is that this will provide insights into things we should care about, or gotchas which might bite us, before rolling out into production. I also use Vagrant boxes to play around with new operating system features, things like containers, cgroups, and systemd. Then there is testing new operating system patches and OS upgrades. I also use Vagrant boxes for building configuration management scripts, you basically have a self contained system that you can test with, gain experience, and then throw away. I also really like creating multi-node test environments, where I can reproduce ticket related issues, performance problems, or test client server interactions.

Vagrant Use Cases

I have prepared several demos to highlight what I like about Vagrant, but before we dive into them, let me give you an overview of my setup and cover prerequisites you will need to install, before you can get going on your own. I mentioned at the start of this episode, Vagrant acts as wrapper around virtualization software, so for Vagrant to work, you need to have some type of virtualization software setup. Probably the easiest way to get going, is to download and install VirtualBox, because it is free, supports all major operating systems, and it works great with Vagrant. Vagrant also supports VMware, but the Vagrant plugin license costs around 80 bucks, so that might be something to checkout if you are really into VMware. Once you have VirtualBox installed, head over to the Vagrant website, where you can download and install it. Vagrant is also free, supports all major operating systems, and it used to be the case that you needed to install Vagrant as a Ruby Gem, but Vagrant is packed up into a single install package now, and I have found that this greatly simplifies the install process.

Now that we have covered the prerequisites, let me show you my setup, where I have both VirtualBox and Vagrant installed. I just wanted to show you the VirtualBox GUI quickly, even though we are not actually going to have much interaction with it directly, since Vagrant acts as a wrapper around it. Many of these virtual machines in here were created by Vagrant via the command line. If you have ever tried to create virtual machines used for testing through a GUI, you will know that it can be a pain, and it is a very manual process. I have found that there is a tendency to leave testing machines around for a long time without rebuilding them. Before Vagrant there is a resistance to creating clean environments, because there is an extra labour cost associated with making this happen, it just a very manual process via a GUI. Vagrant can eliminate much of extra labour so lets go take a look at how that works.

On my system, I have Vagrant version 1.7.1 installed. If you run the vagrant command without any arguments, you will get the default help output, which displays available command options. I am just going to scroll up here for a second, so here we have the version and help options, then down here we have the main command options, these are used for managing the life cycle of Vagrant boxes. I should actually mention that box, or boxes, is a Vagrant term, for virtual machine. So, you might hear me say, box, or virtual machine, through this episode, these are one in the same. That actually brings us to the first command option I wanted to talk about, called box, and this option allows you to list, add, and remove, boxes, or virtual machines that Vagrant knows about. Say for example, that you download a Vagrant box off the net, you can notify Vagrant about it through this command option.

[~]$ vagrant --version
Vagrant 1.7.1
[~]$ vagrant 
Usage: vagrant [options]  []

    -v, --version                    Print the version and exit.
    -h, --help                       Print this help.

Common commands:
     box             manages boxes: installation, removal, etc.
     connect         connect to a remotely shared Vagrant environment
     destroy         stops and deletes all traces of the vagrant machine
     global-status   outputs status Vagrant environments for this user
     halt            stops the vagrant machine
     help            shows the help for a subcommand
     init            initializes a new Vagrant environment by creating a Vagrantfile
     login           log in to HashiCorp's Atlas
     package         packages a running vagrant environment into a box
     plugin          manages plugins: install, uninstall, update, etc.
     provision       provisions the vagrant machine
     push            deploys code in this environment to a configured destination
     rdp             connects to machine via RDP
     reload          restarts vagrant machine, loads new Vagrantfile configuration
     resume          resume a suspended vagrant machine
     share           share your Vagrant environment with anyone in the world
     ssh             connects to machine via SSH
     ssh-config      outputs OpenSSH valid configuration to connect to the machine
     status          outputs status of the vagrant machine
     suspend         suspends the machine
     up              starts and provisions the vagrant environment
     version         prints current and latest Vagrant version

For help on any individual command run `vagrant COMMAND -h`

Additional subcommands are available, but are either more advanced
or not commonly used. To see all subcommands, run the command
`vagrant list-commands`.
[~]$ vagrant box
Usage: vagrant box  []

Available subcommands:
     add
     list
     outdated
     remove
     repackage
     update

For help on any individual subcommand run `vagrant box  -h`
[~]$ vagrant box list
centos64-x86_64-minimal         (virtualbox, 0)
centos66-x86_64-minimal         (virtualbox, 0)
chef/centos-6.5                 (virtualbox, 1.0.0)
chef/centos-7.0                 (virtualbox, 1.0.0)
phusion-open-ubuntu-14.04-amd64 (virtualbox, 0)
sl64-x86_64                     (virtualbox, 0)
ubuntu-14.04-amd64              (virtualbox, 0)
ubuntu/trusty64                 (virtualbox, 14.04)

I should also mention that Vagrant boxes are special, in that they typically have several required packages installed, and a vagrant user added inside the image. So, you cannot typically just import a random VirtualBox virtual machine into Vagrant without some tweaking. Although, it would not require too much work, you can actually check out episode #5, where I talk about creating Vagrant boxes from scratch using Veewee, and the episode goes into detail about these tweaks. In a couple weeks, I will also review Packer, this is a package released by the same guys who make Vagrant, and it also allows you to also create Vagrant images too.

Vagrant init allows you to initialize a new Vagrant environment. A Vagrant environment can be a single Vagrant virtual machine, or a collection of virtual machines. So, an environment will describe what boxes, or virtual machines to boot, along with all of the associated settings, through a configuration file called a Vagrantfile. I will show you what this looks like in a second, and personally I think this is where the real power of Vagrant is, in that you can define really complex environments through a single configuration file, then share that with other people. The added bonus, is that your environments are self documenting, in that it is easy to read the configuration file and see what is going on.

We will cover the status, up, ssh, and destroy commands, though the following demos, so I rather just explain these by way of showing them in action. The vagrant box command allows you to see which virtual machines, or boxes, vagrant knows about. Lets just run vagrant box list here, and it will output a listing of boxes that I have installed on my system. Most of these are used for testing on my side, there are several here that I have created, by way of episode #5, and some that I have downloaded off the net.

So now that we have covered the prerequisites, gone over some command basics, lets jump into the first demo. For each Vagrant environment, or set of machines that I want to play around with, I always create a project directory. This is episode 42, so lets create a project directory called, e42. I am going to run the vagrant init command, actually let me just show you the help output first, by running vagrant without any arguments. You can see here that vagrant init, allows you to initialize a new vagrant environment by creating a Vagrantfile. So, lets run vagrant init, and see what happens.

[~]$ mkdir e42
[~]$ cd e42

[e42]$ vagrant init
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

[e42]$ ls -l
total 4
-rw-rw-r-- 1 jw jw 2986 Dec 16 18:12 Vagrantfile

We receive this helpful message, about how a Vagrantfile has been placed in our current directory, and how you can run vagrant up, to start your vagrant environment. As you can see we have our Vagrantfile in our current project directory, and this by the way is why I created a project directory dedicated for this episode, in that if we did this somewhere that already had a Vagrantfile, we would get an error message about a Vagrantfile conflict. So, a good habit is to always create a project directory when working with Vagrant.

[e42]$ vagrant init
`Vagrantfile` already exists in this directory. Remove it before
running `vagrant init`.

Lets open up the default Vagrantfile and have a look. The Vagrantfile contains all of the information about the environment we want to create, things like how many virtual machines to launch, which operating system image they should use, network settings, along with configuration management scripts, for things like Puppet, Chef, or Ansible. I am going to talk through this at a pretty high level, but you can check out the Vagrantfile manual page over on the Vagrant website, link is in the episode notes below.

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(2) do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://atlas.hashicorp.com/search.
  config.vm.box = "ubuntu/trusty64"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  # config.vm.provider "virtualbox" do |vb|
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
  #   vb.memory = "1024"
  # end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

  # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
  # such as FTP and Heroku are also available. See the documentation at
  # https://docs.vagrantup.com/v2/push/atlas.html for more information.
  # config.push.define "atlas" do |push|
  #   push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
  # end

  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  config.vm.provision :shell, path: "bootstrap.sh"
  # config.vm.provision "shell", inline <<-SHELL
  #   sudo apt-get install apache2
  # SHELL
end

The first configuration option that I wanted to cover, is the box, or virtual machine image, that we want to use in our environment, and this setting allows us to define that. There is also this handy Atlas site, which allows you to search for vagrant boxes created by the community, we will check that out in a minute.

This block here, allows you to map ports between your host machine, and your vagrant boxes. An example use case would be that you have a web server running on one of the vagrant boxes, and you want to access it via your host machine via localhost. For example, I have used this in several episodes, where I will install an apache web server, and then use this port forwarding feature. You can map a port from localhost 8080 to the vagrant box on port 80. This is really handy for testing all sorts of web services, so lets enable it, and I will show you what it looks. This block here, allows you to define the network address of the vagrant box, which can be really useful if you have several vagrant boxes defined in the same Vagrantfile, just so that they can all communicate on known network addresses.

This block here, allows you to share a folder on your host machine, and mount it inside the vagrant box. This can be really useful for developing locally on your host machine, but making the files available to the vagrant box, this allows you to test things inside a vagrant box, but have the convenience of developing outside of it. Down here, you can also define VirtualBox specific things bits, like if you want access to the GUI, or amount of memory the box should have.

Finally, this block here, allows you to run configuration management tools after the vagrant box has booted. Say for example that you wanted to run some Puppet or Chef scripts to configure Apache and MySQL. Check out episode #8, linked to in the episode notes below, where I talk about Learning Puppet with Vagrant. We can actually insert a line here that will tell Vagrant to execute a shell script for us, this is a provisioning example taken off the Vagrant website, link is in the episode notes below. I will show you the script in a second, but basically we are going to boot the vagrant box, install the apache webserver, and then I will show you how to use port forwarding and the /vagrant mount to serve out web content. It is pretty cool.

Just wanted to quickly pop back to the top of the file where we define a virtual machine image to use, via the config.vm.box option. Lets head over to this Atlas website, where we can browse vagrant boxes created by the community, I should mention that this website used to be called Vagrant Cloud, but was recently merged into the Atlas product. Here you can discover Vagrant Boxes created by the community, and it is actually really simple to use, say for example that you wanted to play around with the latest CentOS, just type in the operating system you are looking for and hit enter. The site is run by Hashicorp, the guys behind Vagrant, Packer, and many other useful tools. I am just going to use this Ubuntu 14.04 box here, as you can see it is pretty popular, with close to a million downloads. We are just going to copy the name, and the hop back to our Vagrantfile, and update the box field. Easy as that. If you really wanted too, you could also click into the description on the Atlas website for the Ubuntu Trusty box, but I find just copying that text name works pretty well. Okay, so that it, lets save this and head back to the command line.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://atlas.hashicorp.com/search.
  config.vm.box = "ubuntu/trusty64"

Before we fire up our first Vagrant box, let me just create an html directory, and throw an example index.html file in there. Lets also create that bootstrap shell script which uses the provisioning option in the Vagrantfile to install apache. You can see here, we update the package cache and install apache, then we remove the default webroot and point it at our /vagrant mount, then restart apache to make sure everything is happy. When the machine boots, it will run this script, and we should be in business.

[e42]$ mkdir html
[e42]$ cat >html/index.html



    _____________________________________________________
   /       _  _  ____    _            _   _              \
   |   ___| || ||___ \  | |_ ___  ___| |_(_)_ __   __ _  |
   |  / _ \ || |_ __) | | __/ _ \/ __| __| | '_ \ / _` | |
   | |  __/__   _/ __/  | ||  __/\__ \ |_| | | | | (_| | |
   |  \___|  |_||_____|  \__\___||___/\__|_|_| |_|\__, | |
   \                                              |___/  /
    -----------------------------------------------------
           \   ^__^
            \  (oo)\_______
               (__)\       )\/\
                   ||----w |
                   ||     ||

[e42]$ cat >bootstrap.sh #!/usr/bin/env bash # update & install apt-get update apt-get install -y apache2 # point /var/www at /vagrant mount if ! [ -L /var/www ]; then rm -rf /var/www ln -fs /vagrant /var/www fi # restart apache /etc/init.d/apache2 restart [e42]$ ls -la total 40 drwxrwxr-x 4 jw jw 4096 Dec 16 19:00 . drwxr-xr-x 129 jw jw 20480 Dec 16 18:59 .. -rw-rw-r-- 1 jw jw 240 Dec 16 19:00 bootstrap.sh drwxrwxr-x 2 jw jw 4096 Dec 16 19:00 html drwxrwxr-x 2 jw jw 4096 Dec 16 18:12 .vagrant -rw-rw-r-- 1 jw jw 3046 Dec 16 18:53 Vagrantfile

You can use Vagrant status to see the current state of your vagrant environment, as defined by the Vagrantfile, and as you can see, ours has not been created yet. So, lets run vagrant up, to fire up our first vagrant box. I have already run this command using the Ubuntu Trusty box, but if you have not, then Vagrant will download the virtual machine image for you. I think it is around 600 megs, so depending on your internet connection, it might take a couple minutes, the nice thing is that these are cached on your machine, so future vagrant up commands based on this image will not download anything. Here, you can see, vagrant has created a new VirtualBox machine called e42. Vagrant acts as a wrapper around VirtualBox, by creating a virtual machine, importing our image, and tweaking all of the setting based on the Vagrantfile. You can see here we have port mapping happening as defined in our Vagrantfile, ssh is set by default, so that is what these ports here are about.

[e42]$ vagrant status
Current machine states:

default                   not created (virtualbox)

The environment has not yet been created. Run `vagrant up` to
create the environment. If a machine is not created, only the
default provider will be shown. So if a provider is not listed,
then the machine is not created for that environment.

Something noteworthy here, is that Vagrant makes a copy of the Ubuntu Trusty box we downloaded when creating this new environment. This means that you can test destructive changes inside our Vagrant environment, and you can always reload to the Ubuntu Trusty source at any time. We will test this out in a minute, but I also created a couple diagrams to show you what is happening too, in the hopes that it will really drive the point home. These lines mention that Vagrant mounted our current e42 project directory inside the Vagrant box as /vagrant. This is useful for sharing files between your host machine and the Vagrant box. A common workflow it to have your editor running on the host machine, sitting in this directory, and then you can manipulate files in the Vagrant box via /vagrant. It just so happens that we are about to install apache on this vagrant box, and it will point to the html directory we created earlier, in our project directory. Now you can see our bootstrap shell script executing, these green lines are the package cache getting updated, and then apache being installed, and a couple errors in relation to our hostname.

[e42]$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu/trusty64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'ubuntu/trusty64' is up to date...
==> default: Setting the name of the VM: e42_default_1418785296661_7593
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 80 => 8080 (adapter 1)
    default: 22 => 2222 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if its present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
    default: /vagrant => /home/jw/e42
==> default: Running provisioner: shell...
    default: Running: /tmp/vagrant-shell20141216-27068-1yn8ul1.sh
....
==> default:    ...done.

Listing the directory contents, you can see we have the Vagrantfile, the html directory, and our bootstrap shell script. Now lets connect to our newly created vagrant box, by running vagrant ssh, and we are in. You will notice that we did not have to find the IP address, or anything like that. We just ran, vagrant ssh, and vagrant figures out where our machine is based off the mapping we noticed a minute ago, and just connects us, which is pretty nice.

[e42]$ vagrant ssh
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-35-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

 System information disabled due to load higher than 1.0

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

0 packages can be updated.
0 updates are security updates.

Lets run lsb_release to verify we are actually on the machine we expect, along with uptime to verify the machine was just launched, what about df to verify our /vagrant mount. As you can see, the html directory, and our index.html file exists. Let open up a browser and test this by connecting to localhost port 8080. Pretty cool. As a test, lets open a second terminal window on our host machine, outside of the vagrant environment, and overwrite our e42 projects html/index.html file. Then lets jump back to the browser and verify its working. This is extremely useful if you are a developer, and want to run your code on a system that looks like production, with the comfort of your desktops development tools.

vagrant@vagrant-ubuntu-trusty-64:~$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 14.04.1 LTS
Release:	14.04
Codename:	trusty

vagrant@vagrant-ubuntu-trusty-64:~$ uptime
 03:03:48 up 2 min,  1 user,  load average: 0.27, 0.23, 0.10

vagrant@vagrant-ubuntu-trusty-64:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        40G  1.2G   37G   3% /
none            4.0K     0  4.0K   0% /sys/fs/cgroup
udev            241M   12K  241M   1% /dev
tmpfs            50M  336K   49M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            246M     0  246M   0% /run/shm
none            100M     0  100M   0% /run/user
vagrant         235G  172G   64G  73% /vagrant

vagrant@vagrant-ubuntu-trusty-64:~$ ls -la /vagrant/
total 24
drwxrwxr-x  1 vagrant vagrant 4096 Dec 17 03:00 .
drwxr-xr-x 23 root    root    4096 Dec 17 03:02 ..
-rw-rw-r--  1 vagrant vagrant  240 Dec 17 03:00 bootstrap.sh
drwxrwxr-x  1 vagrant vagrant 4096 Dec 17 03:00 html
drwxrwxr-x  1 vagrant vagrant 4096 Dec 17 03:01 .vagrant
-rw-rw-r--  1 vagrant vagrant 3046 Dec 17 02:53 Vagrantfile

Back to the console, and you will notice we are connected as the vagrant user, and this user has permissions to su to root, lets just verify this by running sudo su -. This goes back to how Vagrant boxes have specific requirements for how they are built, in that a vagrant user should exist. Okay, so that is a vagrant box in a nutshell, lets go ahead and logout. You can also check the current environment state, by running vagrant status, and we can see that our machine is up and running. Now we could just leave it running, halt it to save your system for later, or you can destroy it, deleting all of the data. I just wanted to show you a couple diagrams, which I hope will explain the vagrant box life cycle, and it is one I frequently use for testing in a clean environment.

vagrant@vagrant-ubuntu-trusty-64:~$ sudo su -

root@vagrant-ubuntu-trusty-64:~# id
uid=0(root) gid=0(root) groups=0(root)

root@vagrant-ubuntu-trusty-64:~# exit
logout

vagrant@vagrant-ubuntu-trusty-64:~$ exit
logout
Connection to 127.0.0.1 closed.
[e42]$ vagrant status
Current machine states:

default                   running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.


[e42]$ 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...
==> default: Running cleanup tasks for 'shell' provisioner...

So we start out with the VirtualBox and Vagrant prerequisites installed on our system. From there we use vagrant init to create a Vagrantfile which defines an environment, and the environment can be defined to launch one of more boxes, although we are just using one for now. We can either use a box we have locally, or use Atlas (aka Vagrant Cloud), and download one for the net. From here, a new virtual machine is launched, based on the configuration stored in our Vagrantfile. Lets say we do some testing, I typically use Vagrant to test configuration management scripts which alter the default state of the box, but I continually want to test on a fresh machine, because most of my changes do not work the first time I try them, so I want to make sure I am testing on a fresh machine each time. So, I just test my changes, make some tweaks, then run vagrant destroy, which deletes this copy of the Ubuntu Trusty box, then I can run, vagrant up again, which reviews the Vagrantfile, and then launched a fresh copy of the Ubuntu Trusty box. This is the killer feature for me, since I have created my own custom vagrant boxes, which look exactly my production systems, so I can test all types of changes on what looks like a real production system, and have confidence that what I am doing is going to work. Vagrant allows me to launch a fresh environment in around a minute, so it makes the testing loop really tight, compared to doing this manually through the VirtualBox GUI.

Vagrant Workflow

Lets jump back to the command line and let me show you what I am talking about in a live example. So, in our last session we just destroyed our first vagrant box, lets just recreate it by running vagrant up, and as you can see it is making a fresh copy of the Ubuntu Trusty box, you should know that it look about a minute to load, I just sped the video up to save time. We can verify the state by running, vagrant status. You do not always have to destroy the box either, say for example I might have vagrant boxes kicking around for many months, as I am playing around with different ideas, so you can use the halt and suspend commands too, but lets just destroy this box for now. Jumping back to the diagrams, you can see that we just destroyed our second Ubuntu Trusty box, now if we typed, vagrant up again, it would launch another copy, based on settings from our Vagrantfile. This was all done in a matter of minutes. Think about all of the work that would be involved with doing this manually. Building the images, getting the networking configured, port mapping, mounts, post install scripts, etc. Then think about how hard it would be to tear down this setup and rebuild it, say for example, that you wanted to start from scratch again.

[e42]$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu/trusty64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'ubuntu/trusty64' is up to date...
==> default: Setting the name of the VM: e42_default_1418788586715_9237
...
==> default:    ...done.


[e42]$ vagrant status
Current machine states:

default                   running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.


[e42]$ 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...
==> default: Running cleanup tasks for 'shell' provisioner...

At this point, hopefully you can see the benefit of using Vagrant rather than just stock VirtualBox or VMware. But, I wanted to show you a final example of how I use Vagrant to create complex multi-node test environments. In upcoming episodes, we are going to look at Nagios, and then Sensu, to see what they can offer in terms of monitoring host state, along with the services these hosts provide. So, I created an example lab environment with web, database, and file server, as hosts we want to monitor, from there I was thinking about installing some example services on each host, that we could monitor with Nagios and Sensu. We will have a fourth monitoring machine down here, connecting to each server, checking state. For that to happen, we need to configure the network on each machine, so that they can all talk to each other, and it helps if we have a known network config. The goal of these episodes, is to show you how Nagios and Sensu work in a fairly real setting, and then to have the monitoring server verify that each host is in an okay state.

Vagrant multi-node environment

So lets have a look at how we would go about doing this with Vagrant. Right now we have our Vagrantfile configured to boot a single Ubuntu Trusty box, but lets look at setting up a multi-node environment. First, lets open up the Vagrantfile again. I am just going to select everything and paste in a template that I have been working with. Let me just size this so that is fits above the fold. So this block here, which is a very trimmed down Vagrant configuration, defines four vagrant boxes, those being web, db, file, and nagios. We are going to be using a CentOS 6.5 box, but this could really be anything, or you could mix and match different boxes. Then over here, we are defining how the network interfaces should be configured inside these vagrant boxes. I have not done this yet, but for the services that I want to install on each host, things like apache, mysql, and samba, I was thinking of adding some configuration management scripts. You can tell Vagrant to run some Puppet scripts for example, that when a box finishes booting, it will launch Puppet, Chef, etc, and go and install software as defined, like we did with the bootstrap script. I think I already mentioned it, but you check out episode #8, to see this in action with Puppet. Since this is just an example, lets save the Vagrantfile and head back to the command line.

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

  config.vm.define :web do |web_config|
      web_config.vm.box = "chef/centos-6.5"
      web_config.vm.network :private_network, ip: "10.0.15.21"
  end

  config.vm.define :db do |db_config|
      db_config.vm.box = "chef/centos-6.5"
      db_config.vm.network :private_network, ip: "10.0.15.22"
  end

  config.vm.define :file do |file_config|
      file_config.vm.box = "chef/centos-6.5"
      file_config.vm.network :private_network, ip: "10.0.15.23"
  end

  config.vm.define :nagios do |nagios_config|
      nagios_config.vm.box = "chef/centos-6.5"
      nagios_config.vm.network :private_network, ip: "10.0.15.15"
  end

end

Vagrant status gives us the current state of the environment, as you can see, we have our four boxes defined, but they are not running, so lets fire them up by running, vagrant up. You are watching four vagrant boxes booting, in a configuration we defined, via our Vagrantfile. This is extremely cool, when you think about all the manual work that would be involved doing this through the VirtualBox GUI. You would need to clone all of these machine, configure network, etc. Lets not forget about how you would get your configuration management scripts into each of these boxes without Vagrant. The thought of going back to doing this stuff manually is not a pleasant one for me. I should mention that I sped this up a little bit, it took roughly 3 minutes and 10 seconds to boot these four machines. There are a couple things to be aware of when using Vagrant. First is that, it can take some time to download the vagrant boxes the first time you launch a box, although if you do it from somewhere that has a decent internet connection, it should go pretty quick. Second, since vagrant makes a copy of the box, for each instance you start, it is copying the vagrant box into a virtual machine container, so that could be 600 megs of disk writes too. I have an SSD, so this is not really an issue, but in this example, we are copying roughly 2.5 gigs of data around, since we are launching four boxes, of around 600 megs each. I have not tested this, but it might make sense to use Vagrant on a ZFS filesystem with deduplication enabled, since you would likely only write the changes between boxes. Maybe something for a future episode.

[e42]$ vagrant status
Current machine states:

web                       not created (virtualbox)
db                        not created (virtualbox)
file                      not created (virtualbox)
nagios                    not created (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.
[e42]$ vagrant up
Bringing machine 'web' up with 'virtualbox' provider...
Bringing machine 'db' up with 'virtualbox' provider...
Bringing machine 'file' up with 'virtualbox' provider...
Bringing machine 'nagios' up with 'virtualbox' provider...
==> web: Importing base box 'chef/centos-6.5'...
==> web: Matching MAC address for NAT networking...
==> web: Checking if box 'chef/centos-6.5' is up to date...
==> web: Setting the name of the VM: e42_web_1418453251462_9139
....
    nagios: /vagrant => /home/jw/e42

Okay, so we are all done, lets run vagrant status, to check the current state, and it looks like our four boxes are up and running. Lets ssh into the web node, by running, vagrant ssh web. You will notice that we are now using the instance name, this is because we have several boxes running in the same environment now, where as before we only had one, so we could just use vagrant ssh, and it knows by default that we want to log into the only running box.

[e42]$ vagrant status
Current machine states:

web                       running (virtualbox)
db                        running (virtualbox)
file                      running (virtualbox)
nagios                    running (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.
[e42]$ vagrant ssh web
Last login: Fri Mar  7 16:57:20 2014 from 10.0.2.2

[vagrant@localhost ~]$ cat /etc/redhat-release 
CentOS release 6.5 (Final)

[vagrant@localhost ~]$ uptime
 06:50:54 up 3 min,  1 user,  load average: 0.00, 0.00, 0.00

[vagrant@localhost ~]$ df -h
Filesystem                    Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup-lv_root   38G  765M   36G   3% /
tmpfs                         230M     0  230M   0% /dev/shm
/dev/sda1                     485M   32M  429M   7% /boot
vagrant                       235G  142G   94G  61% /vagrant
[vagrant@localhost ~]$ exit
[e42]$ vagrant status
Current machine states:

web                       running (virtualbox)
db                        running (virtualbox)
file                      running (virtualbox)
nagios                    running (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.
[e42]$ vagrant destroy web
    web: Are you sure you want to destroy the 'web' VM? [y/N] y
==> web: Forcing shutdown of VM...
==> web: Destroying VM and associated drives...
[e42]$ vagrant status
Current machine states:

web                       not created (virtualbox)
db                        running (virtualbox)
file                      running (virtualbox)
nagios                    running (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.
[e42]$ vagrant up web
Bringing machine 'web' up with 'virtualbox' provider...
==> web: Importing base box 'chef/centos-6.5'...
==> web: Matching MAC address for NAT networking...
==> web: Checking if box 'chef/centos-6.5' is up to date...
==> web: Setting the name of the VM: e42_web_1418453564914_65311
...
==> web: Mounting shared folders...
    web: /vagrant => /home/jw/e42

Again, we can check the redhat release, uptime, and our /vagrant mount. What is cool about this, is that this mount is shared across all vagrant boxes, so if you had files you wanted to transfer, this makes it super easy. You can also destroy single boxes within this environment, by running vagrant destroy, and then the box name, then lets verify it is actually gone. Then to bring up a new instance, just run vagrant up, and now we have our four vagrant boxes up and running again. Well, I have probably talked long enough about Vagrant, and hopefully at this point, I have shown that Vagrant can be a great addition to your toolbox. The Vagrant website, listed in the episode notes below, is a great source of documentation too, so be sure to check that out.