Rails on Red Hat Enterprise Linux HOWTO


Installation

First off, insure you have ruby installed. The version shipped with RHEL4 is 1.8.1. You need at least version 1.8.2 for rails to work properly. Ruby 1.8.4 was released in December of 2005. You can download the isos from centos’s testing repository for RHEL here:

wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-docs-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ri-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-libs-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-mode-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-tcltk-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/irb-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/rdoc-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-devel-1.8.4-1.c4.i386.rpm

Rails is so new, and cutting-edge, that rpms do not yet exist for rubygems. I did find one RPM that Mandrake had managed to produce, however, neither the infinitely awesome dag nor the regularly awesome pbone archives had produced one for RHEL yet. So, you have do it the old school way and install from source. Dennis Ritchie would be proud. I downloaded the latest version from rubyforge using the venerable wget.

% wget http://rubyforge.org/frs/download.php/5207/rubygems-0.8.11.tgz
% tar zxf rubygems-0.8.11.tgz
% cd rubygems-0.8.11
% sudo ruby setup.rb


Installing Gems

Next, you’ll need to install rails, and the MySQL bindings from gem. Make sure you have mysql-devel-4.1.12-3.RHEL4.1 installed from disc 4 before doing this.

% rpm -q mysql-devel
mysql-devel-4.1.12-3.RHEL4.1
% sudo gem install rails
% sudo gem install mysql -- --with-mysql-include=/usr/include/mysql\
       --with-mysql-lib=/usr/lib/mysql --with-mysql-config

There are a lot of very useful packages that are available through the gems. So, as you need helpers and additional packages, you can install them through the gem interface. It’s worked great for me so far.

Question: Doesn’t ActiveRecord include the native drivers for MySQL?
Answer: Absolutely not. If you don’t compile these, rails will throw an error when trying to connect to MySQL


SQLite

I updated dag’s spec file and compiled the sqlite-3.3.5 source into a RHEL4 RPM. You’ll need to install these and the sqlite gem. If you still need sqlite < 3, don’t install these as I didn’t update the name from sqlite to sqlite3.

% wget http://rpm.involution.com/sqlite3/sqlite-3.3.5-1.el4.i386.rpm
% wget http://rpm.involution.com/sqlite3/sqlite-devel-3.3.5-1.el4.i386.rpm
% sudo rpm -ivh sqlite-*
% sudo gem install sqlite3-ruby


Installing FastCGI

I compiled fastcgi from source which I hate doing. I always choose RPM over a source install, but at the time I couldn’t find any spec files or rpms for FastCGI. To compile mod_fastcgi, you’ll need httpd-devel which requires pcre-devel, apr-devel, and apr-util-devel as dependencies.

% wget http://www.fastcgi.com/dist/fcgi-2.4.0.tar.gz
% cd fcgi-2.4.0
% ./configure
% make
% sudo make install

% wget http://www.fastcgi.com/dist/mod_fastcgi-2.4.2.tar.gz
% cd mod_fastcgi-2.4.2
% rpm -q httpd-devel
% httpd-devel-2.0.52-22.ent
% cp Makefile.AP2 Makefile
% perl -p -i -e 's,top_dir.*,top_dir      = /usr/lib/httpd,' Makefile
% make
% sudo make install
% mkdir /tmp/fcgi_ipc/
% sudo chown apache.apache /tmp/fcgi_ipc/
% sudo service httpd restart


Apache Configuration for Pound and mongrel_cluster

Apache is configured as passing back all requests to the Pound load-balancer as follows (assuming Pound is running on port 4000).

<VirtualHost *:80>
        ServerName              rails.involution.com
        ProxyRequests           Off
        ProxyPreserveHost       On
        RewriteEngine           On
        RewriteRule             ^/(.*) http://127.0.0.1:4000/$1 [P,L]
        ProxyPassReverse        / http://127.0.0.1:4000/
        ErrorLog logs/error_log
        CustomLog logs/access_log combined
</VirtualHost>

Then, I invoked pound at start-up in /etc/rc.local. This should eventually be jiggered into a run control script in /etc/init.d.

/usr/local/sbin/pound -f /etc/pound.cfg


Performance Tuning FastCGI

Most people run Rails under FastCGI in “Static Mode” for performance reasons. Static mode implies that there are some number of static FastCGI processes running on your server at all times. This prevents the overhead of process invocation every time your infinitely popular Rails application is loaded by your cadre of users. Here is what I put in /etc/httpd.conf to make FastCGI run in static mode.

<IfModule mod_fastcgi.c>
    FastCgiIpcDir /tmp/fcgi_ipc/
    FastCgiServer /path/to/rails/public/dispatch.fcgi -appConnTimeout 0 -processes 2 -listen-queue-depth 100 -flush
    FastCgiConfig -maxProcesses 50 -minProcesses 6 -appConnTimeout 0
    AddHandler fastcgi-script .fcgi

