Archive for October, 2009

Installing Varnish with nginx, Passenger, and Monit on Ubuntu 8.10 intrepid

Posted by Trevor in Ruby/Rails on October 22, 2009

Varnish is a state-of-the-art, high-performance HTTP accelerator. I first came to know about it thanks to Heroku, where they use it to provide built-in HTTP Caching.

As their docs describe, using Varnish is easy:

# This tells the cache (Varnish) to serve the page for 300 seconds (5 minutes).
 
class MyController < ApplicationController
  def index
    response.headers['Cache-Control'] = 'public, max-age=300'
    render :text => "Rendered at #{Time.now}"
  end
end

Simply setting the "Cache-Control" header like so allows you to serve up a page extremely quickly because requests completely bypass your application logic, database, and all of that related overhead and read straight from the cache.

If you're serving up the same page to all visitors, then setting up Varnish HTTP caching is a no-brainer. If you're serving up pages that are mostly the same for all users but have a custom header or something, you can still take advantage of the caching speed boost if you're willing to investigate the ins and outs of ESI and/or serving partials with Javascript. If you're serving pages that are different for each user, then you're out of luck :)

So, using Varnish for HTTP acceleration is great, but unfortunately for me the version of Ubuntu (8.10 intrepid) that we're using has a painfully out of date package in aptitude. If you're already running Varnish, you can check to make sure you're using a relatively recent release by running varnishd -V. If you see anything less than 2.0.4, you should seriously consider upgrading.

Thanks to the helpful people on the Varnish mailing lists, I was able to get things up and running by doing the following:

Note that there’s a new version of Varnish out now. The 2.0.4 tag is still pretty good, but you should check all the available tags and decide if you’d rather use a newer version. If so, you’ll have to adjust the installation instructions found at the top of this post slightly.

Note that the .deb files generated by the “dpkg-buildpackage” command may be different from those generated at the time this post was written. The basic steps still apply, though.

apt-get update
apt-get install subversion autotools-dev automake1.9 libtool autoconf libncurses-dev xsltproc quilt
cd /tmp
svn co http://varnish.projects.linpro.no/svn/tags/varnish-2.0.4
cd varnish-2.0.4/varnish-cache
dpkg-buildpackage
cd ..
dpkg -i libvarnish1_2.0.4-6_i386.deb
dpkg -i varnish_2.0.4-6_i386.deb

You can then use /etc/init.d/varnish to stop/start/restart the service.

But, we're not done yet. Since it took me a while to get the whole app server stack configured, I thought it might help someone else out if I covered the rest of the steps it took to get Varnish working properly with my Ruby application.

You can get nginx installed with Ruby Enterprise Edition and Passenger by following the excellent documentation on the Phusion website.

I might suggest, however, doing the installation like this in order to get the latest (secure) version of nginx. Even before that, you'll probably want to install Ruby Enterprise Edition, which I like to do the old fashioned way.

You may also be interested in this example nginx init.d config, which allows you to use nginx as you would if installed from a package. Make sure to consider doing some kind of log rotation with this kind of setup as well.

...

Now that you're using the fantastic nginx web server, you'll want to configure it to listen to port 8080, so that Varnish can listen to port 80 and forward any requests that aren't in its cache to the backend server (nginx). I'll just post the complete config we're using, and you can pick out the relevant details for your case.

Some interesting things to note is that this setup is configured to use Sinatra, the lightweight Ruby web framework. If the application throws an exception, we're routing to the /error page. We're also using various other settings that you may or may not agree with. The important part with regard to Varnish and nginx cooperating, however, is that you set nginx to listen to port 8080 in your config file. My config is located in /opt/nginx/conf/nginx.conf:

worker_processes 1;
pid /var/run/nginx.pid;

events {
  worker_connections  1024;
}

http {
  access_log /var/log/nginx_access.log;
  error_log /var/log/nginx_error.log;
  passenger_root /opt/ruby-enterprise/lib/ruby/gems/1.8/gems/passenger-2.2.5;
  passenger_ruby /opt/ruby-enterprise/bin/ruby;
  passenger_max_pool_size 10;
  include mime.types;
  default_type application/octet-stream;
  sendfile on;
  tcp_nopush on;
  keepalive_timeout 65;
  gzip on;
  gzip_comp_level 2;
  gzip_buffers 16 8k;
  gzip_disable "MSIE [1-6]\.";
  gzip_proxied any;
  gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

  server {
    listen 8080; # the important part!
    root /var/www/example/current/public;
    passenger_enabled on;
    passenger_use_global_queue on;
    rack_env production; # use rails_env for a rails app
    # serve static files without running more rewrite tests
    if (-f $request_filename) {
      break;
    }
    # disable site via capistrano (cap deploy:web:disable)
    if (-f $document_root/system/maintenance.html) {
      rewrite ^(.*)$ /system/maintenance.html break;
    }
    # expires headers
    location ~* \.(ico|css|js|gif|jp?g|png)(\?[0-9]+)?$ {
      expires max;
      break;
    }
  }
}

