manski's blog

Vagrant Tutorial – From Nothing To Multi-Machine

As developers, we sometimes want to quickly test some software. Instead of installing it directly on our developer machine, it’s better to install it in a virtual machine (VM). But if you don’t have a VM ready, setting one up usally takes a lot of time – and there goes your productivity.

Fortunately, there is a solution: Vagrant

Vagrant is a free tool that lets you quickly spin-up fresh VMs out of thin air. It can even spin-up multiple VMs at the same time.

This article is step by step tutorial for getting from nothing to a multi-VM setup where the VMs can talk to each other.

The Goal

At the end of this tutorial, we’ll have 3 virtual machines, one called “master” and two nodes, that can find each other via their hostnames. Except for Vagrant, no external software is required to achieve this.

The setup we’ll be creating is just a foundation you can expand on. It won’t do anything meaningful. It’s just to get you the infrastructure to do something meaningful.

If you’re just interested in the end result, skip ahead to Putting everything together.

Notes Before We Start

To be able to follow this tutorial, it’s helpful if you have a basic understanding of the following:

  • How to interact with the command line of your operating system – since Vagrant is controlled from the command line.
  • What SSH is for.
  • What virtual machines are in general.

For Vagrant I recommend that you have a fast internet connection. The VMs created by Vagrant are downloaded from the internet and are usually 600 MB or bigger. A slow internet connection will work but you’ll have to wait for a long time for the downloads to finish.

As for software versions, this tutorial was tested against Vagrant 1.8.5 and VirtualBox 5.1.

Installation

To be able to use Vagrant you need two things: Vagrant itself and a hyper-visor.

On Windows you may also want to install a command line replacement.

You can download Vagrant here:

https://www.vagrantup.com/downloads.html

As hyper-visor, you can choose between VirtualBox, VMWare (Fusion), and Hyper-V.

The easiest option is VirtualBox. It’s free and available on every platform.

If you have Hyper-V installed, don’t install VirtualBox. Hyper-V is incompatible with other hyper-visors. (See here for more information.)

If you have VMWare Fusion installed on your Mac, please note that the VMWare provider for Vagrant costs money – even if you already have a VMWare Fusion license. If you don’t want to spend money, just use VirtualBox. It can be installed alongside VMWare Fusion.

To verify that Vagrant is installed, type on the command line:

> vagrant -v
Vagrant 1.8.5

Starting and Interacting with Your First VM

To start your first VM, first create an empty directory somewhere and cd into it.

Then execute the following two commands:

> vagrant init hashicorp/precise64
> vagrant up

This will start a virtual machine running Ubuntu 12.04 (Precise Pangolin).

Note: If you’re using Hyper-V instead of VirtualBox, you have to call vagrant up --provider=hyperv instead of just vagrant up. Alternatively, you may want to configure Hyper-V as the default provider for Vagrant. See this article on how to do this.

To ssh into the VM, call:

> vagrant ssh

To get out of the VM, hit Ctrl + D.

To stop and delete the whole VM, call:

> vagrant destroy -f

This will delete the VM and all of its resources (i.e. the virtual hard drive) from your computer.

The Vagrantfile

When you called vagrant init hashicorp/precise64 earlier, Vagrant created a file called Vagrantfile in the current directory.

This file is everything Vagrant needs to do its work.

The file created by vagrant init contains lots of documentation and is a good starting point for customizing the VM.

However, for the purpose of this tutorial, let’s reduce the file to its bare minimum:

Vagrant.configure("2") do |config|
  config.vm.box = "hashicorp/precise64"
end

In the second line you can see the value you passed to vagrant init earlier: hashicorp/precise64

This is the name of the base image (think: virtual hard disk) used for the VM to create. Such an image is called a box in Vagrant terminology.

When Vagrant creates a VM from a box (base image), it actually creates a copy of this image. Thus, any changes done inside of the VM are lost when the VM is destroyed (via vagrant destroy). A VM can’t modify its base image.

Choosing the Right Box (Base Image)

Vagrant boxes are downloaded by default from HashiCorp’s Atlas system. All available boxes can be searched here:

https://atlas.hashicorp.com/boxes/search

For a single, specific operating system there are usually many boxes to choose from. For example, searching for a Ubuntu 14.04 box returns (among others):

  • ubuntu/trusty64
  • puphpet/ubuntu1404-x64
  • boxcutter/ubuntu1404
  • bento/ubuntu-14.04

Unfortunately, boxes on Atlas differ in quality since anyone can upload a box. A low quality box prevents you from using Vagrant features, such as setting the hostname of VM or creating a private network.

