Managing PHP Environments with Vagrant Baseboxes

One of the most important aspects of setting up your development environment is the notion of DEV-PROD parity. This refers to the practice of making sure that the system that you develop an application on is as close as possible to the system that the application is eventually going to run on when shipped.

This is important because we want to make sure that the behaviour of our application during development matches the behaviour of the application when deployed into the production environment. The problem is there there are so many factors involved in ensuring the environments are the same that it’s practically impossible to manage this manually.

Why Vagrant?

Vagrant is a small Ruby application that provides a user-friendly command-line interface for managing Oracle VirtualBox instances. VirtualBox is a free virtualisation tool that enables us to create fake software-based instances of servers; aka virtual machines.

Vagrant enables us to write “recipes” for setting up these machines, allowing us to programatically create instances of development environments. Because the process of provisioning these virtual machines is completely automated and defined declaratively in a fixed script, we can be sure that each time we use the Vagrant script we’re getting an exact copy of the environment.

Additionally, Vagrant supports industry standard provisioning tools (Puppet and Chef), which means we’re capable of using the same recipes to create production instances of our virtual servers.

Getting Started

Before you start

This tutorial is going to assume a few things.

The first is that you’re running a *nix-based operating system (a Unix or Linux variant, includes Mac OSX). Although the steps are probably relevant for Windows-based systems, they aren’t going to be addressed directly.

The second is that you’ll be running a *nix-based server.

Install Ruby & RubyGems

Because Vagrant is a Ruby based application, you’ll need to make sure you have Ruby and RubyGems installed. You can check this by opening a console / terminal session and typing the following:

$ which ruby
/usr/bin/ruby
$ which gem
/usr/bin/gem

If you have either (or both) of these installed, the console will respond with the path that the binaries currently reside (/usr/bin/ruby in the above example). If these are empty (ie: no path is returned), go ahead and install Ruby and RubyGems.

If you’re using Mac OSX, I recommend you check out the Homebrew project, which makes it easy for you to manage your packages and libraries. If you’re on a Debian-like Linux distribution, you can use $ apt-get install ruby and $ apt-get install rubygems

Install VirtualBox

Vagrant is basically just a fancy wrapper around Oracle’s free VirtualBox software, so obviously you’ll need to download and install it before proceeding. Go ahead and grab VirtualBox.

Install Vagrant

The next step is to install the Vagrant application. You can download the package from the website, but we already have Ruby and RubyGems installed so we might as well just fetch it from there:

$ gem install vagrant

This will take a little while to pull down and install the core files and dependencies. After the install is completed, you can test to make sure it’s installed by typing:

$ vagrant

You should get something similar to the following:

Usage: vagrant [-v] [-h] command [<args>]

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

Creating your “base” box

In the interests of being thorough, we’re going to get a little bit complicated here. We’re going to create our own base box, which means we can select any Linux distribution that we want for the basis of our development environment. Note that there is already a great selection of freely available base boxes that you can also use.

The benefit of creating your own base box instead of using the prebuilt ones is that you can customise it to exactly match what you’re using in production. The drawback of this approach is that you’ll need access to the base box file to perform deployments, so if you don’t share it somewhere on the Internet, you’ll need to transfer it to new development machines before using Vagrant to provision environments.

Feel free to skip over this part and go to Defining the Vagrantfile if this doesn’t interest you.

Download a OS install disk image

The very first step in creating a new base box is to decide on the operating system distribution and version that you want to use. For the sake of this tutorial, we’ll be using Debian Squeeze 6.0.6.

Once we’ve decided on this, we need to source an image file that we can use to install the operating system. Conveniently, the Debian Squeeze installation CD image is readily available, and we’ll be downloading it from here: debian-6.0.6-amd64-netinst.iso (Note: Australian mirror).

Important: When creating a base box, we want to make the default install as small as possible. As we’ll only be using it as a web server, we don’t need to install any media (graphics or sound) drivers or any additional applications. That’s why we’re using the “net installation” version of the distribution, which doesn’t come bundled with any additional software or drivers.

