Rails deployment with Apache and mod_rails on Ubuntu Gutsy (7.10)
If you have ever deployed a Rails app, you have probably used a mongrel cluster running behind a proxy server (usually Apache, Lighttpd or, not so probably, Nginx) while this isnât something painfully difficult, it makes Rails applications harder to deploy when compared to PHP, where you just send your file to the server, or Java, where you bundle your app in a .war file and place it on the serverâs deployment folder.
The biggest problem with the mongrel cluster approach is that you have to take care of at least two processes. Although it was possible to have only an Apache server to deploy your Rails applications (using FCGI) this wasnât a good approach as it will hurt your application performance.
But now, we have a true option to run our Rails applications using just an Apache server, and the option is called (tadĂĄ!) mod_rails!
mod_rails (or Passenger) is an Apache module that aims to enable seamless deployment of your Rails applications using only an Apache server. No proxies, no (visible) clusters, no other processes to handle, just copy your rails application to folder defined at the Apacheâs virtual host configuration and be done with it.
But how does it works?
What mod_rails does is automatically manage a cluster of rails applications inside your Apache server, so you will have all functionalities and performance advantages of running a mongrel cluster without having to manage one. And something that really makes mod_rails special is that your rails applications are independent from the Apache server, if your application blows, the main server wonât go down the tubes. If you want to have a full architectural overview of how it works, take a look at their âPassenger architecture documentâ.
And now, enough of talking, letâs get our hands dirty and prepare the environment to deploy a rails application to mod_rails. First, this tutorial is aimed at preparing an Ubuntu 7.10, but it should probably work if youâre running 7.04 or maybe even a 6.x, but I canât guarantee that.
If you donât have Ruby yetâŚ
If youâre going to do this in a brand new (aka. virgin) server, you will have to install some things (like Ruby
) first, login to the machine and start typing:
sudo apt-get update
sudo apt-get dist-upgrade
sudo apt-get autoremove
This will update your system and take the garbage away. Then you go:
sudo apt-get install build-essential ây
This will install the software needed to build other things (like your native gems). After installing this, itâs time to install your database (in our case, itâs MySQL, but you can take another one, I promise I wonât feel bad about it) and Ruby, you can do this typing:
sudo apt-get install mysql-server mysql-client libmysqlclient15-dev libmysql-ruby1.8 ruby1.8-dev ruby1.8 ri1.8 rdoc1.8 irb1.8 libreadline-ruby1.8 libruby1.8 libopenssl-ruby irb1.8 libdbd-mysql-perl libdbi-perl libmysql-ruby1.8 libmysqlclient15-dev libmysqlclient15off libnet-daemon-perl libopenssl-ruby libopenssl-ruby1.8 libplrpc-perl libreadline-ruby1.8 libruby1.8 mysql-client mysql-client-5.0 mysql-common mysql-server mysql-server-5.0 rdoc1.8 ri1.8 ruby1.8 ruby1.8-dev zlib1g-dev
Itâs possible that the ruby installer hasnât added the symlinks, so, if typing ârubyâdoesnât work, try this:
sudo ln -s /usr/bin/ruby1.8 /usr/local/bin/ruby
sudo ln -s /usr/bin/rdoc1.8 /usr/local/bin/rdoc
sudo ln -s /usr/bin/ri1.8 /usr/local/bin/ri
sudo ln -s /usr/bin/irb1.8 /usr/local/bin/irb
With ruby installed, itâs time to install RubyGems. You can install RubyGems from apt, but itâs better to download it and perform a manual installation. There you go:
wget http://rubyforge.org/frs/download.php/35283/rubygems-1.1.1.tgz
tar xvzf rubygems-1.1.1.tgz
cd rubygems-1.1.1
sudo ruby setup.rb
After installing it, you also have to add a symlink:
sudo ln -s /usr/bin/gem1.8 /usr/bin/gem
Alfter all this typing, you must be really tired, so now comes the easy part..
As youâre planning to perform a Rails deployment, you are probably using Capistrano (why wouldnât you use it?), so I have some recipes to make you type less, a LOT less. First, install the Apache 2 server, Apacheâs development headers, the Apache Common Runtime, and finally the Rails and Passenger gems:
desc 'Installs apache 2 and development headers to compile passenger'
task :install, :roles => :web do
puts 'Preparing the environment'
puts 'Installing apache 2'
sudo 'apt-get install apache2 apache2.2-common apache2-mpm-prefork apache2-utils libexpat1 ssl-cert libapr1 libapr1-dev libaprutil1 libmagic1 libpcre3 libpq5 openssl apache2-prefork-dev -y'
puts 'Installing needed gems'
sudo 'gem install fastthread rake rails passenger'
end
This task will install the Apache 2 server (even if you already have Apache 2 installed, you should run this task to be sure that you also have the development libraries installed) and the required gems. After this, youâre almost there, login again to your server and type:
passenger-install-apache2-module
You will answer some questions (and probably you wonât need to install anything, as we have already installed all software needed) and when the script is done take note of what heâs saying, which means copy the values to your /etc/apache2/httpd.conf file, it should look like this:
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-1.0.1/ext/apache2/mod_passenger.so
RailsSpawnServer /usr/lib/ruby/gems/1.8/gems/passenger-1.0.1/bin/passenger-spawn-server
RailsRuby /usr/bin/ruby1.8
RailsMaxPoolSize 2
On the first line, we are telling Apache to load the passenger_module (this is mod_rails), the next ones are mod_rails configurations. RailsSpawnServer is the path to the executable that starts the Rails servers, RailsRuby is where your ruby executable is and RailsMaxPoolSize is how many application instances (just like the mongrel instances) you want mod_rails to start. Donât leave the RailsMaxPoolSize blank, as itâs default value is 20 (yeah, TWENTY) and you probably donât have enough memory for all 20 rails applications.
And weâre done!
Well, almost
Now that you have apache configured and mod_rails (Passenger) being loaded, we have to tell Apache about our application, we do this using a virtual host configuration, but we are not going to write it with our own hands, oh no, so there is another task to do this for us:
desc 'Creates a virtual server configuration on apache to your application'
task :create_server_config, :roles => :web do template = File.read( File.dirname(__FILE__) + '/vhost_config.erb' )
buffer = ERB.new(template).result(binding)
puts 'Rendering template file'
put buffer, "#{shared_path}/#{application}-vhost"
puts 'Copying virtual server config to apache folder'
sudo "cp #{shared_path}/#{application}-vhost /etc/apache2/sites-available/#{application}-vhost"
puts 'Enabling the site on apache'
sudo "a2ensite #{application}-vhost"
end
This task uses an .erb file called vhost_config.erb that should be on the same directory of the file where this task is defined, hereâs the template:
<VirtualHost <%= domain %>:80>
ServerName <%= server_name %>
DocumentRoot <%= deploy_to + '/current/public' %>
<Directory "<%= deploy_to + '/current/public' %>">
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
It uses our own configuration (from deploy.rb) to define the virtual server. When we call this task, it will not only generate this virtual host config, but also copy it to the sites-available and then call âa2ensite application-vhostâ installing the application on apache.
Then, you can just restart apache with the following task:
task :restart_apache do
puts 'Restarting the apache server'
sudo 'apache2ctl restart'
end
And youâre done, your Ubuntu server is running your rails application without any mongrels or anything else to manage besides Apache. Once your application is running, you can restart it with the following task:
desc 'Restarting the application'
task :restart_app do
puts 'Restarting the application'
run "touch #{deploy_to}/current/tmp/restart.txt"
end
Whenever you want to restart you app without restarting apache, itâs just a matter of touching the “/tmp/restart.txt” file (you have to create this file manually under your “/tmp” folder, it’s just a blank text file).
After all this, when once you have another application to deploy to the same Apache server, you will just generate a new virtual host file for it and it will be running without any other configuration or anything to manage. Could this be any better?
So, what about the good old mongrels?
mod_rails isnât going to replace Mongel all over the world, because itâs a Rails only solution (although it probably can be tweaked to run other frameworks). What the guys at mod_rails are doing is integrating the a rails cluster inside Apache itself, without the need to run a separate server cluster, but as this is a necessity generated by Railsâ mono-threaded model, other frameworks, like Merb, will keep on using mongrel as their application server.
Another reason not to just look at mod_rails is when you already have a cluster of rails applications running on many computers and proxied by a common HTTP server, as you really need to distribute the load through many machines using one as a load balancer, it will not be so easy as Iâm showing here, but even with a proxy, using apache at the application servers might help the performance of static content delivery.
Acknowledgements and references
Most of the installation instructions that you found here where taken from the following post by Vince Wadhwani (Thanks Vince!).
If you are not going to install mod_rails in an Ubuntu machine, checkout the mod_rails documentation.
Trackbacks
Use this link to trackback from your own site.
Thanks for the step by step guide.
Just noticed that you should replace:
sudo apt-get updatesudo apt-get dist-upgradesudo apt-get autoremove
with:
sudo apt-get update
sudo apt-get dist-upgrade
sudo apt-get autoremove
or
sudo apt-get update && sudo apt-get dist-upgrade && sudo apt-get autoremove
Of course it could be that you typed it right, but WordPress is changing the display of the double AND characters.
Anyway,
Thanks again
Actually, in vhost_config.erb:
Shouldn’t the DocumentRoot be set to: = current_path + â/publicâ
The virtual host’s document root must point to your Ruby on Rails application’s public folder.
Yep, my bad on that one, mod_rails needs to be pointed to your public folder.
Weâre now offering Passenger as a deployment option on our Rails 2 portal for Elastic Server. Build a Passenger or Mongrel container here: http://es.cohesiveft.com/site/rails2, then download it as a vmware image or deploy it to EC2 with one click. Check it out!