Upstart



Upstart


У вас есть определенная программа. Не важно, что это, dropbox или самописанный bash-script. Важно то, что вам нужно установить программу в качестве сервиса: чтобы она автоматически стартовала при запуске системы, работала от имени определенного пользователя, перезапускалась в случае незапланированного выхода или просто вызывалась после определенного события.


Раньше для этих целей использовался System-V init. Но теперь в Ubuntu, и некоторых других операционных системахиспользуется новая разработка: Upstart.

Внимание! Здесь и далее я предполагаю, что вы находитесь под учетной записью root. Если это не так, то необходимо ввести префикс sudo перед каждой командой или ввести sudo -i перед продолжением.


Итак, погрузимся в среду upstart!


Создание первого сервиса

nano /etc/init/test.conf

Вставим следующее содержимое

task
script
  echo "Hello, world!"
end script

Запускаем сервис:

start test

first service start

Пока можно не обращать внимание на то, что выводится в ответ, но в кратце: это текущее состояние сервиса (stop, остановлен) и его режим (waiting, ожидание событий).

Посмотрим содержимое лог-файла:

cat /var/log/upstart/test.log

На экране должно появиться сообщение Hello, world! Можно повторить команду start много раз и каждый раз в лог будет выводиться сообщение.

Что только что произошло?

Во-первых все конфигурационные файлы сервисов upstart лежат в директории /etc/init. Можно заглянуть туда, чтобы понять, какие сервисы уже есть в системе. Имя сервиса равно имени файла без расширения ".conf". Т.е. /etc/init/test.conf представляет собой сервис test.

Логи сервисов можно найти в /var/log/upstart/%имя_сервиса%.log

Сервисы управляются утилитой initctl. Чтобы получить список команд initctrl, запустите:

initctl help

start это алиас для initctl start, команда initctl start myservice (или start myservice) запускает сервис myservice
stop это алиас для initctl stop, команда initctl stop myservice (или stop myservice) останавливает сервис myservice

Каждый конфигурационный файл upstart должен содержать секцию script либо exec. exec является однострочной командой и запускает какой-то исполняемый файл. секция script содержит набор команд sh. Может быть либо exec либо секция script.

...
exec /usr/bin/myapp


...
script
  echo "Hello, world!"
  echo "Hello, another world!"
end script


Два вида сервисов


Следующая опция, которая нас интересует — task.

В upstart есть два вида сервисов: те, которые выполняются и завершают свою работу — задачи (task) и те, которые стартуют и продолжают работать в фоне. Если вы создаете сервис, который будет работать в фоне, то task писать не надо. Но в этом случае система upstart будет отслеживать id процесса и состояние сервиса будет зависить от него. Если же вы укажите task, то это будет означать какую-то работу, которая будет выполнена и закончена и upstart будет дожидаться окончания выполнения работы.

Для продолжения установим утилиту nmap:

apt-get install nmap

В отдельном окне терминала запустим эхо-сервис:

ncat -l 7 -k -e /bin/cat

В другом окне терминала попробуем поговорить с сервисом:

telnet localhost 7

Далее вводите все, что угодно и это должно отдаваться эхом (отсюда и название сервиса).

Выйди из telnet можно комбинацией клавиш ctrl + ] и затем написать команду quit.

playing with telnet

Завершите эхо-сервис комбинацией клавиш ctrl+c. Этот сервис — пример сервиса upstart, который является фоновым процессом, а не обычной задачей. Наша задача состоит в том, чтобы создать эхо-сервис в виде сервиса upstart.

nano /etc/init/echoservice.conf

Содержимое файла будет достаточно простым:

exec ncat -l 7 -k -e /bin/cat

Вместо секции script для краткости будем использовать exec. Т.к. нет опции task, то upstart будет считать, что этот сервис — фоновый процесс. Запустим его.

start echoservice

start first process

Теперь кое-что изменилось. Во-первых, состояние процесса — start/running, т.е. он работает. Во-вторых у нас есть process id.

Попробуем найти сервис в списке процессов и сравнить process id:

ps aux | grep -v grep | grep -i ncat

ps aux first process

