Super Lazy Provisioning with Vagrant

It has been almost a year since I posted anything even remotely related to my php series. I guess I should have suspected that SITS would be the death-kneel for the project. It seemed like a good, small sized project but I didn’t anticipate for the fact that it was kinda boring and unexciting. Not to mention the fact that I was over-complicating it in the name of doing things the right way™. Maybe one day I will actually manage to finish it… Maybe. No promises though.

It just so happens that this series was the first time I talked about Vagrant – the super sleek virtual machine manager for developers. Since then I’ve been working on and off with various Vagrant boxes and experimenting with different ways of setting them up in efficient, hassle free ways. So I figured I might as well make a post about it and share a thing or two I learned.

Old Vagrant Logo

Btw, can you believe this used to be the official Vagrant logo?

The simplest way to get a vagrant box up and running is to follow the official manual and simply do something like:

vagrant init my_dev_server http://files.vagrantup.com/precise32.box
vagrant up

This gives you a naked, bare bones Ubuntu installation that you can ssh into and customize to your liking. By default it ships without things like Apache or PHP and that’s actually a good thing, because you can make it into whatever you want. Why would you need PHP on a RoR box for example?

The downside of this is that every time you want to start a new project with a squeaky clean environment you have to spend time setting it up. One way of avoiding this overhead is to set the environment up once, and then package it as a box file using the vagrant package command. This will produce a portable virtual machine that you can share with other people or upload somewhere. Next time you need to set up the same type of environment you just do:

vagrant init new_dev_server http://path/to/my_dev_server.box

This works extremely well and makes sharing environments with co-workers or team-members extremely easy. In fact it lets you give someone the exact copy of your development environment at any time without much hassle.

But box files tend to be rather big. The more complex the setup, the bigger the file tends to be. You have to host them somewhere, or figure out an efficient way of sharing them (like Dropbox for example) because sure as hell you won’t be emailing them to anyone.

Not to mention that forking off copies of your working environment and making them “official” dev boxes for new team members is probably not the best idea. Chances are that your personal vagrant box will diverge from the stock copy and get customized. You will likely import your .bashrc, aliases your text editor config files and etc. You probably don’t want to distribute all that, so chances are that you have some “official” dev box you keep pristine clean installing only the bare bones dev-dependencies.

What is the difference between your “deployment-ready” box and the stock one provided by the vagrant team? In most cases the exact delta between the two is one 15 minute session of furiously typing apt-get commands. That’s honestly about all you need to do to set up a workable lamp box.

So, here is a crazy idea: why not start with a bare bones stock box, and then just run a script that includes all those furiously typed commands? Congratulations my friend, you almost invented provisioning. Vagrant actually has a full “balls to the walls” support for all kinds of hard core provisioning tools such as Puppet, Cheff and Ansible. All of these tools are great and extremely useful, and if you are a sysadmin you might already be familiar with some or all of them. And if you do not, then learning them will definitely be beneficial to you.

That said, many of us are developers who are merely trying to set up a standardized development environment for a team of three to five people. Learning how to create a Puppet Manifest, Cheff Cookbook or an Ansible Playbook might be an overkill. Fortunately latest versions of Vagrant has something for us too: shell provisioning.

The idea behind shell provisioning is exactly what we came up with few paragraphs above: use a stock box, then run a script to set it up. It is simple, easy to configure and hard to screw up. How do you do it?

Well, let’s write the simplest Vagrantfile we can think of:

Vagrant.configure("2") do |config|
  config.vm.box = "lamp"
  config.vm.box_url = "http://files.vagrantup.com/precise32.box"
  config.vm.network :forwarded_port, guest: 80, host: 8080
  config.vm.synced_folder "./www", "/var/www"

  config.vm.provision "shell", path: "./setup.sh"
end

The first five lines are standard. Like 3 tells Vagrant where to find the base box, line 4 sets up port forwarding and line 5 shares the /var/www directory on the guest os with the host. Line 7 is the important part, and it simply points to a shell script, path being relative to your Vagrantfile.

What do you put in the script? Anything you want really. For example if you just want to set up Apache and PHP then it can be dead simple:

#!/bin/bash
sudo apt-get update
sudo apt-get -y -q install apache2 php5

You put both the Vagrantfile and setup.sh in the same directory and run: vagrant up. The script will run immediately after the machine boots for the first time. It is that simple.

If you want to install a full LAMP stack you need to do something a tiny bit more complicated. Why? Because in their infinite wisdom Ubuntu package creators decided that mysql-server package needs to use a dialog to ask for root password. As you can imagine this does not really work during a initial provisioning boot sequence. So you need to be sneaky and set up an unattended mysql install like this:

#!/bin/bash

# Set up unattended install of mysql-server
export DEBIAN_FRONTEND=noninteractive
sudo debconf-set-selections <<< 'mysql-server-5.1 mysql-server/root_password password toor'
sudo debconf-set-selections <<< 'mysql-server-5.1 mysql-server/root_password_again password toor'
sudo apt-get -y update 
sudo apt-get -y -q install apache2 php5 php5-mysql mysql-server mysql-client

What else can you do? Anything you want really. Anything you can think of, and put down in the form of a script should work, as long as it does not need any real-time user input. For example, if you wanted to enable the Apache mod_rewrite module, and install the Composer package manager you could add this to the bottom of our file:

# Enable mod_rewrite
sudo a2enmod rewrite 
sudo service apache2 restart

# install additional non-essential stuff
sudo apt-get -y -q install curl vim

# Install Composer
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

One important note is that Vagrant will check for existence of the provisioning file every time it stars. So if you decided you don't want to keep your provisioning script around on your working instance you have to remember to remove it from the Vagrantfile. Or you can make the script clean up after itself automatically. This is exactly what I did in my personal LAMP setup.

You should note that the linked repository is almost empty. There isn't much there other than the Vagrantfile and setup.sh which is basically all you need. The idea is to distribute these two tiny text files, instead of a gigantic packaged box, because the end result is the same: a vm that is set up and ready to go.

Anyone who wants to create a clean environment with this setup simply has to clone it, and run it:

git clone https://github.com/maciakl/lamp.git
cd lamp
vagrant up

Feel free to use it for your own nefarious purposes. As usual, comments and bug reports are more than welcome.

This entry was posted in Uncategorized. Bookmark the permalink.



3 Responses to Super Lazy Provisioning with Vagrant

  1. If you’re going to be grabbing the same .deb packages a bunch of times I strongly recommend setting up an Apt-Cacher NG server on your network. It’s effectively a fully-up-to-date local mirror where any particular package+version is only ever fetched over the Internet once, lazily. I run one at work where it’s used by a number of people, one of whom uses it to update his dozens of VMs (I think using Puppet). Sharing it is a nice benefit to me because packages tend to already be cached by the time I need them.

    As you’ve probably already seen, I’ve been using live-build to reasonably capture system configuration, allowing for fresh images to be built efficiently on demand. It probably wouldn’t work well for your particular scenario here since live-build can only make live systems, practically read-only, at the moment.

    Reply  |  Quote
  2. Pingback: Provisioning with Vagrant | 0ddn1x: tricks with *nix UNITED STATES PHP

  3. Luke Maciak UNITED STATES Google Chrome Mac OS Terminalist says:

    @ Chris Wellons:

    Nice. This is a great idea. Now I just need to dig out some piece of hardware (or virtual relestate) on which I can set it up, and wrangle an IP away from the ever stingy network dudes. :P

    Reply  |  Quote

Leave a Reply

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