Getting started with Vagrant

Standard

Vagrant

So after my tutorial on how you can use Grunt to get everybody in your team on the same page when compiling SCSS and JavaScript, I figured I would delve into the world of distributable development environments.

Vagrant does just that, it lets you configure a development environment that you can distribute across platforms and team members. The good thing about that is that the environment will be identical for everyone, thus ending the need for anyone to say ‘it works on my box‘ ever again.

Best of all, you don’t have to login to the development environment that Vagrant creates. It leaves your files where you created them on your host machine, so you can still edit them with your favourite text editor, while they are running in the virtual machine, and you will be able to use the browser on your host machine to view the development environment, thanks to all the clever routing that Vagrant puts in place.

For this guide we will get deep and dirty in command line, and even do some bash scripting, but don’t worry I’ll be right there to hold your hand, and explain what all those scary commands mean.

In this guide we are going to:

  • Install Vagrant
  • Install a Vagrant VM (Virtual Machine), called a ‘box’
  • Install a simple LAMP (Linux, Apache, MySQL and PHP) stack on that box
  • Install WordPress on the box
  • View our WordPress installation by navigating to http://localhost:8080

It is worth noting that I am using a Mac OSX, so my examples will be targeted towards that particular system. I’m also using iTerm 2 as my terminal program.

VirtualBox

Vagrant needs to harness the power of virtualisation software. There are a few choices available, but because it is nice and free, we are going to use VirtualBox by Oracle. Fortunately for us we don’t have to get our hands dirty just yet. Just run the installer for your machine, while you are at it go ahead and install the extension pack from the same page.

Vagrant

Now to install the programme of the hour, Vagrant. Again, no mess, no fuss, just go get the Vagrant installer for the platform of your choice.

Configuration

Ok, time to delve into that command line (hold my hand if you need to). First of all you will need to use the terminal to navigate to the root of your project (for example: cd path/to/your/project), and run the following command:

vagrant init

That just created a file in the root of your project called ‘Vagrantfile‘.

The result of 'Vagrant Init'

Don’t worry about this file if you are using version control such as git, it is designed to be included as part of your project so that anyone working on the project will benefit from the same environment.

Go ahead, navigate to your project folder and take a look at the new Vagrantfile. The file is written using Ruby syntax, but don’t worry about that, we are only going to tinker with it a little bit.

Vagrantfile

Get a box

I’m removing the comments for readability, but that script reads as follows:

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "base"
  ...
end

The line VAGRANTFILE_API_VERSION = "2" sets a variable (to a string of 2). The line Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| passes the variable that was defined in the first line into the Vagrant.configure() function. It also sets us up to ‘do‘ stuff with the config property of the Vagrant.configure() function, until we end it with the last line.

There is a line in the middle config.vm.box = "base", and that is where we are going to do our tinkering.

First lets change the line config.vm.box = "base" to:

config.vm.box = "precise32" 

This means that we are going to be using the Linux distribution of Ubuntu 12.04.3 LTS (Precise Pangolin) (the 32 bit build). But we need to get that from somewhere. So look down your Vagrantfile until you come across this commented out piece of code:

# config.vm.box_url = "http://domain.com/path/to/above.box"

Lets uncomment that file and change the path to:

  config.vm.box_url = "http://files.vagrantup.com/precise32.box"

That line of code will go and grab that particular blend of Ubuntu, in a ready configured ‘box’, right from the Vagrant servers for us.

We are not done yet though. Keep hunting down the code until you find the following line, and go ahead and uncomment it:

# config.vm.network :forwarded_port, guest: 80, host: 8080 

That line of code will forward port 8080 on your host machine to the standard port 80 on the box. So you will be able to go ahead and visit http://localhost:8080 and view your website.

Finally, in the commented Vagrantfile you will notice a bunch of commented out things called provisions. One is for Chef, another for Puppet.

These are scripting languages that will let us install things onto our box after we have got it up and running. They are worth exploring in the future, but for now we are going to add our own ‘shell’ (yup, command line) provision, because I think it is the simplest thing to get up and running with without us having to learn an extra load of syntax on top of what we are already doing.

At the bottom of the code, just before the end add the following line:

  config.vm.provision :shell, :path => "provision.sh"

This means that we are going to use a shell script to install all the goodies that we need, and its path is in the root of our directory and is called ‘provision.sh‘.

We haven’t made that file yet, so in a moment we will go ahead and do that.

One more thing though, if you want to follow this tutorial to the bottom and install WordPress, those of you in the know will notice that WordPress has a habit of wanting to create files such as the .wp-config.php file when it is running the installer. To allow WordPress to do this we are going to have to tinker with the permissions for the Vagrant synced folder (the root directory of our project within the Virtual Machine).

To do this add the following line of code before the end statement:

  config.vm.synced_folder ".", "/vagrant", :mount_options => ['dmode=777,fmode=666']

This sets the default directory permissions (dmode) to 777 which means that everyone has full control to the folder. It also sets the default file permissions (fmode) to 666 which means everyone can read and write but not execute files.

