16
Oct

Automatic Rails on Ubuntu 8.04 LTS

A couple of weeks ago there was a post on the FiveRuns blog about automatically installing the Rails stack on an Ubuntu 8.04 VPS.

I prefer to use Passenger and Ruby Enterprise Edition when running my Rails app, so inspired by the FiveRuns script I wrote my own version - here is the gist on github.


#!/bin/bash
# Inspired by http://blog.fiveruns.com/2008/9/24/rails-automation-at-slicehost

apt-get update
apt-get upgrade -y
apt-get -y install build-essential libssl-dev libreadline5-dev zlib1g-dev
apt-get -y install mysql-server libmysqlclient15-dev mysql-client
apt-get -y install ruby ruby1.8-dev irb ri rdoc libopenssl-ruby1.8

RUBYGEMS=”rubygems-1.3.0″
wget http://rubyforge.org/frs/download.php/43985/$RUBYGEMS.tgz
tar xzf $RUBYGEMS.tgz
cd $RUBYGEMS
ruby setup.rb
cd ..

# Install Ruby Enterprise Edition
wget http://rubyforge.org/frs/download.php/41040/ruby-enterprise-1.8.6-20080810.tar.gz
tar xvzf ruby-enterprise-1.8.6-20080810.tar.gz
yes ” | ./ruby-enterprise-1.8.6-20080810/installer

# Install Passenger
/usr/bin/gem1.8 install -v=2.0.3 passenger –no-rdoc –no-ri
apt-get -y install apache2-mpm-prefork apache2-prefork-dev
yes ” | passenger-install-apache2-module

# Create sample Rails app
/usr/bin/gem1.8 install rails –no-rdoc –no-ri
cd /var/www
rails -d mysql hello
cd hello
./script/generate controller welcome hello
echo “Hello World” > app/views/welcome/hello.html.erb
rake db:create RAILS_ENV=production

# Create the Apache2 Passenger module files
cat >> /etc/apache2/mods-available/passenger.load <> /etc/apache2/mods-available/passenger.conf <<-EOF

PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3
PassengerRuby /opt/ruby-enterprise-1.8.6-20080810/bin/ruby

EOF
a2enmod passenger

# Create a site file for the sample Rails app
IP_ADDRESS=`ifconfig eth0 | sed -n ’s/.*dr:\(.*\) Bc.*/\1/p’`
cat >> /etc/apache2/sites-available/hello <<-EOF

ServerName www.yourhost.com
DocumentRoot /var/www/hello/public

EOF
a2ensite hello

# That’s it!
reboot

The script assumes that you have ssh access as root to a clean Ubuntu 8.04 install.

The script will install

  • Ruby 1.8.6
  • RubyGems 1.3.0
  • Passenger 2.0.3
  • Ruby Enterprise Edition 20080810
  • Apache 2.2.8
  • MySQL 5.0.51a
  • A sample Rails app

Note that the Passenger installer will install the latest Rails (2.1.1) and a bunch of other useful gems.

Assuming that your server IP address is 192.168.185.128 you can run it like this:

ssh root@192.168.185.128 “wget -O - http://gist.github.com/raw/16225/a6a16b3a38cd3486679b96fa0f3446e58f3b8423 | sed -e s/$’\r’//g > install.sh; /bin/bash install.sh; rm install.sh”

Sit back and enjoy - in less than ten minutes you will have the full Rails stack and a sample Rails app running. Take a look at it on http://192.168.185.128/welcome/hello

02
Apr

Ruby Fools presentation slides

Today I gave a presentation at the Ruby Fools Copenhagen 2008 Conference.

The presentation was about adding full text search to a Rails app.

Here is a pdf with my presentation:

Adding Full Text Search to Your Rails App

The conference was arranged by the same crew doing the JAOO conference and most (all?) presentations were recorded on video. When the videos are available online I will post a link.

17
Feb

Benchmarking fun with JRuby 1.1 RC2, glassfish, and Rails 2.0.2

Yesterday JRuby 1.1 RC2 was released and two days ago the glassfish gem v 0.1.1 was released. Lots of interesting stuff happening in JRuby land!

I decided to take JRuby and the glassfish gem for a spin with a simple Rails application.
Installing JRuby