During my testing I found that the ubuntu/... boxes have very low quality (which is surprising given that they’re are created by Canonical, the company behind Ubuntu).

Also, HashiCorp (the company behind Vagrant) only provides boxes for Ubuntu 12.04. So they can’t be selected as source of high quality boxes either. (During my testing, even the hashicorp/precise64 box had its problems.)

The Vagrant documentation about official boxes recommends to use the boxes from the bento namespace (apparently created by the team at Chef).

During my (limited) tests they worked flawlessly and so they’re my recommendation, too. We’ll use them for rest of the tutorial. You can find them here:

https://atlas.hashicorp.com/bento

Note: Unfortunately, the bento boxes don’t work with Hyper-V. So if you’re using Hyper-V with Vagrant, you have to find a different box.

Updating a VM after Vagrantfile Has Changed

Modifying a Vagrantfile while its VM is running has no effect on the running VM.

To “synchronize” the VM with its Vagrantfile, you can either:

  1. call vagrant reload or
  2. call vagrant destroy -f followed by vagrant up

Note: If you’re using provisioning (see below) and changed the provisioning data, you need to call vagrant reload --provision in the first case.

To make things simpler, I recommend you’re using the second option for this tutorial.

Multi-Machine: The Naive Way

So far we’ve always started a single VM.

We can also start multiple VMs with a single Vagrantfile. This is called Multi-Machine.

The easiest (or most naive) way to create a multi-machine is with a Vagrantfile like this:

Vagrant.configure("2") do |config|
  config.vm.define "master" do |subconfig|
    subconfig.vm.box = "bento/ubuntu-16.04"
  end

  config.vm.define "node1" do |subconfig|
    subconfig.vm.box = "bento/ubuntu-16.04"
  end

  config.vm.define "node2" do |subconfig|
    subconfig.vm.box = "bento/ubuntu-16.04"
  end
end

This will create 3 VMs (master, node1, node2).

To ssh into any of the VMs, just specify its name. For example, to ssh into node1, call:

> vagrant ssh node1

To destroy all VMs, just call:

> vagrant destroy -f

This is exactly the same command as for a single VM.

Multi-Machine: The Clever Way

The previous multi-machine Vagrantfile had lots of copied code.

The same setup can also be described in a more “programmer-like” manner:

BOX_IMAGE = "bento/ubuntu-16.04"
NODE_COUNT = 2

Vagrant.configure("2") do |config|
  config.vm.define "master" do |subconfig|
    subconfig.vm.box = BOX_IMAGE
  end
  
  (1..NODE_COUNT).each do |i|
    config.vm.define "node#{i}" do |subconfig|
      subconfig.vm.box = BOX_IMAGE
    end
  end
end

Here we:

  • Moved the box name into a constant (BOX_IMAGE).
  • Converted the “nodeX” definitions into a for each loop where NODE_COUNT describes the number of nodes to create.

Connecting the VMs via Network

The setup in the previous section created 3 VMs. However, up until now these VMs had no way of communicating with each other.

Important: Before you do this section, please call vagrant destory -f. This makes things easier.

To fix this we need three things.

First, each VM needs a unique hostname. By default, each of the VMs has the same hostname (vagrant). To change this, we need to add a configuration like the following to each VM definition in the Vagrantfile:

subconfig.vm.hostname = "a.host.name"

Next, we need a way of getting the IP address for a hostname. For this, we’ll use DNS – or mDNS to be more precise.

On Ubuntu, mDNS is provided by Avahi. To install Avahi on each node, we’ll use Vagrant’s provisioning feature.

Before the last end in the Vagrantfile, we’ll add this code block:

config.vm.provision "shell", inline: <<-SHELL
  apt-get install -y avahi-daemon libnss-mdns
SHELL

This will call apt-get install -y avahi-daemon libnss-mdns on every VM.

Note: By default, provisioning is only done the first vagrant up. See here for more details.

Last, we need to connect the VMs through a private network.

For each VM, we need to add a config like this (where each VM will have a different ip address):

subconfig.vm.network :private_network, ip: "10.0.0.10"

Putting everything together

Putting everything mentioned in the previous section together results in a Vagrantfile like this:

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

# Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search.
BOX_IMAGE = "bento/ubuntu-16.04"
NODE_COUNT = 2

