2012-11-27 53 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 

當我在服務器上做sudo kill -s USR2 $(cat /var/www/app_name/shared/pids/unicorn.pid),什麼情況是,新麒麟大師創建,和舊的已(old)追加到它的名稱。舊的從未被殺死,但即使我自己殺了它,新的獨角獸實例仍然顯示我部署之前的舊代碼。新實例的創建時間與舊時間相同 - 就好像它剛被複制一樣。如果我停止實例並重新啓動它,它可以工作,但我希望能夠進行零宕機部署。獨角獸繼續使用舊代碼如下部署+重啓

任何幫助表示讚賞。

編輯

繼sugest伊利亞O.後,我創建了一個Capistrano的任務,這是否:

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}" 

這將運行在PID SIGUSR2,並殺死老麒麟過程。問題是我的所有應用程序服務器都不會更新到我最近部署的代碼中,這個任務似乎只是將我的舊獨角獸環境複製到一個新進程中。如果我簡單地殺掉主進程,然後再啓動獨角獸,但它會在一分鐘左右的時間內丟失請求。

回答

5

您是否在您的Unicorn配置中將preload_app設置爲true?如果是這樣,在新進程啓動並運行之後,您需要發送SIGUSR2,然後將SIGQUIT發送到原始主進程。

還可能發生的是原始主進程已經死亡,但子進程仍在服務請求。你可以嘗試發送SIGUSR2,並且在新的主進程產生後,發送SIGWINCH(給舊的主進程)以殺死舊的獨角獸子進程,然後在舊的主進程上使用SIGQUIT。您現在應該只有提供服務請求的「新」獨角獸進程。

編輯:嘗試做SIGQUIT而不是SIGWINCH。我認爲舊的過程可能會在SIGWINCH之後產生工人。

+0

是的,'preload_app'設置爲true。偉大的信息!我唯一的問題是,識別舊的獨角獸進程ID是什麼是最好的方法? –

+0

.oldbin或(old)會被附加到舊進程的名稱中,所以你可以將'ps'管道傳輸到'grep'來獲得獨角獸和舊版本 –

+0

我使用更新編輯了OP。感謝您迄今爲止提供的幫助。 –

2

獨角獸傢伙有一個公共的init.d腳本,你應該在你的系統中添加它並使用它。

你會發現2種不同的方式重啓麒麟:重啓和升級。

我personnaly添加此腳本/etc/init.d/unicorn,設置此可執行文件,然後您可以在您的capistrano配方中使用sudo service unicorn upgrade

初始化腳本:http://unicorn.bogomips.org/examples/init.sh

+0

我認爲這會工作,但它沒有。 –

+0

無法升級,啓動'unicorn -D -c /var/www/appname/current/config/unicorn.rb'而不是 master無法啓動,請檢查stderr日誌中的詳細信息 –

+0

stderror顯示:E,[2012- 11-29T23:04:45.574813#3345]錯誤 - :添加監聽失敗addr =/tmp/.sock(使用中) E,[2012-11-29T23:04:45.574938#3345] ERROR - :retrying in 0.5秒(剩下4次嘗試) –

3

我不知道你爲什麼把所有的工作對Capistrano的,通常在Capistrano的這樣的事情就夠了:

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 

,然後殺死真正的代碼舊的獨角獸過程實際上是獨角獸配置

# 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 

我使用該設置大部分時間和我的應用程序重新加載沒有任何問題,你可能已經擁有所有這些,因爲它大部分是默認配置,但是如果你錯過了一些東西,請試試看);

+0

嘗試回到這個......結束了我幾周前開始的地方。添加監聽器失敗addr =/tmp/.sock(使用中) –

+0

我不太明白什麼是我應該指向配置中的套接字? –

+0

你使用nginx +獨角獸嗎? – rorra