December 22, 2013 / by Daniel Lucks / / @
Tutorial: Create a development environment using Vagrant and Puppet
A lot of web developers are common with the following situation - somebody in your company shows you a bug on your web platform and wants you to fix it. The first thing to do is to reproduce this bug in your local development environment and start debugging. But whatever you do, you cannot reproduce this bug on your machine. Damn! So the only option left is to ssh onto your production environment and modify some files to find and fix the bug. Very bad idea! - But is this the really only option?
The problem is obvious. Your local development environment is not identical to your production environment! Different OS, different versions of applications and modules, different access data, different technology, and so on. This blog post will help you how to get rid of this problem by showing you how to set up and getting started with two tools: Vagrant and Puppet.
Vagrant
Vagrant is an application for managing and configuring virtual machines. The only thing you have to do is to write a single configuration file which describes the setup and state of your virtual machines. Vagrant takes care of everything else. Vagrant also provides a simple CLI for interacting with the VM.
Puppet
Puppet is a very powerful tool for server configuration management. By writing so called manifest files you describe the setup of your server. That means you define which software or packages should be installed, which services have to run, and so on.
Step 1: Installing the components
To get started with this tutorial, you will have to download and install the latest versions of the following software:
- VirtualBox: https://www.virtualbox.org/wiki/Downloads
- Vagrant: http://downloads.vagrantup.com/
Step 2: Get a Vagrant box
Vagrant works with so called vagrant boxes. A vagrant box is something like a common virtual machine image wrapped by some vagrant container. In this tutorial we will use such a box containing a VirtualBox Ubuntu (Lucid Lynx) image. This box is provided by the Vagrant website. Open your terminal and execute:
$ vagrant box add lucid32 http://files.vagrantup.com/lucid32.box
This command will download this box file and add it to your Vagrant installation. Now this Lucid Lynx VM should be listed under your available boxes:
$ vagrant box list
lucid32 (virtualbox)
And now let’s start a new project and add a Vagrant configuration file to it:
$ mkdir awesome-project
$ cd awesome-project
$ touch Vagrantfile
We created a new file named Vagrantfile
in our project. This empty file has to be filled with the Vagrant
configuration. So open it with your favourite text editor and add the following lines to it:
Vagrant::Config.run do |config|
config.vm.box = "lucid32"
config.vm.forward_port 80, 3000
end
This config block contains two entries. config.vm.box
defines which VM to use and config.vm.forward_port
fowards port 3000 of the host (our local machine) to port 80 of the guest (the VM). This is necessary to access our web
application running on the VM from our local machine later. And that’s it! To boot the VM just execute
the following command in your project directory.
$ vagrant up
Step 3: Get local source code onto the VM
Now the VM is running and ready work with. So let’s add some PHP code to our project - in this case we just add a simple “Hello World” application:
$ mkdir htdocs
$ echo "<?php echo 'Hello World';" > htdocs/index.php
So, we created this PHP code on your local machine. But how do we get it into the VM? You know what!? It is already there! Vagrant mounted our project directory as a shared directory into the VM. To see it, we just log into the VM.
$ vagrant ssh
$ cd /vagrant
$ ls
With vagrant ssh
we are able to log into the VM, and Vagrant mounted our local project directory under
/vagrant
.
Step 4: Setting up a web server using Puppet
The VM is set up and contains our application code. Now it is time to setup a web server. Maybe at this point of this tutorial you’ll ask why there’s not a web server already installed on the VM. Well, of course you can create a vagrant box containing your complete production environment. But what, if you update the software or add some modules in future? You will have to create a completely new box file and distribute it to all of your developers. To avoid this, we are using Puppet.
Let’s create a manifest file in our project directory. By default Puppet tries to load a manifest file located
under manifests/default.pp
. So just create this file in our project.
$ mkdir manifests
$ touch default.pp
Now we fill this file step by step. At first add the following content.
exec { "apt-get update":
path => "/usr/bin",
}
This Puppet statement is called a resource. Every resource has a type
, title
and a set of attributes
. The type
of this resource is exec
, i.e. this resource is an execution of a command. The title of this resource is
the command to execute. In this case we want the package manager to update. To install an start an Apache
web server add the following two resources to the manifest file.
package { "apache2":
ensure => present,
require => Exec["apt-get update"],
}
service { "apache2":
ensure => "running",
require => Package["apache2"],
}
The first resource is a package resource. This is used to describe any pieces of software that should be installed.
In this case, the package apache2
should be installed. You may wonder why not to create just another execution
resource like apt-get install apache2
. This is because you don’t know if this package may already be installed
on the VM. The package resource just says something like “Make sure apache2 is installed” to Puppet.
You even may notify the require
attribute in the package resource. This is used to define dependencies between
single resources in Puppet. In this case the first exec
resource has to be executed completely before installing
the apache2 package. This is to ensure the latest stable version of apache2 will be installed.
The third resource is a service
resource. It is to start the Apache web server on the VM after installing it. So
the web server is installed, but the PHP module is still missing. No problem! Just add another resource to
the manifest file.
package { 'php5' :
ensure => latest,
require => Exec["apt-get update"],
}
The last step is to link our PHP code into the default document root of the Apache. So the last resource we have to add is to create a symlink file to the shared vagrant directory.
file { "/var/www/htdocs":
ensure => "link",
target => "/vagrant/htdocs",
require => Package["apache2"],
notify => Service["apache2"],
}
Step 5: Bringing the things together
The last step of this tutorial is to tell Vagrant to use the Puppet manifest file when starting up the VM. And this is very simple. Just add the following line to your Vagrantfile.
config.vm.provision :puppet
If the Lucid32 VM is still running, destroy and re-up it with our new configuration. Just execute the two commands in our local project folder.
$ vagrant destroy
$ vagrant up
This time the vagrant up
will take a little bit longer because of the packages to install. But when it finished
open your web browser, type http://localhost:3000/htdocs
and be happy!
Benefits
- With a Vagrant and Puppet setup you are able to create a development environment that is identical to your production environment. This makes it easier to debug and fix your application. No more “But it works on my machine!” excuses.
- Very short setup time for new developers in your team. They just have to install VirtualBox and Vagrant and get the code from your repository.
- You will be able to version-control your infrastructure. Just think about including your puppet manifests into your project as a git submodule or something else.
This tutorial just wanted to give you an idea of what powerful tools Vagrant and Puppet are - and how to get things running. There will follow a tutorial showing some little more advanced techniques like setting up virtual hosts, create databases or installing Memcached.