Unicorn продолжает использовать старый код после развертывания и перезапуска.

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). Старый никогда не уничтожается, но даже если я убью его самостоятельно, новый экземпляр единорога по-прежнему будет показывать старый код, существовавший до моего развертывания. Новый экземпляр имеет то же время создания, что и старый, как будто он просто дублируется. Если я остановлю экземпляр и запущу его снова, он сработает, но я хотел бы иметь возможность выполнять развертывание с нулевым временем простоя.

Любая помощь приветствуется.

ИЗМЕНИТЬ

Следуя совету Ильи О., я создал задачу капистрано, которая делает это:

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

Это запускает SIGUSR2 на pid и убивает старый процесс единорога. Проблема в том, что весь мой сервер приложений никогда не обновляется до моего недавно развернутого кода, эта задача просто копирует мою старую среду единорога в новый процесс. Он отлично работает, если я просто убью главный процесс, а затем снова запущу unicorn заново, но затем будет минута или около того отброшенных запросов.


person D-Nice    schedule 27.11.2012    source источник


Ответы (3)


У вас есть preload_app значение true в вашей конфигурации Unicorn? Если это так, вам нужно отправить SIGUSR2, а затем SIGQUIT исходному главному процессу после того, как новый процесс будет запущен и запущен.

Также может случиться так, что исходный главный процесс умер, но дочерние процессы все еще обслуживают запросы. Вы можете попробовать отправить SIGUSR2, и после того, как новый мастер-процесс будет создан, отправьте SIGWINCH (старому мастеру), чтобы убить старые дочерние процессы-единороги, а затем SIGQUIT на старом главном процессе. Теперь у вас должны быть только «новые» процессы unicorn, обслуживающие запросы.

РЕДАКТИРОВАТЬ: Попробуйте использовать SIGQUIT вместо SIGWINCH. Я думаю, что старый процесс может порождать рабочих после SIGWINCH.

person Ilya O.    schedule 27.11.2012
comment
Да, для preload_app установлено значение true. Отличная информация! Мой единственный вопрос: как лучше всего определить, что такое старый идентификатор процесса единорога? - person D-Nice; 27.11.2012
comment
.oldbin или (old) будут добавлены к имени старого процесса, чтобы вы могли передать ps в grep для единорога и старого - person Ilya O.; 28.11.2012
comment
Я отредактировал ОП с обновлением. Спасибо за помощь, которую вы предложили до сих пор. - person D-Nice; 29.11.2012

Я не уверен, почему вы кладете всю эту работу на капистрано, обычно на капистрано достаточно чего-то вроде этого:

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

Я использую эту настройку большую часть времени, и мое приложение перезагружается без каких-либо проблем, возможно, у вас уже есть все это, поскольку в основном это конфигурация по умолчанию, но если вам что-то не хватает, попробуйте;)

person rorra    schedule 05.12.2012
comment
Пытался вернуться к этому... в итоге вернулся к тому, с чего начал неделю назад. добавление прослушивателя не удалось addr=/tmp/.sock (используется) - person D-Nice; 06.12.2012
comment
Я не совсем понимаю, на какой сокет я должен указывать в конфигурации? - person D-Nice; 06.12.2012
comment
А также это не должно быть tmp/.sock, это должно быть что-то вроде /tmp/my_website.socket И сокет является внутренним сокетом, он разрешает запрос как веб-сервер, и ваш реальный веб-сервер просто передает запрос как прокси для единорога, используя этот сокет, единорог обрабатывает запрос, отвечает веб-серверу, а веб-сервер отвечает на запрос клиенту. - person rorra; 06.12.2012

У ребят из Unicorn есть общедоступный скрипт init.d для этого, вам, вероятно, следует добавить его в свою систему и использовать.

Вы найдете 2 разных способа перезапустить единорога: перезапустить и обновить.

Я лично добавляю этот скрипт в /etc/init.d/unicorn, устанавливаю этот исполняемый файл, после чего вы можете использовать sudo service unicorn upgrade в своем рецепте капистрано.

Скрипт инициализации: http://unicorn.bogomips.org/examples/init.sh

person Julien Pellet    schedule 29.11.2012
comment
Не удалось выполнить обновление, запустив 'unicorn -D -c /var/www/appname/current/config/unicorn.rb', вместо этого master не удалось запустить, подробности см. в журнале stderr. - person D-Nice; 30.11.2012
comment
stderror показывает это: E, [2012-11-29T23:04:45.574813 #3345] ОШИБКА -- : добавление прослушивателя не удалось addr=/tmp/.sock (используется) E, [2012-11-29T23:04:45.574938 # 3345] ОШИБКА -- : повторная попытка через 0,5 секунды (осталось 4 попытки) - person D-Nice; 30.11.2012
comment
Убедитесь, что все ваши переменные указаны правильно в этом скрипте. Вы также должны выполнять каждый шаг этого сценария инициализации вручную, чтобы убедиться, что все работает: отправьте сигналы вашему процессу, используя этот PID, и проверьте результат. У вас должен быть и _old pid, и несколько новых процессов, готовых к переходу на новый код. - person Julien Pellet; 01.12.2012
comment
Здесь вы можете найти, как единорог должен реагировать на сигнал USR2, это то, что вы должны попытаться воспроизвести: unicorn.bogomips.org/SIGNALS.html - person Julien Pellet; 01.12.2012
comment
Проблема в том, что СИГНАЛЫ, кажется, ничего не делают. Я подумал, что, возможно, что-то пошло не так с установкой, поэтому я переустановил, понизил пару версий и т. д. Ничего не сработало. При попытке запустить sudo service unicorn upgrade всегда происходит сбой и выдается эхо Couldn't upgrade, starting '$CMD' instead. Даже если увеличить время сна до 15, все равно не получается. Когда я запускаю команды в терминале, ничего не происходит - ни нового мастера, ни старого мастера... просто остается как есть. - person D-Nice; 02.12.2012
comment
Вероятно, у вас есть какие-то сообщения в логах единорога. Вы должны сделать все вручную, прежде чем использовать сценарий инициализации, запустить unicorn / отправить USR2 самостоятельно ... и полностью прочитать документ. - person Julien Pellet; 03.12.2012