Monday, July 23, 2007

Capistrano 2.0 and Mongrel

Rails, Apache, Mongrel, Capistrano - RAMC? Or maybe Mongrel, Apache, Rails, Capistrano - MARC? That should keep my co-founder happy.

Whatever the order, that's what our new server is running(1). It was an uphill battle, but it should be worth it.

I mainly used this excellent guide on the Rails forum, which has instructions for updating Apache 1.3.x to 2.2.x on a cPanel machine, and then installing Mongrel. No Capistrano so far.

I then read Coda's Time For A Grown-Up Server: Rails, Mongrel, Apache, Capistrano and You and Using Capistrano with Rails on the Capistrano site, to wrap my head around Capistrano.

Both are excellent resources, but neither got me 100% of the way there; the first assumes you have Capistrano running and are adding Mongrel to the mix (and is written for an older version of Capistrano), the second doesn't mention Mongrel much. There were also a few key concepts which, as a newcomer to Capistrano, were not immediately obvious to me.

So, assuming you have a working Apache 2.2 / Mongrel / Rails setup, and want to add Capistrano 2 to the mix, here's how I did it:

1) Install Capistrano on your development machine (or the machine you'll be using to initiate deployments - this is usually not the same as the production server):
gem install capistrano

2) Create stubs for the Capistrano config files:
cd /your/rails/app/folder
capify .

This should create two files - "Capfile" and "config/deploy.rb"

3) Install Palmtree - this contains working scripts for Mongrel for Capistrano 2.0 (as of today, as far as I know, the scripts that come with Mongrel don't work with Capistrano 2.0).
gem install palmtree

4) Edit the "config/deploy.rb" file:

- edit the existing information with your app name, server information, and path on the server. This should be self-explanatory. If (like me) the remote server has a different username from your workstation, specify the servers as "username@server".

- get Capistrano to use a different username/password for SVN (from this blog). Replace the "set :repository" line with:
set :svn_user, ENV['svn_user'] || "MY_SVN_USER_NAME"
set :svn_password, Proc.new { Capistrano::CLI.password_prompt('SVN Password: ') }
set :repository,
Proc.new { "--username #{svn_user} " +
"--password #{svn_password} " +
"http://your.domain.tld/path/to/svn/#{application}/trunk/" }

- Tell Capistrano about Mongrel. Add this line at the top of the file:
require 'palmtree/recipes/mongrel_cluster'

and these lines after the "set :deploy_to" line:
set :use_sudo, false
set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml"

(the "set :use_sudo, false" line is optional, depending on whether or not you need to use sudo to control Mongrel).

- optional: I had a problem with SVN and "svn: Can't recode string" messages for some files checked in from a Mac. I fixed it on the server but Capistrano was still experiencing it, so I added the line:
default_environment["LC_CTYPE"] = "en_US.UTF-8"

That should be it! Your finished file might end up looking something like this:
require 'palmtree/recipes/mongrel_cluster'
default_environment["LC_CTYPE"] = "en_US.UTF-8"
set :application, "MY_APP_NAME"
set :svn_user, ENV['svn_user'] || "MY_SVN_USER_NAME"
set :svn_password, Proc.new { Capistrano::CLI.password_prompt('SVN Password: ') }
set :repository,
Proc.new { "--username #{svn_user} " +
"--password #{svn_password} " +
"http://my/svn/server/path" }

# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
set :deploy_to, "/home/MY_SERVER_USER_NAME/#{application}"
set :use_sudo, false
set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml"

# If you aren't using Subversion to manage your source code, specify
# your SCM below:
# set :scm, :subversion

role :app, "MY_SERVER_USER_NAME@production.server.com"
role :web, "MY_SERVER_USER_NAME@production.server.com"
role :db, "MY_SERVER_USER_NAME@production.server.com", :primary => true

5) Almost time to rock and roll! SSH into your server, stop the Mongrel processes, and move your existing Rails application folder to keep it safe (assuming your "deploy_to" variable in deploy.rb points to the same folder as your existing install).

