Использование Upstart для управления Unicorn w / rbenv + binstubs для комплектов с ruby-local-exec shebang - PullRequest
9 голосов
/ 29 декабря 2011

Хорошо, это тает мой мозг. Это может быть связано с тем, что я не понимаю Upstart так хорошо, как должен. Заранее извините за длинный вопрос.

Я пытаюсь использовать Upstart для управления главным процессом Unicorn в Rails-приложении. Вот мой нынешний /etc/init/app.conf:

description "app"

start on runlevel [2]
stop on runlevel [016]

console owner

# expect daemon

script
  APP_ROOT=/home/deploy/app
  PATH=/home/deploy/.rbenv/shims:/home/deploy/.rbenv/bin:$PATH
  $APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production # >> /tmp/upstart.log 2>&1
end script

# respawn

Это прекрасно работает - единороги запускаются великолепно. Что не очень хорошо, так это то, что обнаруженный PID принадлежит не мастеру Unicorn, а процессу sh. Это само по себе не так уж и плохо - если бы я не использовал автоматическую стратегию развертывания Unicorn без простоев. Потому что вскоре после того, как я посылаю -USR2 своему мастеру-единорогу, появляется новый мастер, а старый умирает ... и процесс sh тоже. Поэтому Upstart считает, что моя работа умерла, и я больше не могу перезапустить ее с помощью restart или остановить ее с помощью stop, если я хочу.

Я поиграл с файлом конфигурации, пытаясь добавить -D к строке Unicorn (например, $APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production -D), чтобы демонизировать Unicorn, и я добавил строку expect daemon, но это тоже не сработало , Я тоже пробовал expect fork. Различные комбинации всех этих вещей могут привести к зависанию start и stop, и тогда Upstart действительно запутается в состоянии работы. Затем я должен перезагрузить машину, чтобы исправить это.

Я думаю, что у Upstart возникают проблемы с обнаружением, когда / если Unicorn разветвляется, потому что я использую rbenv + ruby-local-exec shebang в моем $APP_ROOT/bin/unicorn сценарии. Вот оно:

#!/usr/bin/env ruby-local-exec
#
# This file was generated by Bundler.
#
# The application 'unicorn' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
  Pathname.new(__FILE__).realpath)

require 'rubygems'
require 'bundler/setup'

load Gem.bin_path('unicorn', 'unicorn')

Кроме того, скрипт ruby-local-exec выглядит следующим образом:

#!/usr/bin/env bash
#
# `ruby-local-exec` is a drop-in replacement for the standard Ruby
# shebang line:
#
#    #!/usr/bin/env ruby-local-exec
#
# Use it for scripts inside a project with an `.rbenv-version`
# file. When you run the scripts, they'll use the project-specified
# Ruby version, regardless of what directory they're run from. Useful
# for e.g. running project tasks in cron scripts without needing to
# `cd` into the project first.

set -e
export RBENV_DIR="${1%/*}"
exec ruby "$@"

Так что там есть exec, о котором я беспокоюсь. Он запускает процесс Ruby, который запускает Unicorn, который может или не может демонизировать себя, что в первую очередь происходит из процесса sh ... что заставляет меня серьезно сомневаться в способности Upstart отследить все это нонсенс.

Возможно ли то, что я пытаюсь сделать? Из того, что я понимаю, строфе expect в Upstart можно только сказать (через daemon или fork) ожидать максимум двух вилок.

1 Ответ

15 голосов
/ 07 апреля 2012

Ваше задание upstart необходимо настроить так, чтобы upstart точно знал, сколько раз оно разветвляется. И он может только разветвляться один или два раза, не более.

В Unix Land есть два ключевых системных вызова, которые облегчают запуск программ: fork и exec.

fork копирует процесс, который его вызывает. Один процесс вызывает fork, и он возвращает управление двум процессам. Каждый процесс должен определить, какой он (родительский или дочерний) из значения, возвращаемого fork (подробности см. На странице man).

exec запускает новую программу, заменяя процесс, вызвавший exec.

Когда вы просто запускаете команду в оболочке, из-под оболочки оболочка вызывает fork, чтобы создать новый процесс с собственным идентификатором, и этот новый процесс (после некоторой настройки) немедленно вызывает exec, чтобы запустить Команда, которую вы ввели. Именно так запускается большинство программ, будь то оболочка или ваш оконный менеджер или что-то еще. См. Функцию system в C, которая также имеет варианты в большинстве языков сценариев.

Если вы думаете, что это неэффективно, вы, вероятно, правы. Это то, как это было сделано в Unix с давних времен, и, очевидно, никто не может изменить это. Одна из причин заключается в том, что существует множество вещей, которые не заменены на exec, включая (иногда) открытые файлы и идентификаторы пользователя и группы процесса.

Другая причина заключается в том, что МНОГО усилий было потрачено на повышение эффективности fork, и они на самом деле сделали это довольно хорошо - в современных Unix-системах (с помощью ЦП) fork фактически копирует очень мало процесса. Я думаю, никто не хочет выбросить всю эту работу.

И, (пауза для эффекта) pid процессов.

Для демонстрации:

mslade@mickpc:~$ echo $$
3652
mslade@mickpc:~$ bash
mslade@mickpc:~$ echo $$
6545
mslade@mickpc:~$ exec bash
mslade@mickpc:~$ echo $$
6545
mslade@mickpc:~$ exit
exit
mslade@mickpc:~$ echo $$
3652

Большинство популярных языков имеют разновидности fork и exec, включая shell, C, perl, ruby ​​и python. Но не Java.

Итак, учитывая все это, вам нужно сделать так, чтобы ваша работа выскочки работала так, чтобы она разветвлялась столько же раз, сколько думает выскочка.

Строка exec в ruby-local-exec на самом деле хорошая вещь, она предотвращает форк. Также load не запускает новый процесс, он просто загружает код в существующий интерпретатор ruby ​​и запускает его.

Однако ваш сценарий оболочки разветвляется в этой строке:

$APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production # >> /tmp/upstart.log 2>&1

, чтобы предотвратить это, вы можете просто изменить его на

exec $APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production # >> /tmp/upstart.log 2>&1

Если вы сделаете это, единорог AFAICT вообще не должен разветвляться, и вам не нужно будет говорить выскочке, чтобы ожидать разветвления.

...