Eric Holscher has recently written an excellent series of articles about provisioning an app server with chef. Around the same time, Gareth Rushgrove mentioned Vagrant - a tool that takes a lot of the pain out of managing development environments on virtual machines.

Stealing with both hands, I present the fewest commands necessary to get a Django app server running in a virtual machine.

  1. Download & install virtualbox.
  2. Install vagrant (should just be “gem install vagrant“)
  3. vagrant box add lucid32 http://files.vagrantup.com/lucid32.box
  4. git clone https://github.com/ericholscher/chef-django-example.git
  5. cd chef-django-example
  6. vagrant init
  7. edit Vagrantfile
  8. Add (or uncomment) these lines:
    
       config.vm.provisioner = :chef_solo
       config.chef.add_recipe "main"
       config.chef.json.merge!({
                                "run_list"=> [ "main::default", "main::python", "main::readthedocs" ],
                                "base_packages"=> ["git-core", "bash-completion"],
                                "users"=> {
                                  "docs"=> {
                                    "id"=> 1001,
                                    "full_name"=> "Docs User",
                                    "key"=> "ssh-rsa key-goes-here eric@Bahamut"
                                  }
                                },
                                "groups"=> {
                                  "docs"=> {
                                    "gid"=> 201,
                                    "members"=> ["docs"]
                                  }
                                },
                                "ubuntu_python_packages" => ["python-setuptools", "python-pip", "python-dev", "libpq-dev"],
                                "pip_python_packages" => {
                                  "virtualenv" => "1.5.1",
                                  "mercurial" => "1.7"
                                },
                              }
                              )
       
  9. edit cookbooks/main/recipies/default.rb
    
    execute "apt-get update" do
      action :run
    end
    
  10. vagrant up

You now have an app server running the code for Readthedocs. You can’t do a lot with it as this server would need a database and a Celery server to talk to, but you can login and poke at the services:

$ vagrant ssh
vagrant@vagrantup:~$ pgrep gunicorn
vagrant@vagrantup:~$ sudo initctl list
vagrant@vagrantup:~$ sudo tail -f  /home/docs/sites/readthedocs.org/run/celery.log

You can’t resolve the names of Holscher’s other servers so you’ll see celery moaning in the logs.

It’s a shame that we don’t end up with a fully-working server to play with, but this still illustrates how easy vagrant and chef make it to build a server. It’s also not difficult to see how this config could be moved into another file in the repository and used to provision staging and production servers as well.

A brief explanation of what’s going on here:

When you run vagrant up Vagrant reads Vagrantfile, boots the VM then runs chef-solo to provision it (because you set config.vm.provisioner = :chef_solo). Chef follows the directions in cookbooks/main/recipies/default.rb to install the server (because we put the line config.chef.add_recipe "main" in Vagrantfile). When it’s executing, the data from config.chef.json is available in node - in Holscher’s examples this data is stored in the node.json file, though it’s called dna.js by vagrant (which seems to be a Chef convention).