Whilst working on PEEP I ran into an interesting problem. Since I have experience in web development, I've been tasked with creating and maintaining the project's infrastructure. Part of my work has been to manage the git repository and start setting up the play webserver. Fortunately, this wasn't too hard, I simply followed the instructions in the Play documentation and installed the necessary packages in my XUbuntu VM. I created a skeleton play application, a setup script to install all the dependencies and committed it all to our github repository. All good to go, I let my team know that everything was setup and they can start integrating their code into the skeleton application.
During one of our weekly meetings, we were all gathered around a lab computer and were talking about merging the HTML templates we made into the repository. The only lab computers we have admin rights on are single-boot windows machines. We needed to get the codebase (designed to run on linux) installed and running on the machine so that we can add and test the new templates. So we installed virtualbox, downloaded an ubunutu iso, started a vm, installed the packages, cloned the repository and started the application. All-in-all this took probably 30-45 minutes, including download times. Once that was setup, one of my teammates wanted to get that all setup on her machine...another 30-45 minutes later...
I knew there had to be a better way than manually configuring each machine. Ideally I wanted to just hop on any of our lab machines, clone the repo and get to work. Using our current setup, I wasn't motivated to use any other system besides my pre-configured laptop. If I forgot my laptop? Not much work got done that day....
Vagrant
I had heard about Vagrant before. It's one of those words that gets thrown around a lot in the web development world but not something that I had actively looked into using before. I knew what it did, it created VMs, but in a sense I didn't really know WHAT it did.
After spending sometime reading through the documentation , I knew I had found my solution. It's no wonder people swear by it, it's magnificient. In a nutshell, Vagrant allows you to automatically deploy a VM based on a configuration file and then using a provisioner (puppet, chef, ansible, bash) you can configure the packages and services. It provides a completely automated way to develop a VM.
Perfect, I updated our README so that the instructions told the user to install vagrant and virtualbox (default provider). I created a Vagrantfile in the root of our project and committed it to the repo. Now if a team member wants to work on peep, they only need to clone the repository and cd into the project directory run vagrant up
. This would automatically start a VM (headless) in which they can ssh into using vagrant ssh
. But, there was still something missing, the user would still need to install all the packages to run the play application. Enter puppet.
If you're interested in using vagrant for your project, check out the getting started guide.
Puppet
Note: Vagrant allows for several different provisioners, I chose puppet as I have previously had experience with Chef and found it to be too complicated. My first choice was Ansible but it requires a few extra steps to get it running in windows and since my team mostly use windows, I opted for a works-out-of-the-box solution with puppet.
So using puppet is actually pretty straight forward. I created a manifests
directory in the root of our project and created a default.pp
puppet playbook file. I read through the quick start guide for puppet and the types docs to create our playbook. As this was the first time I used puppet, I'm sure my playbook could be much better but for our current usecase it gets the job done quite nicely. Here is a sample of the playbook:
Exec { path => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" }
exec { "apt-get update":
path => "/usr/bin",
}
package{"unzip":
ensure => present,
require => Exec["apt-get update"],
}
##
# Java
##
package {"openjdk-7-jre":
ensure => present,
require => Exec["apt-get update"],
}
package {"openjdk-7-jdk":
ensure => present,
require => Exec["apt-get update"],
}
...
And this is what my final Vagrantfile looked like:
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "precise32"
config.vm.box_url = "http://files.vagrantup.com/precise32.box"
config.vm.network :private_network, ip: "192.168.33.10"
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "manifests"
puppet.manifest_file = "default.pp"
end
end
Conclusion
So now, our project repository includes a vagrant file and a puppet playbook. Anyone in our team can clone the repository, install vagrant and puppet, run vagrant up && vagrant provision
and they have a fully configured VM for development purposes. They can then vagrant ssh
into the vm and start the play application. They can then access the website in their host OS by visiting http://192.168.33.10:9000 and commence development!
Note: Annoyingly, play doesn't have an auto-reloading background service/daemon so the user still has to manually launch the play application.