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: , , , , ,

Wednesday, June 13, 2007

Natural light, it burns!

I'm convinced that the only time to start a successful start-up (in Toronto, at least) is early summer. Being able to work outside, catching some sun, and going for a run or roller blading in the park when you feel your brain is getting full is amazing.

What's even more amazing is the feeling of clarity and focus, which only gets stronger every day. We shared our ideas early on with some of our friends, and since then the beast has grown and morphed in our minds... but in some indescribable way, it doesn't feel like it's changed (even though it has), it only feels like the details that have always been there are revealing themselves to us, one by one.

Toughest parts so far? Finding a "home run" domain name (I don't expect this to happen for another month or two). And for me (since I put on my project manager hat), finding a good task tracking tool. I went through maybe 30 options, and they were all either too basic, too elaborate, or not free (and not worth the money). In the end I settled on http://www.dotproject.net/, which has the advantage that I could set it up through our webhost's control panel. The interface is somewhat clumsy, but it'll do.

Since I'm the resident geek, I feel like a bit of technical babble is called for. And what better subject to babble about than the many benefits of Rails! (I can see other geek readers rolling their eyes right about now. Yes, you.)

This is nothing new to Rails users, but I always thought the way routing is handled is very neat. A very basic example is something like:

http://www.clutterme.com/pages/show?name=username

which we might want to condense to the much friendlier

http://www.clutterme.com/username

Nothing fancy, all it takes is the following in routes.rb:

map.connect ':name',
:controller => "pages",
:action => "show"
Now, the fun part is more complicated paths:

http://www.clutterme.com/users/account?name=username
http://www.clutterme.com/users/settings?name=username
etc.

would ideally go to:

http://www.clutterme.com/username/account
http://www.clutterme.com/username/settings
etc.

I expected this to be fairly complicated to do on top of the first rule, since I now have a parameter as the first part of the path, sometimes followed by an action, and the controller is inferred from the context. Yikes! Well, it's not all that complicated. All it took is one extra line in routes.rb:

map.connect ':name/:action',
:controller => "users"

The best part? I'm far from a Rails or routes.rb expert; the above code just "made sense", and it just worked. That's how computers are supposed to work.

Labels: , , ,