New VM in VirtualBox

The next step in the process of creating a base box is to create a new virtual machine in Oracle VirtualBox.

  • Go ahead and open VirtualBox and click the blue New button at the top-left of the screen.
  • Choose a name that reflects the operating system name and version. Typically, the name of the image file you downloaded in the first step will work well, so that’s what we’ll use here: debian-6.0.6-amd64.
  • Select the OS Type and Version as relevant (in this case “Linux” and “Debian (64 bit)” respectively).
  • For memory size, the default (384MB) will be sufficient.
  • For the hard drive, select:
    • Create a virtual hard drive now
    • The VDI format
    • Dynamically allocated
    • Leave the size as the default of 8.00 GB

The VM is just about ready for us to start installing the operating system. Before we boot it up, right click on it and select Settings. Make sure the following values are set:

  • Audio > Enable Audio is unchecked.
  • Network > Adapter 1 > Enable Network Adapter is enabled and set to NAT.
  • Network > Adapter 2, 3, 4 > Enable Network Adapter are unchecked.
  • Ports > Serial Ports > Ports 1 and 2 > Enable Serial Port are unchecked.
  • Ports > USB > Enable USB Controller is unchecked.

Install the Operating System

We’re now ready to fire up the VM and install the operating system from the CD image. Do this by double clicking on the VM in the list of virtual machines and selecting the image file when prompted to do so.

This should launch the installer for the operating system you’ve selected for your base box. Proceed as you normally would folllowing the install instructions for your particular OS, distribution and version. Take the following things into consideration while performing the install:

  • Select as few install options as possible. Don’t worry about installing web servers and/or other services at the moment. We’ll take care of this later.
  • When prompted to provide network and user settings, use the following values:
    • Root Password: vagrant
    • Main account login: vagrant
    • Main account password: vagrant
    • Hostname: vagrant-[os-name], e.g. vagrant-debian-squeeze *
    • Domain: vagrantup.com *

*: these can vary according to your preferences / network settings.

Set-up the image for Vagrant

Once the install is complete, we need to configure some settings and permissions so that the Vagrant application can interact with the box via SSH and use the provisioning tools. Fire up the virtual machine and log-in using the username vagrant and password vagrant.

Password-less sudo privileges

The Vagrant tool assumes that the main user should be able to execute sudo commands without having to provide credentials. This will allow it to perform commands using an elevated privilege level without logging in as “root.”

Depending on your distribution, it’s possible that the “sudo” tool may not be installed. You can check this by typing which sudo at the command prompt. If it is installed, the console will print the location of the binary. If it doesn’t provide the path, you can install sudo manually. For Debian-like Linux distributions, we can install it by running the following:

$ su

 # Enter password when prompted
Password: ######## 

$ apt-get install sudo

We can perform the required configuration changes by editing the /etc/sudoers file, or by typing visudo which will open a text editor with that file open.

$ sudo visudo

Add the following to the top of the file:

Defaults env_keep = "SSH_AUTH_SOCK"

Add this to the bottom of the file:

%admin ALL=NOPASSWD: ALL

… finally, save to /etc/sudoers, ensuring you select Yes or confirm when you’re prompted to overwrite the original file.

The next step is to create the user group referenced in the sudoers file above and add the main user to it. Again, assuming you’re using a Debian-like distribution, you can use the following:

$ sudo groupadd admin
$ sudo usermod -g admin vagrant

At this step you should log-out and reset the virtual machine.

VirtualBox Guest Additions

Now we need to install the “Guest Additions,” which are a series of “fake” drivers that enable the VirtualBox software to interact with the virtual machine. First we need to install the architecture-specific kernel header files for the Linux kernel which will enable us to build kernel modules.

Do this by running the following from the command prompt:

$ sudo apt-get install linux-headers-$(uname -r) build-essential

