Wednesday, June 15, 2011

Setting up a Thin-cluster with Nginx

Introduction

This guide describes how to properly setup a Ruby on Rails environment for Ubuntu 10.4
The guide assumes that you are logged in as a user with sudo access and have Ubuntu 10.4 LTS installed as a bare minimum. Everything else that is required for the installation of Ruby on Rails will be covered in this guide.
Note that the guide relies on RVM, which needs to be installed by every user manually. That's why developers should have sudo access.

Installation steps

The steps below need to be followed in the correct order to ensure that the environment is correct and the same for all development, production and test systems.

Installing MySQL

One of the most common databases beeing used by a Ruby on Rails application is MySQL. To install MySQL and have proper support for it in Ruby on Rails, we need to install the server software and include the development headers.
To install the server software and the development headers, run the following command: sudo apt-get install mysql-server libmysqlclient-dev
After installing the software, the package configuration wizard will start up and request the password for root access to the mysql server. Because we wish all servers to have the same configuration, the password for root access is Tenforcesql

Installing RVM

RVM is a special Ruby Version Management system that allows you to run several Ruby on Rails & Rubygems configuration on the same system without affecting one another. This is really handy when you have legacy Ruby on Rails projects that you need to maintain alongside fresh Ruby on Rails projects. Installing RVM is rather simple when following this guide.

sudo apt-get install curl
sudo apt-get install git-core gitsosis
bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
echo ' -s "$HOME/.rvm/scripts/rvm" ? && . "$HOME/.rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile
source .bash_profile


NOTE: to copy the above code, please enter edit mode. The wiki markup destroys the correct syntax because it tries to create a link out of it.
NOTE: This has to be only once...

You can now test if RVM is properly installed by typing rvm notes from the console command line.
More details on how to switch ruby versions can be found on the RVM installation page : https://rvm.beginrescueend.com/rvm/install/
Because we're working OS specific with RVM the following dependencies need to be installed as well:

apt-get install build-essential bison openssl libreadline6 libreadline6-dev zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev

Installing Ruby

Ruby on Rails needs to be installed using the apt-get command. Because Rails is a framework written in Ruby, the programming language Ruby needs to be installed first.
The guide assumes ruby 1.8 to be installed
This can be done by running the following command: rvm install 1.8.7
Because we wish all users to have the same default, we can run the following command : rvm use 1.8.7 --default

Installing Rails

NOTE: This step is only required on a development system. On test and production the gem management is done using the builder gem implemented in Rails 3.
The rails framework is what allows all the magic for webdevelopment. To install the rails framework and the correct rubygems we need to set the environment to the correct versions. Run the commands below in sequence to ensure that each server is running the correct version of the Ruby on Rails framework.

rvm rubygems current
rvm 1.8.7
gem install rails
rdoc-data
rdoc-data --install
gem rdoc --all --overwrite


Because we have installed the MySQL Server software, we need to enable support for it in Ruby on Rails. Because the mysql2 gem is developed head of the official Rails release versions, it's very important that the correct version is installed:
  • Rails version < 3.1.X : gem install mysql2 -v 0.2.7
  • Rails version >= 3.1.X : gem install mysql2
After running these commands, you have the Ruby on Rails framework installing with the basic gems and support. However you might receive alot of errors regarding out of date functions and invalid parameter calls, but this is a problem of the rubygems themselves and their developers not implementing the correct calls in regards to the Rails versions that is installed on your system. If you wish to get rid of these warnings you can do so by running the command gem pristine, which restores the gems in pristine condition but removes all changes made to them.

Installing support gems for the most common tasks

Below is a complete list of gem commands that can be used to install various additional gems that are used in the most common operations of Ruby on Rails websites. Look at the websites of each rubygem for more specific information on their use:
  • gem install haml sass hpricot will_paginate httparty json json_pure rack ruby-debug
Because the environmnet is controlled by RVM, these gem steps need to be repeated for every version installation done with RVM, but only once. After switching an environment, all installed gems are present.

What is nginx?

Nginx (pronounced as Engine X) is a lightweight webserver that can be used as reverse proxy, load balancer or static file server for Ruby on Rails application or similar projects. The nginx webserver can run both on Windows as well as on any Linux environment and comes in both 32bit and 64bit versions to fullfill the needs of the server requirements it's beeing deployed on.
Nginx shines in that it's extremely low on resources and takes about 5 minutes to get it up and running as a full fledged webserver/load balancer.

Installing nginx on Ubuntu >= 10.4

To install nginx on ubuntu servers, follow the commands below to install the application in the default location, which is /etc/nginx

sudo -s
nginx=stable # use nginx=development for latest development version
add-apt-repository ppa:nginx/$nginx
apt-get update
apt-get install nginx

if you encounter an error, make sure you have all the python tools installed in order to compile the application.

Configuring nginx as reverse proxy & load balancer

Below is a smallscript that allows nginx to listen on port 80 and handle incomming requests. All incoming requests are directed to a small cluster of webservers running on the same machine. It's possible to offload these requests to different machines as well by simply changing the IP addresses of the server in the configuration file. Note that everything is case sensitivie and needs to be indented properly.
The script below is the genral configuration of nginx. Not much needs to be changed here.
Ofcourse these settings can be altered to meet the requirements of your own application. To support multiple configurations, it's recommneded to save your settings in a seperate file inside the /etc/nginx/conf.d folder instead of manipulating the general nginx.conf file.

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

events {
worker_connections 64;
# multi_accept on;
}

http {

##
# Basic Settings
##

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;

# server_names_hash_bucket_size 64;
# server_name_in_redirect off;

include /etc/nginx/mime.types;
default_type application/octet-stream;

##
# Logging Settings
##

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

##
# Gzip Settings
##

gzip on;
gzip_disable "msie6";

# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

The script below gives the configuration that was used for the EscoMatching demo. The configuration assumes that 4 Thin webservers are running on the same machine using ports 4000 -> 4003 and have their application located at /var/www/apps/current.
Ofcourse you need to change the paths and ports depending on the configuration of your own server, but the global settings are probably very similar.

upstream thin {
server 127.0.0.1:4000;
server 127.0.0.1:4001;
server 127.0.0.1:4002;
server 127.0.0.1:4003;
}

server {
listen 2006;
server_name escomatch.tst;
root /var/www/apps/current/public;
index index.html index.html;

try_files $uri/index.html $uri.html $uri @thin;

location ~*\.(jpeg|jpg|gif|png|ico|css|bmp|js)$ {
root /var/www/apps/current/public;
}

location @thin {
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 off;
proxy_pass http://thin;
}
error_page 500 502 503 504 /50x.html;

location = /50x.html {
root html;
}
}