Pull to refresh

Node.js демон для FreeBSD: forever + rc.d

Node.JS *
Sandbox
В процессе работы над проектом с использованием Node.js в качестве серверсайда, возникла задача запуска JS скрипта в качестве сервиса, со всем плюшками типа start, stop, restart. По этой теме в принципе уже достаточно информации, но она в основном сводится к использованию Monit + Init под линуксом, либо кратких советов типа «use nodemon, Luke».

В моём случае в качестве продакшн-сервера была машина на FreeBSD. В качестве утилиты запускающей JS файл как процесс мне приглянулся forever. Эта штука, в отличии от nodemon, умеет следить за запущенными под ней процессами и поднимать их при падении без ожидания изменения в исходниках скрипта, что очень удобно именно на продакшене. К тому же forever умеет показывать текущий список запущеных процессов, с отображением их аптайма, ну и позволяет останавливать/перезапускать процессы по имени или индексу.

Недолго думая, был написан rc.d скрипт:


# cat /usr/local/etc/rc.d/factory
#!/bin/sh

# PROVIDE: factory
# REQUIRE: NETWORKING SERVERS DAEMON
# BEFORE:  LOGIN
# KEYWORD: shutdown

. /etc/rc.subr

name="factory"
forever="/usr/local/bin/node /usr/local/bin/forever"
workdir="/usr/home/www/factory"
script="index.js"

rcvar=`set_rcvar`

start_cmd="start"
stop_cmd="stop"
restart_cmd="restart"

load_rc_config $name
eval "${rcvar}=\${${rcvar}:-'NO'}"

start()
{
  HOME=/root
  NODE_ENV=production
  ${forever} start -a -l /var/log/forever.log -o /dev/null -e ${workdir}/logs/node_err.log --sourceDir ${workdir} ${workdir}/node/${script}
}

stop()
{
  ${forever} stop ${workdir}/node/${script}
}

restart()
{
  ${forever} restart ${workdir}/node/${script}
}

run_rc_command "$1"


В /etc/rc.conf была добавлена строка: factory_enable=«YES». После чего произведён пробный запуск: /usr/local/etc/rc.d/factory start и скрипт радостно запустился. Проверив так же работу restart и stop я довольный собой отправился налить себе чаю. При этом, дабы проверить запуск скрипта в боевых условиях, отправил в ребут сервер.

Вернувшись с чаем, я открыл браузер и обнаружил что серверсайд не запустился. Журнал forever содержал следующее:
# cat /var/log/forever.log
...
warn: Forever restarting script for 11119 time
warn: Forever detected script exited with code: 127
warn: Forever restarting script for 11120 time
warn: Forever detected script exited with code: 127
warn: Forever restarting script for 11121 time
warn: Forever detected script exited with code: 127
warn: Forever restarting script for 11122 time
warn: Forever detected script exited with code: 127
warn: Forever restarting script for 11123 time
warn: Forever detected script exited with code: 127
warn: Forever restarting script for 11124 time


Таким образом, с момента как сервис «запустился», дочерний JS-процесс успел упасть/подняться уже 11124 раза, пока я топил в кипятке пакетик с чаем.

Всякий раз, когда что-то запускается с консоли но не работает при старте в «чистом» окружении внутренний голос шепчет одно и то же: «Переменные окружения же!». Но как же так? Я же указал HOME о котором везде пишут, и NODE_ENV=production необходимый для работы express. Что ещё надо?

А надо сравнить разницу между пользовательским окружением и окружением при старте системы. В результате этого сравнения в rc.d скрипт, в секцию «start», было добавлено ещё три переменные окружения: USER, PATH и PWD. После этого JS-скрипт начал стартовать корректно. Собственно рабочая версия rc.d скрипта стала выглядеть так:

#!/bin/sh

# PROVIDE: factory
# REQUIRE: NETWORKING SERVERS DAEMON
# BEFORE:  LOGIN
# KEYWORD: shutdown

. /etc/rc.subr

name="factory"
forever="/usr/local/bin/node /usr/local/bin/forever"
workdir="/usr/home/www/factory"
script="index.js"

rcvar=`set_rcvar`

start_cmd="start"
stop_cmd="stop"
restart_cmd="restart"

load_rc_config $name
eval "${rcvar}=\${${rcvar}:-'NO'}"

start()
{
  USER=root
  PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin
  PWD=/root
  HOME=/root
  NODE_ENV=production
  ${forever} start -a -l /var/log/forever.log -o /dev/null -e ${workdir}/logs/node_err.log --sourceDir ${workdir} ${workdir}/node/${script}
}

stop()
{
  ${forever} stop ${workdir}/node/${script}
}

restart()
{
  ${forever} restart ${workdir}/node/${script}
}

run_rc_command "$1"


Покопавшись в «этих ваших интернетах», был удивлён отсутствию информации об отказоустойчивом запуске node.js сервиса таким способом на машине под FreeBSD. Собственно о чём и решил написать, вдруг будет кому полезно.
Tags:
Hubs:
Total votes 19: ↑17 and ↓2 +15
Views 9.5K
Comments Comments 21