Bits and Chaos

Icon

Between bits and chaos, a sysadmin stands.

Multiple Rails applications proxyed by Apache 2.0

At the $JOB we are moving from a traditional LAMP stack to a Ruby on Rails based one. Security has always been central, so we choose to have one operating system user for each application: if an application experiences a security flaw, it could not affect the others as the operating system will prevent from accessing and manipulating files and data that belong to other users. Of course, an application flaw plus a kernel privilege escalation could be a bit more problematic, but it’s a lot more unlikely that two different security problems appear at the same time, so you can keep your kernel (and core libraries) updated and you should be able to contain the problem.
Beside this, we want to have only one access point for the Rails applications, a kinda of grown up intranet-extranet portal where each application is developed by an agile methodology (at least, the developers told me so) so they could more easily updated: it’s like a specific task for a specific application task.
If you search the Internet, you’ll find a lot of example to configure Apache 2.2 to work with Mongrel, leveraging on mod_proxy_balancer. But Red Hat 4.x ships with Apache 2.0, so we need to went another way.
(Note: some of the next is based on some other blogs I’ve read. I’m stupid enough to be unable to remember them, but if you feel it could be your blog and work I’m standing on, tell me and I’ll credit).
The scenario we consider is this one:
  • we have an example.com access site, that could or could not has some contents of its own;
  • we have a first Rail application, called First App, that should be accessed with the starting URL http://example.com/firstapp;
  • we have a second Rail application, called Second App, that should be accessed with the starting URL http://example.com/secondapp;
  • (we need someone payed to find fancy names for our applications, by the way);
Configuration of First App and Second App from the Mongrel point of view is somewhat made, as an example by using my Mongrel helper script. One way or the other, we have that First App is listening on ports 3000,3001,3002, and Second App on 3003 and 3004.
So we first write a file that will tell Apache where each application is listening:
$ cat firstapp.ports
ports 3000|3001|3002
$ cat secondapp.ports
ports 3003|3004
then we define the VirtualHost section for example.com (if you don’t have a virtual host based setup, skip the <VirtualHost> and </VirtualHost> markups):
<VirtualHost *:80>
ServerName example.com
ProxyRequests Off
# First app
ProxyPassReverse /firstapp http://127.0.0.1:3000/
ProxyPassReverse /firstapp http://127.0.0.1:3001/
ProxyPassReverse /firstapp http://127.0.0.1:3002/

# Second App
ProxyPassReverse /secondapp http://127.0.0.1:3003/
ProxyPassReverse /secondapp http://127.0.0.1:3004/

ProxyPreserveHost On
RewriteEngine On

# Rewriting maps
RewriteMap mongrelfirstapp   rnd:/path-to/firstapp.ports
RewriteMap mongrelsegreteria      rnd:/path-to/secondapp.ports

# Rewrite for example.com/firstapp
RewriteCond %{REQUEST_URI} ^/firstapp.*
RewriteRule ^/(.*) http://localhost:${mongrelfirstapp:ports}/$1 [P,L]

# Rewrite for example.com/secondapp
RewriteCond %{REQUEST_URI} ^/secondapp.*
RewriteRule ^/(.*) http://localhost:${mongrelsecondapp:ports}/$1 [P,L]

#RewriteLogLevel 9
#RewriteLog logs/rewrite-example.com.log
ErrorLog logs/error-example.com.log

The ProxyPassReverse directives instruct Apache on how to deal with the Mongrel back-end servers, you need one directive for every listening Mongrel. Then, we define two RewriteMap, one for each application, called mongrelfirstap and mongrelsecondapp. These rewriting maps choose a random value for the port parameter, due to the rnd: prefix. Then we tell Apache that: if the URI starts with /firstapp, it should proxy [P] the request to one of the three Mongrel serving the first application, choosing it randomly (mongrelfirstapp:ports), and this must be the last URL rewriting rule to apply for this request [L]. Otherwise, if the URL is starting with /secondapp, it should do the same with respect che Mongrel servers serving Second App. Otherwise, it will serve the URI with the standard Apache mechanism. If example.com is itself served via mongrel, you can add other configuration directives where the prefix is not /firstapp but a simple /.

Speaking about prefix, the Mongrel istances must be started with the –prefix parameter that should equal the part defined in the RewriteCond (so, –prefix=/firstapp and no trailing slash). It’s possible to made some syntactic sugar to avoid this, but I prefer that the application itself has an idea on where is located.

About performance: this configuration will proxy every request, included the ones asking for static content. It’s possible to define some rewrite conditions that result in the intervention of Mongrel instances only when there’s actually some Rails code to parse, otherwise relying on Apache, but I don’t need this in my setup.

Advertisements

Filed under: ruby on rails, , , , ,

3 Responses

  1. John Dewey says:

    Paolo –

    Nice write-up. You normally do not see mod_proxy_balancer configurations for apps under /different /directories. Very nice!

    John

  2. Nick says:

    Thanks very much for this how-to. It really helped me setup 2 rails apps under the same apache.

    2 more things I would like to add with this setup:
    -prefix for mongrel: if you use the mongrel_cluster setup, you can add that in the mongrel_cluster.yml

    Say you want your app to be accessible on /coolapp , mongrel_cluster.yml might look like:

    cwd: /var/www/myapps/coolapp/current
    log_file: log/mongrel.log
    port: "3000"
    environment: production
    address: 127.0.0.1
    pid_file: tmp/pids/mongrel.pid
    prefix: /coolapp
    servers: 2

    2nd remark: be sure to use relative paths in your css files. I used absolute paths and no background images were loaded. Using

    url(../images/image.jpg)

    in your css resolves this.

    Thanks again!
    Nick.

  3. Paolo says:

    Nick, thanks for you notes.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: