Vagrant allows you to easily manage and control multiple virtual machines. It is built on top of VirtualBox and VMWare, and it provides many exciting capabilities. You can create isolated development environments, experiment with new products and technologies, install new versions of existing packages, create your own private data center on your laptop, and run different operating systems. All that is available in an easy-to-manage and totally safe silo that can’t interfere with your main activities and operating system.
In this tutorial, I’ll show you how to set up Vagrant and start exploring the wealth of opportunities it affords.
Vagrant recently added support for Docker containers too, but I will not discuss that in this tutorial.
- Download the free VirtualBox for your operating system from the VirtualBox website .
- After download, just run the binary and install it.
- Download Vagrant .
- Again, just run the binary to install it.
Vagrant is a command-line based tool. Once installation is complete, open a console window and create a new directory called ‘vagrant_intro’.
cd ~ mkdir vagrant_intro cd vagrant_intro
This will be your working directory from now on. Inside the vagrant_intro directory, type:
vagrant init ubuntu/trusty64
You should see the following text:
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.
The generated ‘Vagrantfile’ is a Ruby file that controls your [one or more] virtual machines. It is a pretty big file with lots of commented out options. I will go over the important ones later, but let’s give it a try first. Type:
vagrant up .
This will cause Vagrant to download a prepared virtual box with the 64-bit Ubuntu 14.04 (trusty) release. It will take a while (several minutes) and spit a lot of text to your screen. While Vagrant is churning, let’s talk about what a VM is in practical terms.
One view (which I usually adopt) is that the VM simulates a remote server. You should only communicate with it over its network interface. This is particularly useful for testing distributed systems where you shouldn’t take advantage of any special VM-host interfaces because they will not be available on your production systems. When operating in this mode, you will normally communicate with your VM over SSH (possibly with a tool like Ansible).
Another view, which is also totally correct and valid, is to think of the VM as another OS running on your local machine. This view is useful if you want to develop directly on your target OS.
For example, a very common setup is to have developers write their code on a Mac using Mac OS X, but deploy to a Linux production environment. If you want to make sure that the code you develop on the Mac runs properly on Linux without having to deploy to a remote server, you can take advantage of synced folders where you read and write files on the host OS (typically Mac OS X) and have them immediately available on the guest OS (typically Linux).
This drops the "deploy" step from the edit-deploy-test cycle when working with remote systems. It also removes the need for an expansive staging environment that is a shared resource and needs to be managed carefully across all developers. With a local VM, nobody is going to break your environment, and you can experiment as much as you want without worrying about breaking someone else’s code.
You can, of course, use both synced folders and still SSH into your VM. There are other options that blur the borders such as port mapping.
Here is a stripped-down version of the Vagrantfile with some of the most important options:
Vagrant.configure(2) do |config| config.vm.box = "ubuntu/trusty64" config.vm.network "forwarded_port", guest: 80, host: 8080 config.vm.network "private_network", ip: "192.168.33.33" config.vm.synced_folder "~/vagrant_intro/data", "/vagrant_data" machine.vm.hostname = "gigi.playground.local"
The syntax is Ruby, which gives you a lot of flexibility if you want to add conditionals and loops. This is particularly helpful if you use your Vagrantfile to manage a cluster of virtual machines.
Connecting to Your VM
You can connect to your running VM by typing:
vagrant ssh .
This will launch an SSH session and let you fiddle with your VM interactively.
Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.13.0-74-generic x86_64) * Documentation: https://help.ubuntu.com/ System information as of Tue Feb 9 06:17:28 UTC 2016 System load: 0.97 Processes: 80 Usage of /: 3.4% of 39.34GB Users logged in: 0 Memory usage: 25% IP address for eth0: 10.0.2.15 Swap usage: 0% Graph this data and manage this system at: https://landscape.canonical.com/ 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.
Once inside your VM, you can pretty much do anything you want: run commands, create users, create files and directories, etc. You are logged in as the ‘vagrant’ user, which has sudo privileges, so you have full control over the VM.
Exchanging Data With Your VM
Vagrant automatically maps the folder on the host machine that contains the Vagrantfile to the /vagrant directory inside the guest machine. This lets you edit files in your favorite editor on the host machine and have it available in the guest or alternatively have the guest write some output file to the /vagrant directory and browse the synced folder on the host.
Mapping Guest Ports
Often you’ll want to run some application that communicates through a port. The most common one is a web server. Vagrant allows you to forward guest ports to the host machine.
For example, in the configuration above, the guest port 80 is mapped to the host 8080 port. What that means is that whatever service is running on port 80 in the guest can be accessed as localhost:8080 on the host machine.
Let’s see a demonstration. First, I’ll put a simple index.html file in the ~/vagrant_intro directory on the host that is synced with the /vagrant_data directory on the guest.
<html> <body> <h1>Vagrant is awesome!</h1> </body> </html>
Then, after I SSH into the guest machine, I’ll run a little web server in the /vagrant_data dir:
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ sudo python3 -m http.server 80 Serving HTTP on 0.0.0.0 port 80 ...
This one-liner Python HTTP server simply serves any static file in its working directory. Now, since port 80 is mapped to the host’s 8080 port, you can browse to localhost:8080 and view the result of running the server.
Connecting to the VM Through the Network
You can also connect to the VM through its IP address as if it’s a really separate server. I always add a hostname to /etc/hosts so I don’t have to use the naked IP address and because the code I’m testing is usually configured with the hostname of a production system. This is what you need to add to your /etc/hosts file:
# Important production system 192.168.33.33 production.server.com
Now, any network access to "production.server.com" on your machine will be sent to your VM.
In this case, the programs that usually connect to the remote server are configured with the VM IP address or hostname. Prior to Vagrant 1.7, there was a single private key that could be used for all machines. This was a serious security problem when people exposed their virtual machine over public networks and anyone could SSH in and get root access. Starting with version 1.7, Vagrant generates a fresh key pair for each machine. To find out where the private key for your machine is, run this command:
vagrant ssh-config .
Here is how you can SSH directly to the VM:
ssh -o UserKnownHostsFile=/dev/null -o CheckHostIP=no -o StrictHostKeyChecking=no -i ~/vagrant_intro/.vagrant/machines/default/virtualbox/private_key email@example.com
Managing Your Virtual Boxes
Vagrant provides many commands to manage boxes. Let’s explore. To see a list of commands, type:
Usage: vagrant [options] <command> [<args>] -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 scp copies data into a box via SCP 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`.
I covered some of the most common commands earlier. The
box commands allow you to manage your boxes directly. For example, to see all the VMs on your machine, type
vagrant box list . You can update boxes to the latest version with
update . The
reload command is very useful if you change your Vagrantfile. Finally,
provision lets you provision, configure and install software on your VM using various provisioners including Chef, Puppet, Salt, my favorite Ansible, and even Docker.
Vagrant gives you an easy-to-use computer within computer. You can manage a fleet of VMs for diverse purposes, and the Vagrantfile is your interface for specifying how the VM should behave. Have fun exploring Vagrant. I just scratched the surface here.