The next step is to “connect” the guest additions via the Oracle VirtualBox user interface. Do this by clicking on the Devices option in the control bar at the top of the screen and then Install Guest Additions.

One this has been done, you’re ready to mount the guest additions image which has been “attached” to the virtual machine’s CD drive. Do this by running the following commands:

$ sudo mount /dev/cdrom /media/cdrom
$ sudo sh /media/cdrom/VBoxLinuxAdditions.run

Once this is completed, we’re ready to install the software that Vagrant will use to set-up and provision new instances of our “base box.”

Install Software

Vagrant assumes that a number of tools and commands will be available when it uses SSH to connect to the virtual machine. These commands are used to perform the provisioning activities. If we don’t install these correctly, the provisioning process will fail.

Note that the steps will be similar for all operating systems, but as the convention has been so far, we’ll assume a Debian-like Linux distribution is being used.

Ruby

We need to install Ruby. Make sure the -dev extension is used so the development package is installed. This includes “mkmf” (a makefile tool) which is required by the Chef gem installed during a later step.

$ sudo apt-get install ruby-dev
RubyGems
$ sudo apt-get install rubygems
Puppet

Puppet is an IT automation tool that provides system administrators with a way of configuring environments using scripts written in a custom DSL (domain specific language).

$ sudo apt-get install puppet
Chef gem

Chef is also an IT automation tool, but with a different DSL that happens to be written in Ruby. You can use Puppet and Chef in tandem to configure environments, or just use one or the other. Either way, we should install the Chef Ruby gem using the following command:

$ sudo gem install chef
SSH

We need to make sure the SSH daemon is installed and running so that Vagrant can connect to the box.

$ sudo apt-get install ssh

Set up SSH key authentication

Finally, we need to configure an authorised SSH key so that Vagrant can connect to the box without providing a username and password.

While we’re at it, let’s install CURL:

$ sudo apt-get install curl

… and then use it to pull down the public certificate into the authorized_keys file:

$ mkdir ~/.ssh
$ curl https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub _
  -o ~/.ssh/authorized_keys

Note that this is an intentionally unsecure (read: compromised) public key. As a result, you should make sure that either a) no secure content is accessible via the Vagrant VM or b) you lock-down network access to the VM so that external parties cannot SSH into it.

Once this is done, we can shut down the virtual machine and close down VirtualBox. We’re nearly there!

Package completed image for Vagrant

Before Vagrant can use the virtual machine we’ve prepared for it, it needs to package it into a .box file. We can do this by changing into the folder where our virtual machines are stored (usually ~/VirtualBox VMs/) and running the vagrant package command:

# vagrant package --base [VirtualBox VM name]
# in our case:
$ vagrant package --base Debian-6.0.6-amd64

This will create a .box file. I suggest you rename it from the default “package.box” and store this somewhere for safe keeping. We now add the box to Vagrant so we can spin up instances of it:

# vagrant box add [name] [image path]
# in our case: 
$ vagrant box add squeeze64 squeeze64.box

Before going any further, I’d suggest you test the box to make sure it works as expected:

$ mkdir -p ~/Vagrant/squeeze64
$ cd ~/Vagrant/squeeze64
$ vagrant init squeeze64
$ vagrant up

You should now be able to open a SSH session by typing the following:

$ cd squeeze64
$ vagrant ssh

If everything went according to plan, we should have a SSH session open on the virtual machine:

Last login: Mon Jan 21 15:59:38 2013
[email protected]:~$

Now that we have our basebox up and running, we can quickly and easily provision new instances simply by creating a new folder, typing vagrant init squeeze64, and then vagrant up. This is pretty useful in itself, but we’re really only just experiencing the tip of the iceberg. The real fun starts to happen when we create custom provisioning scripts.

Conclusion

After completing this article you should be able to get Vagrant up and running and use it to provision instances of your own base box, using the operating system of your choice.