</IfModule>


Trying it Out

% rails hoyhoy
rails hoyhoy
     create
     create  app/controllers
     ...
     config/database.yml
     ...

I’m a super permissions nerd, and I cringe everytime I see anyone execute a recursive 777 chmod. Also, I will admit to having turned off SELinux on my server because if I had to type “chcon -R user_u:object_r:httpd_user_content_t .” one more time, I would have stopped using computers and started a new career at subsistence farming. Sorry, I’m getting off topic here. Here is the RIGHT way to set rails permission in RHEL. Note, I’m RHCE certified, so, I have a piece of paper that looks nice in a frame, and it gives me credentials to feel like I’m an authority.

% sudo chgrp -R apache hoyhoy/
% chmod -R g+r hoyhoy/
% chmod -R g+w hoyhoy/log
% find hoyhoy/ -type d chmod g+x {} \;
% chcon -R user_u:object_r:httpd_user_content_t hoyhoy/

That may seem a bit tedious, but it’s the most secure and sane way that I’ve found to set permissions. You can point an Apache Virtualhost entry directly to that directory, and it should work like the clappers. You only need the chcon command if you still have SELinux enabled (it’s on by default). You may also be wondering about the find command. I do that to only set the execute bit of directories. Do not set the execute bit on everything. It’s a bad idea because it opens up potential security holes. The risk is slight, however, there’s no point in granting a permission on something that it doesn’t require. You wouldn’t give your toddler a key to the gun cabinet would you?


Lighttpd

I ended up installing Lighttpd as well because one of the people using my server requested it. I got the RPMs from Dag’s infinitely excellent RPM repository.

wget http://apt.sw.be/redhat/el4/en/i386/RPMS.dag/lighttpd-1.3.16-1.2.el4.rf.i386.rpm
wget http://apt.sw.be/redhat/el4/en/i386/RPMS.dag/lighttpd-fastcgi-1.3.16-1.2.el4.rf.i386.rpm
wget http://apt.sw.be/redhat/el4/en/i386/RPMS.dag/lighttpd-mod_mysql_vhost-1.3.16-1.2.el4.rf.i386.rpm
rpm -ivh lighttpd*.rpm

Then, I setup some lighttpd VirtualHosts Proxies on the same Apache server that I was running the pure Apache+FastCGI code on. I actually even ran the same instance of Typo twice one on Lighttpd and another on Apache+FastCGI.

<VirtualHost *:80>
        ServerName              typo-l.involution.com:80
        ProxyRequests           Off
        ProxyPreserveHost       On
        RewriteEngine           On
        RewriteRule             ^/(.*) http://127.0.0.1:3334/$1 [P,L]
        ProxyPassReverse        / http://127.0.0.1:3334/
        ErrorLog logs/error_log
        CustomLog logs/access_log combined
</VirtualHost>


Troubleshooting

The most trouble I had was with database settings. Quadruple check the database.yml file and try out the settings on the mysql command line. The other trick you can do is execute the disptach.cgi or dispatch.fcgi directly on the command line to see if the ruby environment is invocated correctly. This will verify about 95% of most of the problems you could potentially have.

Here’s another tip, if you’re getting an httpd error like:

Error 500: Internal Server Error

Or some weird message from FastCGI like

[client 127.0.0.1] FastCGI: incomplete headers (35 bytes) received from server". 

Check your config/database.yml. Insure that the database it’s pointing to has been created, and the user referenced can both read and write to all of the databases referenced. I have screwed this up at least 10 times, and I’ve been running a web server since 1996. There’s no shame in messing it up, however, there is shame in immediately blaming rails, your operating system, Apache, ruby, or Dave Thomas if you haven’t prevericated your database settings. Let me reiterate, check your database.yml! You’ve been warned.

Also, insure that mysql is actually running.


% /sbin/service mysqld status
mysqld (pid 19253) is running...

The rails and httpd error logs are also very valuable guides when you’re getting strange errors. On RHEL, the httpd error log is by default here (unless you’ve overridden it):

/var/log/httpd/error_log

If you don’t create your /tmp/fcgi_ipc directory, you’ll see something like this in your error_log:

[Fri May 05 18:03:15 2006] [crit] (13)Permission denied: FastCGI: can't create server

Another problem you can encounter is directory permissions. If you install your rails app in a user directory, then the apache userid needs to be able to chdir to that directory. So, if your $HOME is go-wrx, this will case FastCGI to error out with something like this:

FastCGI: can't start server "/home/user/rails/public/dispatch.fcgi" (pid 15396), chdir() failed: Permission denied

To fix this, change your permissions like this for $HOME.

