我正在运行Rails 3.2.21应用程序并使用capistrano(nginx和unicorn)部署到Ubuntu 12.04.5框。
我的应用程序设置为零停机部署(至少我想),我的配置文件看起来或多或少像these。
问题出在这里:当部署工作接近完成并重新启动独角兽时,当我看到我的unicorn.log时,我看到它启动了新工作人员,收获旧工作人员......但随后我的应用程序只挂了2-3分钟。此时对应用程序的任何请求都会到达超时窗口(我设置为40秒)并返回应用程序的500错误页面。
这是 unicorn.log 输出的第一部分,因为独角兽正在重启(我有5名独角兽工人):
I, [2015-04-21T23:06:57.022492 #14347] INFO -- : master process ready
I, [2015-04-21T23:06:57.844273 #15378] INFO -- : worker=0 ready
I, [2015-04-21T23:06:57.944080 #15381] INFO -- : worker=1 ready
I, [2015-04-21T23:06:58.089655 #15390] INFO -- : worker=2 ready
I, [2015-04-21T23:06:58.230554 #14541] INFO -- : reaped #<Process::Status: pid 15551 exit 0> worker=4
I, [2015-04-21T23:06:58.231455 #14541] INFO -- : reaped #<Process::Status: pid 3644 exit 0> worker=0
I, [2015-04-21T23:06:58.249110 #15393] INFO -- : worker=3 ready
I, [2015-04-21T23:06:58.650007 #15396] INFO -- : worker=4 ready
I, [2015-04-21T23:07:01.246981 #14541] INFO -- : reaped #<Process::Status: pid 32645 exit 0> worker=1
I, [2015-04-21T23:07:01.561786 #14541] INFO -- : reaped #<Process::Status: pid 15534 exit 0> worker=2
I, [2015-04-21T23:07:06.657913 #14541] INFO -- : reaped #<Process::Status: pid 16821 exit 0> worker=3
I, [2015-04-21T23:07:06.658325 #14541] INFO -- : master complete
之后,当应用程序挂起2-3分钟时,会发生以下情况:
E, [2015-04-21T23:07:38.069635 #14347] ERROR -- : worker=0 PID:15378 timeout (41s > 40s), killing
E, [2015-04-21T23:07:38.243005 #14347] ERROR -- : reaped #<Process::Status: pid 15378 SIGKILL (signal 9)> worker=0
E, [2015-04-21T23:07:39.647717 #14347] ERROR -- : worker=3 PID:15393 timeout (41s > 40s), killing
E, [2015-04-21T23:07:39.890543 #14347] ERROR -- : reaped #<Process::Status: pid 15393 SIGKILL (signal 9)> worker=3
I, [2015-04-21T23:07:40.727755 #16002] INFO -- : worker=0 ready
I, [2015-04-21T23:07:43.212395 #16022] INFO -- : worker=3 ready
E, [2015-04-21T23:08:24.511967 #14347] ERROR -- : worker=3 PID:16022 timeout (41s > 40s), killing
E, [2015-04-21T23:08:24.718512 #14347] ERROR -- : reaped #<Process::Status: pid 16022 SIGKILL (signal 9)> worker=3
I, [2015-04-21T23:08:28.010429 #16234] INFO -- : worker=3 ready
最终,在2或3分钟后,应用程序再次开始响应,但一切都更加缓慢。您可以在New Relic中清楚地看到这一点(水平线表示部署,浅蓝色区域表示Ruby):
我有一个相同的登台服务器,我无法在登台中复制这个问题...已授予,暂存是无负载的(我是唯一一个尝试发出页面请求的人)。
这是我的 config / unicorn.rb 文件:
root = "/home/deployer/apps/myawesomeapp/current"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"
shared_path = "/home/deployer/apps/myawesomeapp/shared"
listen "/tmp/unicorn.myawesomeapp.sock"
worker_processes 5
timeout 40
preload_app true
before_exec do |server|
ENV['BUNDLE_GEMFILE'] = "#{root}/Gemfile"
end
before_fork do |server, worker|
if defined?(ActiveRecord::Base)
ActiveRecord::Base.connection.disconnect!
end
old_pid = "#{root}/tmp/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
end
end
end
after_fork do |server, worker|
if defined?(ActiveRecord::Base)
ActiveRecord::Base.establish_connection
end
end
只是为了描绘一幅完整的图片,在我的capistrano deploy.rb 中,独角兽重启任务如下所示:
namespace :deploy do
task :restart, roles: :app, except: { no_release: true } do
run "kill -s USR2 `cat #{release_path}/tmp/pids/unicorn.pid`"
end
end
为什么独角兽工作人员在部署后立即超时?我认为零停机时间的关键是要保持旧的停机时间,直到新的停机并准备服务?
谢谢!
更新
我做了另一次部署,这一次关注 production.log ,看看那里发生了什么。唯一可疑的是以下几行,这些行与正常请求混在一起:
Dalli/SASL authenticating as 7510de
Dalli/SASL: 7510de
Dalli/SASL authenticating as 7510de
Dalli/SASL: 7510de
Dalli/SASL authenticating as 7510de
Dalli/SASL: 7510de
更新#2
正如下面的一些答案所示,我更改了before_fork
块以添加sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
,因此工作人员将逐渐被杀死。
同样的结果,非常缓慢部署,我在上图中说明了相同的峰值。仅仅为了上下文,在我的5个工作进程中,前4个发送了一个TTOU信号,第5个发送了QUIT。不过,似乎没有什么区别。
答案 0 :(得分:3)
我最近在试图在Digital Ocean上设置Rails / Nginx / Unicorn时遇到了类似的问题。在调整了一些事情之后,我能够实现零停机部署。以下是一些尝试:
px aux | grep unicorn
确保删除旧流程。kill [pid]
手动停止仍在运行的任何独角兽进程。这是我的独角兽配置供参考:
working_directory '/var/www/yourapp/current'
pid '/var/www/yourapp/current/tmp/pids/unicorn.pid'
stderr_path '/var/www/yourapp/log/unicorn.log'
stdout_path '/var/www/yourapp/log/unicorn.log'
listen '/tmp/unicorn.yourapp.sock'
worker_processes 2
timeout 30
preload_app true
before_fork do |server, worker|
old_pid = "/var/www/yourapp/current/tmp/pids/unicorn.pid.oldbin"
if old_pid != server.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
end
end
end
deploy.rb
lock '3.4.0'
set :application, 'yourapp'
set :repo_url, 'git@bitbucket.org:username/yourapp.git'
set :deploy_to, '/var/www/yourapp'
set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml', 'config/application.yml')
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
set :format, :pretty
set :log_level, :info
set :rbenv_ruby, '2.1.3'
namespace :deploy do
after :restart, :clear_cache do
on roles(:web), in: :groups, limit: 3, wait: 10 do
end
end
end
after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
task :restart do
#invoke 'unicorn:reload'
invoke 'unicorn:restart'
end
end
答案 1 :(得分:1)
您是否正在销售独角兽并且在部署时运行捆绑安装?如果是这样,这可能是一个可执行的问题。
当您执行Capistrano部署时,cap会为您的修订创建一个新的版本目录,并将current
符号链接移动到指向新版本。如果您还没有告诉正在运行的独角兽优雅地更新其可执行文件的路径,那么如果您添加此行,它应该可以工作:
Unicorn::HttpServer::START_CTX[0] = ::File.join(ENV['GEM_HOME'].gsub(/releases\/[^\/]+/, "current"),'bin','unicorn')
您可以找到更多信息here。我认为你的before_fork块看起来不错,但我也会在@travisluong's回答中添加sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
行;随着新工人的产生,这会逐渐杀死工人。
顺便说一句,我不会删除preload_app true
因为它会大大改善工人的生成时间。