First step was to download and install JRuby. This is pretty straightforward:
cd /tmp
wget http://dist.codehaus.org/jruby/jruby-src-1.1RC2.tar.gz
tar xvzf jruby-src-1.1RC2.tar.gz
cd jruby-1.1RC2/
ant
export JRUBY_HOME=`pwd`
export PATH=$JRUBY_HOME/bin:$PATH
jruby –version
ruby 1.8.6 (2008-02-17 rev 5944) [i386-jruby1.1RC2]

Yep, seems to work.

Installing gems

Next step was to install the Rails and glassfish gems:
unset GEM_HOME
unset GEM_PATH
gem install rails
gem install glassfish

Creating a Rails application

On to the Rails application… I used scaffold to have a simple application up and running quickly:
cd ..
rails glassfishtest –database=mysql
cd glassfishtest/
export RAILS_ENV=production
rake db:sessions:create
script/generate scaffold Book title:string
rake db:create
rake db:migrate
script/runner “Book.create(:title => ‘JRuby Rocks’)”

I use the database session store, so I added this line to the config/environment.rb file
config.action_controller.session_store = :active_record_store

Firing up glassfish

Let’s fire up the glassfish server:
cd ..
glassfish_rails glassfishtest -n 2

The -n 2 option will make glassfish start 2 Rails instances.

Benchmark fun!

Glassfish
I used the ab command to perform some simple benchmarks.
Each ab command was run twice with a freshly started glassfish server. The first run warms up the JIT in the JVM. The results listed below are for the second run (and the fifth run for some). All benchmarks were performed on my 2.33GHz MacBook Pro running Leopard 10.5.2 with Java version 1.5.0_13-b05-237.