В системе должен присутствовать процесс с этим ID. Попробуйте пообщаться с эхо-сервисом через telnet, как и раньше.

Попробуем запустить сервис еще раз:
start echoservice

job is already running

Upstart контролирует состояние работы процесса и говорит о том, что процесс уже запущен. Проверить текущее состояние сервиса можно командой:

status echoservice

status — это алиас для команды initctl status.
статус процесса

Остановить сервис можно командой:

stop echoservice

остановка эхо сервиса


Автоматический перезапуск сервисов


Запустим опять наш эхо сервис и проверим через telnet, что он работает:

start echoservice

А теперь принудительно выгрузим процесс (вместо %processid% подставьте свой номер процесса):

kill -9 %processid%

service is killed

Telnet не реагирует, inictl говорит, что процесс не запущен, ps подтверждает это. 

Откроем наш конфиг и добавим в него строчку, состояющую из одного слова: respawn.

nano /etc/init/echoservice.conf

respawn
exec ncat -l 7 -k -e /bin/cat

Теперь запустим процесс и попробуем принудительно выйти из него снова.

process was restarted

Как видно, upstart будет автоматически перезапускать его. При этом, естественно, будет меняться process id.

Демоны, форки и правильный запуск сервисов

Если запускать приложение в секции exec и использовать спец-символы (двойные кавычки, $ и т.д.), то upstart будет стартовать это приложение через dash (/bin/sh). В этом случае вы увидите как минимум два процесса: /bin/sh и /usr/bin/myapp.

Если это произошло — используйте следующую конструкцию:

script
  exec /usr/bin/myapp параметры
end script


В этом случае будет создан только один процесс.

Также некоторые процессы вызывают системную функцию fork при запуске себя. Некоторые делают это один раз (fork), некоторые два (daemon). В этом случае process id тоже не будет соответствовать. Если это происходит — вставьте в ваш конфиг одну из двух строк:

Если fork был вызван один раз:

expect fork

Если fork был вызван два раза:

expect daemon


Если вы не уверены, то можно поступить просто. Начать с daemon и попробовать запустить процесс. Если при запуске команда start не завершается, значит нужно попробовать поменять на fork. Если опять не завершается, значит не нужно использовать ни одну из двух секций.

Также можно использовать утилиту strace


События


Запуск сервисов вручную это удобно, но нам нужен автоматизм. Отредактируем наш тестовый сервис, который мы создали в самом начале, добавив в него строку start on myevent и немного модифицируем скрипт, выведя текущую дату/время для удобства.

nano /etc/init/test.conf

start on myevent
task
script
  echo "Hello, world! (`date`)"
end script

Откроем отдельное окно терминала и поставим наблюдение за логом.

tail -f /var/log/upstart/test.log

Для начала запустим сервис вручную и проверим, что все работает.

start test

hello world с датой

Мы должны увидеть очередную строку «Hello, world!», но на этот раз с датой.

А теперь сгенерируем событие myevent:

initctl emit myevent

hello world со второй датой

В логах должно появиться новое сообщение. Командой initctl emit мы сгенерировали новое событие, на которое был подписан наш сервис. В данном случае это было наше собственное событие.

Но как использовать свои события? Можно добавить, к примеру, следующую строку в crontab:

* * * * *   /sbin/initctl emit everyminevent

В этом случае каждую минуту будет генерироваться событие everyminevent. Можно создать любое количество сервисов, которые будут срабатывать на это событие. Используя cron, можно сгенерировать события с любым нужным вам интервалом. 

Но почему бы просто не вызывать нужные команды сразу в cron? Плюсов использования upstart достаточно много: автоматическое отслеживание и перезапуск процессов, которые завершились по какой-то причине. Возможность запуска сервиса вручную. Возможность задания сервису нескольких событий сразу. И так далее. В планах разработчиков upstart заменить систему cron полностью, добавив необходимый синтаксис для временных событий в upstart. А пока что можно пользоваться этим способом.

