Ruby Rescue uses unicorn for all of our deployed Ruby applications. Chris Wanstrath promoted it in a blog post on github, and we decided soon after to give it a shot. Because it’s based on mongrel, it takes a reliable and well-tested app server and bolts a ruby layer on top that handles restarts, adding and removing workers, and uses unix sockets to wait for requests.
There’s a good article by Ryan Tomayko on Unicorn and the reasons why it’s “Unixyness” is a feature. If you’re a long-time unix user, you won’t find much to be revolutionary. If you’re a web-developer without a CS-background, he covers how unix fundamentals make Unicorn more reliable.
Before Unicorn we were running Passenger and apache, and we decided to switch to unicorn and nginx at the same time. It takes a bit more work to setup unicorn than passenger, but we’ve found the reliability to be worth it.
- gem install unicorn
- create a unicorn.rb file like this
rails_env = ENV['RAILS_ENV'] || 'production'
worker_processes 3
preload_app true
timeout 75
socket_path = '/var/www/APPLICATION/shared/sockets/unicorn.sock'
pid_path = '/var/www/APPLICATION/shared/pids/unicorn.pid'
listen socket_path, :backlog => 2048
pid pid_path
# http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
if GC.respond_to?(:copy_on_write_friendly=)
GC.copy_on_write_friendly = true
end
before_fork do |server, worker|
old_pid = RAILS_ROOT + '/tmp/pids/unicorn.pid.oldbin'
if File.exists?(old_pid) && server.pid != old_pid
begin
Process.kill("QUIT", File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
end
end
end
after_fork do |server, worker|
ActiveRecord::Base.establish_connection
begin
uid, gid = Process.euid, Process.egid
user, group = 'admin', 'admin'
target_uid = Etc.getpwnam(user).uid
target_gid = Etc.getgrnam(group).gid
worker.tmp.chown(target_uid, target_gid)
if uid != target_uid || gid != target_gid
Process.initgroups(user, target_gid)
Process::GID.change_privilege(target_gid)
Process::UID.change_privilege(target_uid)
end
rescue => e
raise e
end
end
- Your deploy.rb will get a bit more complicated as it needs to send the proper signal to the Unicorn master to signal it to reload the workers.
namespace :deploy do
task :stop, :roles => :app do
run "cd #{current_path} && kill -QUIT `cat tmp/pids/unicorn.pid`"
end
task :start, :roles => :app do
run "cd #{current_path} && /opt/ruby/bin/unicorn_rails -c config/unicorn.rb -E production -D"
end
desc "restart unicorn"
task :restart, :roles => :web do
run "cd #{current_path}; [ -f tmp/pids/unicorn.pid ] && kill -USR2 `cat tmp/pids/unicorn.pid` || /opt/ruby/bin/unicorn_rails -c config/unicorn.rb -E production -D"
end
Thanks! I’m switching from passenger to unicorn now and your post helped me little.