Завершение замедленных заданий + лучшие практики Capistrano
Я просто хотел понять лучшие практики для возобновления замедленных рабочих мест с использованием capistrano. У меня есть куча рабочих, которые обрабатывают длинные задания (до 10 минут).
Я столкнулся с двумя сценариями во время развертывания, пока рабочие обрабатывают -
1)
Я останавливаю замедленных рабочих мест перед развертыванием: перезапустите задачу и запустите их снова после задачи deploy: restart.
Но в этом случае он не перезапустит мое приложение до тех пор, пока не завершится задание с задержкой (это может быть нормально, но развертывание крышки script буквально сидит там, пока работа не будет завершена, и она может остановить всех работников) до выполните задачу перезагрузки приложения.
2)
Я также попытался остановить/запустить рабочих замедленных работ после задачи перезапуска, но это вызвало всевозможные драмы, в результате которых задачи были бы остановлены, не дожидаясь, пока таблица замедленных сообщений не будет перечислена и назначена работнику с ПИД-кодом, которого не существует!
Любые другие варианты? Или я вынужден ждать, как упоминалось в сценарии 1.
Большое спасибо.
Изменить: Я только что понял со сценарием 1.. он не ждет! Задача остановки будет принудительно убить моего работника, хотя она еще не закончилась!
** [out] delayed_job: trying to stop process with pid 9630...
** [out] delayed_job: process with pid 9630 won't stop, we forcefully kill it...
** [out]
** [out] delayed_job: process with pid 9630 successfully stopped.
Ответы
Ответ 1
Если ваши изменения в развертывании меняют схему базы данных, вы вынуждены ждать.
Если это не так, вы можете установить флаг, который работники проверяют в конце каждого задания. После развертывания вы устанавливаете флаг, и как только продолжительный рабочий заканчивается, он заменяет себя, выполняя новый рабочий. Таким образом, вы никогда не начинаете и не останавливаете рабочих - они просто работают и загружают последний код при запуске следующей работы.
Ответ 2
На самом деле не уверен, что мое решение достаточно хорошее. Но в любом случае у него есть шанс жить. Просто создайте задачу Rake и запустите ее в процессе развертывания:
desc 'Wait for delayed job completion'
task wait_for_complete_background_jobs: :environment do
loop do
exit unless Delayed::Job.where(locked_at: nil).exists?
sleep 2
end
end
Задача rake ждет заблокированных заданий и выходит сразу после того, как список заблокированных задач пуст. Так я представляю его использование (обратите внимание, что последовательность операций важна):
- Остановить веб-сервер
- Запустите задачу rake
- Задержка с задержкой.
- Обновить код
- Запустить задержанное задание
- Запустить веб-сервер
Ответ 3
Вы можете исправить второй сценарий, чтобы поместить свой файл pid в общий каталог, чтобы избежать его исчезновения. Второй сценарий может работать, и если у вас нет миграции, вы можете столкнуться с задержкой.
Ответ 4
Я закончил перенос своих "длинных процессов" на собственный Ruby script, используя драгоценный камень daemons. Спасибо за совет, хотя ребята!
Ответ 5
Хороший вопрос - я пробовал много разных вещей, но моя стратегия заключается в том, чтобы все было просто.
- Не используйте God или Monit или что-либо, что работает под управлением root, требуя, чтобы тот же пользователь запускал ваше приложение, чтобы иметь привилегии sudo для его перезапуска. Учитывая недавние события в безопасности rails, последнее, что вы хотите, - это пользователь вашего приложения, имеющий хорошую возможность получить root.
- Вместо этого используйте задачу
rake
или thor
для запуска/остановки/перезапуска задач.
- Используйте
nohup
для демонтизации процесса.
Вот несколько основных задач, которые могут выполнять простые фоновые задачи с помощью resque
, например:
class Resque < Thor
desc "start", "Starts resque workers"
def start
system('(PIDFILE=/tmp/resque-myapp.pid nohup rake resque:work QUEUES="*" &) &')
end
desc "stop", "Stops resque workers"
def stop
pidfile = '/tmp/resque-myapp.pid'
if File.exist?(pidfile)
pid = File.open(pidfile).first.to_i
puts "Killing process #{pid}..."
system("kill -QUIT #{pid}")
File.delete(pidfile)
sleep(5)
else
puts "No resque pidfile found"
end
end
end
Затем в файле deploy.rb
:
after 'deploy', "resque:restart"
namespace :resque do
task :restart do
puts "Stopping resque workers.."
run("cd #{current_path} && thor resque:stop", :options => { :pty => true })
puts "Starting resque workers.."
run("cd #{current_path} && thor resque:start > /dev/null 2>&1 &", :options => { :pty => true })
end
end
Эта стратегия может даже работать с RVM, если это интересно. В дополнение к обычным задачам rvm-capistrano
вам понадобится что-то подобное перед командой перезапуска:
after 'deploy:setup', 'thor:install'
namespace :thor do
task :install do
puts "Installing thor..."
run_locally('cap rvm:install_gem GEM=thor')
end
end
after "deploy", "rvm:trust_rvmrc"
namespace :rvm do
task :trust_rvmrc do
run "rvm rvmrc trust #{release_path}"
end
end
И последнее, но не менее важное: с RVM лучше всего положить в ~/.bashrc
то, что установщик обычно разделяет между этим файлом и .bash_profile
, и убедиться, что режим файла равен 0400, поэтому он не будет сбиваться с программа установки RVM:
PATH=$HOME/.rvm/bin:/usr/local/bin/:$PATH # Add RVM to PATH for scripting
export PATH
export RAILS_ENV=production
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
Ответ 6
Я бы порекомендовал вам проверить whenever - возможно, лучший камень для управления отложенными заданиями. Я пробовал разные решения, начиная с старого забытого cron + wget, но всякий раз дает лучший контроль над этим. И он отлично сочетается с Capistrano. НТН