Escolhi uma solução diferente da do SpamapS .. Também estou executando um aplicativo com preload_app = true, gerenciado pelo Upstart.
Quando eu estava procurando resolver esse problema sozinho, eu estava usando o "exec" do Upstart para iniciar meu aplicativo ("exec bundle exec unicorn_rails blah blah"). Então eu encontrei sua pergunta, e percebi que ao invés de usar o "exec" do Upstart para especificar meu executável, eu poderia usar uma sub-rotina de script, que seria executada em seu próprio processo, o processo que o Upstart iria assistir. >
Então, meu arquivo de configuração do Upstart inclui isto:
respawn
script
while true; do
if [ ! -f /var/www/my_app/shared/pids/unicorn.pid ]; then
# Run the unicorn master process (this won't return until it exits).
bundle exec unicorn_rails -E production -c /etc/unicorn/my_app.rb >>/var/www/my_app/shared/log/unicorn.log
else
# Someone restarted the master; wait for the new master to exit.
PID='cat /var/www/my_app/shared/pids/unicorn.pid'
while [ -d /proc/$PID ]; do
sleep 2
done
# If we get here, the master has exited, either because someone restarted
# it again (in which case there's already a new master running), or
# it died for real (in which case we'll need to start a new process).
# The sleep above is a tradeoff between polling load and mimizing the
# restart delay when the master dies for real (which should hopefully be
# rare).
fi
done
end script
O before_fork no meu arquivo de configuração do Unicorn é exatamente como sugerido no exemplo do site unicornio, link :
before_fork do |server, worker|
ActiveRecord::Base.connection.disconnect! if defined?(ActiveRecord::Base)
old_pid = '/var/www/my_app/shared/pids/unicorn.pid.oldbin'
if server.pid != old_pid
begin
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
Process.kill(sig, File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
# someone else did our job for us
end
end
sleep 0.5
end
Então: na inicialização, o script Upstart não encontra o pidfile, então ele executa o unicorn_rails, que continua rodando.
Mais tarde, reimplementamos nosso aplicativo e uma tarefa do Capistrano aciona a reinicialização do aplicativo por meio de:
kill -USR2 'cat /var/www/my_app/shared/pids/unicorn.pid'
Isto diz ao antigo mestre Unicorn para iniciar um novo processo mestre Unicorn, e como o novo mestre inicia os trabalhadores, o bloco unicorn before_fork envia sinais TTOU para o master antigo para desligar os trabalhadores antigos (graciosamente), então QUIT uma vez só um trabalhador foi embora.
Aquele QUIT faz com que o antigo mestre seja encerrado (mas somente quando novos trabalhadores já estão manipulando a carga), então o "bundle exec unicorn_rails" retorna no script de unicórnio. Esse script, em seguida, faz um loop ao redor, vê o pidfile existente e aguarda a saída do processo. Não vai sair até a próxima implantação, mas vamos dar uma volta novamente se isso acontecer; nós também damos a volta novamente sempre que o mestre morre.
Se o script bash morrer, o Upstart irá reiniciá-lo, porque esse é o processo que está assistindo (como você vê se você já fez status my_app
- Upstart relata o PID do script bash. Você ainda pode stop my_app
, ou restart my_app
, que não faz nada gracioso.