Over the last few years, various tools have become available that try to standardise, in some way, the environment in which software is developed and deployed.
In the next few blog posts, we’ll touch on one of these tools, Vagrant, and see how it can be used in conjunction with other devops tools to perform a few staple deployments.
In this first blog post, we’ll introduce some basic concepts, and use Vagrant to set up a development / deployment environment on a local machine.
In a second blog post, we’ll see how easy it is to shift the development / deployment environment to the Cloud, specifically to an AWS EC2 instance.
In part 3, we’ll add considerable power to Vagrant by combining it with Chef, a devops tool that transforms proverbial recipes into deployments. These recipes are in plain text, and by incorporating them into our version control system, our deployment process simply becomes part of our code base.
In the final part of the series, we’ll briefly mention other devops tools that have become increasingly popular and important.
During development and deployment, it all too easily happens that differences in development and / or deployment environments cause unexpected errors. The familiar refrain “But it works on my computer!” says it all. These differences are mostly software-related and stem from two sources:
- The Operating System. For example, Linux vs Windows, but also Windows 7 vs Windows 8.
- Application Software. For example, Apache v2.2.0 vs Apache v2.4.10.
Vagrant takes a first step at solving this problem by making it easy to set up a virtual environment that can be used for both development and deployment. This virtualised environment may be one that runs locally (using VirtualBox, KVM or VMWare, for example) or it may run in the Cloud (using Amazon EC2, for example).
The virtualised environment is instantiated from a given “image”. This image contains a standard or tweaked operating system, but may also include application software. Once the virtualised environment is up and running, Vagrant also provides tools to manage it and to communicate with it.
So: While the software on developers’ machines may differ wildly (running Linux, OS X or Windows with different software and versions), with Vagrant it’s easy to create and manage a standardised but bespoke environment that is the same for all developers and deployments. Because the environment is standardised, an entire source of development / deployment errors can be eliminated.
In part 3 of the series, we’ll see how to use Chef to configure a Vagrant-virtualised environment once it is up and running. Used in this combination, Vagrant is often left to take care of setting up a base operating system (such as Ubuntu), while the task of installing and configuring application software (such as Apache) is left to Chef. For this reason, in this blog post and the next, we’ll focus on using Vagrant to create and manage standard Ubuntu 14.04 virtual environments, both locally and in the Cloud. In part 3, we’ll focus on provisioning application software.
Local Virtualisation with VirtualBox
When an operating system is virtualised on a local machine, it’s customary to distinguish between the Host OS (which runs on the actual hardware) and the Guest OS (which is virtualised and runs from within the Host OS). (This is simplified terminology but useful.)
The steps that follow assume that we are using OS X 10.10 as our Host OS. However, the steps should be very similar on other host operating systems.
Vagrant can be downloaded from its home page. This post has been written using Vagrant version 1.7.1. Once downloaded, Vagrant can be installed by double clicking the dmg file and then the pkg file, and then following the on-screen instructions.
Vagrant supports a number of so-called “providers”. The term “provider” can be thought of as short for “virtualisation technology provider”. Examples include VMWare, Docker, Hyper-V and custom providers. The default provider is VirtualBox. Before VirtualBox can be used as a virtualisation technology provider, it must be separately downloaded and installed.
VirtualBox can also be downloaded from its home page. We use version 4.3.20. The installation proceeds straightforwardly. Once again, the dmg file and then the pkg file can be double clicked after which the on-screen instructions can be followed.
With both Vagrant and VirtualBox installed, we are ready to configure and interact with a local virtualised environment. Start off by creating a test directory in some location that suits you. This directory will serve as the home to the particular virtualised environment that we will create.
The main vehicle for interacting with Vagrant is the vagrant command line call, as well as a configuration file called Vagrantfile. To initialise the vagrant_test directory as the home of a Vagrant virtualisation environment, and to create the Vagrantfile file, call the Vagrant initialisation command:
This command creates a virgin Vagrantfile file within the directory. Open this file in your favourite text editor. The file contains Ruby-syntaxed configuration settings and their descriptions, although almost all of the lines have been commented out. The following configuration setting has not been commented out, though:
config.vm.box = “base”
This line introduces the concept of a “box”. A Vagrant Virtual Machine “box” is analogous to an operating system “image” as we spoke of it before. By default, the Vagrantfile file specifies the use of a “base” box. A “base” box is a specially crafted operating system image that includes a number of extras that Vagrant requires for full integration (for example, a package manager, an SSH server, and standard pre-populated usernames).
Boxes can be custom-made, but one of the strengths of Vagrant is its public repository of boxes, stocked by both vendors and community members.
Let’s use the Ubuntu 14.04 64-bit version as our base image. Searching the public repository, we quickly see that the box we’d like is called “ubuntu/trusty64″. (This image of Ubuntu has already been souped up to qualify as a base box.) To download this box to our local machine, we execute the following command:
vagrant box add ubuntu/trusty64
Although this may take quite some time depending on the speed of the Internet connection, it typically only needs to be performed once per box type.
Once the Ubuntu box has been downloaded, open the Vagrantfile file and specify the name of the new box to be used by changing the relevant setting above to:
config.vm.box = “ubuntu/trusty64”
Finally we get to instantiate our development / deployment environment:
This command instantiates a virtual machine instance from the specified box. This instance is bound to the vagrant_test directory and its Vagrantfile file.
To gain command line access to the instance, we use the command
This SSHs into the running instance. The instance username is vagrant, and by default this user has password-less super-user access.
One is now logged into a neutral development / deployment environment. Although the environment still needs to be properly “provisioned” (with development code or deployment binaries, for example), it is already very useful: it is easily configurable, instantiable and Host OS-agnostic.
To exit the instance, type exit. Whilst this closes the SSH connection, the underlying virtual machine remains running. Another incantation of vagrant ssh will immediately return one to the command line of the virtual machine.
In addition to vagrant up, there are various other commands that control the life cycle of the virtual machine. The most pertinent are:
- vagrant suspend – This command pauses the virtual machine’s operations. It copies the contents of the virtual machine’s RAM to the host’s disk, and stores the contents of the virtual machine’s disk on the host’s disk. This allows a subsequent call to vagrant up to rapidly return the virtual environment to its exact running state prior to the vagrant suspend command having being given.
- vagrant halt – This command passes a regular OS-level shut-down command to the virtual machine. Therefore the contents of the virtual machine’s RAM is lost, but not that of its disk. A follow-up vagrant up command simply causes the machine to boot back up.
- vagrant destroy – The vagrant destroy command shuts down the virtual machine and destroys its RAM and disk contents. Subsequently calling vagrant up creates a fresh virtual machine, once again based on the Vagrantfile file of the enclosing directory.
All of the above reinforces the separation between the Host OS and the Guest OS. However, no matter the workflow one follows, one very often wants to be able to share specific files between the two operating systems, perhaps through a portal of sorts.
Because the Guest OS is NAT-networked by default, in our case, one-way sharing could be done manually via a scp or rsync command, for example. (But only because the Guest OS is *nix-based in our case.) However, Vagrant itself makes dynamic, bi-directional sharing easy. At the root level within the Guest OS, a vagrant directory is created by default. The contents of this directory is dynamically synchronised with the contents of the Host OS vagrant_test directory itself. This is very useful for the sharing of source code, resources or even binaries.
To verify this, create a file in the Host OS’ vagrant_test directory. It should immediately appear in the Guest OS’ /vagrant directory. At the same time, deleting the file from the Guest OS’ /vagrant directory should lead to it being immediately removed from the Host OS’ vagrant_test directory.
As a tiny foretaste of the more advanced provisioning we’ll be able to perform using Chef in the third part of this series, we’ll end off here by providing the Guest OS with provisioning instructions in the Host OS’ Vagrantfile file. These instructions will be performed each time the Guest OS is instantiated from scratch from the base box.
Open the Vagrantfile file and insert the following code at the very top of the file:
$script = <<SCRIPT
sudo apt–get install –y apache2
Then after the config.vm.box line from before, enter the following:
config.vm.provision :shell, :inline => $script
The syntax is quite intuitive: the OS shell is to be used for provisioning; the provisioning instructions are given inline (as opposed to in a distinct shell file, for example); the provisioning instruction is simply to install apache.
The above can easily be tested. In the Host OS, execute:
Then in the Guest OS, execute:
This shows that Apache has automatically been installed in the Guest OS based on the shell instructions given in the Host OS’ Vagrantfile.
In this first post, we’ve gotten our hands dirty with the basics of Vagrant. Specifically, we’ve run Vagrant on a local machine with VirtualBox serving as the provider.
We’ve learnt of the command line life cycle of Vagrant virtual environments, and we also saw how to easily share files between the Host OS and Guest OS. In anticipation of part 3, where we’ll see how to use Chef for provisioning, we did some simple provisioning by means of a shell instruction.
Although we did make small changes to the Vagrantfile file while specifying the base box and the simple provisioning script, most of the settings in the Vagrantfile file were left untouched. These include network settings, sharing settings, provider settings and further provisioning settings. With the descriptions of the settings provided in the file, these are generally easy to explore.
In the next post, we’ll modify what we’ve done above and show how easy it is to have our virtualised environment run in the Cloud instead of on a local machine.