8
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 Ilya 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,并杀死旧的独角兽进程。问题是我所有的应用程序服务器从未更新到我最近部署的代码,这个任务似乎只是将我的旧独角兽环境复制到一个新进程中。如果我简单地杀死主进程然后重新启动独角兽,它工作正常,但是有一分钟左右的请求被丢弃。

4

3 回答 3

6

您是否preload_app在 Unicorn 配置中设置为 true ?如果是这样,您需要在新进程启动并运行后向原始主进程发送 SIGUSR2 和 SIGQUIT。

也可能发生的是原始主进程已经死亡,但子进程仍在服务请求。您可以尝试发送 SIGUSR2,在新的主进程产生后,发送 SIGWINCH(给旧的主进程)以杀死旧的独角兽子进程,然后对旧的主进程进行 SIGQUIT。您现在应该只有“新的”独角兽进程为请求提供服务。

编辑:尝试使用 SIGQUIT 而不是 SIGWINCH。我认为旧进程可能会在 SIGWINCH 之后产生工人。

于 2012-11-27T06:42:46.807 回答
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

我大部分时间都使用该设置,并且我的应用程序重新加载没有任何问题,您可能已经拥有所有这些,因为它主要是默认配置,但是如果您缺少某些东西,请尝试一下;)

于 2012-12-05T09:56:10.383 回答
2

Unicorn 家伙为此有一个公共的 init.d 脚本,您可能应该将其添加到您的系统中并使用它。

您会发现 2 种不同的方式来重启 unicorn:重启和升级。

我个人将此脚本添加到 /etc/init.d/unicorn 中,设置此可执行文件然后您可以sudo service unicorn upgrade在您的 capistrano 配方中使用。

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

于 2012-11-29T23:27:57.020 回答