important that you choose a location for the unicorn PID file that is # outside of the RAILS_ROOT directory. We use capistrano for deployment, where we # deploy via remote_cache. We noticed that when we had the unicorn PID file defined # in a directory under RAILS_ROOT (the default PID location is RAILS_ROOT/tmp/pids/unicorn.pid), # that the script was not able to reclaim the old unicorn PID file after the symlink # for current/ gets moved to the latest deploy by capistrano. pid '/var/www/rails-application/shared/pids/unicorn.pid' listen "/var/www/rails-application/tmp/sockets/ #{rails_env}.sock", :backlog => 2048 else listen 3001 listen "#{`pwd`.strip}/tmp/sockets/#{rails_env}.sock" end Friday, August 2, 13
Capistrano users # We need this since we automatically run deploy:clean to # cleanup old releases. before_exec do |server| ENV["BUNDLE_GEMFILE"] = "/var/www/rails-application/current/ Gemfile" end Friday, August 2, 13
Unicorn will suffix its pidfile with .oldbin and # immediately start loading up a new version of itself (loaded with a new # version of our app). When this new Unicorn is completely loaded # it will begin spawning workers. The first worker spawned will check to # see if an .oldbin pidfile exists. If so, this means we've just booted up # a new Unicorn and need to tell the old one that it can now die. To do so # we send it a QUIT. # # Using this method we get 0 downtime deploys. old_pid = '/var/www/rails-application/shared/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 # someone else did our job for us end end end Friday, August 2, 13
app then forks off workers - because of the way # Unix forking works, we need to make sure we aren't using any of the parent's # sockets, e.g. db connection # defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection # Redis and Memcached would go here but their connections are established # on demand, so the master never opens a socket # $redis = Redis.connect end Friday, August 2, 13
asset precompilation happens after the current/ symlink is created. We # changed asset precompilation to happen before the current/ symlink is moved so that we don't have a period # where stylesheets, etc. for the running unicorn process are invalid. before 'deploy:create_symlink', 'deploy:assets:precompile' after 'deploy', 'deploy:cleanup' namespace :deploy do desc <<-DESC Send a USR2 to the unicorn process to restart for zero downtime deploys. runit expects 2 to tell it to send the USR2 signal to the process. DESC task :restart, :roles => :app, :except => { :no_release => true } do run "sv 2 #{application}" end end Friday, August 2, 13
listening on a UNIX socket behind NGINX. upstream unicorn { server unix:/var/www/rails-application/tmp/sockets/ production.sock fail_timeout=0; } server { ... # set up the rails servers as a virtual location for use later location @rails { ... proxy_pass http://unicorn; ... } ... } Friday, August 2, 13
https://gist.github.com/1039720 #!/bin/bash exec 2>&1 <% unicorn_command = @options[:unicorn_command] || 'unicorn_rails' -%> # # Since unicorn creates a new pid on restart/reload, it needs a little extra love to # manage with runit. Instead of managing unicorn directly, we simply trap signal calls # to the service and redirect them to unicorn directly. # # To make this work properly with RVM, you should create a wrapper for the app's gemset unicorn. # Friday, August 2, 13
] && kill -0 $1 >/dev/null 2>&1; then echo "yes" fi set -e } echo "Service PID: $$" CUR_PID_FILE=/var/www/rails-application/shared/pids/unicorn.pid OLD_PID_FILE=$CUR_PID_FILE.oldbin if [ -e $OLD_PID_FILE ]; then OLD_PID=$(cat $OLD_PID_FILE) echo "Waiting for existing master ($OLD_PID) to exit" while [ -n "$(is_unicorn_alive $OLD_PID)" ]; do /bin/echo -n '.' sleep 2 done fi Friday, August 2, 13
kill -USR2 $CUR_PID # Make runit restart to pick up new unicorn pid sleep 2 echo "Restarting service to capture new pid" exit } function graceful_shutdown { echo "Initializing graceful shutdown" kill -QUIT $CUR_PID } function unicorn_interrupted { echo "Unicorn process interrupted. Possibly a runit thing?" } Friday, August 2, 13
KILL trap unicorn_interrupted ALRM echo "Waiting for current master to die. PID: ($CUR_PID)" while [ -n "$(is_unicorn_alive $CUR_PID)" ]; do /bin/echo -n '.' sleep 2 done echo "You've killed a unicorn!" Friday, August 2, 13