Note to security experts: I am sure there is a nicer way to do this, and if so hit me up in the comments, but don’t worry too much, we can change these permissions in the vagrantfile after our initial setup, and these changes will be implemented the next time we run vagrant up.

Our full Vagrantfile (minus the comments) should now look a little like the following:

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "base"
  config.vm.box = "precise32"
  config.vm.box_url = "http://files.vagrantup.com/precise32.box"
  config.vm.network :forwarded_port, guest: 80, host: 8080
  config.vm.provision :shell, :path => "provision.sh"
  config.vm.synced_folder ".", "/vagrant", :mount_options => ['dmode=777,fmode=666']
end

Shell script (building the LAMP stack)

In the root of your project make a new file called ‘provision.sh‘.

We are fortunate that PHP MySQL and Apache are all deployed with Ubuntu, so we don’t have to do any downloading before we install them, but what we do need to do is configure a password for MySQL before we can install it. To do this, add these two lines to provision.sh.

sudo debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password password mypass'
sudo debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password_again password mypass'

These lines of code set the root password of MySQL (twice actually, an initial time and then again for confirmation) to ‘mypass‘ (you can change this to your choice of password).

In the code ‘sudo‘ means do something as a super user, and debconf-set-selections lets us set defaults in our Ubuntu config file (debconf). The rest of the lines simply set the password and the confirmation for MySQL.

Now we can install our LAMP stack. To do that add the following lines of code to provision.sh:

sudo apt-get update
sudo apt-get -y install mysql-server-5.5 php-pear php5-mysql apache2 php5

In the code above the apt part of apt-get stands for ‘Advanced Packaging Tool’. The command apt-get update will make sure that the source list for the apt-get tool is up-to-date.

The next line sudo apt-get -y install mysql-server-5.5 php-pear php5-mysql apache2 php5 means that we are going to install the packages mysql-server-5.5 php-pear php5-mysql apache2 php5 using apt-get. The -y means that we are going to automatically answer yes to any prompts that may come up.

Exposing the Vagrant folder

By default the box will go ahead and serve files from its /var/www folder. That isn’t much good to us, because we want to be able to edit our files using our host machine. The files from our project directory (where your Vagrantfile sits at the root of) is available at /vagrant on the guest box.

To be able to expose these files when we hit http://localhost:8080 we need to make a symbolic link between the /var/www folder and the folder that you want to be served (in most cases the /vagrant folder).

You don’t have to, but in I am actually going to create a public directory within the /vagrant folder that apache will use to serve files. That way the project will be configured so that the script containing your root MySQL password is outside of that public directory and in the root /vagrant folder.

To do this, we need to enter the following code into the provision.sh file:

if [ ! -h /var/www ]; 
then 
    mkdir /vagrant/public
    rm -rf /var/www 
    ln -s /vagrant/public /var/www
    a2enmod rewrite
    sed -i '/AllowOverride None/c AllowOverride All' /etc/apache2/sites-available/default
    service apache2 restart
fi

What on earth does all that mean? Well, I’ll tell you. first of all we have an if block of code.

That looks something like this:

if [...]; 
then 
    ...
fi

That means if whatever is in the square brackets [] is true then do something, until you get to the end if statement (which for some reason is fi in this language).

So what does that if [ ! -h /var/www ] part of it mean? Well, /var/www is the folder on the virtual machine that serves our web files. The -h checks if the file exists and if it is a symbolic link (or a shortcut to another file, for those more familiar with that term). Finally the ! means not, so reverses a false to a true and vice versa. So basically the statement will be true if the file does not exist or is not a symbolic link (phew!).

Fortunately the next lines are a little bit simpler. The line mkdir /vagrant/public simply means make the directory /public within the /vagrant folder.

We then remove rm the directory /var/www with the line rm -rf /var/www sudo. The switches -rf mean force this to happen without confirmation (-f) and -r means remove any sub directories.

The magic happens with the line ln -s /vagrant/public /var/www. This means make a link (ln) that is symbolic (-s) from the directory /vagrant/public and call it /var/www (essentially makes a shortcut). With this line of code, the files that are in our project folder (specifically the ones in the newly created /public folder) will be served by apache when we hit http://localhost:8080.

The line a2enmod rewrite turns on the rewrite module of apache.

The line sed -i '/AllowOverride None/c AllowOverride All' /etc/apache2/sites-available/default edits (sed is the stream editor command) the file /etc/apache2/sites-available/default in place (with the -i switch). It changes the line AllowOverride Node to AllowOverride All using a regular expression. We do this so that our apache redirect can work.

Finally we restart apache with the line service apache2 restart.

We are putting our statements within the if statement so that if we run the code vagrant provision (to re-provision the Vagrant install) it doesn’t try to do all of the above again.

Installing WordPress

To be honest, unless you want to use WordPress, you can go ahead and enter vagrant up into your terminal window now, and whatever index file you put in your projects new /public folder will will be there http://localhost:8080.

But, because we can, and because I love WordPress, lets get get the latest stable build of WordPress and put it in the /public folder. To do this, you need to add the following lines of code to your provision.sh file:

