Running Ruby applications with Mongrel and Apache2
Posted by Steve on Thu 1 Feb 2007 at 15:35
When you start working with Ruby on Rails applications you're probably content with using the integrated HTTP server, webbrick, for development. Once you're using them in production though you'll want something more capable. This is where mongrel comes in.
There are several options for integrating Ruby on Rails applications within an Apache2 server, the most common are:
- Running them as CGI processes using mod_fastcgi
- Running them under mongrel and using Apache's proxy support to forward requests.
- This is the setup we'll be examining here.
By default when you've created a new Ruby on Rails application several files with have been generated for you, notably:
- script/server - This is a small webserver which can be used for testing work.
- public/dispatch.cgi - This is a CGI interface for your application
Using the server is very simple, but it doesn't scale well. To start it just run script/server:
steve@steve:~/Rails/Books$ ./script/server => Booting WEBrick... => Rails application started on http://0.0.0.0:3000 => Ctrl-C to shutdown server; call with --help for options [2007-02-01 15:11:41] INFO WEBrick 1.3.1 [2007-02-01 15:11:41] INFO ruby 1.8.5 (2006-08-25) [i486-linux] [2007-02-01 15:11:41] INFO WEBrick::HTTPServer#start: pid=9558 port=3000
Here you can see that we've started a server listening upon port 3000, with the WEBrick HTTP server. This allows you to test your application.
When it comes to deployment the simplest solution is to install the mongrel HTTP server - this is a small server which can be used to start your application, and is powerful enough to be used in production.
(Mongrel also supports clustered operation, which we'll not discuss here.)
To install Mongrel you'll use the gem command, which allows you to add or remove ruby-specific extensions upon your Debian machine. If you've installed the rubygems package from the source distribution you may execute:
root@host:~# gem install mongrel
The Debian rubygems package places the gem command in a location which is probably not upon your PATH, so you will need to fully qualify the location of the command:
root@host:~# /var/lib/gems/1.8/bin/gem install mongrel
Either way you should see something like the following:
Bulk updating Gem source index for: http://gems.rubyforge.org Select which gem to install for your platform (i486-linux) 1. mongrel 1.0.1 (ruby) 2. mongrel 1.0.1 (mswin32) 3. mongrel 1.0 (mswin32) ...
Enter "1" to choose the most recent version of the server, which is not compiled for Microsoft Windows.
Note: You must have a compiler and development environment setup, as well as the appropriate ruby-dev package in order to build the shared-library mongrel uses.
If you're missing these you can install them by running:
root@host:~# apt-get install ruby1.8-dev build-essential
Once Mongrel has been installed you can start it up to host your project by executing the following, from the root of your application:
skx@host:~/Rails/Books$ mongrel_rails start -d -e production -a 127.0.0.1 -p 3000
Here we've used several arguments (for an overview of more options run "mogrel_rails start -t") here is a quick explanation of what they mean:
- -d
- To detach and run in the background
- -e production
- To run the application in "production" mode.
- -a 127.0.0.1
- To bind to the localhost only.
- -p 3000
- To listen upon port 3000.
Note: you don't need to start the server as root, since you're binding to a "non privileged" port.
All being well you should receive no errors and browsing at http://localhost:3000 should show you your applications front page.
To stop the server you may run:
skx@host:~/Rails/Books$ mongrel_rails stop Sending TERM to Mongrel at PID 9698...Done.
If this works we can look at adding a handler for Apache2 to make it externally visible.
The first thing to do is to make sure that you have the mod_proxy module enabled for Apache. To do this run:
root@host:~# a2enmod proxy Module proxy installed; run /etc/init.d/apache2 force-reload to enable.
Now create a configuration file for your host within the directory /etc/apache2/sites-available/ - here we'll be using the hostname test.example.com, so we'll create the file with the same name.
The contents of the file would look like this:
<VirtualHost *>
# Server name
ServerName test.example.com
# Proxy ACL
<Proxy *>
Order allow,deny
Allow from all
</Proxy>
# Proxy directives
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
ProxyPreserveHost on
# Logfiles
ErrorLog /var/log/apache2/test.example.com.error.log
CustomLog /var/log/apache2/test.example.com.access.log combined
</VirtualHost>
Once you've created the configuration file you should enable the site and finally restart the Apache server:
root@host:~# a2ensite test.example.com Site test.example.com installed; run /etc/init.d/apache2 reload to enable. root@host:~# /etc/init.d/apache2 restart
If you've done this correctly you should now be able to visit http://test.example.com/ which will cause Apache2 to forward your request, or proxy, to the local mongrel server running upon localhost on port 3000.
In addition to the logfiles from Apache you'll also see ruby-specific logging in the log/ subdirectory of your application.
Do you code Ruby apps, because you want to do it or because you need it? I'm asking you, because I'm very curious.
[ Parent | Reply to this comment ]
[ Send Message | View Steve's Scratchpad | View Weblogs ]
The thing which inspired this particular article was the migration of a PHP-based blog over to a Ruby-based one.
Ruby the language I like, because it is small, uniform, and reasonably well designed.
I'm getting a little experience with Ruby on Rails and find that a pleasant and fun way to develop simple applications with - but there is so much "magic" in the framework that I'm not sure I'd feel the same way about larger projects. (I've not used it enough to know yet.)
I don't have much patience for language wars, although at the same time I do have a strong Perl-preference. Mostly I believe learning new languages, toolkits, and environments is almost always a useful thing to do - even if you don't use them again.
I'm not sure I really answered that terribly well, but I hope it gave you something to work with!
[ Parent | Reply to this comment ]
So, I have no exit plan and I have to try Ruby ;)
[ Parent | Reply to this comment ]
This can be done easily with mod_rewrite.
RewriteEngine On
RewriteRule ^/$ /index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule .* http://localhost:3000%{REQUEST_URI} [P,QSA]
This checks if the file is static and lets apache handle it, everything else goes to mongrel.
[ Parent | Reply to this comment ]
[ Parent | Reply to this comment ]
[ Send Message | View Steve's Scratchpad | View Weblogs ]
Thanks for sharing, the tip about a file for maintenance is neat, as is letting Apache serve some of the files directly.
[ Parent | Reply to this comment ]
[ Parent | Reply to this comment ]
[ Send Message | View Steve's Scratchpad | View Weblogs ]
Seems to work well if you install the rubygems package, but I've not used source installs to know how that works out.
[ Parent | Reply to this comment ]
Some my experience:
without fcgi (fastcgi) - very slow.
With fcgi - much better speed, but sadly not stable, when load increased from just developers to some more people.
(working load for website with ruby: ~5000 visitors/day, ~30000 pages/day)
Now it works for about half year with such config:
apache2 - main server (static content, some php, some perl, dynamic compression)
pen - load balancer among mongrel processes
8 mongrel processes - it may seem pradoxal with 1 CPU, but with complex page with many flash.swf etc. 8 mongrel processes working in parralel gives out page much quicker, than with only one process.
Sig.
[ Parent | Reply to this comment ]
[ Parent | Reply to this comment ]
[ Send Message | View Steve's Scratchpad | View Weblogs ]
Mongrel scales impressively well with its own clustering support.
mod-fcgid appears to no longer be recommended for running rails applications - I know that I had significant load problems with it in production.
Still use whichever you prefer .. you have the freedom to choose.
[ Parent | Reply to this comment ]
I have another related question:
I have severall sites running on my server with ROR. I am still using fastcgi and have problems switching to fcgid... Have tried reading o nthe net for severall day, still no luck.
No I found your article and have been considering switching to mongrel for ROR content but still there is one problem I had:
say customer_x makes changes to his site, he requests me to do a killall dispatch.fcgi to reflect those changes to the production environment. I am just wondering how I could give him the possibility to kill his own process? The dispatch.fcgi processes are running as www-data (debian apache user).
any hints or links for me please?
[ Parent | Reply to this comment ]
[ Send Message | View Steve's Scratchpad | View Weblogs ]
Give him sudo access to run "/etc/init.d/apache2 restart" or "apache2 reload".
[ Parent | Reply to this comment ]
of course I could d othis, but I don't want every client restarting apache2 :-)
I heard rumours one could get fastcgi or fcgid running with suexec? suexec is already working for my server with cgi scripts but I couldn't get it running with fcgid... is there a possibility to have it running somehow with mongrel and suexec? so this one customer could restart his mongrel processes? And have his own processes (mongrel) run as his user/group?
[ Parent | Reply to this comment ]
[ Send Message | View Steve's Scratchpad | View Weblogs ]
Mongrel can certainly run as its own user, so you could do it that way. But I'm unsure about fcgid - I stopped using it very quickly as it just wasn't stable enough.
Sudo seems like the right thing to do, if you can't/won't use mongrel - but be careful you don't give them the ability to kill all process! ;)
[ Parent | Reply to this comment ]
[quote]Once Mongrel has been installed you can start it up to host your project by executing the following, from the root of your application:
skx@host:~/Rails/Books$ mongrel_rails start -d -e production -a 127.0.0.1 -p 3000
[/quote]
does this mean that I could setup their vhost for proxying, having apache2 serve static content and proxy all other requests to mongrel? as these users have chrooted access, I could include mongrel into the chroot, and as they would start their own mongrel processes they could also kill them, but they could pass too many variables to mongrel, possibly this is giving them to many options and too much responsibility for the user...
I guess if I can't get fcgid running with suexec I will have to use the sudo solution.
thx for helping :-)
[ Parent | Reply to this comment ]
[ Send Message | View Steve's Scratchpad | View Weblogs ]
Yes, if apache2 is doing the proxying then the mongrel's can be run as a regular user - either in a chroot or not.
They can then restart their own mongrel themselves.
[ Parent | Reply to this comment ]
You need libapache2-mod-fcgi & libfcgi-ruby, et. al.
blah blah blah
AddHandler fcgid-script .fcgi
SuExecUserGroup migrane migrane
DocumentRoot /var/www/migrane/public
<Directory /var/www/migrane/public/>
Options MultiViews ExecCGI
AllowOverride None
Order allow,deny
allow from all
RewriteEngine On
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
</Directory>
blah blah blah
But make sure that migrane *owns* public and public/dispatch.fcgi and that the latter is executable -- this is mostly likely the source of your frustration.
Note that setting RAILS_ENV is broken with sarge fcgid. As a work around, I set it in dispatch.fcgid based on the hostname and/or path. Also, the sarge libapache2-mod-fcgid has a bug preventing file uploads with ssl, but it's easy to backport the etch version.
[ Parent | Reply to this comment ]
I'm curious about your load issues, because there should be less overhead using fcgid than mongrel (fastcgi is a far less complex protocol than http), and because fcgid's behavior is much more flexible (e.g. it's able to add rails processes on demand, and can respawn old or unresponsive processes, etc.) It could, for instance, be configured to exactly mimic mongrel's load behavior by setting a fixed process count. What did you set your DefaultMaxClassProcessCount to?
In fact, it's mongrel's lack of support for periodically restarting processes that makes me most uncomfortable with it--ruby not being the most robust of languages, I really wouldn't want to run a rails process indefinitely. But I do appreciate mongrel's ability to run in a cluster without needing a web server on each node. On the other hand, I'm glad to run apache on each node if it gives me fcgid's advantages.
[ Parent | Reply to this comment ]