概要
Nuxt.jsのアプリケーションをサーバー環境にデプロイする際、Nuxt.jsのプロセスをバックグラウンドで起動して永続化するために、一般的にforeverを使う。
加えて、foreverをデーモン化して、serviceコマンドで[start, stop, status, restart]したり、chkconfigで自動起動しようと思ったら、initd-foreverを使う。
initd-foreverの記事はいくつか存在したけど、Nuxt.jsでそれを扱うという記事は見たところ存在しなかったので、今回はNuxt.jsのアプリケーションでinitd-foreverを導入する例を示す。
最終的には以下のような状態となる。
- Nuxt.jsアプリケーションがservice app_name startといった具合にserviceコマンドで起動、停止、状態確認、再起動できる
- Nuxt.jsアプリケーションをchkconfigでOS起動時に自動起動できる
# 環境
- Amazonlinux 1
準備
- nuxt.jsで作成したアプリケーションをサーバーにデプロイしておく
手順
foreverをインストール
# npm install -g -y forever
foreverでNuxt.jsを起動してみる
# cd /var/www/app_name/
# forever start -c "npm run start" ./
Nuxt.jsのアプリケーションの/(ルート)に移動してforever startを実行する。
-c はコマンドを指定するという意味で、ここではNuxt.jsの起動コマンドである、"npm run start"を指定している。これでforever経由でNuxt.jsの起動ができた。
initd-foreverをインストール
# npm install -g initd-forever
initd-foreverでデーモンファイルを生成
# cd /var/www/app_name/
# initd-forever -a /var/www/app_name/ -n app_name
Script daemon file saved to app_name
# ls
app_name
第一引数はNuxt.jsアプリケーションの/を指定、-n の引数はデーモンファイル名となる。カレントディレクトリにapp_nameというファイルが生成される。
ファイルの中身は以下のようになっている。
# cat /var/www/app_name/app_name
#!/bin/bash
### BEGIN INIT INFO
# If you wish the Daemon to be lauched at boot / stopped at shutdown :
#
# On Debian-based distributions:
# INSTALL : update-rc.d scriptname defaults
# (UNINSTALL : update-rc.d -f scriptname remove)
#
# On RedHat-based distributions (CentOS, OpenSUSE...):
# INSTALL : chkconfig --level 35 scriptname on
# (UNINSTALL : chkconfig --level 35 scriptname off)
#
# chkconfig: 2345 90 60
# Provides: /var/www/app_name/
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: forever running /var/www/app_name/
# Description: /var/www/app_name/
### END INIT INFO
#
# initd a node app
# Based on a script posted by https://gist.github.com/jinze at https://gist.github.com/3748766
#
if [ -e /lib/lsb/init-functions ]; then
# LSB source function library.
. /lib/lsb/init-functions
fi;
pidFile="/var/run/app_name.pid"
logFile="/var/run/app_name.log"
command="node"
nodeApp="npm run start /var/www/app_name/"
foreverApp="forever"
start() {
echo "Starting $nodeApp"
# Notice that we change the PATH because on reboot
# the PATH does not include the path to node.
# Launching forever with a full path
# does not work unless we set the PATH.
PATH=/usr/local/bin:$PATH
export NODE_ENV=production
#PORT=80
$foreverApp start --pidFile $pidFile -l $logFile -a -d -c "$command" $nodeApp
RETVAL=$?
}
restart() {
echo -n "Restarting $nodeApp"
$foreverApp restart $nodeApp
RETVAL=$?
}
stop() {
echo -n "Shutting down $nodeApp"
$foreverApp stop $nodeApp
RETVAL=$?
}
status() {
echo -n "Status $nodeApp"
$foreverApp list
RETVAL=$?
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
*)
echo "Usage: {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL
[start, stop, status, restart]のいずれかの引数を1つ受け取るシェルスクリプトになっている。
デーモンファイルを再配置・権限を付与
# mv app_name /etc/init.d/
# chmod +x /etc/init.d/app_name
他のデーモンと同様、/etc/init.d/に配置し、実行権限を付与した。
Nuxt.jsの場合、このままでは実行してもエラーになってしまうので、デーモンファイル(/etc/init.d/app_name)をNuxt.js用に編集する。
デーモンファイルをNuxt.js用に編集
編集後のファイル
# cat /etc/init.d/app_name
#!/bin/bash
### BEGIN INIT INFO
# If you wish the Daemon to be lauched at boot / stopped at shutdown :
#
# On Debian-based distributions:
# INSTALL : update-rc.d scriptname defaults
# (UNINSTALL : update-rc.d -f scriptname remove)
#
# On RedHat-based distributions (CentOS, OpenSUSE...):
# INSTALL : chkconfig --level 35 scriptname on
# (UNINSTALL : chkconfig --level 35 scriptname off)
#
# chkconfig: 2345 90 60
# Provides: /var/www/app_name/
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: forever running /var/www/app_name/
# Description: /var/www/app_name/
### END INIT INFO
#
# initd a node app
# Based on a script posted by https://gist.github.com/jinze at https://gist.github.com/3748766
#
if [ -e /lib/lsb/init-functions ]; then
# LSB source function library.
. /lib/lsb/init-functions
fi;
pidFile="/var/run/app_name.pid"
logFile="/var/run/app_name.log"
command="node"
nodeApp="/usr/bin/npm --prefix /var/www/app_name/ run start"
foreverApp="forever"
appName="app_name"
start() {
echo -e "Starting $appName\n"
# Notice that we change the PATH because on reboot
# the PATH does not include the path to node.
# Launching forever with a full path
# does not work unless we set the PATH.
PATH=/usr/local/bin:$PATH
export NODE_ENV=production
#PORT=80
$foreverApp start --pidFile $pidFile -l $logFile -a -d -c "$command" $nodeApp
RETVAL=$?
}
restart() {
stop
start
RETVAL=$?
}
stop() {
if [ -e $pidFile ]; then
echo -e -n "Shutting down $appName\n"
cat $pidFile | xargs $foreverApp stop
fi;
RETVAL=$?
}
status() {
if [ -e $pidFile ]; then
pid=$(cat $pidFile)
echo -e -n "$appName (pid $pid) is running...\n"
else
echo -e -n "$appName is stopped\n"
fi;
RETVAL=$?
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
*)
echo "Usage: {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL
[start, stop, status,restart]を実行できるようにした。
差分ファイル
# cat /etc/init.d/app_name
#!/bin/bash
### BEGIN INIT INFO
# If you wish the Daemon to be lauched at boot / stopped at shutdown :
#
# On Debian-based distributions:
# INSTALL : update-rc.d scriptname defaults
# (UNINSTALL : update-rc.d -f scriptname remove)
#
# On RedHat-based distributions (CentOS, OpenSUSE...):
# INSTALL : chkconfig --level 35 scriptname on
# (UNINSTALL : chkconfig --level 35 scriptname off)
#
# chkconfig: 2345 90 60
# Provides: /var/www/app_name/
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: forever running /var/www/app_name/
# Description: /var/www/app_name/
### END INIT INFO
#
# initd a node app
# Based on a script posted by https://gist.github.com/jinze at https://gist.github.com/3748766
#
if [ -e /lib/lsb/init-functions ]; then
# LSB source function library.
. /lib/lsb/init-functions
fi;
pidFile="/var/run/app_name.pid"
logFile="/var/run/app_name.log"
command="node"
-nodeApp="npm run start /var/www/app_name/"
+nodeApp="/usr/bin/npm --prefix /var/www/app_name/ run start" # 起動のコマンドを変更
foreverApp="forever"
+appName="app_name" # アプリケーション名を定義、echo出力用
start() {
- echo "Starting $nodeApp"
+ echo -e "Starting $appName\n" # echo出力に改行コードを追加
# Notice that we change the PATH because on reboot
# the PATH does not include the path to node.
# Launching forever with a full path
# does not work unless we set the PATH.
PATH=/usr/local/bin:$PATH
export NODE_ENV=production
#PORT=80
$foreverApp start --pidFile $pidFile -l $logFile -a -d -c "$command" $nodeApp
RETVAL=$?
}
restart() {
- echo -n "Restarting $nodeApp"
- $foreverApp restart $nodeApp
+ stop # stopを呼び出す
+ start # startを呼び出す
RETVAL=$?
}
stop() {
- echo -n "Shutting down $nodeApp"
- $foreverApp stop $nodeApp
+ if [ -e $pidFile ]; then # $pidFileが存在した場合、serviceが起動中であると見なす
+ echo -e -n "Shutting down $appName\n" # echo出力に改行コードを追加
+ cat $pidFile | xargs $foreverApp stop # $pidFileにpidをもとにforeverの該当プロセスを停止する
+ fi;
RETVAL=$?
}
status() {
- echo -n "Status $nodeApp"
- $foreverApp list
+ if [ -e $pidFile ]; then # $pidFileが存在した場合、serviceが起動中であると見なす
+ pid=$(cat $pidFile) # pidを変数に代入する
+ echo -e -n "$appName (pid $pid) is running...\n" # 起動中ステータスをユーザーに通知する
+ else # $pidFileが存在しなかった場合、serviceが停止中であると見なす
+ echo -e -n "$appName is stopped\n" # 停止中ステータスをユーザーに通知する
+ fi;
RETVAL=$?
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
*)
echo "Usage: {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL
ちょっと解説
# forever start --pidFile /var/run/app_name.pid -l /var/run/app_name.log -a -d -c "node" /usr/bin/npm --prefix /var/www/app_name/ run start
これが実行されるコマンド。
- 非カレントディレクトリにあるnpm scriptは--prefix /var/www/app_name/ run startという感じで呼び出す。
- npmの指定は、/usr/bin/npmといった具合にフルパスで指定する。コマンド名のみで指定すると$PATH変数を読み込む前にinitで起動されて、npm does not exist.となり実行できない。
serviceコマンドで実行してみる
# service app_name status
app_name is stopped
# service app_name start
Starting app_name
warn: --minUptime not set. Defaulting to: 1000ms
warn: --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info: Forever processing file: /usr/bin/npm
# service app_name status
app_name (pid 11019) is running...
# service app_name restart
Shutting down app_name
info: Forever stopped process:
uid command script forever pid id logfile uptime
[0] 74Ci node /usr/bin/npm --prefix /var/www/html/app_name/ run start 10973 11308 /var/run/app_name.log 0:0:0:0.582
Starting app_name
warn: --minUptime not set. Defaulting to: 1000ms
warn: --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info: Forever processing file: /usr/bin/npm
# service app_name stop
Shutting down app_name
info: Forever stopped process:
uid command script forever pid id logfile uptime
[0] eqhV node /usr/bin/npm --prefix /var/www/html/app_name/ run start 11354 11444 /var/run/app_name.log 0:0:0:0.850
# service app_name restart
Starting app_name
warn: --minUptime not set. Defaulting to: 1000ms
warn: --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info: Forever processing file: /usr/bin/npm
start, stop, status, restartが実行できている。
statusは起動時、停止時に想定した挙動となっていて、restartも起動時、停止時に想定した挙動となっている。
chkconfigで自動起動を設定してみる
# chkconfig app_name --add
# chkconfig app_name on
# chkconfig app_name --list
app_name 0:off 1:off 2:on 3:on 4:on 5:on 6:off
これでrunlevelが[2, 3, 4, 5]でOSが起動する際にNuxt.jsアプリケーションが自動起動されるようになった。
参考リンク
- forever - npm
- initd-forever - npm
- forever - github
- initd-forever - github
- initd-forever-nuxt.js - github ← サンプルコードはこちら
まとめ
シェルスクリプトを読み書きできる人なら問題なくできるっぽい。
このスクリプトを改造したら、foreverじゃなくても、いろいろ使えそう。