Skip to content

Instantly share code, notes, and snippets.

@kesor
Last active February 9, 2022 09:20
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save kesor/6255584 to your computer and use it in GitHub Desktop.
Save kesor/6255584 to your computer and use it in GitHub Desktop.
Unicorn that receives USR2 signal on upstart's "stop unicorn", but also allows upstart to respawn it when for some reason it crashed on its own.
# unicorn
description "unicorn ruby app server"
start on (local-filesystems and net-device-up IFACE=lo and runlevel [2345])
stop on runlevel [!2345]
env WORKDIR=/data
env PIDFILE=/data/tmp/pids/unicorn.pid
env CFGFILE=/data/config/unicorn.rb
env CHOWN=deploy:deploy
respawn
script
trap "start-stop-daemon --signal HUP --stop --pidfile $PIDFILE" HUP
[ -e $PIDFILE ] && {
pid=$(cat $PIDFILE)
while kill -0 $pid &>/dev/null; do sleep 2; done
} || {
start-stop-daemon --start --pidfile "$PIDFILE" --chdir "$WORKDIR" --chuid "$CHOWN" --exec "$WORKDIR/bin/unicorn.sh" -- -c "$CFGFILE"
}
end script
pre-stop script
[ -e $PIDFILE ] && kill -0 $(cat $PIDFILE) &>/dev/null && {
start-stop-daemon --signal USR2 --stop --pidfile "$PIDFILE"
start # don't send TERM and KILL!
}
end script
root@testbox:/etc/init# start unicorn
unicorn start/running, process 2823
root@testbox:/etc/init# pgrep -lf unicorn
2824 unicorn.sh master -c /data/config/unicorn.rb
2953 unicorn.sh worker[0] -c /data/config/unicorn.rb
2956 unicorn.sh worker[1] -c /data/config/unicorn.rb
root@testbox:/etc/init# stop unicorn
unicorn start/pre-stop, process 2823
pre-stop process 2961
root@testbox:/etc/init# pgrep -lf unicorn
2965 unicorn.sh master -c /data/config/unicorn.rb
3092 unicorn.sh worker[0] -c /data/config/unicorn.rb
3095 unicorn.sh worker[1] -c /data/config/unicorn.rb
root@testbox:/etc/init# initctl list | grep -i unicorn
unicorn start/running, process 2823
root@testbox:/etc/init# kill 2965
root@testbox:/etc/init# pgrep -lf unicorn
3112 unicorn.sh master -c /data/config/unicorn.rb
3241 unicorn.sh worker[0] -c /data/config/unicorn.rb
3244 unicorn.sh worker[1] -c /data/config/unicorn.rb
root@testbox:/etc/init# initctl list | grep -i unicorn
unicorn start/running, process 3111
project_folder = File.expand_path("../..", __FILE__)
sockets_folder = File.join project_folder, 'tmp', 'sockets'
pids_folder = File.join project_folder, 'tmp', 'pids'
logs_folder = File.join project_folder, 'logs'
# -- buffer sizes from sysctl net.core.rmem_max=229376 & net.core.wmem_max=229376
# -- backlog size from sysctl net.core.netdev_max_backlog=1000
listen File.join(sockets_folder, 'unicorn.sock'), backlog: 64, rcvbuf: 131071, sndbuf: 131071
preload_app false
worker_processes 2
timeout 30
# chdir into this folder on restarts, fixes capistrano's `current` symlink to be workdir
working_directory project_folder
stderr_path File.join(logs_folder, 'unicorn_error.log')
stdout_path File.join(logs_folder, 'unicorn_output.log')
pid File.join(pids_folder, 'unicorn.pid')
# change who is the unicorn executable - so it can reload itself even when rvm ruby was changed
# this will be the "new" executable unicorn loads when it receives a USR2 signal.
Unicorn::HttpServer::START_CTX[0] = File.join(project_folder, 'bin', 'unicorn.sh')
before_exec do |server|
# Bundler loads gem environment from original deploy path instead of current
# Capistrano's `current` symlink will break deploys without this tweak.
ENV["BUNDLE_GEMFILE"] = File.join(project_folder, 'Gemfile')
end
before_fork do |server, worker|
old_pid = server.config[: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 already killed old master
end
end
end
#!/bin/bash
set -e
source /usr/local/rvm/scripts/rvm
rvm use 1.9.3 &> /dev/null
exec bundle exec unicorn "$@"
@mikerev
Copy link

mikerev commented Sep 22, 2017

@kesor thanks for this, this is the most reasonable solution to accomplish zero downtime w/ unicorn for us. We added forced respawn and a respawn limit and had to wire up pre-up stuff to dial it in a little more so it would fire up on deployment and restart properly, but it's beautifully seamless.

respawn
respawn limit 10 10

pre-start script
  if [ -e $PIDFILE ] && [ ! -e /proc/$(cat $PIDFILE) ]; then
    rm -f $PIDFILE
  fi
end script


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment