nodejs right way to made daemon services - McKAY brothers, multimedia emulation and support

About McKAY's blog

ads

Post Top Ad

Your Ad Spot

2024/03/29

nodejs right way to made daemon services

.. do you use node server command? dont use shitty containers for stupid windosers? foudn a so dumb stupid unit files in stackoverflow and cannot made a right well made unit service? ok i have it ! check this entry and use this right made scritps in your servers.. all the code is CC-BY-SA licensed by the way.. i provide script unit files for SHITstemd, sysvinit, openrc and some more.. 


Why runing as service, why not docker?

Please, i will skip stupid questions, you can runs several docker containers in team to provide a big service, but docker is just a toy with limits.. big deploys just use bare metals.. 

Why not just send to background the service with noup etc etc?

The system must control the daemons.. such daemons must be supervised..so then i also skip several stupid questions.. 

Take in consideration the name of the script (nodeapp) vs the executable (nodejs)?

The service is run by nodejs/node but the app is not nodejs, in this example will be called "nodeappp", the process manager will show "/usr/bin/node server index.js".. but the thread will be "nodeapp", change the nodeapp name with your app node project, but your path must be also named as is... so then

  1. The application name is nodeapp
  2. base parent directory of your application web is /var/lib/node
  3. working directory base of your application web then is /var/lib/node/nodeapp
  4. entry point of your application web is index.js
  5. the web application will run as normal user, by www-data user


USING SHITSTEMD

This is pretty versatile, but also so inflexible, you can made a derived from this script unit file and run several different apps with same unit file using "@" etc etc, but the permission checks are handled separately and this is not so good.. for you not for the system.. by example it does not implement reloading in well way.. so log file must be use the append way and just cutting off by logrotate. Of course, shitstemd already manages their own logging but that is not in sync with the application ..

If you try to search will find that most of the sites argin to use "simple" type and do not managed fail tolerant down, we managed restarting in fails here!

put i on as /usr/lib/systemd/system/nodeapp.service with:

[Unit]
Description=nodeapp
After = network.target syslog.target mysql.service maridb.service
Wants = network-online.target
StartLimitBurst=6

[Service]
Type=exec
Environment = NODE_ENV=production
ExecStart = /usr/bin/node server index.js
Restart = on-failure
RestartSec = 900ms
User = www-data
Group = www-data
WorkingDirectory = /var/lib/node/nodeapp
SyslogIdentifier = nodeapp
StandardOutput = append:/var/log/nodeapp.log

[Install]
WantedBy = multi-user.target

WARNING append log only works with systemd>230 if not just send to syslog and later separate it with a syslogrc rule

USING OPENRC init system

Openrc cannot handle management of fault tolerant like shitstemd, but it has the great "supervise-daemon", we then provide two flavors, the first one the most common you will find in stupid sites like statoverflow, the second one.. using the powerfully "supervise-daemon".

Openrc is default on Geento and also used in alpine linux and BSD systems, just put the unit script as path /etc/init.d/nodeapp

#!/sbin/openrc-run

NODE_ENV=production
export NODE_ENV=$NODE_ENV

name = nodejsapp
command = "/usr/bin/node"
command_user = "www-data"
command_args = "server index.js"
command_background = yes
directory = "/var/lib/node/${RC_SVCNAME}"
pidfile = "/run/nodejs/${RC_SVCNAME}.pid"
output_log = "/var/log/${RC_SVCNAME}.log"

depend() {
  use logger dns
  need net
  after firewall mysql postgresql
}

start_pre() {
    checkpath -d --owner $command_user:www-data --mode 0775 /var/lib/node
    checkpath -d --owner $command_user:www-data --mode 0775 /var/lib/node/${RC_SVCNAME}
    checkpath --file --owner $command_user:www-data --mode 0644 /var/lib/node/${RC_SVCNAME}.log
}

This will run a simpel node app, but in next we will use a real practice new software, wikijs with openrc and supervise-daemon, lest remembered that this script is CC-BY-SA

USING OPENRC + supervise-daemon

This was tested with production made wikijs on some companies, the service was runnig for many weeks, and the reload command is much superior rather than the shitstemd counterpart that is not well managed.

Here the app web name changed from "nodeapp" to "wikijs", there is no index.js but still a web app base directory that changes from /var/lib/node to /srv/web and the app dir path then is /srv/web/wikijs , the fault tolerant part will be managed by the powerfully supervise-daemoon that will be the real father of the running process, so the script will change from /etc/init.d/nodeapp to /etc/init.d/wikijs as:

#!/sbin/openrc-run

NODE_ENV=production
export NODE_ENV=$NODE_ENV

name = wikijs
command = "/usr/bin/node"
command_user = "wikijs"
command_args = "server"
directory = "/srv/web/${RC_SVCNAME}"
pidfile = "/run/${RC_SVCNAME}.pid"
output_log = "/srv/web/wikijs/${RC_SVCNAME}.log"
error_log = "/srv/web/wikijs/error.log"
retry = ${GRACEFUL_TIMEOUT:-60}

supervisor = supervise-daemon
supervise_daemon_args = "--env NODE_ENV='${NODE_ENV:-production}' --chdir '${WORK_DIR:-/srv/web/wikijs}' --stdout '${LOG_FILE:-/srv/web/wikijs/wikijs.log}' --stderr '${LOG_FILE:-/srv/web/wikijs/error.log}'"
respawn_delay = 90
respawn_max = 6

extra_started_commands = "reload"

