如果他们重生得太快,Monit会因为停止麒麟工人而超时

时间:2013-03-05 15:47:16

标签: ruby-on-rails timeout unicorn monit

我正在尝试监控monicorn的独角兽,所以当它们达到一定的内存阈值时,它会优雅地杀死它们。

问题:

当我告诉monit重新启动一个worker时,它首先尝试停止它,然后触发我的/etc/init.d/unicorn kill_worker 0脚本命令。

# my /etc/monit/config.d/unicorn file
check process orly_unicorn_worker_0 with pidfile /tmp/unicorn.orly.0.pid
  start program = "/bin/true"
  stop program = "/etc/init.d/unicorn_orly kill_worker 0"

当我通过top命令监视进程时,我看到工作者是如何被杀死的,以及主人如何产生一个新的工作者,当然还有另一个pid。

但是,Monit会等待一段时间并在其日志中抛出“未能停止”错误。它实际上等待30秒并超时。

一旦超时,monit会识别出restart action is done,然后注意到工作者PID已经改变并继续按预期监视该过程。

因此一切正常,monit能够在需要时重新启动工作人员并继续监视它们,但是日志中充满了错误,Web界面显示了令人讨厌(且令人困惑)的execution failed错误状态。工作者,我想如果设置它们会发送错误的电子邮件警报。

这是日志的相关部分,当我尝试通过Web界面重启工作者时(注意它如何与工作者父PID混淆):

[UTC Mar  5 13:29:17] info     : 'orly_unicorn_worker_0' trying to restart
[UTC Mar  5 13:29:17] info     : 'orly_unicorn_worker_0' stop: /etc/init.d/unicorn_orly
[UTC Mar  5 13:29:47] error    : 'orly_unicorn_worker_0' failed to stop
[UTC Mar  5 13:29:47] info     : 'orly_unicorn_worker_0' restart action done
[UTC Mar  5 13:29:47] error    : 'orly_unicorn_worker_0' process PID changed to 13699
[UTC Mar  5 13:29:49] error    : 'orly_unicorn_worker_0' process PPID changed to 0
[UTC Mar  5 13:30:19] info     : 'orly_unicorn_worker_0' process PID has not changed since last cycle
[UTC Mar  5 13:30:19] error    : 'orly_unicorn_worker_0' process PPID changed to 13660
[UTC Mar  5 13:30:49] info     : 'orly_unicorn_worker_0' process PPID has not changed since last cycle

这花了我很长时间才弄明白,但是,这里发生的事情是工人被杀,然后如此迅速地重生,monit甚至没有注意到这种变化。

我的猜测是monit在执行停止操作时读取/tmp/unicorn.orly.0.pid以获取进程的pid,然后查看该进程是否存在。

然而,由于 kill-respawn worker操作发生得如此之快,monit没有意识到worker的pid已经改变并且一直等待(Bran new)worker死亡。然后它超时,然后它意识到pid实际上已经改变了,它正常。

我找到的肮脏解决方案:

为了证明这个假设,我试图减慢提到的 kill-respawn worker操作。因此,我编辑了unicorn配置文件,以便在他们在/tmp/unicorn.orly.0.pid中写下新pid之前几秒钟让新工作人员入睡。

我是这样做的:

after_fork do |server, worker|
  sleep 3

  # write down the new worker PID so monit can monitor it
  child_pid = server.config[:pid].sub(".pid", ".#{worker.nr}.pid")
  system("echo #{Process.pid} > #{child_pid}")
end

它运作得非常好:鸟儿和鲜花在阳光灿烂的日子里唱歌,网络界面现在显示出良好的process running状态,日志显示一切顺利,看看:

[UTC Mar  5 13:30:44] info     : 'orly_unicorn_worker_0' trying to restart
[UTC Mar  5 13:30:44] info     : 'orly_unicorn_worker_0' stop: /etc/init.d/unicorn_orly
[UTC Mar  5 13:30:45] info     : 'orly_unicorn_worker_0' stopped
[UTC Mar  5 13:30:45] info     : 'orly_unicorn_worker_0' start: /bin/true
[UTC Mar  5 13:30:46] info     : 'orly_unicorn_worker_0' restart action done

问题:

是否有实现此目的的 monit-way ?睡觉我的工人3秒似乎不是一个好的解决方案。有什么想法吗?

我明白这不是monit的正常情况。我们打破了monit的重启进程周期,因为我们不希望monit的start program执行任何操作,而是让unicorn主进程处理它(如解释的那样)这里:http://www.stopdropandrew.com/2010/06/01/where-unicorns-go-to-die-watching-unicorn-workers-with-monit.html

1 个答案:

答案 0 :(得分:0)

在我们的环境中,monit监视独角兽大师,独角兽大师监视它的孩子。我们使用一个简单的cron来监视独角兽工作者,如果超过内存阈值就杀死他们:

    #!/usr/bin/env ruby
    #       

    def get_mem(pid)
      pid = pid.to_i
      mem = 0 
      if File.exist?("/proc/#{pid}/status")
        File.read("/proc/#{pid}/status").each_line do |status|
          next unless status =~ /^VmRSS:\s+(\d+) kb/i
          mem = $1.to_i / 1024
        end     
      end     
      mem     
    end     

    %x{pgrep -f 'unicorn worker'}.each_line do |pid|
      Process.kill('QUIT', pid.to_i) if (get_mem pid) >= 300
    end

独角兽大师注意到孩子被杀,并自动重新生成一个新孩子。我很确定独角兽工作人员会在当前请求完成后接受QUIT信号关闭。