2012-11-27 14 views
7
task :restart_unicorn, :except => { :no_release => true } do 
    run "#{try_sudo} kill -s USR2 $(cat /var/www/app_name/shared/pids/unicorn.pid)" 
end 

Quando faccio sudo kill -s USR2 $(cat /var/www/app_name/shared/pids/unicorn.pid) sul server, ciò che accade è che un nuovo padrone unicorno è stato creato, e quello vecchio ha (old) aggiunto al suo nome. Il vecchio non viene mai ucciso, ma anche se lo uccido da solo, la nuova istanza di unicorno mostra ancora il vecchio codice prima della distribuzione. La nuova istanza ha lo stesso tempo di creazione del vecchio - è come se fosse appena stata duplicata. Se interrompo l'istanza e la riavvio, funziona, ma mi piacerebbe essere in grado di eseguire distribuzioni zero-downtime.Unicorn continua a utilizzare il vecchio codice seguente distribuire + riavvio

Qualsiasi aiuto è apprezzato.

EDIT

Dopo aver seguito la sugest Ilya O., ho creato un compito Capistrano che fa questo:

old_pid = get_pid('/var/www/appname/shared/pids/unicorn.pid') 
run "#{try_sudo} kill -s SIGUSR2 $(cat /var/www/appname/shared/pids/unicorn.pid)" 
/var/www/app/current/tmp/pids/unicorn.pid)" 
run "#{try_sudo} kill -s SIGWINCH #{old_pid}" 

Questo viene eseguito SIGUSR2 sul pid, e uccide il vecchio processo unicorno. Il problema è che tutto il mio server delle applicazioni non viene mai aggiornato al mio codice distribuito di recente, questa attività sembra solo copiare il mio vecchio ambiente unicorno in un nuovo processo. Funziona bene se semplicemente uccido il processo principale e poi ricomincio l'unicorno di nuovo, ma poi ci sono un minuto o più di richieste perse.

risposta

5

Hai impostato preload_app su true nella configurazione di Unicorn? In tal caso, è necessario inviare SIGUSR2 e poi SIGQUIT al processo master originale dopo che il nuovo processo è attivo e in esecuzione.

Ciò che potrebbe anche accadere è che il processo master originale è morto ma i bambini stanno ancora servendo le richieste. Puoi provare a inviare SIGUSR2 e dopo che il nuovo processo master è stato generato, invia SIGWINCH (al vecchio master) per terminare i vecchi processi figlio unicorno e poi SIGQUIT sul vecchio processo master. Ora dovresti avere solo i "nuovi" processi di unicorno che servono le richieste.

MODIFICA: provare a eseguire SIGQUIT anziché SIGWINCH. Penso che il vecchio processo potrebbe essere la generazione di lavoratori dopo SIGWINCH.

+0

Sì, 'preload_app' è impostato su true. Grandi informazioni!La mia unica domanda è qual è il modo migliore per identificare il vecchio id del processo unicorno? –

+0

.oldbin o (vecchio) verrebbe aggiunto al nome del vecchio processo in modo da poter pipe 'ps' a un' grep' per unicorno e vecchio –

+0

Ho modificato l'OP con un aggiornamento. Grazie per l'aiuto che hai offerto finora. –

2

I ragazzi di unicorno hanno uno script pubblico init.d per questo, dovresti probabilmente aggiungerlo al tuo sistema e usarlo.

Sono disponibili 2 diversi modi per riavviare unicorno: riavvio e aggiornamento.

Personalmente aggiungo questo script in /etc/init.d/unicorn, imposta questo eseguibile quindi puoi usare sudo service unicorn upgrade nella tua ricetta capistrano.

Init script: http://unicorn.bogomips.org/examples/init.sh

+0

Ho pensato che avrebbe funzionato, ma non ha funzionato. –

+0

Impossibile aggiornare, a partire da 'unicorn -D -c /var/www/appname/current/config/unicorn.rb' invece il master non è stato avviato, controllare il log di stderr per i dettagli –

+0

stderror mostra questo: E, [2012- 11-29T23: 04: 45.574813 # 3345] ERRORE -: aggiunta listener non riuscita addr =/tmp/.sock (in uso) E, [2012-11-29T23: 04: 45.574938 # 3345] ERRORE:: riprovare in 0,5 secondi (4 tentativi a sinistra) –

3

io non sono sicuro perché stai mettendo tutto quel lavoro su Capistrano, di solito su Capistrano qualcosa come questo è sufficiente:

set :unicorn_pid, "unicorn.my_website.pid" 

desc "Zero-downtime restart of Unicorn" 
task :restart, roles: :app do 
    if remote_file_exists?("/tmp/#{unicorn_pid}") 
    puts "Killing /tmp/#{unicorn_pid}" 
    run "kill -s USR2 `cat /tmp/#{unicorn_pid}`" 
    else 
    run "cd #{current_path} ; RAILS_ENV=#{rails_env} bundle exec unicorn_rails -C#{unicorn_config} -D" 
    end 
end 

e poi il vero e proprio codice per l'uccisione il vecchio processo unicorno è in realtà dalla configurazione unicorno

# config/unicorn.rb 

# Set environment to development unless something else is specified 
env = ENV["RAILS_ENV"] || "production" 

# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete documentation. 
worker_processes 2 # amount of unicorn workers to spin up 

APP_PATH = "/u/apps/my_website/current" 

listen "/tmp/my_website.socket" 

preload_app true 

timeout 30   # restarts workers that hang for 30 seconds 

pid "/tmp/unicorn.my_website.pid" 

# By default, the Unicorn logger will write to stderr. 
# Additionally, ome applications/frameworks log to stderr or stdout, 
# so prevent them from going to /dev/null when daemonized here: 
stderr_path APP_PATH + "/log/unicorn.stderr.log" 
stdout_path APP_PATH + "/log/unicorn.stdout.log" 

if env == "production" 
    # Help ensure your application will always spawn in the symlinked 
    # "current" directory that Capistrano sets up. 
    working_directory APP_PATH 

    # feel free to point this anywhere accessible on the filesystem 
    user 'deploy', 'deploy' # 'user', 'group' 
    shared_path = "/u/apps/my_website/shared" 

    stderr_path "#{shared_path}/log/unicorn.stderr.log" 
    stdout_path "#{shared_path}/log/unicorn.stdout.log" 
end 

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 
    if defined?(ActiveRecord::Base) 
    ActiveRecord::Base.connection.disconnect! 
    end 


    # When sent a USR2, 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. 
    # 
    # This enables 0 downtime deploys. 
    old_pid = "/tmp/unicorn.my_website.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 

after_fork do |server, worker| 

    # Unicorn master loads the 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 (since "preload_app true") 
    if defined?(ActiveRecord::Base) 
    ActiveRecord::Base.establish_connection 
    end 

    # 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 

io uso che l'installazione la maggior parte del tempo e la mia domanda è ricaricare senza qualsiasi problema, probabilmente hai già tutto questo dal momento che è principalmente la configurazione predefinita, ma se ti manca qualcosa, provalo;)

+0

Provato a tornare a questo ... finì di nuovo dove ho iniziato una settimana fa. aggiungendo listener non riuscito addr =/tmp/.sock (in uso) –

+0

Non capisco quale sia la presa che dovrei puntare nella configurazione? –

+0

stai usando nginx + unicorno? – rorra

Problemi correlati