Now, I'm fairly new to using Varnish, so perhaps someone can advise me about how to better configure things, but I'll cover how I did it. Varnish comes with a few example configs loaded up in /etc/default/varnish. I ended up using the following:

NFILES=131072
MEMLOCK=82000
INSTANCE=$(uname -n)
DAEMON_OPTS="-a :80 -f /etc/varnish/default.vcl -s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G"

You can review the examples provided and see how you want to go about it, though.

Once you're all set with the general config, you'll need to provide a VCL config. I'm putting ours in /etc/varnish/default.vcl and it looks like this:

backend default {
  .host = "127.0.0.1";
  .port = "8080";
}

# Warning: read the following before using this config:
# http://varnish.projects.linpro.no/wiki/VCLExampleCacheCookies

sub vcl_recv {
  unset req.http.cookie;
}

The first part tells Varnish to forward any requests that aren't in its cache to a webserver running on localhost port 8080. That's nginx!

The second part unsets any cookies that are sent along with any requests. You won't want to do this unless you're serving the same pages for all users for your entire application. If you aren't careful here, you can very easily end up serving pages meant for one user to another. Be careful!

In my case this works fine because we're serving a site with no user-specific actions or views. It's all public. You're case might be different. Perhaps you can cache things like static files (images, etc) or certain areas of your application. Maybe you have a CMS with an admin interface that can't be cached, but with publically viewable pages that could be cached. I dunno. In any case, you should definitely read more about this on the Varnish wiki.

Finally, since we're using Monit to monitor the health of our systems, I'll throw in an example config that covers SSH, nginx, and Varnish. This Monit config would email you using a Google Apps Domain if there was a problem. You probably wouldn't want to use this as-is, but it should serve as a decent starting point for you to create your own. We've got the config in /etc/monit/monitrc:

# Alerts
set daemon 120
set logfile syslog facility log_daemon
set mailserver smtp.gmail.com port 587
    username "noreply@example.com" password "sldkjkfdsj"
    using tlsv1
    with timeout 30 seconds
set alert tech@example.com with reminder on 30 cycles
set httpd port 2812
allow example:slkdjlskdjflskjd

# SSH
check process sshd with pidfile /var/run/sshd.pid
start program "/etc/init.d/ssh start"
stop program "/etc/init.d/ssh stop"
if failed port 22 protocol ssh then restarts
if 5 restarts within 5 cycles then timeout

# nginx
check process nginx with pidfile /var/run/nginx.pid
start program = "/etc/init.d/nginx start"
stop  program = "/etc/init.d/nginx stop"
if failed host 127.0.0.1 port 8080 then restart
if cpu is greater than 40% for 2 cycles then alert
if cpu > 60% for 5 cycles then restart
if 10 restarts within 10 cycles then timeout

# Varnish
check process varnish with pidfile /var/run/varnishd.pid
start program = "/etc/init.d/varnish start"
stop  program = "/etc/init.d/varnish stop"
if failed host 127.0.0.1 port 80 then restart
if cpu is greater than 40% for 2 cycles then alert
if cpu > 60% for 5 cycles then restart
if 10 restarts within 10 cycles then timeout

That's all folks!

Weekly Digest, 10-12-09

Posted by Weekly Digest in Weekly Digest on October 12, 2009

Monthly Digest? ;)

Pomodoro

Pomodoro Desktop is a desktop application for Time Management on your Mac OSX. It is a simple but effective way to manage your (coding) time, and it's based on the Pomodoro technique

Clean CSS

Optmize and Format your CSS

Hacker News | GitHub's Unicorn Setup

[Interesting comments as usual.]

Picnik

Photo editing the easy way, online in your browser. Picnik makes your photos fabulous with easy to use yet powerful editing tools. Tweak to your heart’s content, then get creative with oodles of effects, fonts, shapes, and frames.

Unicorn! - GitHub

We’ve been running Unicorn for more than a month. Time to talk about it.

Munin

Munin the monitoring tool surveys all your computers and remembers what it saw. It presents all the information in graphs through a web interface. Its emphasis is on plug and play capabilities. After completing a installation a high number of monitoring plugins will be playing with no more effort.

On Gem Forking

So, GitHub has recommended Gemcutter as an alternative to hosting gems on GitHub...

Cloudvox

API-driven phone calls, in minutes (call your code: AGI/AMI, HTTP, Asterisk-Java, Adhearsion). Cloudvox is the most practical, stable, open environment for API-driven phone calls.

Gem Building is Defunct

We will continue to serve old gems at http://gems.github.com/ for at least one year. [New gems can't be built, though.]

* is Unix

Ryan’s I like Unicorn because it’s Unix appears to have started a mini-meme of folks writing simple forking network servers in their language of choice. I’m really enjoying reading ‘em — they’re a sort of Rosetta Stone of network code...

Chargify

Chargify simplifies recurring billing for Web 2.0 and SaaS companies.

Everything I know about Open Source I learned from Indie Hip Hop

