Perfect Rails Stack... part deux

I just picked up a new VPS with Etch (Debian 4.0) and was amazed at how much easier it is build a nice Rails deployment stack as compared to Sarge (Debian 3.1).

Yeh Debian, you’ve come along way since the Perfect Rails Stack

Let’s take a look at just how easy it is…

Terms and Conventions

The # prompt means we are logged into the Etch server:

# uname -n
etch

The $ prompt means we are logged into our dev machine:

$ uname -n
Darwin

References to yourusername refer to a non-root user on the Etch server and is likely the same username as you use on your development machine.

We will be deploying an app named foobar to a domain example.com. When you see these terms being used, please substitute your own app and domain name.

The Basics

This section is basically ripped straight from the Perfect Rails Stack

Login for first time

# ssh -l root example.com

Create your user

# adduser yourusername

Get sudo!

# apt-get install sudo

Add user to sudoer list by adding this line to /etc/sudoers

yourusername ALL=(ALL) ALL

Diable ssh login via password for root. Change /etc/ssh/sshd_config to read:

PermitRootLogin without-password

Restart SSH

# /etc/init.d/ssh reload

Ruby and friends

# apt-get -y install ruby ruby1.8 ruby1.8-dev ri ri1.8 rdoc rdoc1.8 irb irb1.8 ruby1.8

That wasn’t hard:

# ruby -v
ruby 1.8.5 (2006-08-25) [i486-linux]

Ruby gems

# cd /usr/local/src/ 
# wget http://rubyforge.iasi.roedu.net/files/rubygems/rubygems-0.9.4.tgz
# tar xzvf rubygems-0.9.4.tgz
# cd rubygems-0.9.4
# ruby setup.rb
# cd ..
# rm rubygems-0.9.4.tgz

Rails

# gem install rails -y

Mongrel

We’ll need compilers first…

# apt-get -y install build-essential
# gem install mongrel -y

Mongrel Cluster

We want mongrel_cluster 1.0 which currently is a beta gem so we need to specify the source:

# gem install mongrel_cluster -s http://mongrel.rubyforge.org/releases/

Configure mongrel_cluster to fire up those mongrels on reboot. Update the mongrel_cluster location depending on the gem version.

# cp /usr/lib/ruby/gems/1.8/gems/mongrel_cluster-1.0.1.1/resources/mongrel_cluster /etc/init.d/
# chmod +x /etc/init.d/mongrel_cluster
# update-rc.d mongrel_cluster defaults

Add mongrel user

# adduser --system --group --no-create-home mongrel

Create directory for mongrel_cluster config files and set permissions

# mkdir -p /etc/mongrel_cluster
# mkdir -p /var/run/mongrel_cluster
# chown mongrel:www-data /etc/mongrel_cluster
# chown mongrel:www-data /var/run/mongrel_cluster
# chmod g+w /etc/mongrel_cluster

Postfix (optional)

# apt-get -y install postfix

During the install two questions will be asked. Answer:

  1. Internet site
  2. example.com

Mysql

# apt-get -y install mysql-server-5.0 mysql-client-5.0 libmysql-ruby libmysql-ruby1.8
# mysqladmin -u root password yourrootsqlpassword

Subversion

# apt-get -y install subversion

Apache 2.2 with proxy balancer

# apt-get -y install apache2
# a2enmod deflate
# a2enmod rewrite
# a2enmod proxy
# a2enmod proxy_balancer
# a2enmod proxy_http

Setup deployment permissions

# adduser yourusername www-data
# chown root:www-data /var/www
# chmod g+w /var/www

Get rid of the default apache site

# a2dissite default

Reload

# /etc/init.d/apache2 force-reload

Almost essential

Your definitely going to want to use public keys with capistrano and ssh and Rails Machine has excellent instructions posted at https://support.railsmachine.com/index.php?pg=kb.page&id=33

You should check out the above link for more details but the basics are:

Create key pair (you probably have done this already)

$ ssh-keygen -t rsa -f ~/.ssh/id_rsa -C "yourusername@devmachine.com"

Copy public key to app:

$ ssh yourusername@example.com 'mkdir ~/.ssh;chmod 700 ~/.ssh'
$ scp ~/.ssh/id_rsa.pub yourusername@example.com:~/.ssh/authorized_keys
$ ssh yourusername@example.com 'chmod 600 ~/.ssh/authorized_keys'

