Upstart

Upstart - событийно-ориентированная система инициализации разработанная для дистрибутива Ubuntu.

По умолчанию, большинство демонов в юникс-системах используют обычные шелл-скрипты расположенные в /etc/init.d/ для контроля запуска и остановки. Эти скрипты можно запустить с помощью команды service. Но в Ubuntu, она сперва попробует запустить системную задачу Upstart в каталоге /etc/init/, перед тем, полезть в /etc/init.d/.

Зачем нужен Upstart

Для того, чтобы написать демон, разработчику необходимо решить достатоточно много задач. Например, отделение приложения от терминала, обработка форков, передача прав, логгирование и много ещё чего. Upstart берёт все эти задачи на себя. Так же, с Upstart, разработчику не нужно писать инициализирующий шелл-скрипт, который учитывает все аспекты запуска, остановки и перезагрузки приложения. Вместо этого, разработчик может писать обычное консольное приложение, а Upstart сам решит как передать права на исполнение, в правильной ли рабочей директории запущен проект, куда писать логи и т.д. Upstart отвечает за то, чтобы наше приложение запустилось снова после внезапного краха.

Задачи (Jobs)

Чисто технически, задачи - это процессы, запущенные нашей системой инициализации, но здесь мы будем называть так сами конфигурационные файлы.

Задачи бывают двух основных типов: сервисы и задания. Сервисы - это привычные нам постоянно работающие процессы, демоны: nginx, postgres, postfix и т.д. Задания - это недолгоживущие процессы, команды, которые выполняются один раз и останавливаются. Ещё существует третий тип, абстрактные задачи. Это задачи без явно задекларированных способов выполнения, т.е. без секции script или exec, а следовательно и без PID. Тем не менее, они тоже могут быть запущены и остановлены.

Так же, задачи можно разделить на системные и сессионные. Системные находятся в каталоге /etc/init/. Сессионные (начиная с 13.04) в $XDG_CONFIG_HOME/upstart/, $XDG_CONFIG_DIRS или /usr/share/upstart/sessions. Эти задачи будут работать только для залогиненного пользователя, и они запускаются не системным процессом Upstart (PID 1), а пользовательским, т.е. для пользователя запустится своя копия Upstart. На момент 13.10, системные задачи по умолчанию работают только в десктопном дистрибутиве. Но их легко запустить и на сервере. Все файлы задач имеют расширение .conf.

Ещё, задачи бывают добавочными, если у них отсутствуют секции start on или stop on. Такие задачи будут запускаться или останавливаться вручную администратором или другим процессом.

Настройка задач

Иногда нам нужно внести изменения в файл задачи, но хотелось бы оставить сам файл нетронутым. Для этого, мы можем создать файл с тем же именем и расширением override. Например, для того чтобы отключить автозапуск демона, мы можем добавить в ovverride-файл строчку "manual". Или перезаписать там переменные окружения, которые будет использовать наш демон.

События

Любое событие неминуемо доходит до всех заинтересованных в нём сторон (задач или других событий). "Заинтересовать" задачу в событии можно объявив это событие в "stop on" или "start on" секциях.

Администратор может вручную выпустить событие командой initctl emit <событие>.

События бывают трёх видов: сигналы, методы и хуки. Методы представляют из себя блокирующие события. Будучи "выпущенным", метод дождётся окончания initctl, будь оно успешным или нет. Сигналы - это неблокирующие, асинхронные события. Эмиттер, посылает сигнал и всё остальное не его дело, примет его кто-нибудь или нет, успешно его примут или нет. Хуки тоже блокирующие события. Они используются, когда надо оповестить принимающие стороны о том, что какое-то событие только собирается произойти (например, starting и stopping).

Если одна задачи зависит от другой, то её можно запустить с помощью события started (в случае, если вторая задача должна быть уже запущена) или starting (в случае, если первая задача должна идти раньше второй), например:

start on starting apache2
stop on stopped apache2

respawn

exec /usr/sbin/memcached

Разные заметки об инициализации

Команды, которые не умеют становиться демонами, используют в своих инит-скриптах утилиту start-stop-daemon, которая запускает процессы в фоновом режиме.

Специально для задач Upstart так же есть команды start, stop, status. Ну, и пожалуй, лишним будет говорить, что /sbin/init в Ubuntu тоже из пакета Upstart.

И в инит-скриптах, и в upstart-задачах принято сорсить конфигурацию из одноимённого файла в каталоге /etc/defaults/. Обычно там собраны просто переменные окружения, которые как-то влияют на запуск демона. Или опции командной строки.

Уровни запуска

Уровни запуска (runlevels) представляют из себя классическую концепцию инициализации, которая использовалась задолго до событийно-ориентированной концепции Upstart. Они позволяют запускать процессы или останавливать процессы только на определённых этапах (фактически на "определённом этапе").

Несмотря на то, что события позиционируются как более современная система, уровни запуска не собираются никуда пропадать, и очень активно используются.

0: Система выключена 1: Однопользовательский режим (root-only, аналог безопасного режима в Windows) 2: Многопользовательский режим (по-умолчанию) 3: То же самое, что и 2, но не используется 4: То же самое, что и 2, но не используется 5: То же самое, что и 2, но не используется 6: Перезагрузка

Демоны

Ранее я упомянул, что Upstart берёт всю работу по демонизации на себя, а разработчик может писать обычное терминальное приложение. Но я не уточнил, что такое положение вещей является предпочтительным. То есть, Upstart мотивирует разработчиков писать именно простые консольные приложения, если нету никаких дополнительных требований. Но некоторые приложения всё-таки запускаются демонизированными, т.е. сперва запускают мастер-процесс, потом делают форк и создают рабочий процесс. И это несмотря на то, что Upstart'у необходимо знать ID процесса, чтобы например, при крахе перезапустить его. Upstart просто берёт первый PID из директив script или exec и полагает, что это и есть наш демон. Что в случае форка конечно же оказывается неверным. Для этого существует директива expect.

В случае если это будет единичный форк:

expect fork

Или в случае, процесс делает форк дважды:

expect daemon

Больше двух раз делать форк процессу бессмысленно.

Как только директива expect установлена правильно, можно ставить директиву respawn. Если воспользоваться директивой respawn с неверным ожиданием, то upstart будет респаунить запускающий процесс, что очевидно не то, чего кому-то хотелось бы.