Pounding Mongrel Light(tpd)ly
Posted by theBlatherskite Mon, 20 Nov 2006 22:27:00 GMT
As witnessed by my recent(ish) articles on installing and optimizing FreeBSD, I’ve been spending my spare bits of time developing a production server for our internal environment. There’s been quite a push within the rails community away from Apache+FCGI, and when it came time to look at the server application I decided to jump in and see what other options are out there.
My eventual deployment decisions: lighttpd to serve static files, a mongrel cluster taking care of business on the rails side, and the pound load-balancer sitting up front managing customer service (and, for bonus points, reverse-proxing to allow the mongrels to serve content over https).
Note that this is by no means the only possible choice. A little research into what other rubyists have been doing reveals a number of other options that would also be worth exploring (for example, Coda Hale and Jonathan Weiss seem to prefer apache feeding the mongrel pack).
Mongrel
Mongrel is an easily-configured web server, tuned for serving up rails apps, that’s sweeping the RoR community. The software’s author, the oh-so-entertaining Zed Shaw, is also quite dedicated to supporting users.
Details can be found by scouring the mongrel website, but the general gist of using Mongrel is pretty easy. Mongrel is a single-threaded application (as is rails itself), so we can scale to handle higher loads by deploying multiple mongrel processes in parallel as a mongrel “cluster.”
Obtaining
First, we need to install Mongrel and Mongrel Cluster:
$ sudo gem install mongrel
$ sudo gem install mongrel_cluster(On a windows machine, you may also want to install mongrel_service)
Configuring
Second, we need to configure the cluster. From your rails application’s root:
$ mongrel_rails cluster::configure -e production -p 8000 -N 3 -a 127.0.0.1Where p is the port for the first mongrel instance to bind to, and -N is the number of mongrels to be released (so in this case, we’ll end up with three servers listening on 8000, 8001, and 8002). Finally, the -a switch tells the daemons to ignore all requests that don’t originate from the given IP (in this case, localhost, which allows us to enforce a single point-of-entry into our server).
Running
Third… actually, that’s about it. The following do pretty much what you’d expect:
$ mongrel_rails cluster::start
$ mongrel_rails cluster::restart
$ mongrel_rails cluster::stopLighttpd
Lighttpd is an amazingly tight webserver that apparently rocks the socks off apache in terms of pretty much everything but load balancing (that’s why we’ll be using Pound as the front end, rather than letting lighty manage the distribution of work to the mongrel cluster).
You can read all about how to configure it as a standalone server in the manual, or you can just install it on your machine and work with what rails gives you. You’ll see what I mean.
Obtaining
Lighttpd can be downloaded from here or extracted from SVN at svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x/. For FreeBSD users, a port exists in /usr/ports/www/lighttpd.
Configuring
This is where I spent a good bit of time reading through the lighttpd configuration docs and trying to translate all the examples (written using PHP) into the rails world. I’m pretty new to FreeBSD, and I was having difficulty understanding where to find the appropriate socket to attach the fcgi processes to… I finally gave up and went to bed around 6am.
When I woke up the next day, however, I realized all I need to do was type script/server at my application’s root (funny how you don’t think of these things late at night). Once lighttpd is installed on the system, rails automatically leverages this as the default server… which means it writes out a functioning config file for us.
$ nano -w config/lighttpd.confSince we know this configuration already functions correctly, we just need to tune it as desired. Set server.port to whatever you wish (we’ll use 3000 for this example), and server.bind to 127.0.0.1 (if server.bind is set to 0.0.0.0, it will accept connections from any IP address (e.g., it’ll respond to outside users). Since we want all traffic to be proxied through pound, we only want lighttpd to respond to the localhost).
Running
$ script/serverViola.
Pound
Pound is the glue that will bind our various servers together. Pound will be the only web-facing component of our application. It will accept a connection from a remote user, and then pass that connection off for another program to handle (in our case the web servers all reside on the same machine, but Pound would also work with a physical cluster of boxes).
In addition, Pound has the capability to reverse-proxy HTTPS traffic, thus allowing our regular mongrel cluster to serve content securely without changing any configuration whatsoever (outside of Pound, of course).
Obtaining
The source is accessible in RPM format, as a FreeBSD port (/usr/ports/www/pound), and as source from the main page (scroll down).
You may also want to install the PCRE (/usr/ports/devel/pcre) and hoard (/usr/ports/devel/libhoard) libraries – Pound will link against them automatically, which helps performance significantly.
Configuring
Configuration of pound is relatively simple, compared to lighttpd or, to take it up a notch, apache. This entry is already getting quite long, so I’ll refer you to Rob Orsini’s walkthrough (Step 3 – Configure Pound), which provides a detailed information on a complex deployment environment, and Aidan’s article, which focuses on a more practical deployment. In addition, pound’s official documentation is available here – it’s not pretty, but it gets the job done.
Note that while the install apparently should create a default config file at /usr/local/etc/pound.cfg, there was no such file after my FreeBSD install. Perhaps I was looking at old docs, or perhaps there’s just a snafu somewhere along the line – at any rate, you may need to create this file yourself.
For our example, the configuration file:
User "www"
Group "www"
LogLevel 2
Alive 30
ListenHTTP
Address 0.0.0.0
Port 80
# Lighttpd instance on 3000 for static content
Service
URL "\.(jpg|gif|png|js|css|jpeg|html|txt)"
BackEnd
Address 127.0.0.1
Port 3000
End
Session
Type IP
TTL 300
End
End
# Mongrel cluster from 8000
Service
BackEnd
Address 127.0.0.1
Port 8000
End
BackEnd
Address 127.0.0.1
Port 8001
End
BackEnd
Address 127.0.0.1
Port 8002
End
Session
Type IP
TTL 300
End
End
End
# Also serve content over HTTPS
ListenHTTPS
Address 0.0.0.0
Port 443
# Change this path to your SSL certificate
Cert "/usr/local/etc/domain-cert.pem"
# pass along https hint
AddHeader "X-Forwarded-Proto: https"
HeadRemove "X-Forwarded-Proto"
--- SNIP ---
[Copy and paste the two 'Service' sections from above -- there's no difference]
--- SNIP ---
EndRunning
$ poundThat’s about it. Type man pound for full documentation.
Final notes
Once everything’s been properly configured, presumably deployment should be relatively simple. During the development process, however, I found two techniques (applicable in *nix environments) to be very helpful in keeping track of which servers were, or were not, running on which ports:
$ ps aux | grep poundTo list all running processes from a specific program, replace ‘pound’ with lighttpd / mongrel as necessary.
In addition,
$ lsof -i -PLists all open network sockets on the local machine, as well as the executable which is listening behind each socket.
If anyone has similar tips and tricks, feel free to share them below for the benefit of all :)
Note: apparently Mongrel runs dramatically faster on FreeBSD if you compile the nopthreads version of ruby (/usr/ports/lang/ruby18-nopthreads).
This is a great article. Thanks.
Thank you for this interesting serie about freebsd and rails deployement.
Hey,
Thanks for writing this up, it was very useful for setting up my stack.
By the way, I found that Pound didn’t start and (like you said) provided me with nothing more then sweet silence… Investigation showed that my machine didn’t have a “www” user yet. A swift “useradd www -M” and “groupadd www” solved the issue and left me a happy camper.