Installing Apache 2.2 with mod_rewrite and mod_proxy_balancer

As mentioned in my post, Configuring Apache to work with a Mongrel Cluster, you need an Apache installation with mod_rewrite and mod_proxy_balancer. Unfortunately on the Red Hat installation I was using the default installation is Apache 2.0 which does not have mod_proxy_balancer. When I tried to upgrade it using up2date I got some errors that seemed to indicate this install was not properly registered. I then tried using the Apache 2.2 install done by the tech guy but this install did not have the modules I needed.

Therefore it was time for me to do the install by myself. Fortunately it was not hard with the help of this article, Building Apache 2.2 plus the manual.

Here is what I did.

$ wget http://apache.mirror99.com/httpd/httpd-2.2.8.tar.gz
$ tar xvfz httpd-2.2.8.tar.gz
$ cd httpd-2.2.8
$ ./configure --enable-rewrite=shared --enable-proxy=shared \
    --enable-proxy_balancer=shared --enable-proxy_http=shared
$ make
$ sudo make install
$ sudo /usr/local/apache2/bin/apachectl start

Configuring Apache to work with a Mongrel Cluster

Previously I wrote about Configuring Capistrano and Mongrel. Now we are going to configure Apache to work with the Mongrel cluster.

For this article I used the chapter Setting Up A Development Environment in Agile Web Development with Rails, version 2.0, plus this article.

Capistrano working together with Mongrel allows you to deploy and restart Mongrel clusters quite nicely.

Configure Apache proxy balancer

Apache has a mod_proxy_balancer module which must be enabled. Once this is done you can add the following to the end of conf/httpd.conf or if you are on Red Hat Linux you can put the proxy balancing section in /etc/httpd/conf.d/myapp.proxy_cluster.conf and the virtual host section in /etc/httpd/conf.d/myapp.conf.

<Proxy balancer://mongrel_cluster>
  BalancerMember http://127.0.0.1:8000
  BalancerMember http://127.0.0.1:8001
  BalancerMember http://127.0.0.1:8002
</Proxy>

<VirtualHost *:80>
  Include conf/myapp.common (or Include conf.d/myapp.common)
  ErrorLog logs/myapp_errors_log
  CustomLog logs/myapp_log combined
</VirtualHost>

Configure Apache Virtual Host

Next you configure the virtual host that represents the Ruby on Rails application in the custom file conf/myapp.common or if you are on Red Hat Linux in /etc/httpd/conf.d/myapp.common.

ServerName myapp.com
DocumentRoot /usr/local/rails/myapp/current/public

<Directory "/usr/local/rails/myapp/current/public">
  Options FollowSymLinks
  AllowOverride None
  Order allow,deny
  Allow from all
</Directory>

RewriteEngine On

# Uncomment for rewrite debugging
#RewriteLog logs/myapp_rewrite_log
#RewriteLogLevel 9 

# Check for maintenance file and redirect all requests
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /system/maintenance.html [L]

# Rewrite index to check for static
RewriteRule ^/$ /index.html [QSA] 

# Rewrite to check for Rails cached page
RewriteRule ^([^.]+)$ $1.html [QSA]

# Redirect all non-static requests to cluster
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]

Restart Apache

$ sudo /usr/local/apache2/bin/httpd -k restart

or

$ sudo /etc/init.d/httpd restart

Difference between require and require_dependency

I was wondering what the difference was between require and require_dependency.

According to this post in comp.lang.ruby the difference is not significant but interesting. Looks like I’ll be using require_dependency from now on.

require loads a file (shared object or ruby source) once from the load path.

require_dependency isn’t a ruby core feature, it’s part of rails. It remembers the given file and loads it on each new server request (like load “path/file.rb” does), if development mode is enabled:

http://wiki.rubyonrails.com/rails/pages/RequireDependency


Florian Frank

Configuring Capistrano and Mongrel

For this article I used the chapter Setting Up A Development Environment in Agile Web Development with Rails, version 2.0, plus these two articles.

Capistrano working together with Mongrel allows you to deploy and restart Mongrel clusters quite nicely.

Configure Mongrel Cluster

First you need to configure a Mongrel cluster. Here is an example that creates three Mongrel instances starting at port 8000, listening on the local interface, 127.0.0.1.

$  mongrel_rails cluster::configure -N 3 -p 8000 -e production -a 127.0.0.1 \
   -c /usr/local/rails/production/current \
   -C /usr/local/rail/production/current/config/mongrel_cluster.yml

Here is how the created file looks like.

---
cwd: /usr/local/rails/production/current
log_file: log/mongrel.log
port: "8000"
environment: production
address: 127.0.0.1
pid_file: tmp/pids/mongrel.pid
servers: 3

Note that for testing purposes you should comment out the address line.

#address: 127.0.0.1

We specified listening only on the local interface for security purposes but for testing the cluster we need to access it directly via its remote IP address.

