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.
- Reuse existing hardware i.e. a single server to handle all web traffic.
- Reduce response time for moderate traffic from multiple concurrent users.
- The solution must require minimal maintenance.
- The solution must be fairly simple to setup.
- OpenBSD will be kept as OS.
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;
- OpenBSD 4.4 OS (later probably even better) with network access.
- MySQL 5.x
- ruby 1.8.6
- gem plus needed gems by my applications
- A simple text editor like
nano
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/apps/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/apps/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
- jaws - fast web server in erlang. Similar speed as NGINX. See Nginx vs Yaws vs MochiWeb : Web Server Performance Deathmatch, RESTful Services with Erlang and Yaws and Erlang, Yaws, and the deadly Tornado.