Unicorn is a traditional UNIX(which makes great use of Unix) prefork web server.No threads are used at all, this makes applications easier to debug and fix.
Deploying
With Unicorn one can deploy with zero downtime. From Unicorn documetation:
You can upgrade Unicorn, your entire application, libraries and even your Ruby interpreter without dropping clients.
Rails on Unicorns
Now we start setting up rails using ngnix with unicorn.
Installing Nginx
To install Nginx web server on our ubuntu server:
$ sudo apt-get install nginx
We could start the server with the nginx command but the installer also installs an init.d file that we can use to manage our nginx server as well.
$ /etc/init.d/nginx -h
Usage: nginx {start|stop|restart|reload|force-reload|status|configtest}
We can run thisinit.d
command directly or we can interact with it through the service command and we'll use this to start it up.
$ sudo service nginx start
Starting nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful nginx.
Configuring nginx to run on a port => 80
All main configuration file reside at /etc/nginx/nginx.conf
and by default it looks like this:
$ cat /etc/nginx/nginx.conf
user nobody nogroup; # for systems with a "nogroup"
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
# multi_accept on;
}
http {
include /etc/nginx/mime.types;
access_log /var/log/nginx/access.log;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
tcp_nodelay on;
gzip on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
In nginx.conf
you may have stumbled upon this line:
user nobody nogroup; # for systems with a "nogroup"
Its generally adviced to run as a seperate user for security reasons and increased control. We’ll create an nginx user and a web group.
$ sudo useradd -s /sbin/nologin -r deploy
$ sudo usermod -a -G deploy deploy
Configure your static path in nginx.conf
to /var/www
, and change the owner of that directory to the web group:
$ sudo mkdir /var/www
$ sudo chgrp -R deploy /var/www # set /var/www owner group to "deploy"
$ sudo chmod -R 775 /var/www # group write permission
To avoid permission issues we're going to make Nginx run as a deployer, the user that's going to deploy the application code, this way we can rest safely that no “permission denied†on files will ever happen (at least as long as we don’t deploy as another user).
Now change this default nginx.conf
file like this:
user deploy deploy;
worker_processes 1;
worker_priority -5;
timer_resolution 100ms;
error_log /var/log/nginx/error.log;
# To enable to open as many files as possible within a single nginx worker process without crashing increase default 1024 value to some higher value
worker_rlimit_nofile 30240;
events {
use epoll;
worker_connections 10240;
}
http {
client_header_buffer_size 1460;
client_body_timeout 1460;
client_max_body_size 25m;
client_body_buffer_size 128k;
client_body_temp_path /tmp/client_body_temp;
large_client_header_buffers 8 8m;
include mime.types;
default_type application/octet-stream;
keepalive_timeout 1300;
gzip on;
gzip_http_version 1.1;
gzip_disable "msie6";
gzip_vary on;
gzip_min_length 500; #Compress everything no matter the size
gzip_buffers 64 8k;
gzip_comp_level 3;
gzip_proxied any;
gzip_types text/plain text/css application/x-javascript text/xml application/xml;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
proxy_buffering on;
proxy_buffers 8 16k;
proxy_buffer_size 32k;
# For better performance we use Linux sendfile():
sendfile on;
# To send all response headers in one packet. This allows a client to start rendering content immediately after the first packet arrives:
tcp_nopush on;
tcp_nodelay off;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
To run a single application in this server, we can go on and edit the /etc/nginx/sites-available/default
file directly, if you are going to host more than one application in this server you should create separate files for each of them and then symlink them to the /etc/nginx/sites-enabled/
folder.
Add a new file unicorn_rails_app.conf
in the site-available directory and copy and paste the following into it:
upstream unicorn_rails_app {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response (in case the Unicorn master nukes a
# single worker for timing out).
# for UNIX domain socket setups:
server unix:/var/www/rails_app/current/tmp/unicorn.rails_app.sock fail_timeout=0;
}
server {
# if you're running multiple servers, instead of "default" you should
# put your main domain name here
listen 80 default;
# you could put a list of other domain names this application answers
server_name your.domain_name.com;
# path for static files
root /var/www/rails_app/current/public;
access_log /var/log/nginx/rails_app_access.log;
location / {
proxy_pass http://unicorn_rails_app;
proxy_redirect off;
proxy_set_header Host $host:$proxy_port;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 4m;
# proxy_ignore_client_abort on;
}
# if the request is for a static resource, nginx should serve it directly
# and add a far future expires header to it, making the browser
# cache the resource and navigate faster over the website
location ~ ^/(images|javascripts|stylesheets|system)/ {
root /var/www/rails_app/current/public;
add_header Last-Modified "";
add_header ETag "";
expires max;
break;
}
# Rails error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /path/to/app/current/public;
}
}
After creating this new file unicorn_rails_app.conf
, we need to symlink it to the sites-enabled directory:
$ sudo ln -s /etc/nginx/sites-available/unicorn_rails_app.conf /etc/nginx/sites-enabled/unicorn_rails_app.conf
Unicorn setup
Now we have nginx running. Install the Unicorn gem:
$ gem install unicorn
Add the unicorn gem to your Rails application's Gemfile
Gemfile
source 'https://rubygems.org'
gem 'rails', '3.2.12'
gem 'unicorn'
Create the Unicorn configuration file in your Rails application config/unicorn.rb
(copy the below lines):
$ vim config/unicorn.rb
# Unicorn configuration file to be running by unicorn_init.sh with capistrano task
# read an example configuration before: http://unicorn.bogomips.org/examples/unicorn.conf.rb
#
# working_directory, pid, paths - internal Unicorn variables must to setup
# worker_process 4 - is good enough for serve small production application
# timeout 30 - time limit when unresponded workers to restart
# preload_app true - the most interesting option that confuse a lot of us,
# just setup is as true always, it means extra work on
# deployment scripts to make it correctly
# BUNDLE_GEMFILE - make Gemfile accessible with new master
# before_fork, after_fork - reconnect to all dependent services: DB, Redis, Sphinx etc.
# deal with old_pid only if CPU or RAM are limited enough
#
# config/server/production/unicorn.rb
app_path = "/var/www/rails_app/current"
working_directory "#{app_path}"
pid "#{app_path}/tmp/pids/unicorn.pid"
stderr_path "#{app_path}/log/unicorn.log"
stdout_path "#{app_path}/log/unicorn.log"
listen "#{app_path}/tmp/unicorn.rails_app.sock" , :backlog => 64
user 'deploy','deploy'
#listen 80, :tcp_nopush => true
#listen 8080, :tcp_nopush => true
worker_processes 4
timeout 600
preload_app true
GC.respond_to?(:copy_on_write_friendly=) and
GC.copy_on_write_friendly = true
before_fork do |server, worker|
# the following is highly recomended for Rails + "preload_app true"
# as there's no need for the master process to hold a connection
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
end
after_fork do |server, worker|
# per-process listener ports for debugging/admin/migrations
# addr = "127.0.0.1:#{9293 + worker.nr}"
# server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
# the following is *required* for Rails + "preload_app true",
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
# if preload_app is true, then you may also want to check and
# restart any other shared sockets/descriptors such as Memcached,
# and Redis. TokyoCabinet file handles are safe to reuse
# between any number of forked children (assuming your kernel
# correctly implements pread()/pwrite() system calls)
end
Then Unicorn is configured!
Create a Unicorn initializer shell script in the /var/www/rails_app/current/config
directory to start|stop|restart unicorn processes:
$ touch /var/www/rails_app/current/config/unicorn
$ vim /var/www/rails_app/current/config/unicorn
Paste the following into that new file:
#! /bin/bash
### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the unicorn web server
# Description: starts unicorn
### END INIT INFO
APP_ROOT="/var/www/rails_app"
USER="deploy"
DAEMON=unicorn_rails
DAEMON_OPTS="-c $APP_ROOT/shared/config/unicorn.rb -E staging -D"
NAME=unicorn
DESC="Unicorn app for $USER"
PID=$APP_ROOT/shared/pids/unicorn.pid
case "$1" in
start)
CD_TO_APP_DIR="cd $APP_ROOT"
START_DAEMON_PROCESS="bundle exec $DAEMON $DAEMON_OPTS"
echo -n "Starting $DESC: "
if [ `whoami` = root ]; then
su - $USER -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS"
else
$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS
fi
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
kill -QUIT `cat $PID`
echo "$NAME."
;;
restart)
echo -n "Restarting $DESC: "
kill -USR2 `cat $PID`
echo "$NAME."
;;
reload)
echo -n "Reloading $DESC configuration: "
kill -HUP `cat $PID`
echo "$NAME."
;;
*)
echo "Usage: $NAME {start|stop|restart|reload}" >&2
exit 1
;;
esac
exit 0
Start nginx server
Start your nginx server from its installation directory:
$ /etc/init.d/nginx restart
Start Unicorn server
Start your Unicorn server from your Rails application directory, with no port specified (it will use the Unix socket from its configuration instead):
$ unicorn_rails -E production -D -c /var/www/rails_app/current/config/unicorn.rb
Voila here's your chocolate.
blog comments powered by Disqus
Vivek Parihar
About Me, Elsewhere
A Weboniser and Rubyist. Work on open source web development technologies (Ruby and their frameworks like Rails and Sinatra , PHP and its framework CAKE) and mobile technologies like Android. Deep passion for elegant design,cloud infrastructure(Rackspace and Amazon Web Services, AWS, S3, EC2) and their automation, distributed applications development, systems scaling, agile development methodologies, and experimental technologies. Frequent speaker at conferences. Like to hack on d3.js(data visulaization). Hobby boxing, cricket and tracking.