Deployment

Now that your Etch box is ready to rock, it’s time to fire up Capistrano and deploy your app!

Let’s use Capistrano 2 because it’s going to be released very soon and it has some nice new features. Check out the Capistrano tutorial for more info.

CAUTION

Installing Cap2 may break deployment tasks on existing apps. Check the Capistrano upgrade notes before you upgrade.

Cap2

$ gem install net-ssh
$ gem install net-sftp
$ gem install highline  
$ gem install -s http://gems.rubyonrails.com capistrano

Capify

From the root of your Rails app:

$ capify .

Setup server

$ cap deploy:setup

Here is a simple little deploy.rb file:

config/deploy.rb

require 'mongrel_cluster/recipes'

set :application, 'foobar'
set :repository,  'svn+ssh://svn.example.com/foobar/trunk'
set :deploy_to,   "/var/www/#{application}"
set :domain,      'example.com'

set :mongrel_conf, "/etc/mongrel_cluster/#{application}.yml"
set :mongrel_clean, true

role :app,  domain
role :web,  domain
role :db,   domain, :primary => true

namespace :deploy do

  task :cold do
    update
    migrate
    setup_apache
    setup_mongrel_cluster
    start
  end

  # until mongrel_cluster updates to cap2...
  task :start,    :roles => :app do start_mongrel_cluster end
  task :stop,     :roles => :app do stop_mongrel_cluster end
  task :restart,  :roles => :app do restart_mongrel_cluster end

  task :setup_apache do
    sudo "cp #{current_path}/config/apache_vhost.conf /etc/apache2/sites-available/#{application}"
    sudo "a2ensite #{application}"
    sudo "/etc/init.d/apache2 reload"
  end

  task :setup_mongrel_cluster do
    sudo "cp #{release_path}/config/mongrel_cluster.yml #{mongrel_conf}"
    sudo "chown mongrel:www-data #{mongrel_conf}"
    sudo "chmod g+w #{mongrel_conf}"
  end

end

and here is a nice simple apache config file that you can customize:

config/apache_vhost.conf

<VirtualHost *:80>
  ServerName www.example.com
  ServerAlias example.com *.example.com
  DocumentRoot /var/www/foobar/current/public

  <Directory "/var/www/foobar/current/public">
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  </Directory>

  # Configure mongrel_cluster
  <Proxy balancer://foobar_cluster>
    Order allow,deny
    Allow from all
    BalancerMember http://127.0.0.1:8000
    BalancerMember http://127.0.0.1:8001
  </Proxy>

  RewriteEngine On

  # Prevent access to .svn directories
  RewriteRule ^(.*/)?\.svn/ - [F,L]
  ErrorDocument 403 "Access Forbidden"

  # Check for maintenance file and redirect all requests
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
  RewriteRule ^.*$ /system/maintenance.html [L]

  # Rewrite index to check for static
  RewriteRule ^/$ /index.html [QSA]

  # Rewrite to check for Rails cached page
  RewriteRule ^([^.]+)$ $1.html [QSA]

  # Redirect all non-static requests to cluster
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://foobar_cluster%{REQUEST_URI} [P,QSA,L]

  # Deflate
  AddOutputFilterByType DEFLATE text/html text/plain text/xml
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4\.0[678] no-gzip
  BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

  ErrorLog /var/log/apache2/foobar-error_log
  CustomLog /var/log/apache2/foobar-access_log combined
</VirtualHost>

Final check

Don’t forget to check config/deploy.rb and config/apache_vhost.conf into subversion before you move on!

Setup production database

# mysql -p
mysql> create database foobar_production;
Query OK, 1 row affected (0.00 sec)

Cold Deploy

$ cap deploy:cold

Check mongrels

# mongrel_rails cluster::status -C /etc/mongrel_cluster/foobar.yml 
found pid_file: /var/run/mongrel_cluster/foobar.8000.pid
found mongrel_rails: port 8000, pid 4153

found pid_file: /var/run/mongrel_cluster/foobar.8001.pid
found mongrel_rails: port 8001, pid 4156

Parting thoughts

Etch makes deployment simple (at least compared to Sarge) and when mongrel_cluster 1.0 and Cap2 are released from beta captivity it’ll be even easier.

Happy deployment!

Comment or question via
FYI: This post was migrated over from another blogging engine. If you encounter any issues please let me know on . Thanks.