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
Пока можно не обращать внимание на то, что выводится в ответ, но в кратце: это текущее состояние сервиса (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.
Завершите эхо-сервис комбинацией клавиш ctrl+c. Этот сервис — пример сервиса upstart, который является фоновым процессом, а не обычной задачей. Наша задача состоит в том, чтобы создать эхо-сервис в виде сервиса upstart.
nano /etc/init/echoservice.conf
Содержимое файла будет достаточно простым:
exec ncat -l 7 -k -e /bin/cat
Вместо секции script для краткости будем использовать exec. Т.к. нет опции task, то upstart будет считать, что этот сервис — фоновый процесс. Запустим его.
start echoservice
Теперь кое-что изменилось. Во-первых, состояние процесса — start/running, т.е. он работает. Во-вторых у нас есть process id.
Попробуем найти сервис в списке процессов и сравнить process id:
ps aux | grep -v grep | grep -i ncat
В системе должен присутствовать процесс с этим ID. Попробуйте пообщаться с эхо-сервисом через telnet, как и раньше.
Попробуем запустить сервис еще раз:
start echoservice
Upstart контролирует состояние работы процесса и говорит о том, что процесс уже запущен. Проверить текущее состояние сервиса можно командой:
status echoservice
status — это алиас для команды initctl status.
Остановить сервис можно командой:
stop echoservice
Автоматический перезапуск сервисов
Запустим опять наш эхо сервис и проверим через telnet, что он работает:
start echoservice
А теперь принудительно выгрузим процесс (вместо %processid% подставьте свой номер процесса):
kill -9 %processid%
Telnet не реагирует, inictl говорит, что процесс не запущен, ps подтверждает это.
Откроем наш конфиг и добавим в него строчку, состояющую из одного слова: respawn.
nano /etc/init/echoservice.conf
respawn
exec ncat -l 7 -k -e /bin/cat
Теперь запустим процесс и попробуем принудительно выйти из него снова.
Как видно, 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!», но на этот раз с датой.
А теперь сгенерируем событие myevent:
initctl emit myevent
В логах должно появиться новое сообщение. Командой 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
Как видно, наш сервис отреагировал на событие «Остановлен сервис 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
Удалим файл:
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:
Комментарии
Отправить комментарий