Время ожидания монитора истекает при остановке рабочих-единорогов, если они возрождаются слишком быстро.

Я пытаюсь контролировать рабочих-единорогов с помощью monit, поэтому он изящно убивает их, когда они достигают определенного порога памяти.

Проблема:

Когда я приказываю monit перезапустить воркер, он сначала пытается его остановить, запуская мою команду сценария /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 может перезапускать воркера, когда это необходимо, и продолжать следить за ним, но в журнале полно ошибок, веб-интерфейс показывает неприятный (и сбивающий с толку) статус ошибки execution failed для воркера, и я предполагаю, что это отправили бы ошибочные оповещения по электронной почте, если бы они были настроены.

Это соответствующая часть журнала, когда я пытаюсь перезапустить воркера через веб-интерфейс (обратите внимание, как он также путается с родительским 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 не понимает, что pid рабочего процесса изменился, и продолжает ждать смерти рабочего (отрубленного) рабочего. Затем время ожидания истекает, затем он понимает, что pid действительно изменился, и все идет как обычно.

Грязное решение, которое я нашел:

Чтобы доказать эту гипотезу, я попытался замедлить упомянутую операцию kill-respawn worker. Поэтому я отредактировал конфигурационный файл единорога, чтобы новые рабочие засыпали за несколько секунд до того, как они запишут свой новый pid в /tmp/unicorn.orly.0.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

Вопрос:

Есть ли метод контроля для достижения этой цели? Засыпать моих рабочих на 3 секунды - не лучшее решение. Любые идеи?

Я понимаю, что с monit это не нормальная ситуация. Мы как бы нарушили цикл перезапуска monit, так как мы не хотим, чтобы start program monit выполнял какие-либо действия, а вместо этого позволяли главному процессу единорога обрабатывать его (как объясняется здесь: http://www.stopdropandrew.com/2010/06/01/where-unicorns-go-to-die-watching-unicorn-workers-with-monit.html)


person ariera    schedule 05.03.2013    source источник
comment
Вы нашли решение с тех пор? Я также думаю, что sleep 3 дурацкий.   -  person kenn    schedule 24.01.2014


Ответы (1)


В нашей среде 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 после завершения текущего запроса.

person John S    schedule 07.03.2013
comment
Не тот ответ, который я искал, но похоже на альтернативу, которую стоит иметь в виду. Большое спасибо за помощь, Джон;) - person ariera; 08.03.2013