The performance with respect to static files is impressive:
ab -n 5000 -c 10 http://localhost:3000/
Requests per second: 2705.63 [#/sec] (mean)

Now onto a page created by Rails:
ab -n 1000 -c 8 http://localhost:3000/books/1
Requests per second: 54.10 [#/sec] (mean)

JRuby can be tweaked a little bit with the -server parameter:
JAVA_OPTS="-server" glassfish_rails glassfishtest -n 2
ab -n 1000 -c 8 http://localhost:3000/books/1
Requests per second: 53.82 [#/sec] (mean) 2nd run
Requests per second: 63.06 [#/sec] (mean) 5th run

After a little warmup the performance is approximately 20% better than without the -server option.

Let’s try adding more Rails instances:
JAVA_OPTS="-server" glassfish_rails glassfishtest -n 4
Requests per second: 50.71 [#/sec] (mean) 2nd run
Requests per second: 60.69 [#/sec] (mean) 5th run

On my dual core machine this actually degrades performance a little bit. I guess it is a good idea to have the number of Rails instances match the number of cores in your server.

But what about one Rails instance:
JAVA_OPTS="-server" glassfish_rails glassfishtest -n 1
Requests per second: 31.56 [#/sec] (mean) 2nd run
Requests per second: 34.48 [#/sec] (mean) 5th run

That hurts!

Mongrel

How does mongrel compare to glassfish?
Single Mongrel - JRuby
JAVA_OPTS='-server' jruby script/server -e production
Requests per second: 54.99 [#/sec] (mean) 2nd run
Requests per second: 63.20 [#/sec] (mean) 5th run

Two Mongrels behind pen - JRuby
Requests per second: 58.39 [#/sec] 2nd run(mean)
Requests per second: 69.16 [#/sec] (mean) 10th run

Static files:
Requests per second: 313.57 [#/sec] (mean)

Mongrel and the glassfish server have comparable performance with respect to Rails generated pages.
With respect to serving static files, glassfish outperforms Mongrel significantly. That said, you shouldn’t really let Mongrel serve static content - it is better to leave that to nginx or Apache.

Mongrel - MRI

What is the performance when using MRI?
Single Mongrel - MRI
Requests per second: 120.79 [#/sec] (mean)

Two Mongrels behind pen - MRI
Requests per second: 123.42 [#/sec] (mean)

The MRI Mongrel seems to have a lot better performance for this (admittedly simple) benchmark.

Conclusion

With respect to ease of running a server the JRuby/glassfish combo is very appealing:

  • static files are served very fast
  • no need for a separate load balancer
  • the whole thing is started with just one command

For this particular Rails application benchmark, the performance of the JRuby stack is only half of the performance of MRI, which is kind of sad. I am pretty sure that this is not the case for all Rails applications. In fact, evidence from Mingle seems to indicate that JRuby is faster than MRI. So I guess the best thing is to try it out on your own Rails app - and please blog about your findings. If you decide to benchmark your own Rails app I highly recommend this peepcode screencast about benchmarking.

28
Dec

My first Rails Contribution

Yeah! I am a Rails contributor!

In an application at work we are using a Rails REST application for the backend of the application and another Rails application as the frontend. The frontend application does not use the database at all but only the REST api provided by the backend application.

When sending lots of data between the two applications serializing to and from XML turned out to be a performance bottleneck. We turned to JSON and this improved performance significantly.

The JSON support in ActiveResource was added recently and there are still some areas where XML is better supported than JSON. So I submitted a patch to improve the JSON support. The patch got submitted to trunk in this changeset.

It feels really good to contribute back to Rails when Rails have brought me so many hours of joy :-)

06
Nov

Experimenting with Amazon S3 EU edition

Today Amazon announced the availability of S3 in Europe.

Nice! Let’s play with it! Please notice that I am located in Denmark and that all tests were performed on my 2048/512 ADSL line.

Download the new version of Amazon S3 Authentication Tool for Curl

Unzip it and create an .s3curl file containing you AWS keys as described in the readme file.

Now let’s create some buckets - a US bucket and an EU bucket:

s3curl.pl –id personal –createBucket — http://s3.amazonaws.com/erichsen.net.us
s3curl.pl –id personal –createBucket=EU — http://s3.amazonaws.com/erichsen.net.eu

Fetch some test files a 50K file and a 10MB one:

wget ftp://ftptest1.tele.dk/pub/50Ktest.rnd
wget ftp://ftptest1.tele.dk/pub/10Mtestb.rnd

And upload them:

s3curl.pl –id=personal –acl public-read –put 10Mtestb.rnd — http://erichsen.net.us.s3.amazonaws.com/10Mtestb.rnd
s3curl.pl –id=personal –acl public-read –put 50Ktest.rnd — http://erichsen.net.us.s3.amazonaws.com/50Ktest.rnd
s3curl.pl –id=personal –acl public-read –put 10Mtestb.rnd — http://erichsen.net.eu.s3.amazonaws.com/10Mtestb.rnd
s3curl.pl –id=personal –acl public-read –put 50Ktest.rnd — http://erichsen.net.eu.s3.amazonaws.com/50Ktest.rnd

Try fetching the large file from the US bucket a couple of times

ab -n 1 http://erichsen.net.us.s3.amazonaws.com/10Mtestb.rnd

Time taken for tests: 50.325 seconds

Transfer rate: 208.37 [Kbytes/sec] received

ab -n 1 http://erichsen.net.us.s3.amazonaws.com/10Mtestb.rnd

Time taken for tests: 48.351 seconds

Transfer rate: 216.87 [Kbytes/sec] received
And the EU bucket

ab -n 1 http://erichsen.net.eu.s3.amazonaws.com/10Mtestb.rnd

Time taken for tests: 47.907 seconds

Transfer rate: 218.88 [Kbytes/sec] received

ab -n 1 http://erichsen.net.eu.s3.amazonaws.com/10Mtestb.rnd

Time taken for tests: 50.943 seconds

Transfer rate: 205.84 [Kbytes/sec] received
With respect to transfer rate they seem to perform about the same from my local machine’s point of view. But I guess that this is what is to expect. The EU bucket should give better response times and for large files the response times are only a small fraction of the total transfer time.

But what about the small file?

US bucket

ab -n 50 http://erichsen.net.us.s3.amazonaws.com/50Ktest.rnd

Time taken for tests: 60.308 seconds

Time per request: 1206.16 [ms] (mean)

EU bucket

ab -n 50 http://erichsen.net.eu.s3.amazonaws.com/50Ktest.rnd

Time taken for tests: 26.676 seconds

Time per request: 533.52 [ms] (mean)

Now we’re talking!

Summary: For large files you could just as well use the US variant of S3. If you use S3 for serving the static files of your web site and most of your visitors come from Europe switching to the EU S3 should give your users significantly better load times.

04
Nov

Creating an Amazon EC2 Ubuntu 6.06 LTS server edition image

Last week I decided to try out Amazon EC2 mainly for running and testing Rails applications and so far it has been great fun!

This blog posting describes how I created an Ubuntu 6.06 LTS server edition image for use with Amazon EC2.

Download and install the EC2 command line tools

curl -O http://s3.amazonaws.com/ec2-downloads/ec2-api-tools.zip
mkdir ~/.ec2
cd ~/.ec2
unzip ec2-api-tools.zip
ln -s ec2-api-tools-1.2-13740 ec2-api-tools

Set the environment variables necessary to run the tools

export EC2_HOME=~/.ec2/ec2-api-tools
export PATH=$PATH:$EC2_HOME/bin
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home/

Download your private key and certificate from your Amazon Web Services account to the ~/.ec2 folder.

Generate a key pair

export EC2_PRIVATE_KEY=~/.ec2/pk-8WU9XGOPO65IKA7O96M2KEKVOS5288KU.pem
export EC2_CERT=~/.ec2/cert-8WU9XGOPO65IKA7O96M2KEKVOS5288KU.pem
mkdir ~/.ec2
ec2-add-keypair gsg-keypair > ~/.ec2/id_rsa-gsg-keypair
chmod 600 ~..ec2/id_rsa-gsg-keypair

Launch a Fedora Core 4: Base instance.

ec2-run-instances ami-20b65349 -k gsg-keypair

This returns an instance number like i-9536dcfc.

Try running

ec2-describe-instances i-9536dcfc

until the status returned is no longer ‘pending’ but ‘running’.

Allow ssh access and log in

ec2-authorize default -p 22
ssh -i ~/.ec2/id_rsa-gsg-keypair root@ec2-67-202-21-218.compute-1.amazonaws.com

On the EC2 instance run

wget http://erichsen.net/blog/fc4-base
chmod 755 fc4-base
./fc4-base

fc4-base is a script found in this forum posting. I adapted it to create an Ubuntu 6.06 image instead of 6.10.

After the script has finished execution copy the private key and certificate from the local machine to the EC2 instance

scp -i ~/.ec2/id_rsa-gsg-keypair ~/.ec2/pk-8WU9XGOPO65IKA7O96M2KEKVOS5288KU.pem root@ec2-67-202-21-218.compute-1.amazonaws.com:/root/
scp -i ~/.ec2/id_rsa-gsg-keypair ~/.ec2/cert-8WU9XGOPO65IKA7O96M2KEKVOS5288KU.pem root@ec2-67-202-21-218.compute-1.amazonaws.com:/root/

Create an image and sign it with the private key

ec2-bundle-image -i /mnt/ubuntu606base.img -k /root/pk-8WU9XGOPO65IKA7O96M2KEKVOS5288KU.pem -c cert-8WU9XGOPO65IKA7O96M2KEKVOS5288KU.pem -u ‘5171-9220-6573′

The image must be stored on S3 so I create a bucket (from my local Mac)

sudo gem i aws-s3 -y
export AMAZON_ACCESS_KEY_ID=”1IQ8AHOAWNRDMQOI91ZK”
export AMAZON_SECRET_ACCESS_KEY=”VCddmbA9C4D8w/mw6aLZjzCkMoEyx5EUvouJdY/4″
s3sh
Bucket.create(’erichsen.net’)

On the EC2 instance I upload the image

ec2-upload-bundle -b erichsen.net -m /tmp/ubuntu606base.img.manifest.xml -a ‘1IQ8AHOAWNRDMQOI91ZK’ -s ‘VCddmbA9C4D8w/mw6aLZjzCkMoEyx5EUvouJdY/4′

From my local Mac I register the instance and that’s it!

ec2-register erichsen.net/ubuntu606base.img.manifest.xml

The ec2-register returns an AMI id - in this case ami-4acd2823. Let’s try it out

ec2-run-instances ami-4acd2823 -k gsg-keypair
ec2-describe-instances i-9536dcfc
ssh -i ~/.ec2/id_rsa-gsg-keypair root@ec2-67-202-24-151.compute-1.amazonaws.com

YES! I was able to ssh into an EC2 instance running my Ubuntu 6.06 image! Note that the ssh configuration of the image is not the best - it allows root logins which is not in general a good idea.

Hope this helps someone else wanting to play with Ubuntu on EC2.

31
Oct

My favourite Rails stack

Operating System

Boy, things have come a long way since I first installed SLS Linux on my pc using floppy discs back in 1994 :-)
These days I prefer Ubuntu 6.06 LTS server edition. It is supported until 2011 - nice that you don’t have to reinstall the server in a year or two. Furthermore, deprec makes it extremely simple to setup a Rails stack on an Ubuntu server.

Webserver

nginx. Nginx is fast, stable, lightweight and has easy configuration.

Load balancer

Usuyally I use the one built in to nginx.
Alternatives: Pen or HAProxy.

Rails-server

Well, Mongrel of course. In a cluster handled by Mongrel cluster.

Database

MySQL. It’s the database used by most Rails applications. Furthermore, it has some nice scaling support with master-slave setups. A couple of years ago I used and liked PostgreSQL a lot. I still prefer PostgreSQL’s query analyzer to MySQL’s.

27
Oct

My Rails presentations

A list of the presentations I have given in aarhus.rb (my local Ruby Brigade which I co-founded).

27
Oct

tv2.dk traffic costs

In an earlier posting we saw that tv2.dk in August 2007 served 142.132.680 pages and that the average page size was 395K and the average number of requests per page was 54.

How much traffic is this?

Doing some math it turns out that approximately 54 TB traffic and 7.7 billion requests during August! That’s a lot of traffic!

Jay.net in their Grand National package say that 33000 GB traffic/month will set you back 10000 kr. which is approximately 1900$. Note that it is 33000 GB Danish traffic and that you have to pay for international traffic as well. According to Alexa 87% of the tv2.dk’s traffic comes from Denmark. My guess is that tv2.dk’s 54 TB traffic in the jay.net setup will cost them around 4000$.

What if the site was hosted on Amazon EC2/S3?

One option is to let Amazon S3 serve all static content. Using the AWS Simple Monthly Calculator the traffic is going to cost 8,699.44$ for the traffic itself and a request fee of 7,675.17$ totalling 16,374.61$! Letting the content from S3 removes the need for a dedicated server to serve static content but the request fee makes it rather costly.

Another option is to have a dedicated EC2 server for serving static content. The traffic is going to cost 8,699.44$ and the server itself 74.40$.

So jay.net turns out to be much cheaper than the Amazon offer for a high-traffic site like tv2.dk.

24
Oct

tv2.dk on Rails on Amazon EC2

What if tv2.dk (one of the most popular Danish sites) was running Rails and ran on Amazon EC2? This posting is a followup to this posting

Let’s assume that we on average during 24 hours have 50 requests/sec. The traffic during the 24 hours could for example be distributed like this

Hours Traffic
00 - 08 5 requests/sec
08 - 11 30 requests/sec
11 - 12 150 requests/sec
12 - 14 250 requests/sec
14 - 15 150 requests/sec
15 - 24 30 requests/sec

This works out to be 50 requests/sec on average. As we saw in the tv2.dk on Rails blog posting each quad core server will get you 80 requests/sec. Unfortunately we have to have servers that have enough power to satisfy the peak number of requests and not the average number of requests. In this example we need four quad core servers in order to be able to serve the 250 requests/sec needed from 12-14 (actually they are able to serve 320 requests/sec). So on average we need one server but in the peak hours we need four servers.

What if we were able to adjust the number of servers dynamically to satisfy the current load on the website?

Amazon EC2 to the rescue! Amazon EC2 is a service that allows you to run one or many virtual servers and you pay by the hour for each virtual server. So we can start and stop a number of virtual machines depending on the load on the website. Each EC2 instance has one (virtual) cpu core so with 10 mongrels doing 2 requests/sec it should be able to handle 20 requests/sec.
So how many servers do we need to run?

Hours Traffic # of servers
00 - 08 5 requests/sec 1
08 - 11 30 requests/sec 2
11 - 12 150 requests/sec 8
12 - 14 250 requests/sec 13
14 - 15 150 requests/sec 8
15 - 24 30 requests/sec 2

If we calculate the number of “server hours” it is 74. How much is this going to cost? Using the AWS Simple Monthly Calculator it turns out to cost 222$ for a 30 day month. Not too bad! Of course we have to add a server for static content (or use Amazon S3) as well as a couple of DB servers. With a small instance server for static content and two large instances (virtual quad cores with 7.5 GB ram) the monthly bill goes up to 811$ - still not too bad.

But you also have to pay for traffic. I will cover that in another blog posting.