10. November 2013

Rails on Vagrant - a working example

Some time ago I stumbled over Vagrant. A tool to standardize your development environment throughout your team by running it all in a virtual machine. To set up this virtual machine you can use Puppet or Chef as provisioning service. In all my tries yet I failed installing a ruby version manager (RVM, rbenv) with the provisioning tool. But finally I got it running. Here is how.

Step 1: Install Vagrant and VirtualBox

Install Vagrant and VirtualBox on your machine. Make sure you don’t install Vagrant as a Gem because that’s quite an old version.

Step 2: Install Vagrant Plugins

vagrant plugin install vagrant-librarian-chef
vagrant plugin install vagrant-vbguest

The plugin vagrant-librarian-chef takes care of installing our chef cookbooks upon firing up the virtual machine. vagrant-vbguest makes sure the virtual box guest additions have the same version as our VirtualBox installation. You might want to install this later because it takes quite some time to build the additions.

Step 3: Create the configuration files

Create a new folder and our two main configuration files.

mkdir my_vagrant
cd my_vagrant
vagrant init
touch Cheffile

The command vagrant init creates an initial Vagrantfile. This contains the main configurations for our virtual machine.

Step 4: Add some cookbooks

Cookbooks contain recipes which tell our chef how he has to install and set up software in our virtual machine. Out there you can find cookbooks for almost everything. A good starting point is the official chef cookbooks repository. Now open Cheffile in your editor of choice and add the following lines:

site 'http:/community.opscode.com/api/v1'

cookbook 'apt'
cookbook 'build-essential'
cookbook 'ruby_build'
cookbook 'nodejs', git: 'https:/github.com/mdxp/nodejs-cookbook.git'
cookbook 'rbenv', git: 'https:/github.com/fnichol/chef-rbenv', ref: 'v0.7.2'
cookbook 'vim'

These are all dependencies we need for the start. This files works like a Gemfile in a Ruby (on Rails) project. The cookbooks are installed by the librarian plugin when we first start the virtual machine. But before this we need to tell vagrant how we want our machine to look like.

Step 5: The virtual machine settings

Now it’s time to edit the Vagrantfile. Most of the following settings are prepared for you in the Vagrantfile. You just have to uncomment and change them to your needs.

We want a plain Ubuntu Precise 64bit machine. You don’t have to download and install it yourself. That’s what we have Vagrant for. Just tell it where you want to download the base box and Vagrant takes care of the rest. Vagrant provides some of the base boxes.

config.vm.box = "precise64"
config.vm.box_url = "http://files.vagrantup.com/precise64.box"

The Rails development server usually runs on port 3000. We now just route port 3000 of the guest machine to the same port of our host machine. Later we can reach the rails server just as we are used to by calling http://localhost:3000 in our browser.

config.vm.network :forwarded_port, guest: 3000, host: 3000

Our rails project will stay on our host machine so we can use the IDE or text editor of choice. So we have to make the project folder available to the virtual machine. With the following setting our project folder will be mounted at “/superproject” in the virtual machine.

config.vm.synced_folder "../our_project_folder", "/superproject"

Now you can set some parameters for virtual box. Here I set its RAM to 2Gb.

config.vm.provider :virtualbox do |vb|
  vb.customize ["modifyvm", :id, "--memory", "2048"]
end

Step 6: Provisioning settings

And now comes the really interesting part which took me quite some time to set it right. We add the needed cookbooks and store their settings. Make sure you set these things in the correct block: we use Chef Solo for provisioning. You can delete the others.

config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = ["cookbooks", "site-cookbooks"]
    chef.add_recipe "apt"
    chef.add_recipe "nodejs"
    chef.add_recipe "ruby_build"
    chef.add_recipe "rbenv::user"
    chef.add_recipe "rbenv::vagrant"
    chef.add_recipe "vim"
    chef.json = {
      rbenv: {
        user_installs: [{
          user: 'vagrant',
          rubies: ["2.0.0-p247"],
          global: "2.0.0-p247",
          gems: {
            "2.0.0-p247" => [
              { name: "bundler" }
            ]
          }
        }]
      }
    }
  end
end

We added recipes for rbenv to install Ruby 2.0.0-p247 for the user vagrant. Additionally the recipe should install the bundler gem. NodeJs is used as javascript runtime.

Step 7: Start the machine!

If everything was set correctly we can now fire up the machine!

vagrant up

After some time you should have a running virtual machine with everything set up to start working with ruby. With the following command you get into the machine.

vagrant ssh

Check if ruby was set up correctly:

vagrant@precise64:~$ ruby -v
ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux]

When you now change something in your Vagrantfile or Cheffile just run vagrant provision to configure the machine. You should never install software or do settings directly in the machine. Because when you give the settings files to some colleagues they will have exactly the same machine as you have.

Now you can use other cookbooks to install MySQL or MongoDB or some other fancy stuff you use in your project.

See my Github Repo for more settings.

And this has helped me a lot: https://github.com/devert/vagrant-rails-app-starter.