The simplest deployment that could possibly work

April 19, 2011 Matthew Kocher

I’ve been working on a basic chef solo based rails deployment. I started with Building an AMI, then bootstrapping to get the instance running chef with capistrano. Since part two left us with chef running on a box with recipes, it’s time to get a rails app running and serve some requests.

For the moment, I’ve created an application.rb recipe, which is the only thing in the soloistrc. It declares dependencies:

include_recipe "joy_of_cooking::daemontools"
include_recipe "joy_of_cooking::mysql"

and then gets on with dealing with the app server.

‘What is daemontools‘? I hear you cry out. it’s the best way I’ve found to ensure processes are running as you expect. If you aren’t using daemontools (and you probably aren’t) it’s worth looking at seriously. Daemontools takes the pids out of process management. There are no pid files to get out of sync with processes, and there’s no polling to see if something is up. Daemontools know if something exists because it is a child process, and when it exits it returns control to the supervise process. Deamontools is worth wrapping your head around, but for now, all you need to know is that you give it a script and it runs the script in an infinite loop. I stole the install procedure from Michael Sofaer’s Hellspawn, so you’ll have to excuse the fact that it looks like ruby instead of chef.

ruby_block "install daemontools" do
  block do
    directory = "/package/admin"
    repo = "git://github.com/MikeSofaer/daemontools.git"
    dir_name = "daemontools-0.76"
    FileUtils.mkdir_p directory
    system("cd #{directory} && git clone #{repo} #{dir_name}")
    system("cd #{File.join(directory, dir_name)} && ./package/install")
  end
  not_if "ls /command/svscanboot"
end

Once daemontools is installed, we move on to installing mysql. A database isn’t necessary for an app, but it’s necessary for doing anything interesting. I made a philisophical decision to compile mysql from source. I tend to view compiling from source as a continuum – you usually would compile your own app – you usually wouldn’t compile your own kernel. The DB is closely related enough to your app that it’s nice to have exact documentation about what you’re running. It’ll also lower the barrier to entry to trying out Percona or Drizzle.

The mysql recipe is a little too long to inline here, but you can and should check it out on github. The recipe starts by installing some dependencies (by all means, leverage your package management system here) create a mysql user and download/cmake/make install. Add a deamontools run script, and up it comes. Set up the users and we’re all set. The most interesting part of the recipe to me is the block which waits for mysql to start up:

ruby_block "wait for mysql to come up" do
  block do
    Timeout::timeout(60) do
      until system("ls /tmp/mysql.sock")
        sleep 1
      end
    end
  end
end

Usually I’d just throw in a sleep, but I was ashamed to share that with the world. This is a technique I’ll carry forward, and would love to see it make its way into chef so those less prone to dropping into ruby could make use of it. (Also, if there’s a better way or something that’s already in Chef that I haven’t come across, I’m all ears)

Once mysql is up and running, we do the git clone/bundle/db:create/db:migrate steps rails developers have come to know and love, then write out a deamontools run script and use svc to make sure the unicorn restarts on every deploy. I’m not a fan of the cap style cached copy/magical rollback that the chef deploy resource reimplements, so for now I’m rolling my own.

Once the app is ready to be started, write out a daemontools run script:

file "/service/unicorn/run" do
  content %{#!/bin/bash
cd /var/staging/foo/src
rvm_path=/home/mkocher/.rvm/
export RAILS_ENV=staging
source /home/mkocher/.rvm/scripts/rvm
rvm use ruby-1.8.7-p299@captest
exec /command/setuidgid mkocher rackup -p 3000
}
  mode "0755"
end

And daemontools starts up the app. The run script is pretty easy to follow – set up RVM, chose our ruby, and exec unicorn.

Hitting the app on 3000 reveals a rails app in all its scaffolded glory:

app screenshot

There’s more work still do to. There’s a repetition that needs to be refactored out. Capistrano, Chef and Rails all need to share some knowlege about the world. Cap and chef need to know the directory we’re deploying the thing to, chef and rails need to know what the database configuration is, and so on. Cap has settings, Chef has nodes and Rails has Configure blocks. I’m leaning towards reinventing the weel again very simply, but I’m open to suggestions. Other todo items are putting nginx in front of the app server, and moving to a dedicated database server.

I’m not sure which direction this will go next, but I hope by now you’re convinced that chef recipes aren’t magic – they’re just a thin ruby wrapping around the things you do to configure a box – execute some commands and edit some files.

All the recipes mentioned are available on github and MIT licensed, please steal them and make them better.

Notes:

  • You’ll need to add whatever ports you’d like open to your EC2 security group.
  • Having mysql on a AMI backed instance with the data on the local disk is the opposite of production ready.
  • Sometimes things in the world changes – either mirror files yourself, or expect the occasional mirror to disappear failing a chef run. Wayne Seguin will also occasionally change the install script location for RVM. Chef recipes are living things which must be tended to occasionally.

About the Author

Biography

Previous
Cedar vs. Xcode 4 (round one: the command line)
Cedar vs. Xcode 4 (round one: the command line)

I've finally found a bit of time to update Cedar to work with Xcode 4, and I hope to have it working smooth...

Next
What Happens When You vmc push an Application to Cloud Foundry
What Happens When You vmc push an Application to Cloud Foundry

This post covers the Cloud Foundry vmc CLI interface and how it interacts with Cloud Foundry. There will b...

×

Subscribe to our Newsletter

!
Thank you!
Error - something went wrong!