Lightweight, fast Rails stack - Thin & nginx 1
Since purchasing a slice of heaven a few days ago, I’ve setup a very lightweight Rails stack consisting of Thin & nginx for my Rails needs. Since I went for a slice with just 256MB of RAM, memory consumption becomes a pretty serious issue. nginx has been around for quite a while now, and has recently started to become more and more popular in Rails deployments due to the fact that’s incredibly lightweight, very fast and stable - perfect not only for VPS jerks like me, but for anyone who really doesn’t feel it’s necessary to run Apache for static content/cluster proxying/load balancing. Thin is something I came across very recently and I decided to try is as a replacement for mongrel since I’d heard some great things about it, even if it is still alpha. It’s performance in comparison to mongrel (even with a tacked on event machine) looks very impressive on paper:

Setting up clustering with it is a snap, just spawn the processes and pipe them into nginx. Here’s a startup script borrowed from Stephen Celis:
namespace :thin do
namespace :cluster do desc 'Start thin cluster'
task :start => :environment do
`cd #{RAILS_ROOT}`
port_range = RAILS_ENV == 'development' ? 3 : 8
(ENV['SIZE'] ? ENV['SIZE'].to_i : 4).times do |i|
Thread.new do
port = ENV['PORT'] ? ENV['PORT'].to_i + i : ("#{port_range}%03d" % i)
str = "thin start -d -p#{port} -Ptmp/pids/thin-#{port}.pid"
str += " -e#{RAILS_ENV}"
puts str
puts "Starting server on port #{port}..."
`#{str}`
end
end
end
desc 'Stop all thin clusters'
task :stop => :environment do
`cd #{RAILS_ROOT}`
Dir.new("#{RAILS_ROOT}/tmp/pids").each do |file|
Thread.new do
if file.starts_with?("thin-")
str = "thin stop -Ptmp/pids/#{file}"
puts "Stopping server on port #{file[/\d+/]}..."
`#{str}`
end
end
end
end
end
end
Then spawn however many processes you want using something like:
rake thin:cluster:start RAILS_ENV=production SIZE=2 PORT=3000
To stop them, use:
rake thin:cluster:stop
With that all nicely setup, you can use an nginx config similar to mine to get things in order:
upstream dapperjerk {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
server {
listen 80;
server_name www.dapperjerk.com;
rewrite ^/(.*) http://dapperjerk.com permanent;
}
server {
listen 80;
server_name dapperjerk.com;
access_log /home/jason/public_html/blog/log/access.log;
error_log /home/jason/public_html/blog/log/error.log;
root /home/jason/public_html/blog/public/;
index index.html;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect false;
if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
if (!-f $request_filename) {
proxy_pass http://dapperjerk;
break;
}
}
}
And that’s really all there is too it. I’m not a masochist so I’ve never bothered to fully read Apache’s documentation, but I don’t think I’m going out on a limb here by saying that it seems to be a lot easier to manage nginx. I haven’t really put thin through its paces yet, but we’ll see in the coming weeks as I cobble together a custom blog app to run this place.
Runt - SVG & Rails
While developing Runt over the past few months it became obvious mid-way through development that the integration of graphing was somewhat of an afterthought. That’s okay though because we’re agile, right?
Graphing SNMP data in Rails applications (or any other application for that matter) is difficult for a number of reasons:
- What the hell do I graph?
- SNMP data is so varied in scope that we need to narrow our scope in order to graph something useful. We don’t want to graph sysName against sysDescr because that’s retarded and completely useless.
- Graphs obviously need need be composed of numerical data, so any SNMP data composed of strings is likely going to be useless.
- Right, so what kind of data types should we be looking at (assuming we’re working with protocol version 2c)?
- Integer32/Integer (Enumerated)/Unsigned32/Gauge32/Counter32/TimeTicks/Opaque/Bits
Tomato generates its graphs in between AJAX requests and renders them to the user using SVG. Unfortunately, browser SVG support is patchy for Intel Mac users with Safari 2.0.4 (even though SVG support landed in WebKit landed over two years ago). Adobe have offered a plugin for SVG support in Safari (which is going to be unsupported next year), but only for users running Safari on PowerPC Macs. As an Intel user you could of course run Safari under Rosetta to enable the plugin to function but jesus don’t. Thankfully SVG support will finally find its way into Safari 3.0 in Leopard, but that’s several months away. IE6/7 users enjoy SVG support through the same Adobe plugin, while Gecko based browser users see SVG built-in.
With this in mind, and due to the fact I’m a selfish jerk who uses Safari, I decided against using SVG in Runt, at least for the time being. My options for graphing in Rails boiled down to three contenders:
- Gruff - pretty graphs that are rendered as PNGs. Rails plugin.
- Scruffy - pretty graphs, internally rendered using SVG and it can spit out your graphs in pretty much any format imaginable. Unfortunately it hasn’t been updated in over 9 months.
- ZiYa - pretty Flash graphs. Flash, ugh.
I eventually rolled with Gruff since I found it the easiest to get to grips with and because it somehow felt more Rails like than the others. I was seriously considering ZiYA for several weeks but considering how much I’ve come to hate almost all implementations of Flash – hay guys lets totally offload the rendering of this website to some proprietary plugin! This is a great idea, right guys! Guys?! – I decided it was best to roll with something I could stomach myself using. Unfortunately, this meant that interactivity with the graph was going to be seriously limited as Gruff was just going to throw rendered PNGs at a user with a rather horrible rendering delay between AJAX requests. This is something that has bothered me ever since I chose to use Gruff, but what’s done is done.
The next iteration of Runt will see me revisit the idea of using SVG rendering because it’s sexy as hell and because, for the time being at least, I’ve begun using Firefox 2.0 regularly once more.
Runt - almost ready for release
For the past few months at college I’ve been developing a web-based Rails application called Runt; Ruby Network Tool. It’s a small application which displays nicely formatted, up to the second SNMP data from pretty much any device attached to your network. If you’re after specific data from specific devices, you simply load a custom MIB for that device and Runt can get data from it. It features a sort of tacked on ability to graph interface traffic for a given device also, but it really doesn’t feel all that at home in the application itself at the moment for a variety of reasons. I’d like to expand upon this a lot with later releases, but I find myself hindered with current browser SVG support (at the moment RUNT uses Gruff to generate static PNG graphs each pass).
The response from the demonstrations I’ve done over the past few months has been encouraging but the codebase remains a bit too clumsy for my liking to release it to the general public, at least not yet anyway. It had been my first foray into the world of both Ruby and Rails so there’s a lot of cruft which needs reworking.
The version which I’ll release will be v0.1, and I have a lot of other functionality planned for v0.2. One of the biggest problems I encountered while developing the application was finding myself constantly hindered by the very nature of web-based applications - they sort of need interaction before they’ll do things. As a result, v0.2 will see a lot of functionality handled by a Ruby daemon (using BackgrounDRb) such as graphing and some cool alerting functionality through SMS should certain user specified conditions arise like a device fails to respond to SNMP requests.
One thing I will say about this learning experience is that I’m quickly understanding why so many developers are using both Rails and Ruby for development, regardless of of their rather sluggish performance in comparison to say Django and Python. There’s just something about blocks like
foo.collect {|bar| b.to_i/5*8}
which makes me consider how amazingly eloquent Ruby really is. I’m having a blast with this stuff.
Here’s some screenshots:



