Rails Production Environment Using Mongrel Cluster, Apache 2.2 mod_proxy, and Capistrano on Ubuntu Gutsy
on Wednesday 13th February, 2008 Gabe speculated thusly…Recently, I had to setup a production environment for Rails applications at work. I encountered many difficulties, including problems with permissions on client/server computers, and not least because the documentation I had was out of date (Agile web dev and The Rails Way). Online tutorials were helpful but incomplete. I have compiled all that I discovered here.
Install required software on the production server
To install a mongrel_cluster behind an Apache proxy on a server install of Ubuntu (Gutsy) you need to install serveral items.
Make sure Apache 2.2 is installed:
# aptitude install apache2
Install Ruby Gems and necessary libraries:
# aptitude install rubygems ruby1.8-dev build-essential
Update gems to the latest version:
# gem update --system
Install Rails:
# gem install rails
Install Mongrel Rails application server:
# gem install mongrel
Install Mongrel_Cluster, scripts for managing multiple Mongrel instances:
# gem install mongrel_cluster
Mongrel is the name of the server that will server your Rails application. It is a good idea to create a dedicated user and group on your system for this process:
# adduser --system --no-create-home --disabled-password --group mongrel
Next you should plan where your applications will be stored on the server. I chose /var/www/apps so I created that directory:
# mkdir /var/www/apps
Then ensure it is owned by the user I will be deploying with:
# chown gabriel:gabriel /var/www/apps
Configuring Apache to cope with a Mongrel cluster
Enable several apache modules that allow us to perform URI rewriting and reverse proxying to our mongrels:
# a2enmod rewrite # a2enmod proxy # a2enmod proxy_balancer # a2enmod proxy_http
Create a configuration file for a virtual host in Apache, you can do this as follow:
# touch /etc/apache2/sites-available/railsapp.conf
Then edit that file you would any other. Essentially you need a configuration as follows (more or less):
<VirtualHost *>
ServerAdmin admin@dev.server.com
ServerName railsapp.dev.server.com
ServerAlias www.railsapp.dev.server.com
ServerAlias railsapp
DocumentRoot /var/www/railsapp/current/public
ErrorLog /var/log/apache2/railsapp.error.log
# Enable proxying.
<Proxy *>
Order allow,deny
Allow from all
</Proxy>
# Define the members of the cluster to load balance with.
<Proxy balancer://railsapp_cluster>
BalancerMember http://127.0.0.1:8000
BalancerMember http://127.0.0.1:8001
</Proxy>
<IfModule mod_rewrite.c>
RewriteEngine On
# If capistrano has left a maintenance file then we should display that.
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://railsapp_cluster%{REQUEST_URI} [P,QSA,L]
</IfModule>
<Directory "/var/www/railsapp/current/public">
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</Directory>
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel notice
</VirtualHost>
Now enable that site:
# a2ensite railsapp
Configuring your development machine/desktop to make deployments to the production server
It is a good idea to have Mongrel and Mongrel Cluster installed on your local machine along with Capistrano. This is accomplished in different ways depending on your operating system. The following part of the guide assumes that you are deploying from your desktop to a production server via SSH. I personally use the Bazaar revision control system for all my projects, and so we will make the configuration to suit that. The next commands are to be carried out on your desktop.
Change to the directory containing your Rails application. For me that is stored at ~/Desktop/railsapp:
$ cd ~/Desktop/railsapp
Instruct Capistrano to make needed scripts:
$ capify .
This will create a file called deploy.rb in the config folder of your Rails application. Go to edit that file now and make it look similar to the following, I will discuss what each option does after:
require 'mongrel_cluster/recipes'
set :application, "railsapp"
set :repository, "/home/bazaar/rails/railsapp"
# 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, "/var/www/apps/#{application}"
# If you aren't using Subversion to manage your source code, specify
# your SCM below:
set :scm, :bzr
set :revision, 'last:1'
set :password, "mypassword"
set :user, "gabriel"
role :app, "railsapp.dev.server.com"
role :web, "railsapp.dev.server.com"
role :db, "railsapp.dev.server.com", :primary => true
task :after_cold, :roles => [:app,:web,:db] do
sudo "chown -R mongrel:mongrel /var/www/apps/#{application}/"
end
task :after_deploy, :roles => [:app, :web, :db] do
sudo "chown -R mongrel:mongrel /var/www/apps/#{application}/"
end
- This first line beginning with require includes extra mongrel scripts that Capistrano will need. You specify the name on the application with set :application.
- Give it the location to the Bazaar repository where your project is stored. In my case it was on the same machine, and I did it with
set :repository, “/home/bazaar/rails/railsapp”. - Next, using set :deploy_to tell Capistrano where on the production server your application will be served from. Remember we used /var/www/apps so the line will read as set :deploy_to, “/var/www/apps/#{application}”.
- We notify Capistrano that we’re using the Bazaar revision control system with set :scm, :bzr.
- Due to a bug in the version of Capistrano I was using it would attempt to make a bzr branch with a certain revision. However, it would neglect to provide a revision number which would cause bzr to error. This can be overcome by specifying the latest revision to use, in bzr terms this is last:1. So we set the revision with set :revision, ‘last:1′.
- Provide your username and password for logging in to the production server via SSH with set :password and set :user. This user should be the same that owns the /var/www/apps directory that we chowned earlier.
- Setup the roles with role :app, role :web. I haven’t paid much attention to the use of roles yet.
- Finally, I had to add a couple of tasks to the end of the deploy file. After deploying your application to the live server all the files will be owned by you and the Mongrel server will not be allowed to write to any of them. This is a problem because the mongrel process will need to write session and cache files etc. These tasks started with task :after_. If you setup your server like mine your Mongrel processes will run as the mongrel user so you will need to chown mongrel:mongrel the files.
Now on your desktop machine you need to create a config file for mongrel_cluster. This simplifies management of a cluster of Mongrel servers. You can create the required file by running the command mongrel_rails cluster::configure along with several arguments. However, I prefer to create a file called mongrel_cluster.yml in the config directory:
$ touch config/mongrel_cluster.yml
Now pop the following data in to it using your favourite editor:
--- environment: production address: 127.0.0.1 user: mongrel group: mongrel port: "8000" servers: 2 cwd: /var/www/apps/railsapp/current pid_file: tmp/pids/mongrel.pid log_file: log/mongrel.log
- environment: production specifies the environment e.g. testing, development, production.
- address: 127.0.0.1 tells the mongrel instances on what address to listen. 127.0.0.1 means that will only accept requests from the localhost. Since we are proxying requests to them via Apache on the same machine this works.
- Specify the username and group the Mongrel instances should run as with group: mongrel, and user: mongrel.
- What port number the Mongrel instances should start at port: “8000″.
- How many instances of Mongrel you want attached to the particluar application. In development you can just use one or two, going up to about 10 for very busy sites: servers: 2.
- Specify the path to your application on the production server, appending current to the end. Using Capistrano means that your application will always end up under /current cwd: /var/www/apps/studiomaker/current.
- We tell Mongrel where to store the lock files for it’s processes. This directory is relevant to the one set in CWD: pid_file: tmp/pids/mongrel.pid.
- Lastly, where Mongrel should keep it’s logs log_file: log/mongrel.log.
Deploying from your desktop
We are now ready for Capistrano to create the required directory structure on the production server. Issue the following command from your application’s directory:
cap setup
Then get it to do a cold-deployment. Note: this step will probably error out as we have not finished configuring the production server. However, it is convenient for us to let Capistrano create the necessary files on the production server first:
cap deploy:cold
Finishing of configuration of the production server
Create a mongrel_cluster directory in /etc directory
# mkdir /etc/mongrel_cluster
Create a link to the mongrel_cluster.yml in the /etc/mongrel_cluster folder (name the link as your application, so many ruby application’s mongrel cluster file will be linked there and they will be booted at startup:
# ln -s /var/www/apps/railsapp/current/config/mongrel_cluster.yml /etc/mongrel_cluster/railsapp.yml
Copy (or link) the mongrel_cluster init script, this allows your clusters to restart after a reboot:
# cp /usr/lib/ruby/gems/1.8/gems/mongrel_cluster-1.0.5/resources/mongrel_cluster /etc/init.d
Make the script executable
# chmod +x /etc/init.d/mongrel_cluster
Install the script as a service
# cd /etc/init.d/ # /usr/sbin/update-rc.d -f mongrel_cluster defaults
Stop any running Mongrel clusters (just incase):
# /etc/init.d/mongrel_cluster stop
Delete your application and start again, just to minimise the risk of an problems from the last failure:
# rm -r /var/www/apps/railsapp
Going back to your desktop machine. Run the final commands
Setup the application again:
cap setup
Do another cold deployment:
cap deploy:cold
With a little bit of luck you should have a production system running!
Great tutorial. I am experiencing so much grief trying to rails to run on a production server. I am new to all of this so I am hitting walls left and right. Since I am such a noob I do have a few questions.
In your railsapp.conf file, I assume anywhere that says “railsapp” should be replaced with the name of actual rails app?
I am deploying from my employer provided windows dev box. Do I just dump the contents of my projects folder into the directory of the production? Or is there something special that capistrano does? Thanks!
Explained very well.Excellent for beginners to write deploy.rb file
@ Daniel
I’m glad you find it useful. You are correct in assuming that ‘railsapp’ should be replaced by whatever the name of your particular application is.
@Jayashree
thanks very much for the positive feedback.
I’ve personally got a chip on my shoulder about rails deployment. It is something of a dark art. I think the problem lies in Ruby not being thread-safe. Otherwise you could serve multiple rails applications just by using mod_ruby.
Rails 2.2 will be thread safe. But why exactly is threading an issue for deployment on production? There is no dark art with Rails production deployment. Capistrano has an issue with documentation and there isn’t anyone interested in fixing the problem.