Setup Capistrano

Next you create a stub Capfile and config/deploy.rb.

$ capify .

Now you can get a list of all the tasks that are available and there quite a few.

$ cap -T

Next modify config/deploy.rb like in this example.

require 'mongrel_cluster/recipes'

set :application, "foobook"
set :repository, "http://example.com/svn/foo/trunk/foobook"

role :web, "192.168.3.17"
role :app, "192.168.3.17"
role :db,  "192.168.3.117", :primary => true

set :deploy_to, "/usr/local/rails/production"
set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml"
set :svn_user, "fkim"
set :svn_password, "fkim"
#set :user, "root"            # defaults to the currently logged in user
set :scm, :subversion         # defaults to :subversion
set :svn, "/usr/bin/svn"      # defaults to searching the PATH

Deploy using Capistrano

Now run setup which will create the directories remotely.

$ cap deploy:setup

Check dependencies.

$ cap -q deploy:check

Deploy for the first time.

$ cap deploy:cold

Everytime after you can deploy like this.

$ cap deploy

Starting and Stopping Mongrel using Capistrano

If you want to just start the mongrel cluster.

$ cap mongrel:cluster:start

If you want to just stop the mongrel cluster.

$ cap mongrel:cluster:stop

And that’s it.

Not that bad at all. Starting and stopping mongrel was the biggest issue for me. I realized that I did not need to create the spin script as suggested in the Using Capistrano with Rails article. If the mongrel configuration is correct then Capistrano will correctly start and stop it and you can use the above cap commands to test it.

Installing Ruby on Rails on Red Hat Enterprise Linux 4

This post is based on instructions from these articles.

Determine what Ruby RPMs are installed:

$ rpm -qa | egrep '(ruby)|(irb)'
ruby-libs-1.8.1-7.EL4.2

Uninstall Ruby RPMs:

$ sudo rpm -e ruby-libs-1.8.1-7.EL4.2

Install Ruby from source:

$ wget ftp://ftp.ruby-lang.org/pub/ruby/ruby-1.8.6-p114.tar.gz$ tar xvfz ruby-1.8.6-p114.tar.gz
$ cd ruby-1.8.6-p114
$ ./configure --prefix=/usr (the default prefix is /usr/local)
$ make
$ make test
$ sudo make intall

Install Ruby Gems:

$ wget http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz
$ tar xvfz rubygems-1.0.1.tgz
$ cd rubygems-1.0.1
$ sudo ruby setup.rb

Install Rails:

$ sudo gem update
$ sudo gem update --system
$ sudo rm `gem env gempath`/source_cache
$ rm -f ~/.gem/source_cache
$ sudo gem update
$ sudo gem install rails -v 1.1.6
$ sudo gem install rails -v 1.2.6
$ sudo gem install rails (if you want the latest version, 2.0.2)

Install Capistrano:

$ sudo gem install capistrano

Install Mongrel:

$ sudo gem install mongrel
$ sudo gem install mongrel_cluster

Install Memcache Client:

$ sudo gem install memcache-client

You Used Ruby to Write WHAT?!

The brilliant Zed Shaw, author of Mongrel and many other open source projects, wrote a fantastic article called You Used Ruby to Write WHAT?! which is a part of series of acticles called You Used THAT Programming Language to Write WHAT?!

Some interesting quotes from the Ruby article.

Web programming, sometimes. Ruby on Rails (RoR) is potentially a huge cost-saving framework for developing Web applications. Complex applications are measured in thousands of lines of code rather than 10- or 100-thousands of lines.

The caveat on Ruby for Web programming is that Rails is better suited for building Web applications. I’ve seen many projects attempt to create a WebDAV server or a content management system (CMS) with Rails and fail miserably. While you can do a CMS in Rails, there are much more efficient technologies for the task, such as Drupal and Django. (In fact, I’d say if you’re looking at a Java Portal development effort, you should evaluate Drupal and Django for the task instead.)

Large data crunching. It’s sad to say, but Ruby (all versions worth using in production, and even Ruby 1.9) suffer from huge problems with large-scale garbage collection, I/O processing and thread operations. Ask anyone running a Rails application that accepts large file uploads; they’ll tell you it chews their CPU just to process the MIME boundaries in the request body. Ruby has had (and still has) frequent bugs and misfeatures in its garbage collector (GC) implementation that makes handling large chunks of data difficult. Yes, you can do it; and yes, being clever helps; but why bother, when you can just use a language without such problems?

Image manipulation. I don’t really think many languages are great at image manipulation, but Ruby is particularly bad at it. The primary libraries available are based on Image Magick, which is slow, bloated and takes forever to install on many systems. Ruby talks to Image Magick through RMagick, which suffers from memory leaks, spawns external processes silently for many operations and is difficult to install (unless your computer is nearly exactly the same as the author’s). Other libraries are no better. Either they don’t support many operations, or they also require a myriad of dependencies, build tools and other requirements just to do limited image manipulation.