Также можно генерировать эти события командой initctl emit из любого скрипта, который запущен от имени root. Скажем, вы стартуете какую-то программу скриптом и отловили ошибку. Можно сгенерировать новое событие, myapperror и создать несколько сервисов. Первый сервис может писать подробное описание возникшей ошибки в лог-файл. Второй сервис, к примеру, отошлет письмо вам на мыло. Все сервисы будут работать параллельно, ускоряя работу системы.

Если нужно временно остановить реагирование на события, просто добавьте строчку manual в ваш конфиг-файл:

manual

Кроме своих собственных событий, есть события других процессов. Немного модифицируем наш тестовый сервис, добавив в него еще одно событие:

nano /etc/init/test.conf

start on myevent or (stopped echoservice)
task
script
  echo "Hello, world! (`date`)"
end script


А теперь остановим echoservice.

stop echoservice

hello world третье сообщение

Как видно, наш сервис отреагировал на событие «Остановлен сервис echoservice». У каждого сервиса есть 4 события:

starting -> started -> stopping -> stopped

Событие starting возникает в тот момент, когда мы дали команду запустить какой-то сервис, но ДО самого запуска. Событие started возникает сразу после запуска сервиса. Событие stopping возникает после того, как мы дали команду завершить какой-то сервис, но ДО его завершения. Событие stopped возникает после завершения сервиса. Таким образом можно строить зависимости сервисов. К примеру, можно сделать сервис, который будет заниматься подготовительным процессом для другого сервиса. В этом случае он должен срабатывать на starting.

Частным случаем является запуск сервиса во время запуска системы и остановка его при перезагрузке или выключении системы (чтобы понять полностью этот синтаксис, обратитесь к документации, либо просто пользуйтесь этим шаблоном):

start on runlevel [2345]
stop on runlevel [016]


Запуск от имени определенного пользователя

Во всех примерах выше мы запускали все сервисы от имени root. Но что, если нужно запустить сервис от имени другого пользователя? 

Это сделать достаточно просто. Для начала произведем эксперимент. Отредактируем наш тестовый сервис:

nano /etc/init/test.conf

Добавим создание тестового файла:

start on myevent or (stopped echoservice)
task
script
  echo "Hello, world! (`date`)"
  touch /tmp/testfile
end script

Запустим сервис и посмотрим на атрибуты файлы:

start test
ls -la /tmp/testfile

временный файл от имени root

Удалим файл:

rm -rf /tmp/testfile

Теперь добавим секции setuid и setgui (замените на имя вашего пользователя и группы):

nano /etc/init/test.conf

start on myevent or (stopped echoservice)
setuid zeroed
setgid zeroed
task
script
  echo "Hello, world! (`date`)"
  touch /tmp/testfile
end script

start test
ls -la /tmp/testfile

временный файл с пользовательскими аттрибутами

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


Шпаргалка


/etc/init — директория, содержащая все conf файлы
initctl — основная утилита upstart
start someservice — алиас для initctl start someservice, стартует сервис
stop someservice — алиас для initctl stop someservice, останавливает сервис
status someservice — алиас для initctl status someservice, показывает статус сервиса
initctl emit myevent — сгенерировать событие myevent

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

Создание сервиса-задачи, которая запускается при генерировании определенного события или при старте другого сервиса. Запуск происходит от имени пользователя www-data, группа www-data.

start on myevent or (starting someservice)
task
setuid www-data
setgid www-data
exec /usr/bin/myapp

Сервис-процесс с автоматическим перезапуском, стартующий при старте системе.

start on runlevel [2345]
stop on runlevel [016]
respawn
script
  echo "Hello, world!"
  exec /usr/bin/myapp
endscript

Процесс-демон, стартующий перед остановкой сервиса myservice. Сервис временно отключен от событий.

start on stopping myservice
respawn
expect daemon
exec /usr/bin/myapp
manual

Система Upstart содержит очень много возможностей и настроек: создание нескольких сущностей процессов, вместо одного; управление логированием; скрипты, запускающиеся перед/после выполнения процесса и многое другое. Для более подробной информации обратитесь к руководству по upstart:

Комментарии

Популярные сообщения из этого блога

Права на папки и файлы (unix/chmod)

Автоматическое монтирование дисков и разделов в Linux или что такое fstab? Проблема Debian

Как подключить сетевой диск Windows в Linux