Использование runit вместо Init и вывод логов в файл
Я достаточно долгое время использовал связку Init+Monit для запуска скриптов node, но со временем стал натыкаться на минусы такой связки. Одна из самых серьёзных проблем — отсутствие возможности вывести лог в разные файлы в ранних версиях start-stop-daemon. Версия, поставляемая с Debian Lenny не поддерживает такой функционал, а сборка новой версии тянет за собой уйму непонятного барахла. В итоге, попробовав разные варианты, я остановился на runit как замене Init-скриптам.
Установка и настройка
Для большинства систем runit ставится из стандартного менеджера пакетов (apt-get install runit для Debian). Если в Вашем менеджере нужного пакета нет, можно скачать исходники для сборки с официальной страницы.
Каждому сервису в runit соответствует директория в /etc/sv. В этой директории создаём файл запуска run:
mkdir /etc/sv/testservice vim /etc/sv/testservice/run
В файле run пишем строку для запуска нашего скрипта, с перенаправлениями ввода-вывода, без демонизации (&), но с exec:
exec node /etc/mysite/server.js 1>>/var/log/mysite/output 2>>/var/log/mysite/error
Ставим этому файлу разрешение на выполнение:
chmod +x run
Теперь чтобы запустить сервис надо передать runit команду и имя директории внутри /etc/sv:
sv start testservice
Чтобы остановить сервис, вторым параметром передаётся stop, чтобы перезапустить — restart.
Чтобы проверить runit я использовал немного модифицированный “Hello World” с официального сайта:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
console.log('Log message #' + i);
console.error('Err message #' + i);
i++;
}).listen(8124, "192.168.175.128");
console.log('Server running at http://127.0.0.1:8124/')
console.log выводит строку в стандартный поток вывода (stdout), console.error выводит строку в stderr. Строки, переданные в console.log будут выводиться в /var/log/mysite/output, то что передано в console.error будет записано в /var/log/mysite/error.
(Кстати, если сделаете то же самое и увидите что с каждым обновлением в лог попадают сразу две строки с последовательными номерами, не спешите искать баг в коде. Скорее всего Ваш браузер запрашивает вместе со страницей файл favicon.ico для сайта.)
Чтобы прикрутить runit к monit’у, можно создать симлинк в директории /etc/init.d/ на /usr/bin/sv. При таком вызове runit будет запускать/останавливать сервис, соответствующий имени симлинка:
ln -s /usr/bin/sv /etc/init.d/testservice
Теперь при вызове /etc/init.d/testservice start Ваш скрипт будет запущен. Так проще будет перейти с Init на runit если Вы всё таки задумаете это сделать
раз уж используется супервайзер runit то стоит всё делать в этой идеологии.
=== BEGIN /etc/sv/testservice/run ===
#!/bin/sh
NODE_USER=nobody
exec 2>&1
exec chpst -u$NODE_USER node /etc/mysite/server.js
=== END ===
я бы еще добавил softlimit но это по вкусу
логирование тоже через runit сервис
=== BEGIN /etc/sv/testservice/log/run ===
#!/bin/sh
exec chpst -ulog svlogd -tt ./main
=== END ===
Здорово, надо будет поглубже покопать. С chpst я не работал пока, хотя знаю что он есть в наборе (не было надобности ограничивать процесс).
я бы еще добавил немного bash magic, чтобы использовать один run для разных сайтов с разными конфигом
У меня есть немного bash magic для генерации конфигов/скриптов запуска (подстановка версии ноды, порта и т.п.), но об этом пока не писал. У меня просто обычно один сайт на сервере.
А чем этот вариант отличается например от forever? Как раз подошел в разработке к этапу, когда все должно запускаться по-человечески и пытаюсь найти оптимальный вариант
Forever я честно говоря пока не пробовал (думаю потом опишу и его тоже). Судя по всему это замена для всей связки Runit + Monit: управляет перенаправлением ввода-вывода, предоставляет способ запускать и перезапускать процесс, поднимает его в случае необходимости.
Будем ждать описания! А пока сам потихоньку попробую все по полочкам для себя разложить ))
По моему вы заблуждаетесь. И ему до того же Runit далеко. Кстати Runit же прекрасно следит за живучестью сервиса и поднимает его если тот упал и Monit ему не нужен (ну разве только для того, чтобы поднять Runit в случае падения).
К тому же Runit уже заматерелый продукт и шансов получить неприятную багу с ним меньше.
P.S. Аналог Runit это http://supervisord.org. Он написан на python и по виду (конфиги и удобство) выглядит приятней Runit. К тому же можно управлять сервисами через xml-rpc. А еще есть дополнение для него, которое позволяет следить за разными параметрами и в случае чего перезапускает сервис. Скажем можно отслеживать утечки памяти.