logo_vagrant-81478652

Vagrant is a powerful tool that simplifies the setup of a development environment by creating a virtual machine on your local machine that can be configured to look exactly like ServerGrove’s production server. With this post you will learn how to configure Vagrant using the same specifications of your ServerGrove VPS.

It’s been a few months now since ServerGrove has released repositories for the php installations on Ubuntu, Debian and CentOS VPSs. One of the reasons we did this is so you can use these php packages on your Virtual Machine.

Installing Vagrant

The Vagrant installer is available for several OS and it can be downloaded from the downloads page. It is also available as a RubyGems package but this method is considered deprecated since the release of the first stable version. In order to run Vagrant you will need to have VirtualBox installed as well.

Creating the configuration

Once Vagrant has been installed, you need to create the configuration in the project root. This can be done by executing a simple command, which will create a default configuration file.

$ cd /path/to/project
$ vagrant init

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.

You now need to edit this Vagrantfile file (which is written in Ruby) so you can configure the VM according to your needs.

The Box

A box is a portable pre-created image, used to create virtual environments. There are several boxes available for download with different OS all around the web. A good place to look for a box is Vagrantbox.es.

Once you import the box, it becomes available for its usage in all the projects we may need it, creating a different environment for each of these projects.


$ vagrant box add precise64 http://files.vagrantup.com/precise64.box

This way, the box is downloaded from the web and is imported directly into Vagrant. The process may take a while, depending on the size of the box and the speed of the internet connection.

As you can create boxes, or re-package existing ones, Vagrant allows you to add these boxes located in our local computer. Replace the URL for the local path from the previous command.

$ vagrant box add precise64 /path/to/precise64.box

Now, you have to tell Vagrant what box to use. The box url can be specified too, allowing to download the box if it doesn’t exists on the local computer.

# /path/to/project/Vagrantfile
Vagrant::Config.run do |config|
  # The name of the box
  config.vm.box = "precise64"
  # The URL from where the box can be fetched
  config.vm.box_url = "http://files.vagrantup.com/precise64.box"

  # ...
end

Starting the Virtual Machine

You have configured the box for our project, which is enough requirement to start the Virtual Machine. In order to do so, you must execute the up command, which will boot and configure the environment for us.

$ vagrant up

After a few messages your virtual environment will be ready for usage or provisioning. You can login into the VM with the ssh command.

$ vagrant ssh

Vagrant mounts our project folder in the VM at /vagrant. If you have for example the DocumentRoot located at /path/to/project/web, then you will see it inside the VM as /vagrant/web.

Provisioning

Manual installation is not a good option in these cases, as you may need to export the VM or share it with other team members. That is why Vagrant supports provisioning through the use of provisioners. Chef, Puppet and Shell are some of the standard provisioners. In this case you are going to use Puppet.
The provisioning is executed every time you boot the VM, either by executing vagrant up or vagrant reload. It can be executed manually with the VM running using the command vagrant provision.

The first thing you need to do is tell Vagrant that you are going to use a provisioner and it is Puppet. For this you need to add a couple lines to the Vagrantfile. The manifest file and path are required.

# /path/to/project/Vagrantfile
Vagrant::Config.run do |config|
  # ...
  config.vm.provision :puppet do |puppet|
    puppet.manifests_path = "puppet/manifests"
    puppet.manifest_file = "base.pp"
  end
  # ...
end

Now, you can proceed to write your manifest, a script with the actions to be performed by Puppet.

Before installing anything, you need to add the ServerGrove‘s repository. For this you will create the repository file in your local project folder, so it can be used inside the VM.

Ubuntu

As Ubuntu uses APT as package manager, you need to add the repository file to the sources.list, and then refresh the cache.

# /path/to/project/puppet/base.pp
class repository {
  # We need cURL installed to import the key
  package { 'curl': ensure => installed }

  # Installs the GPG key
  exec { 'import-key':
    path    => '/bin:/usr/bin',
    command => 'curl http://repos.servergrove.com/servergrove-ubuntu-precise/servergrove-ubuntu-precise.gpg.key | apt-key add -',
    unless  => 'apt-key list | grep servergrove-ubuntu-precise',
    require => Package['curl'],
  }

  # Creates the source file for the ServerGrove repository
  file { 'servergrove.repo':
    path    => '/etc/apt/sources.list.d/servergrove.list',
    ensure  => present,
    content => 'deb http://repos.servergrove.com/servergrove-ubuntu-precise precise main',
    require => Exec['import-key'],
  }

  # Refreshes the list of packages
  exec { 'apt-get-update':
    command => 'apt-get update',
    path    => ['/bin', '/usr/bin'],
    require => File['servergrove.repo'],
  }
}
# ...

stage { pre: before => Stage[main] }

class { 'repository':
  # Forces the repository to be configured before executing any other task
  stage => pre
}

CentOS

