Setup NGINX, passenger and Jetty on OpenBSD

Background

My current requirements on the web server stack is to support a few Ruby On Rails applications (this wiki is one of them) with moderate traffic and some Java web applications. I currently use a single web server server behind a dedicated firewall. I also have a backup web server which may be deployed shortly if something goes wrong with the primary server. All servers run some recent version of OpenBSD.

For a long time I have used lighttpd as front end with Fast CGI connections to the Ruby On Rails applications behind it. I have realized that RoR applications aren't multi threaded i.e. requests must be completed one at a time by a single loaded RoR application. Because of this a number of architectures has been introduced to handle this situation. Many setups include a single web server front-end (acting as a load balancer) with a set of RoR applications servers each capable of handling requests from the front-end. Usually a separate database server is used to serve all application servers. The most common RoR application servers are based on Mongrel which is a web server in it self. Several mongrel servers can be deployed on the same server hardware using mongrel_cluster.

To get rid of the singlethreadedness of my setup to improve the response time from multiple users I have searched for a more modern solution. The general goals of my new setup has been.

After some googling I have come up with a solution using NGINX webserver as front end with a mod_rails/Phusion Passenger as back-end RoR application server. Phusion Passenger is a fairly new solution which load a number of RoR environments dynamically depending on load. It is also known to be rather simple to setup. This seems to suit my needs, perfectly.

Prerequisities

As I have a full Ruby On Rails setup with lighttpd I have the most basic components already running. Some things that are already setup are;

Compile NGINX with passenger support

NGINX does not support loadable modules as e.g. Apache does, therefore one must compile NGINX with mod_rails as a statically linked module. There are several (3) ways to do this as described in the Phusion Passenger users guide Nginx version. I first went for the automatic installation method which automatically download source for nginx and pcre, configures the compile, compiles and lastly install nginx with passenger support. This is probably the recommended way to do it.

Install passenger gem and run installer. It will guide you through the complete installation process.

gem install passenger
passenger-install-nginx-module

Because I wanted to use the pcre library supplied by OpenBSD, I then went for manual compilation of nginx.

Install pcre from OpenBSD packages, download and compile NGINX with passenger support;

pkg_add pcre
wget http://sysoev.ru/nginx/nginx-0.7.61.tar.gz
cd nginx-0.7.61
./configure
--prefix='/usr/local/share/nginx-nginx-0.7.61' 
--conf-path=/etc/nginx/nginx.conf
--sbin-path=/usr/local/sbin/nginx
--pid-path=/var/run/nginx.pid
--http-fastcgi-temp-path=/var/tmp/fastcgi_tmp          
--http-proxy-temp-path=/var/tmp/proxy_tmp
--http-client-body-temp-path=/var/tmp/client_body_temp
--http-log-path=/var/log/nginx.log                     
--error-log-path=/var/log/nginx-error.log
--user=www                                             
--group=www                               
--with-http_ssl_module
--with-http_stub_status_module
--add-module='/usr/local/lib/ruby/gems/1.8/gems/passenger-2.2.4/ext/nginx'
make
make install

Corresponding for OpenBSD 4.7

pkg_add pcre
wget http://nginx.org/download/nginx-0.7.67.tar.gz
cd nginx-0.7.67
./configure \
--prefix='/usr/local/share/nginx-0.7.67' \
--conf-path=/etc/nginx/nginx.conf \
--sbin-path=/usr/local/sbin/nginx \
--pid-path=/var/run/nginx.pid \
--lock-path=/usr/local/share/nginx-0.7.67/tmp/nginx.lock \
--http-proxy-temp-path=/var/tmp/proxy_temp \
--http-client-body-temp-path=/var/tmp/client_body_temp \
--http-fastcgi-temp-path=/var/tmp/fastcgi_temp \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log \
--user=www \
--group=www \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-mail \
--with-mail_ssl_module \
--with-ipv6  \
--add-module='/usr/local/lib/ruby/gems/1.8/gems/passenger-2.2.15/ext/nginx'
make
make install

Note that you must replace the line feeds of the configure script with a single line to run directly in shell.

The different options supplied to configure is suitable for OpenBSD. Some modules may not be needed for a minimal nginx/passenger setup. Check out the OpenBSD port makefile for nginx, /usr/ports/www/nginx/Makefile, for suitable defaults in OpenBSD. See also for NGINX on OpenBSD for some more information. Note also hat the last line of the configure script includes passenger as an module to nginx.

When compiled nginx, must be configured appropriertly to your environment.

Configure NGINX for passenger

How to configure nginx with passenger is explained thorougly in the Phusion Passenger users guide Nginx version. Basically to setup a new virtual host it requires an additional server directive in your /etc/nginx/nginx.conf file.

server {
    listen                  80;
    server_name             localhost www.lounge.se;

    root                    /var/www/pages/rails/wiki/public;
    passenger_enabled       on;
    rails_env               production;
}

Also general passenger has to be added to the http directive.

passenger_root      /usr/local/lib/ruby/gems/1.8/gems/passenger-2.2.4;
passenger_ruby      /usr/local/bin/ruby;

# Optional passenger configuration
passenger_log_level         0;
passenger_default_user      www;
rails_spawn_method          smart;
passenger_pool_idle_time    86400;

See full example of nginx.conf.

I also made made sure that my application is owned by user www which both nginx and passenger is run as.

chown -R www /var/www/pages/rails/wiki

This is about it. Run nginx and open a browser to check the application out.

nginx
lynx www.lounge.se

Patch passenger to handle ruby bug for OpenBSD/amd64

Initially I didn't get anything to work when running towards my rails applications. After a lot googling and browsing in forums I realized that this is due to a ruby bug for the amd64 architecture. This is not specific for OpenBSD but also existing in MacOS X and FreeBSD. A patch of passenger to handle this situation has been committed (here, by Bernd Ahlers OpenBSD ports maintainer of ruby) but has not made it yet to the official passenger gem.

I needed to apply the patch myself to make everything to work. So if you run OpenBSD with amd64 architecture check the following file and see if the patch above has been committed in your version of the gem, otherwize you need to perform the patch manually.

nano /usr/local/lib/ruby/gems/1.8/gems/passenger-2.2.4/lib/phusion_passenger/utils.rb

Setup nginx with Jetty

Because of licensing issues of Sun's Java version below 1.7, the jdk has to be compiled for OpenBSD. A prebuild package is available for jdk 1.7, but as this is a preliminary version I prefer to use some stable release of jdk 1.5. To build the jdk in OpenBSD is not very hard but takes some time.

This is the way I compiled jdk 1.5.

Install Jetty. TBD.

Configure nginx for Jetty. TBD.

Ruby On Rails is multi threaded from version 2.2

Ruby On Rails actually are multi threaded from version 2.2 of the framework. See RoR 2.2 release notes for more information. This may be used by your web server stack but I haven't dug into this regarding my mod_rails based stack.

Benchmarking

References