Vagrant.configure("2") do |config|
  config.vm.define "master" do |subconfig|
    subconfig.vm.box = BOX_IMAGE
    subconfig.vm.hostname = "master"
    subconfig.vm.network :private_network, ip: "10.0.0.10"
  end
  
  (1..NODE_COUNT).each do |i|
    config.vm.define "node#{i}" do |subconfig|
      subconfig.vm.box = BOX_IMAGE
      subconfig.vm.hostname = "node#{i}"
      subconfig.vm.network :private_network, ip: "10.0.0.#{i + 10}"
    end
  end

  # Install avahi on all machines  
  config.vm.provision "shell", inline: <<-SHELL
    apt-get install -y avahi-daemon libnss-mdns
  SHELL
end

You can now call vagrant up and then ssh into any of the VMs:

> vagrant ssh node1

From there you can ping any other VM by using their hostname (plus .local at the end):

> ping node2.local

Wrap Up

As you’ve seen, it just takes a Vagrantfile with 22 lines and a call to vagrant up to create multiple VMs in one step. Easy, isn’t it?

To continue from here, have a look at these resources:

You can also leave a comment below.

Changelog

  • 2016-10-02

    • Added note about bento box not being available for Hyper-V
    • Found mention of the Bento boxes in the official Vagrant documentation; changed links to there
  • 2016-10-01

    • Added link to command line replacements on Windows
    • Added NODE_COUNT to Vagrantfile
  • 2016-09-29

    • Initial release

19 comments

  1. KirillVolkovich said:

    Thank you for sharing useful information. But it is impossible to copy-paste code with correct formatting. All code became one-lined.

    • Manoj replied:

      I am also facing the same issue on a Windows 7 64-bit machine with Google Chrome v60.0.3112.90 (Official Build) (64-bit).
      The complete code in the ‘Putting everything together’ section gets pasted on a single line. Copying the code via IE10 works, so maybe its a windows/chrome issue.

  2. KirillVolkovich said:

    Addition to last comment.

    Problem reproduced if select last big code example with hash marked top comments.

    • Sebastian Krysmanski (post author) replied:

      Sorry to hear that copy doesn’t work for you. I can’t reproduce the problem, though. For me copying works fine in Firefox, Chrome, and Internet Explorer. What browser and OS are you using?

  3. Don Morton said:

    Hi, thank you very much for this. I’m new enough to this, so I may be missing some details. I need to use the ubuntu/xenial64 box, and I know that’s been problematic for some in setting up their private networks.

    When I tried your approach, it didn’t work, but when I looked at documentation for private_network I saw a syntax of “private_network” rather than the private_network: that you show in your tutorial, and now it works.

    • Don Morton replied:

      Sorry, error in the above – I meant to say that when I used your :private_network syntax, it didn’t work for me, but using “private_network” did.

  4. Mukesh Purohit said:

    thanks a lot for sharing these examples. These were really helpful and worked for me perfectly.

  5. joseph said:

    Thank you very much. Nice post.

  6. Stephen said:

    Very helpful material, thanks a lot for it.

    Question, what is the essence of the |config| / |subconfig| ?

    • Sebastian Krysmanski (post author) replied:

      It’s some Ruby construct. It basically defines a variable for the inner scope – at least as far as I can tell. I’m by no means an expert on Vagrant or Ruby.

  7. Gabriel Vacaliuc said:

    Super useful little tutorial! Thanks a bunch!

  8. Jean said:

    Nice tutorial! Thanks for sharing it.

  9. Pingback: Ansible Vagrant beginners guide - inpimation.com

  10. Vaguerant said:

    This info got me out of a hole .. Thanks

  11. Ani said:

    i tried the multi machine config from windows version of Vagrant.
    But getting below error message. Kindly suggest …
    ==> master: Box ‘bento/ubuntu-16.04’ could not be found. Attempting to find and install…
    master: Box Provider: virtualbox
    master: Box Version: >= 0
    The box ‘bento/ubuntu-16.04’ could not be found or
    could not be accessed in the remote catalog. If this is a private
    box on HashiCorp’s Vagrant Cloud, please verify you’re logged in via
    `vagrant login`. Also, please double-check the name. The expanded
    URL and error message are shown below:

    URL: [“https://vagrantcloud.com/bento/ubuntu-16.04”]
    Error: could not resolve proxy

    • SAP replied:

      The error says it all. ‘Error: could not resolve proxy’
      Install vagrant-proxyconf and replace IP:port relevant to proxy behind which your machine lies. Then start with the tutorial.

      https://github.com/tmatilai/vagrant-proxyconf

  12. Pingback: Mass deploy Vagrant-boxes previsioned with salt – Petrus Manner

  13. nel bunting said:

    three year old tutorial and still useful and awesome! Thank you.

  14. yash said:

    Huge thank you for this write up! I found it very helpful for provisioning multple Vagrant boxes based on a specified count.

Leave a Reply to yash Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.