6) Let's set up the folders Capistrano needs on the server. On your local machine, run:
cap deploy:setup

Capistrano should ask you the password for the account on the server. If all goes well, Capistrano connects to your server and makes a few folders:
#{deploy_to}/
#{deploy_to}/releases
#{deploy_to}/shared
#{deploy_to}/shared/log
#{deploy_to}/shared/system
#{deploy_to}/shared/pids

7) We need one change to our config/mongrel_cluster.yml file. Capistrano checks out each release into #{deploy_to}/releases/timestamp, then makes a symbolic link from #{deploy_to}/current to the current release. What this means for config/mongrel_cluster.yml is that you might have to change:
cwd: /home/MY_USER_NAME/MY_APP_NAME

to
cwd: /home/MY_USER_NAME/MY_APP_NAME/current

and make sure the log_file and pid_file entries are either relative paths, or point to the new folders.

Save config/mongrel_cluster.yml and commit it to SVN.

8) If you're using Rails, Capistrano WILL run your migration scripts - now might be the time to check that they're in order or that you don't have anything you don't want.

9) Finally time to rock and roll! On your local machine, run:
cap deploy:cold

Capistrano will ask you first for the SVN password, then for the server password. If everything goes well, the last few lines should be the Mongrel cluster starting.

10) One last thing. If you're using a symbolic link to point to public_html, you probably need to update it. SSH into the server and type
cd ~
rm public_html
ln -s /home/MY_USER_NAME/MY_APP_NAME/current/public public_html

(careful with that "rm" command! Make sure you understand what it does before you use it... though if you got this far you probably know what you're doing).

That should be it! Future deployments should now be as simple as "cap deploy".

(1) Disclaimer: ClutterMe.com isn't actually running on our new server yet - I'll hopefully transfer the domain over in the next couple of days.

Labels: , , , , ,

4 Comments:

Blogger Mark Molckovsky said...

Alex Curelea = rockstar

July 23, 2007 11:39 PM  
Blogger Jamis said...

Nice writeup! Thanks for this. Note, though, that with cap2, svn_username and svn_password won't work: you need scm_username and scm_password now. Also, rather than the ugly "set :foo, Proc.new { ... }" you can do "set(:foo) { ... }".

July 24, 2007 12:17 PM  
Anonymous viktor tron said...

great,very helpful post!

Some remarks though:

* don't even try without palmtree, the mongrel recipes are not for capistrano 2.X

* A word of warning:
I personally use svn+ssh for subversion access, so I can use ssh-agent (public key) to communicate between development-machine - server and most importantly server and subversion repository.
Using

set :repository, Proc.new { "--username #{svn_user} " + "--password #{svn_password} " + "yourrep" }


is very dangerous, cause cap will show your svn password on the screen, or even worse dump it into a persistent log file.

* I think it is best practive to "deploy via export" rather than checkout. With that you don't have to juggle with apache to make your .svn files unaccessible, plus you spare yourself even the temptation to commit changes from your production server (I know you wouldn't do that).
This is done with

# deprecated way:
# set :checkout "export"
# new way:
set :deploy_via, :export

* this deployment is not totally cold (maybe cool), because cap does not run

rake RAIL_ENV=production db:create

so if you don't do that on the server, migrations will obviously fail.

* also, the present method starts the cluster with local reference to the mongrel cluster configuration in your rail app directory.

mongrel_rails cluster::start -C yourapp/current/config/mongrel_cluster.yml

You have to make sure this is the same config that you reference when you take care of mongrel persistance (crontab or init.d).

October 29, 2007 10:08 PM  
Blogger Phil Smy said...

Not to be picky, but this:

rake RAIL_ENV=production db:create

should be:

rake RAILS_ENV=production db:create

(you have RAIL not RAILS)

Otherwise thanks!!!

July 12, 2008 5:31 AM  

Post a Comment

<< Home