延迟作业重启+Capistrano最佳实践

13
我想了解使用capistrano重启延迟作业工人的最佳实践。 我有一堆处理长时间作业(长达10分钟)的工人。
在部署过程中,我遇到了两种情况 -
1) 在部署之前停止延迟作业工人:重启任务,并在部署:重启任务后再次启动它们。
但在这种情况下,直到延迟作业完成,我的应用程序才会重新启动(这可能没问题 - 但是cap部署脚本会一直等待作业完成并停止所有工人,然后才继续进行应用程序重新启动任务)。
2) 我还尝试在重启任务后停止/启动延迟作业工人 - 但这会导致各种问题,其中任务将被停止而不等待,但延迟作业表中仍列出它们并分配给一个不存在的PID的工人!
还有其他选择吗? 还是像场景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.
6个回答

1

实际上我不确定我的解决方案是否足够好。但无论如何,它有机会存活下来。只需创建一个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任务等待锁定的作业,仅在锁定任务列表为空后退出。以下是我设想的使用方式(注意,操作顺序很重要):

  1. 停止Web服务器
  2. 启动rake任务
  3. 停止延迟任务
  4. 更新代码
  5. 启动延迟任务
  6. 启动Web服务器

1
如果您的部署更改了数据库架构,那么您就不得不等待。
如果没有更改,您可以设置一个标志,工作人员在每个作业结束时检查该标志。部署时,您设置该标志,一旦长时间运行的工作人员完成,它将通过 exec 调用新的工作人员来替换自己。这样,您永远不会真正启动和停止工作人员 - 它们只是一直运行,并在启动下一个作业时加载最新的代码。

0
我最后把我的“长时间运行的进程”移动到一个使用daemons gem的自定义Ruby脚本中。不过,还是谢谢你们的建议!

0

你可以修复第二种情况,将你的pid文件放在共享目录中,以避免它消失。第二种情况可以工作,如果你没有一些迁移可能会与你的延迟任务发生冲突。


是的,它们已经在 /shared/pids/ 中了,就像所有其他共享文件(图像和日志等)一样。 - James

0

好问题 - 我尝试了很多不同的方法,但我的策略是保持简单。

  1. 不要使用 God 或 Monit 或任何以 root 运行的东西,需要与运行您的应用程序的相同用户具有 sudo 权限才能重新启动它。鉴于最近在 rails 安全方面发生的事件,您最不希望的是您的应用程序用户有很大可能性获得 root。
  2. 相反,使用 rake 或 thor 任务来启动/停止/重新启动任务。
  3. 使用 nohup 将进程变为守护进程。

这里有一些非常基本的 thor 任务,可以管理使用 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之间分割的内容放入~/.bashrc中,并确保文件的模式为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"

我不同意不使用Monit。Monit以deploy用户身份启动工作进程,而且deploy用户只有对monit命令的sudo访问权限。 - rubish
但是,monit配置文件不是在应用程序配置本身中找到的吗?它们可以由config/recipes/templates/monit/*中的部署用户进行写入。人们总是提到“部署用户”,但实际上它是“应用程序运行为用户”。因此,如果您授予monit sudo访问权限,并且如果您的应用程序被黑客攻击,则monit的配置实际上是全局可写的...抱歉,我认为这不是一个安全的解决方案。 - platforms
我的观点是,如果你给予 app-runs-as 用户任何 sudo 特权,那么你也会将这些特权赋予来自 Web 浏览器的潜在黑客。即使你将访问权限限制在一个程序上,我从未见过任何安全协议建议允许攻击者以 root 的身份运行任何东西。 - platforms
+1 我理解您不想给予 root 访问权限的顾虑,但请将我的模板放在 /etc/monit/conf/* 中。我没有注意到 monit 配置是全局可写的。 - rubish
我想改变我的投票,但是SO锁定了它直到答案被编辑。 - rubish

-2

我建议你去看看whenever - 这可能是最好的用于管理延迟作业的宝石。我曾尝试过不同的解决方案,从旧的被遗忘的cron+wget开始,但是whenever可以最好地控制这个问题。而且它与Capistrano集成得非常好。希望对你有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接