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, , , , ,

Mongrel integration for RHEL, Fedora and derivatives: new release

I have made a new release of my script for controlling mongrel instances in RHEL, Fedora and derivatives.

The most important thing is that now you can selectively choose which instances start or stop. To do so, after the start or stop directive, you can add a prefix, each filename starting with that prefix (and ending with .conf, as usual) will be processed.

As an example, if you have these instance description files in your /etc/mongrel:

testsite.internal.example.com.conf
newapp.internal.example.com.conf
newportal.internal.example.com.conf
fileserver.extranet.example.com.conf

a command line as service mongrel start test will start each instance described by a filename like test*conf, so for the above example you’ll start the instance(s) described in testsite.internal.example.com.conf. In the same way:

  • service mongrel start new will start instances described in newapp.internal.example.com.conf and newportal.internal.example.com.conf;
  • service mongrel stop newapp will stop instance(s) described in newapp.interna.example.com.conf (newportal.etc won’t be touched);

If you don’t specify any prefix, the command will be applied on all files. Note that service mongrel status will ignore any prefix.

Click on the link to download the mongrel service script.

EDITED: The script is available here, but you can better use mod_passenger, which embed a Ruby interpreter in an Apache or NGINX web server.

Filed under: fedora, rhel, ruby on rails, , , , , ,

The day that Ruby was as fast as PHP

The main objection against Ruby on Rails it’s about its performances, especially when they are compared to PHP. Although it’s cheaper to add up new computational power (or lease it from services like Amazon EC2) than pay for more developers, this objection has some real reason. Ruby 1.8.x is not really fast, and this is a problem for every framework built on top of it as RoR, but things are changing, as this benchmarks from Antonio Cangiano shows:

  • Ruby 1.9 is 2-3 times faster than Ruby 1.8.6;
  • JRuby is now as fast as Ruby 1.8.6, so you can now get all the power of Ruby on a Java application server;
  • There’s a lot of room for improvements, as some other projects (XRuby, Rubinius) indicate.

Filed under: ruby on rails, , ,

Integration of Mongrel with RHEL and derivatives

(EDITED: You probably live a lot better using mod_passenger)

Mongrel is the Ruby on Rails application server. It’s not included in RHEL 4.x nor 5.x, so I wrote this little script to have it managed by the service command. If you’re used to Apache server, Mongrel is a bit different as it’s only an application server, and it’s mono-threaded. So if you want more than one instance working, you need to explicitly decides how many of them you want and on which port they will listen (one instance for one port). Mongrel works better as the back-end, on the front-end put Apache that will do a lot better work. The script I’m describing in this blog entry is for automatic starting/stopping of this instances, integration with Apache will be described in another entry.

Read the rest of this entry »

Filed under: rhel, ruby on rails, , , , , ,