Server protocols. Ruby’s problems with I/O processing means that writing truly scalable servers is a waste of time. Most of the people I know doing server work in Ruby eventually give up and either use a mix of Ruby and C or they switch to another language entirely.

With Ruby’s garbage collection problems, you’ll have issues with long-running processes, especially if they have to process large data streams and you’re not careful about how you deal with them. With Ruby’s Threads you’ll find many problems when you try to scale your application beyond the 1,024 open files Ruby can handle. Ruby’s Thread contains several technical flaws that make it unsuitable for really huge-scale operations. Ruby can handle large network traffic, but it takes more careful planning and programming than with many other languages.

Enterprise deployments. The “enterprise” hates Ruby, and this is mostly a social problem. The majority of corporate systems are based on either Java or C#, with no room for a rogue language like Ruby.

In fact, take a good hard look at many of the companies running large-scale systems. You’ll see Python at Google, PHP at Yahoo, Perl at Amazon, Java at eBay—but very little Ruby. This will change as Ruby on Rails becomes popular.

I discovered that most of the college students either were working with Ruby or planned to adopt it. Either that, or they used Java because their university required it. Very few were interested in languages like Python, Perl, Java or C, except when it was required or could get them a job. This has probably been the most overlooked “feature” of Ruby by most people advocating it: Young kids actually want to work for you if you build software in Ruby or Python, Erlang, Haskell or Lisp.

Upgrading MeetingKoreans.com to Rails 1.2.6

Today I updated MeetingKoreans.com to Rails 1.2.6 using these instructions and it was quite a frustrating experience. There were three problems that took up most of the time.

  1. No route found to match “/photo/photo/nnn.png”

After upgrading to Rails 1.2.6 I immediately noticed that I was having problems accessing user photos from the database. I am embarrassed to say that this has been a problem on production since February 23, 2007 but until today I had not been able to reproduce this. Obviously the issue was that on production my web host had upgraded Rails but in my development environment I had not.

Previously I was creating the image tag for user photos like this.

  <%= image_tag url_for(:controller => 'photo', :action => 'photo',                        :id => user.primary_photo.id) %>

This would result in HTML like this.

  <img src="/photo/photo/1.png">

This was not a problem before but after the Rails upgrade I would see this error.

  [ERROR] application#index (ActionController::RoutingError)  "no route found to match "/photo/photo/1.png" with {:method=>:get}"

Therefore I changed the RHTML code like this.

  <img src="<%= url_for(:controller => 'photo', :action => 'photo',                        :id => user.primary_photo.id) %>"

This would result in HTML like this.

  <img src="/photo/photo/1">

Fortunately this solved the problem.

  1. undefined method ‘model’ for ApplicationController:Class

When I first did the upgrade I did not do the final step.

  rake rails:update:configs

This did not seem to be a problem at first when I ran it into the development mode. But when I ran it in production I saw errors like this, apparently because it was trying to run the application as a Rails 2.0 application.

  Status: 500 Internal Server Error

  undefined method 'model' for ApplicationController:Class

   /meetingkoreans.com/app/controllers/application.rb:13

Fortunately after running the rake update these errors no longer appeared.

  1. undefined method ‘model’ for ApplicationController:Class

After solving the above problem I immediately started seeing these errors.

  Status: 500 Internal Server Error

  Expected /meetingkoreans.com/app/controllers/mk_controller.rb to define MkController

    /usr/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/

    dependencies.rb:249:in `load_missing_constant'

This one really puzzled me and as I searched around I learned that apparently many others were puzzled too. In this bug report, Expected x.rb to define X error, I found within the comments a work around from saizai that worked for me.

I suspect this setting in /config/environments/production.rb: config.action_controller.consider_all_requests_local = false

I changed this to ‘true’, restarted the server, and it worked. I then set it BACK to ‘false’, restarted… and it still worked.

So, I don’t really know for sure WHY it started working again or how this might be causing it, but hopefully it helps.

I have no idea why that worked but I am glad it did.

  1. Conclusion

I have been a web application programmer for over ten years and though I am still excited about Ruby on Rails I must admit this experience has lowered my opinion of it.  I have never seen upgrades to the platform cause applications to break so catastrophically like this and I have orchestrated migrations of major web applications before.  I shudder to think what the upgrade to Rails 2.0 will be like.

ruby: no such file to load — ubygems (LoadError)

Today when I started my Ruby on Rails application I received this error.

> ruby script/server
ruby: no such file to load -- ubygems (LoadError)

After Googling for this error message I realized somehow my Ruby gems installation was corrupted. I had been planning on upgrading my Ruby from 1.85 to 1.86-26 so now seemed as a good time as ever. 🙂

Fortunately after installing the latest version of Ruby and updating to Ruby on Rails 1.2.6 everything seemed fine.