sudo chgrp apache ~user
chmod g+rx ~user

Yet another problem that can happen is getting the dreaded “Internal Server Error: 500″. This happens a lot with Mongrel especially if you forget to set the mode to production when a database isn’t defined. I hit this running Instiki, and even with Mongrel in debug mode, it gave no indication of what the problem was in any of the log files. This is also true from Apache and Lighttpd as well. If you see this error from your app, check the database settings.

% mongrel_rails start -e production


Rails 1.1.2

I was running Ruby 1.8.3 which was incompatible with Rails 1.1.2. So, I updated with the CentOS RPMs….

wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-docs-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ri-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-libs-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-mode-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-tcltk-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/irb-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/rdoc-1.8.4-1.c4.i386.rpm
wget http://dev.centos.org/centos/4/testing/i386/RPMS/ruby-devel-1.8.4-1.c4.i386.rpm
rpm -Uvh *.rpm

Then, I did the normal dance.

sudo gem update --system
sudo gem update rake
sudo gem update rails

For some strange reason, it made me update rake before updating Rails. Weird.


Mongrel

sudo gem install mongrel
cd ~/railsapp
mongrel_rails start -p 4000

I hacked Apache to Proxy out the request as follows:

ProxyPass / http://localhost:4000
ProxyPassReverse / http://localhost:4000


Pound and Mongrel Cluster

I setup Pound 2.1 so I could run a proxy balanced mongrel cluster under Apache. This allows concurrent application threads to process requests and prevents against any single Rails application process failing.

First, I downloaded and compiled Pound.

% wget http://www.apsis.ch/pound/Pound-2.1.tgz
% cd Pound-2.1
% ./configure
% make
% sudo make install

Then, I made a Pound configuration file.

User "apache"

Group "apache"
ListenHTTP
  Address 0.0.0.0
  Port 4000
  Service
    BackEnd
      Address 127.0.0.1
      Port 8000
    End
    BackEnd
      Address 127.0.0.1
      Port 8001
    End
    BackEnd
      Address 127.0.0.1
      Port 8002
    End
  End
End

Next, I installed and configure mongrel_cluster, and invoked Pound.

% sudo gem install mongrel_cluster
% cd /home/user/railsapp
% mongrel_rails cluster::configure -p 8000 -N 3 -e production \
  -l /home/user/railsapp/mongrel.log -m /etc/mime.yaml -u apache -g apache
% mongrel_rails cluster::start
% pound -f /etc/pound.cfg

Since I’m running Apache on port 80, I had to then proxy all connections to my Rails application’s VirtualHost name to the Pound proxy.

<VirtualHost *:80>
        ServerName              ninjarailsapp.com:80
        ProxyRequests           Off
        ProxyPreserveHost       On
        RewriteEngine           On
        RewriteRule             ^/(.*) http://127.0.0.1:4000/$1 [P,L]
        ProxyPassReverse        / http://127.0.0.1:4000/
        ErrorLog logs/ninja_error_log
        CustomLog logs/ninja_access_log combined

</VirtualHost>

So, my setup conceptually looks like this.


Troubleshooting Pound

Make sure that pound has permission to write into /var/run or you will get an error like this in /var/log/messages.

pound: Create "/var/run/pound.pid": Permission denied

Either run pound as root or set the User and Group in your configuration file. Pound does not display an error to stdout. So, if it fails, look for these types of errors in your syslog messages if you cannot otherwise connect to the port that you intended to start it on.


Apache 2.2 Configuration for mod_proxy_balancer and mongrel_cluster

I moved to using Apache 2.2’s mod_proxy_balancer instead of Pound. I installed mod_proxy and mod_proxy_balancer and then configured my VirtualHost as follows.

<VirtualHost *:80>
  ServerName rails.involution.com
  ProxyRequests Off
  ProxyPreserveHost On
  RewriteEngine On
  RewriteRule ^/(.*) balancer://mybalancer/$1 [P,QSA,L]
  ProxyPass / balancer://mybalancer
  ProxyPassReverse      / balancer://mybalancer
  ErrorLog logs/error_log
  CustomLog logs/access_log combined
  <Proxy balancer://mybalancer>
	BalancerMember http://127.0.0.1:8000
	BalancerMember http://127.0.0.1:8001
	BalancerMember http://127.0.0.1:8002
  </Proxy>
</VirtualHost>
Comments

Leave a response

  1. blog.ariffic » links for 2007-03-20 Tue, 20 Mar 2007 18:25:41 MDT

    [...] Involution ยป Rails on Red Hat Enterprise Linux HOWTO Covers Apache, Rails, Mongrel, Pound, Ruby… pretty much everything except what I need, unfortunately. (tags: howto linux ruby rubyonrails) [...]

Comments