Nodejs-Server via Apache-Reverse-Proxy und Monit

Um einen Nodejs-Server aufzusetzen braucht es nicht viel. Eigentlich nur die Binaries und eine JS-Datei mit dem eigentlichen Server. Doch wenn man den Server dauerhaft und bequem zugreifbar betreiben möchte, sind ein paar mehr Schritte notwendig.

Zunächst wird Nodejs und der Paketmanager npm installiert:

1
apt-get install nodejs npm

Danach richten wir den Proxy ein, sonst wäre ein Aufruf nur über einen Nicht-Standard-Port (80 ist ja schon vom Apachen belegt) möglich. In der Regel verwendet Nodejs hier Port 3000, aber das ist je nach Script unterschiedlich. Wir aktiviren also zunächst den Passthrough-Modus:

1
2
a2enmod proxy
a2enmod proxy_http

Für die eigentliche Einrichtung kann jetzt ein eigener VHost (z.B. eine Subdomain) angelegt werden, oder – wie in meinem Beispiel – ein Unterverzeichnis hergenommen werden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<VirtualHost ip:80>
	ServerName   myhost.com:80
	ServerAlias  www.myhost.com
	UseCanonicalName Off
	DocumentRoot /var/www/vhosts/myhost/httpdocs
	ErrorLog  /var/www/vhosts/myhost/statistics/logs/error_log
 
	...
 
	<Directory /var/www/vhosts/myhost/httpdocs>
		Options -Includes +ExecCGI
	</Directory>
 
	<Proxy *>
  		Order deny,allow
  		Allow from all
	</Proxy>
 
	<Location /mynodejspath>
  		ProxyPass http://localhost:3000
  		ProxyPassReverse http://localhost:3000
	</Location>
</VirtualHost>

Würde man nun nodejs manuell starten, könnte man schon per http://myhost.com/mynodejspath darauf zugreifen. Da Nodejs aber noch ein wenig fehleranfällig ist und dann gern mal sang- und klanglos abtritt, benutze ich monit zur Überwachung und ggf. zum Neustart des Servers (der Nodejs-App).

1
apt-get install monit

Als Startscript verwende ich ein Standard-init.d-Script, sodass ich auch beim Systemstart problemlos den Nodejs-Server mitstarten lassen kann. Dabei ist zu beachten, dass ich auf Benutzer „root“ prüfe und den Nodejs-Server ggf. über einen extra User starte. Andere Nutzer starten die Nodejs-App mit diesem Script als sie selbst.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/bash
DIR=/var/www/vhosts/myhost/httpdocs/mynodejspath
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
NODE_PATH=/usr/lib/node_modules
NODE=/usr/bin/node
 
test -x $NODE || exit 0
 
function start_app {
  if [ $(whoami) = "root" ]
  then
    NODE_ENV=production nohup sudo -u nodejsusername "$NODE" "$DIR/app.js" 1>>"$DIR/logs/node.log" 2>&1 &
    echo $! > "$DIR/pids/node.pid"
  else
    NODE_ENV=production nohup "$NODE" "$DIR/app.js" 1>>"$DIR/logs/node.log" 2>&1 &
    echo $! > "$DIR/pids/node.pid"
  fi
}
 
function stop_app {
  kill `cat $DIR/pids/node.pid`
}
 
case $1 in
   start)
      start_app ;;
    stop)
      stop_app ;;
    restart)
      stop_app
      start_app
      ;;
    *)
      echo "usage: myapp {start|stop|restart}" ;;
esac
exit 0

Die Verzeichnisse „pids“ und „logs“ müssen unter dem App-Pfad noch angelegt werden. Danach kommt das Monit-Script nach /etc/monit/noderc:

1
2
3
4
5
6
7
8
9
10
#!monit
set logfile /var/log/monit.log
 
check process nodejs with pidfile "/var/www/vhosts/myhost/httpdocs/mynodejspath/pids/node.pid"
    start program = "/etc/init.d/myapp start"
    stop program  = "/etc/init.d/myapp stop"
    if failed port 3000 protocol HTTP
        request /
        with timeout 10 seconds
        then restart

… und ein Starter-init.d Script nach /etc/init.d/monit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
 
case $1 in
   start)
      monit -d 60 -c /etc/monit/noderc
      ;;
    stop)
      killall monit
      ;;
    restart)
      stop_app
      start_app
      ;;
    *)
      echo "usage: monit {start|stop|restart}" ;;
esac
exit 0

Ein Symlink nach rc2.d komplettiert die Automatisierung:

1
2
cd /etc/rc2.d/
ln -s ../init.d/monit S99monit

 

Somit haben wir viele Probleme gelöst:

  • Nodejs startet mit dem Server
  • Es wird überwacht und falls notwendig neu gestartet
  • Der Jeweilige Besitzer (und root) können es manuell neu starten
  • Es läuft mit eingeschränkten Userrechten
  • Es ist über Port 80 zugreifbar
  • und wir haben wieder etwas gelernt 😉