Slides from an Aloha on Rails presentation.

FlipClock

Get the FlipClock Widget for your Dashboard!

nginx and varnish on Mac OS X

Since I'm a happy user of the macports collection already anyway, I let it do the 'heavy lifting' of actually installing nginx and varnish. In addition I provided a launchd startup item for varnish and also added a host entry for wahlcomputer to enable virtual hosting for nginx and varnish.

atmos's rack_hoptoad

A gem that provides exception notifications to hoptoad as rack middleware.

Ubuntu Intrepid - adding an Nginx init script

...let's go ahead and create one for easy control of Nginx, and to ensure it restarts on a reboot.

Generate gem.yml and .gems for Rails

A rake task which will generate gem configuration files based on your “config.gem” specifications in Rails. This comes in handy when deploying to Engine Yard, or Heroku.

Custom Chef Recipes with Engine Yard Cloud

One of the power user features of Engine Yard Cloud is the ability to use custom Chef recipes to install or configure anything that can run on Gentoo Linux that we have not already automated as part of the platform. This allows for extensive customizations of your environments and empowers you to run virtually all custom software you might need.

SHJS

SHJS is a JavaScript program which highlights source code passages in HTML documents. Documents using SHJS are highlighted on the client side by the web browser.

Getting ready for CouchDB 0.10

I’ve setup a local copy of CouchDB, from the 0.10 branch, just to see if my application code could handle its awesome powers. Here are my two big takeaways...

Paperclip, S3 & Delayed Job in Rails

Here’s how I use Paperclip (with storage on S3) and delayed_job to process images after they’re uploaded in the background.

Ryan Singer of 37signals - UI Fundamentals for Programmers

I gave a talk on “UI Fundamentals for Programmers” at WindyCityRails in Chicago last month. The talks covered modeling, breaking apps into screens, visual techniques, flows, and a few coding tips. [This is a great talk. Highly recommended viewing.]

Source code beautifier / syntax highlighter – convert code snippets to HTML

hilite.me converts your code snippets into pretty-printed HTML format, easily embeddable into blog posts and websites.

Keeping controllers empty with faux attributes - Momoro Machine

The barely any code way... use a fake attribute. Define an attribute setter on User, and then just make a small form in the view to set it.

Build it

The good innovation -- the innovation that makes the world a better place and builds real wealth in society -- that stuff is done by radically self-reliant creators who get their hands dirty. Not talkers. Not dreamers. Builders.

ActionRails

At ActionRails, we offer a variety of services designed to push your Rails developers to the next level... Application Evaluation; Weekly Code Reviews; Developer Hotline.

And if only 1% of those people...

...he forgot there was a number lower than one percent...

Working hard is overrated

Much more important than working hard is knowing how to find the right thing to work on.

Nginx_As_a_Reverse_Proxy - Couchdb Wiki

Nginx can serve as a reverse proxy to CouchDB for scenarios such as URL rewriting, load-balancing, access restriction, etc.

S3Sync

This is a ruby program that easily transfers directories between a local
directory and an S3 bucket:prefix. It behaves somewhat, but not precisely, like the rsync program. [I can verify that this is an awesome little tool.]

Full Stack ETag Support

The conclusion that I draw is that I definitely want to use Rack::Cache—100% improvement over reassembling the HTML on each request is too good to pass up. As for the 20% speed boost that full stack ETag buys me, I am not sure that the complexity that is introduced warrants the speed boost.

Browser Cache Usage Exposed

...reducing the number of HTTP requests has the biggest impact on reducing response time...

Place Pages for Google Maps

A Place Page is a webpage for every place in the world, organizing all the relevant information about it. By every place, we really mean *every* place — there are Place Pages for businesses, points of interest, transit stations, neighborhoods, landmarks and cities all over the world.

couchapp

CouchApp is designed to structure standalone CouchDB application development for maximum application portability. CouchApp is a set of scripts and a jQuery plugin designed to bring clarity and order to the freedom of CouchDB's document-based approach.

nginx, worker_processes, and passenger_max_pool_size

The Phusion Passenger pool size is independent from the number of
Nginx worker processes that you have. Setting the number of Nginx workers to the number of CPU cores should be sufficient.

Sinatra: Writing Extensions

Sinatra includes an API for extension authors to help ensure that consistent behavior is provided for application developers.

How Should Mac Apps Be Distributed?

...but to make this work for apps delivered by disk image, users have to understand that they must copy the app from the image to their startup drive. This is where some get lost.

Sammy.js, CouchDB, and the new web architecture

Welcome to the new world. HTTP Databases and JSON Storage. The simple act of making the database and the browser more powerful on either end has destroyed the need for the middle tier.

GitHub is Moving to Rackspace!

In just a few short weeks we will be moving GitHub to a new home at Rackspace. We’re aware of the current stability and performance issues, and we want to let you know what we’re doing about it.

hurl

Hurl makes HTTP requests. Enter a URL, set some headers, then view the response. Perfect for APIs.