Deploying Rails with Pound in Front of Mongrel, Lighttpd, and Apache

Problem

You have a cluster of Mongrel processes serving your Rails application, and you want a lightweight, yet powerful software load-balancing solution for directing requests to the cluster. The load balancer also needs to be able to route requests to other web servers you have running, such as Lighttpd and Apache.

Solution

For a lightweight and flexible software load balancing, use Pound.

Perl Compatible Regular Expression (PCRE) is one of Pound's prerequisites.This package lets you use advanced regular expressions for matching properties of incoming requests. Let's download, configure, and install PCRE:

$ wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/\
> pcre-5.0.tar.gz
$ tar xvzf pcre-5.0.tar.gz 
$ cd pcre--5.0
$ ./configure
$ make 
$ sudo make install

Now get and install the latest stable version of Pound with:

$ tar xvzf Pound-2.0.9.tgz
$ cd Pound-2.0.9
$ ./configure
$ make
$ sudo make install

On a Debian GNU/Linux system, use apt to get and install Pound. (The benefit of using a packaged version is that you get an init script automatically installed on you system.)

$ apt-get install pound

The number of ways in which you can configure Pound is limitless. What Pound is good at, in addition to load balancing, is allowing a number of different web servers to exist together in the same server environment. The following configuration file sets Pound up to listen on port 80, and forwards various requests to three different backend web servers using service directives. Each directive handles a subset of requests based on matching text patterns in the request.

/etc/pound/pound.cfg:

User "www-data" 
Group "www-data" 
LogLevel 2
Alive 30
ListenHTTP
 Address 69.12.146.109
 Port 80
End
# Forward requests for www to Apache Service
 HeadRequire "Host:.*www.tupleshop.com.*" 
 BackEnd 
 Address 127.0.0.1
 Port 8080 
 End
 Session 
 Type BASIC 
 TTL 300 
 End End
# Forward requests Quicktime movies to Lighttpd Service
 URL ".*.mov"
 BackEnd 
 Address 127.0.0.1
 Port 8081 
 End
 Session 
 Type BASIC 
 TTL 300 
 End End
# Handle all remaining requests with Mongrel Service
 # Catch All
 BackEnd 
 Address 127.0.0.1
 Port 9000 
 End 
 BackEnd
 Address 127.0.0.1
 Port 9001
 End
 Session
 Type BASIC
 TTL 300
 End End

This configuration is set up to pass requests back to Apache (listening on port 8080), Lighttpd (listening on port 8081), and a small Mongrel cluster (listening on ports 9000 and 9001).

On some systems, including Debian GNU/Linux, you need to modify the following file (setting startup equal to 1):

/etc/default/pound:

 startup=1

Start Pound using its init.d script.

$ sudo /etc/init.d/pound start

With Pound up and running, you can test it simply by passing requests to the port it's listening on. If Pound is routing requests to a backend service that is not running or misconfigured, you'll get an HTTP 503 error. In this case, try to access the problem service directly to rule out your Pound configuration as the cause of the problem.

Discussion

Pound is a very fast and stable software load balancer that can sit out in front of Lighttpd, a pack of Mongrels, or any other web servers waiting to process and respond to requests. Because of the way Pound handles headers, the correct value of request.remote_ip is preserved by the time the request is received by Rails. This is not the case when Pound is configured behind another web server, such as Lighttpd. Keep this in mind when you decide exactly how your servers are organized.

Before beginning to set up an even moderately complex deployment configuration, it helps to have a documented plan as to how your services are to interact. For this kind of planning, nothing beats a clearly labeled network diagram, such as .

Figure 13-2. A Rails deployment configuration load balancing with Pound

The Pound configuration file in the solution contains three types of directives: global, listener, and service. The global directives specify the user and group that Pound is to run under. The log level states how much logging we want Pound to send to syslog, if any. Loglevel takes the following values:


0

For no logging


1

For regular logging (default)


2

For extended logging (shows chosen backend server as well)


3

For Apache-like format (Common Log Format with Virtual Host)


4

Same as 3 but without the virtual host information

The listener directive, ListenHTTP, specifies the IP address and port that Pound is to listen for requests from (you'll want a real address here).

The remainder of the configuration file contains service directives that define what backend servers handle different types of requests. The first Service directive states that anything with a Host header containing should be routed to port 8080 of the local host address (127.0.0.1). In this case Apache, running PHP (among other things), is listening on port 8080, waiting to handle whatever requests Pound passes to it. (There's no reason this IP address couldn't be on another physical server, but in this case all three web servers are on the same box.) The next Service directive uses URL ".*.mov" to match requests for QuickTime movie files. For performance reasons, we want Lighttpd to handle these requests. So while a request for would be handled by the Mongrel cluster, a request for would never make it to Mongrel and would instead be served by Lighttpd. The location of .mov files on the server is pretty much irrelevant here; they can be anywhere as long as Lighttpd knows where to find them. The final Service directive effectively serves as a catch all because it's the last one in the file and because there is no URL or header matching criteria defined. This is the one doing the actual load balancing for the Mongrel processes. In this case there are two Mongrel processes listening on ports 9000 and 9001, on the local IP address.

See Also