depend() {
  use logger dns
  need net
  after firewall mysql postgresql
}
reload() {
  ebegin "Truncationg ${RC_SVCNAME} log files, please restart if you want only"
    checkpath -F --owner $command_user:www-data --mode 0644 /srv/web/wikijs/${RC_SVCNAME}.log
    checkpath -F --owner $command_user:www-data --mode 0644 /srv/web/wikijs/error.log
  eend $?
}
start_pre() {
    checkpath --directory --owner $command_user:www-data --mode 0775 /srv/web/wikijs
    checkpath --file --owner $command_user:www-data --mode 0644 /srv/web/wikijs/${RC_SVCNAME}.log
    checkpath --file --owner $command_user:www-data --mode 0644 /srv/web/wikijs/error.log
}
stop_pre() {
    sleep 2
}
stop_post() {
    if [ "${RC_CMD}" == "restart" ] ; then
        einfo "Waith for sync... "
        sleep 1
    fi
}

No lest see how is using sysvinit the most complex but the most ancient and simple to modify and tune it.

USING SYSVINIT scripts

This version will use the start-stop-daemon already present with Alpine linux and debian based distros, this means that you can add "-1 $LOGFILE -2 $LOGFILE" to manage loggin, for a distro agnostic please use telegram venenux groups for more.

This script was tested in Debian wheeze and Devuan daealus also.. so just works.

#!/bin/bash
### BEGIN INIT INFO
# Provides:          nodeapp
# Required-Start:    $remote_fs $named $syslog
# Required-Stop:     $remote_fs $named $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: nodeapp web
# Description:       web applications server by nodejs
### END INIT INFO

PATH=/sbin:/usr/sbin:/bin:/usr/bin
DAEMON_ARGS="server"
WORKDIR="/var/lib/node/nodeapp"
DESC="intranet nodeapp server"
NODEUSER=www-data
NODEGROUP=www-data
PIDWDIR=/run
NAME=nodeapp
DAEMON=/usr/bin/node
PIDFILE="$PIDWDIR/$NAME.pid"
LOGFILE="$WORKDIR/$NAME.log"

[ -x "$DAEMON" ] ||  { echo "can't find Node.js ($DAEMON)"  >&2; exit 0; }
[ -d "$PIDWDIR" ] || { mkdir -p -m 770 $PIDWDIR; }
[ -d "$WORKDIR" ] || { mkdir -p -m 770 $WORKDIR; }
[ -f "$LOGFILE" ] || { touch $LOGFILE; }
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
chown -R $NODEUSER:$NODEGROUP $WORKDIR

. /lib/lsb/init-functions

do_start()
{
    start-stop-daemon --start --quiet --chuid $NODEUSER --pidfile $PIDFILE  --background --chdir $WORKDIR --exec $DAEMON --test > /dev/null \
    || { [ "$VERBOSE" != no ] && log_daemon_msg  "Daemon already running $DESC" "$NAME"; return 1; }
    start-stop-daemon --start --quiet --chuid $NODEUSER --make-pidfile --pidfile $PIDFILE --background --no-close --chdir $WORKDIR --exec $DAEMON -- $DAEMON_ARGS >> $LOGFILE 2>&1 \
    || { [ "$VERBOSE" != no ] && log_daemon_msg  "could not be start $DESC" "$NAME" ; return 2; }
    [ "$VERBOSE" != no ] && log_daemon_msg  "started $DESC" "$NAME"
}

do_stop()
{
    start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --chdir $WORKDIR --chuid $NODEUSER --name $DAEMON
    RETVAL="$?"
    [ "$RETVAL" = 2 ] && return 2
    start-stop-daemon --stop --quiet --oknodo --retry=0/3/KILL/5 --pidfile $PIDFILE --chdir $WORKDIR --chuid $NODEUSER --exec $DAEMON -- $DAEMON_ARGS
    [ "$?" = 2 ] && return 2
    rm -f $PIDFILE
    [ "$VERBOSE" != no ] && [ "$RETVAL" = 1 ] && log_daemon_msg "$DESC not running" "$NAME"
    [ "$VERBOSE" != no -a "$RETVAL" = 0 ] && log_daemon_msg "$DESC stopped" "$NAME"
    return "$RETVAL"
}

do_reload() {
    return 0
}

do_status() {

    if [ -f $PIDFILE ]; then
      ispidactive=$(pidof $DAEMON | grep `cat $PIDFILE 2>&1` >/dev/null 2>&1)
        case "$?" in
            0) [ "$VERBOSE" != no ] && log_success_msg "$NAME is running";;
            1) [ "$VERBOSE" != no ] && log_success_msg "$NAME is not running";;
            2) [ "$VERBOSE" != no ] && log_success_msg "$NAME is runing orphaned, you must kill";;
        esac
    else
      log_success_msg "$NAME is not running and no zomby detected as $USER, of $NAME found"
      exit 3
    fi
  
}

case "$1" in
  start)
    [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "NAME"
    do_start
    case "$?" in
    0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
    2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
    esac
    ;;
  stop)
    [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
    do_stop
    case "$?" in
    0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
    2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
    esac
    ;;
  restart|reload)
    log_daemon_msg "Restarting $DESC" "$INIT_SCRIPT_NAME_NOEXT"
    do_stop
    case "$?" in
      0|1)
    do_start
    case "$?" in
        0) log_end_msg 0 ;;
        1) log_end_msg 1 ;; # Old process is still running
        *) log_end_msg 1 ;; # Failed to start
    esac
    ;;
      *)
        # Failed to stop
    log_end_msg 1
    ;;
    esac
    ;;
  status)
    do_status
  ;;
  *)
    echo "Usage: $NAME {start|stop|restart|status}" >&2
    exit 3
    ;;
esac

exit 0

No hay comentarios.:

Publicar un comentario

no stupid winbuntu users allowed!

Entradas populares

Post Top Ad

Your Ad Spot