CentOS uses YUM as package manager, so you need to create a different repository configuration.

# /path/to/project/puppet/manifests/base.pp
class repository {
  # Install the GPG key
  exec { 'import-key':
    path    => '/bin:/usr/bin',
    command => 'curl http://repos.servergrove.com/servergrove-rhel-6/RPM-GPG-KEY-servergrove-rhel-6 -o /etc/pki/rpm-gpg/RPM-GPG-KEY-servergrove-rhel-6',
    require => Package['curl'],
  }

  # PHP packages require EPEL to satisfy dependencies
  exec { "epel.repo":
    command => 'rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-7.noarch.rpm',
    path    => ['/bin', '/usr/bin'],
    unless  => 'rpm -qa | grep epel',
  }

  # Enable the repository
  yumrepo { 'servergrove.repo':
    baseurl  => 'http://repos.servergrove.com/servergrove-rhel-6/$basearch',
    enabled  => 1,
    gpgcheck => 1,
    gpgkey   => 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-servergrove-rhel-6',
    require  => Exec['import-key']
  }
}

...
stage { pre: before => Stage[main] }

# Forces the repository to be configured before any other task
class { 'repository': stage => pre }

It’s time to install the three packages we need, Apache, MySQL and PHP.

Apache

You are going to use Apache2 as the web server, which will be installed from the default repository, using the package manager of the OS.

You need to ask Puppet to install Apache and ensure it is running after the install. For this, create a class named Apache2, composed by a package(what we are going to install) and a service(what it has to be running). Apache will be installed if it was not previously installed.

# /path/to/project/puppet/manifests/base.pp
class apache {
  # Ensures Apache2 is installed
  package { 'apache2':
    name => 'apache2-mpm-prefork', # httpd if CentOS
    ensure => installed,
  }

  # Ensures the Apache service is running
  service { 'apache2':
    ensure  => 'apache2', # httpd if CentOS
    ensure  => running,
    require => Package['apache2'],
  }
}
# ...

include apache

MySQL

In order to install MySQL and get the server running, you need to add the following code to your manifest. MySQL server and client will be installed.

# /path/to/project/puppet/manifests/base.pp
class mysql {
  # Installs the MySQL server and MySQL client
  package { ['mysql-server', 'mysql-client']: ensure => installed, }

  # Ensures the Apache service is running
  service { 'mysql':
    ensure  => running,
    require => Package['mysql-server'],
  }
}
# ...

include mysql

PHP

ServerGrove provides PHP packages for Ubuntu, Debian and CentOS, all 64bit only. You can select among PHP 5.3 and 5.4, always with the latest versions. In this case you are going to use PHP 5.4, as it is the default installed version in a VPS. The installed packages by default in a VPS are php54, php54-apc and php54-mod-php. Don’t worry about finding php54-mysql, it’s already included in the main package. After the install, Puppet will reload Apache with the new PHP module.

# /path/to/project/puppet/manifests/base.pp
class php {
  # Installs PHP and restarts Apache to load the module
  package { ['php54', 'php54-apc', 'php54-mod-php']:
    ensure  => installed,
    notify  => Service['apache2'],
    require => [File['servergrove.repo'], Package['mysql-client'], Package['apache2']],
  }
}

# ...

include php

Final step

Last, but not least, you need to configure the web server, by adding a VirtualHost to Apache, pointing to our DocumentRoot. For this we are going to create the configuration file inside our project folder and then tell Puppet to copy it into Apache. The file should look like the following example:

# /path/to/project/puppet/files/site.conf

NameVirtualHost *:80
<VirtualHost *:80>
  DocumentRoot /vagrant/web
  ServerName example.dev
  ServerAlias localhost
  DirectoryIndex index.php
</VirtualHost>

Now we can create the class that creates the vhost and restarts apache.

# /path/to/project/puppet/manifests/base.pp
class webserver {
  # Setups the virtual host
  file { '/etc/apache2/sites-enabled/site.conf':
    source  => '/path/to/project/puppet/files/site.conf',
    notify  => Service['apache2'],
    require => Package['apache2'],
  }
}

# ...

include webserver

We need to be able to access the application running on the VM, so we are going to forward the port 80 to a port on our local computer, for example 8080. This way we can access the application from the web server at http://localhost:8080/. This can be done by adding the following line to the file Vagrantfile:

# /path/to/project/Vagrantfile
Vagrant::Config.run do |config|
  # ...
  config.vm.forward_port 80, 8080
  # ...
end

We can do this with any port number.

Result

To simplify the examples in this post, all the classes were created in the same file base.pp, but our recommendation is to create a module per class, which will make the class exportable and you will also be able to import other modules like a noSQL database engine. For further reference about modules, you can refer to the Puppet documentation at http://docs.puppetlabs.com/puppet/3/reference/modules_fundamentals.html

You can find the full Puppet manifests from this post in this repository.