if [ ! -d /vagrant/public/wp-admin ];
then
    cd /vagrant/public
    wget http://wordpress.org/latest.tar.gz  
    tar xvf latest.tar.gz 
    mv wordpress/* ./  
    rmdir ./wordpress/  
    rm -f latest.tar.gz
fi

This code is also wrapped in an if statement. It checks that the directory /vagrant/public/wp-admin does not exist (by using ! -d before it).

Once we are sure of that the file doesn’t exist, we change the directory (hey, that’s what cd means, who knew?) to the /vagrant/public folder.

We then grab the latest stable release of WordPress from the URL http://wordpress.org/latest.tar.gz with the command wget.

We unzip it using tar. The switch x means overwrite any existing folders, v means print the output to the terminal window, and f means read from the file we specified (which in this case is latest.tar.gz).

So far, the script will have put everything in a /wordpress folder within the /public folder. The next line moves (mv) all the files within that folder (wordpress/*) folder to the root of the current folder (./) .

We now do a little bit of tidying up, by removing the WordPress directory by using rmdir ./wordpress/ , and forcing the removal of the .tar.gz file with the command rm -f latest.tar.gz.

Creating the database

We are almost there. We just need to create our initial WordPress database so that we can install WordPress without much fiddling about on our new  box. To do this run the following code:

if [ ! -f /var/log/databasesetup ];
then
    mysql -u root -pmypass -e "CREATE DATABASE wordpress;"
    mysql -u root -pmypass -e "CREATE USER 'mywpuser'@'localhost' IDENTIFIED BY 'mywppass';"
    mysql -u root -pmypass -e "GRANT ALL PRIVILEGES ON wordpress.* TO 'mywpuser'@'localhost';"
    mysql -u root -pmypass -e "FLUSH PRIVILEGES;"
    touch /var/log/databasesetup
fi

The code above checks to make sure that a file called /var/log/databasesetup does not exist with ! -f.

We then run a few SQL commands with the keyword mysql. The user is root which is entered by the line -u root (-u means user), and the password for root is the one we defined at the start of the provision.sh script as mypass (you may have entered your own root password), combine this with -p (for password) making the command in this case -pmypass.

The part that lets us run our SQL commands is the -e which essentially means echo the following SQL string into the mysql console.

I will not explain all the SQL to you in detail, but a quick summary of what we are doing in this part is as follows:

  • Create a database called ‘wordpress
  • Crease a user called ‘mywpuser‘ with the password ‘mywppass
  • Give all privileges to the database ‘wordpress‘ to ‘mywpuser
  • Flush out the previlages

The final line touch /var/log/databasesetup will create the file specified.

Putting it all together

The final provision.sh script should look a little something like the following:

sudo debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password password mypass'
sudo debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password_again password mypass'
sudo apt-get update
sudo apt-get -y install mysql-server-5.5 php5-mysql apache2 php5

if [ ! -h /var/www ]; 
then 
    mkdir /vagrant/public
    rm -rf /var/www 
    ln -s /vagrant/public /var/www
    a2enmod rewrite
    sed -i '/AllowOverride None/c AllowOverride All' /etc/apache2/sites-available/default
    service apache2 restart
fi

if [ ! -d /vagrant/public/wp-admin ];
then
    cd /vagrant/public
    wget http://wordpress.org/latest.tar.gz  
    tar xvf latest.tar.gz 
    mv wordpress/* ./  
    rmdir ./wordpress/  
    rm -f latest.tar.gz
fi

if [ ! -f /var/log/databasesetup ];
then
    mysql -u root -pmypass -e "CREATE DATABASE wordpress;"
    mysql -u root -pmypass -e "CREATE USER 'mywpuser'@'localhost' IDENTIFIED BY 'mywppass';"
    mysql -u root -pmypass -e "GRANT ALL PRIVILEGES ON wordpress.* TO 'mywpuser'@'localhost';"
    mysql -u root -pmypass -e "FLUSH PRIVILEGES;"
    touch /var/log/databasesetup
fi

To get it all working, in your terminal program, you need to cd to your project root directory (the place where you ran vagrant init), and enter the following command:

vagrant up

You may have to wait a while, and there will be a lot of output. But eventually you will end up with an output similar to the following:

'vagrant up' - output

That last bit of output was the unzipping of the WordPress files.

Now go ahead and navigate to http://localhost:8080 with your usual browser. Sure enough, there is our WordPress installer screen waiting for us to configure WordPress with our newly created username and password.

WordPress installer screen

This guide really only scratches the surface of what is possible with Vagrant. I will be doing more Vagrant work in the future, and I am pretty sure I will blog about it.

Stoping and starting

You should of course go read up on Vagrant to learn some more commands, but before you go I will give you a couple of pointers that should keep you busy for a while.

You can stop your VM at any time by going to your project folder and entering the command vagrant halt in your terminal.

If you have really really had enough, you can get rid of Vagrant by going to your project folder and typing vagrant destroy. By using the script that I have provided, none of your files will be deleted (but you will lose all of your WordPress SQL data). This means you will still have that /public folder in your project.

Start Vagrant again at any time by going to your project folder and entering vagrant up.

2 thoughts on “Getting started with Vagrant

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>