Wie sorgt man dafür, dass ein Serverprozess automatisch neugestartet wird? Man hat einen zweiten Prozess, der ihn neustartet, wenn er abstürzt oder wenn der Server neugestartet wird. Ein solcher Prozessmanager ist Supervisor, der unabhängig vom Initsystem läuft.
Mit supervisord kann man auch Ruby/Sinatra-Webanwendungen überwachen, klar. Allerdings gibt es da eine Reihe von Fallstricken, die von rvm und bundler kommen. Ein simples Skript wird weder die Ruby-Version noch bundler laden können, und dann daran Scheitern. Und benutzt die Anwendung Puma als Webserver, startet sie aber mit rackup, scheitert Supervisor am Beenden des Pumaprozesses.
Das Problem an Supervisor ist, dass die Umgebungsvariablen des Nutzers nicht geladen werden. Daher funktioniert dann rvm und bundler nicht. Zum Glück braucht es nur eine: $HOME
. Die kann man im Startskript setzen, und dann die Hilfsprogramme laden:
#!/bin/bash --login
HOME="/home/appuser"
echo "init local ruby environment"
if which ruby >/dev/null && which gem >/dev/null; then
PATH="$(ruby -rubygems -e 'puts Gem.user_dir')/bin:$PATH"
fi
source /etc/profile.d/rvm.sh
export GEM_HOME=$(ruby -e 'print Gem.user_dir')
PATH="$(ruby -e 'print Gem.user_dir')/bin:$PATH"
echo "starting server"
cd /home/appuser/app
bundle exec puma --environment production -b unix:///tmp/puma.sock
Achte auch darauf, dass in dem Skript die Anwendung über puma
und nicht über rackup
gestartet wird. Dadurch kann Supervisor den Prozess des Webservers beenden. Ich sah keine Nachteile daran, nur Vorteile, denn Puma kann so auch umfangreich konfiguriert werden. Der hier genutzte Unixsocket zum Beispiel, der dann von Nginx gelesen wird, soll einen kleinen Performancevorteil bieten.
Dies ist die zugehörige Konfigurationsdatei für supervisor, die unter /etc/supervisor/conf.d/ liegt:
[program:webapp]
user = appuser
command = /home/appuser/bin/start_server.sh
autorestart = true
stopasgroup = true
Man könnte die Umgebungsvariable auch hier setzen, siehe das Beispiel in der Dokumentation.
Jetzt sollte alles funktionieren, und die Anwendung mit supervisorctl kontrolliert werden können:
supervisorctl restart webapp
Nicht vergessen, supervisor selbst bei einem Reboot ebenfalls neuzustarten, und dafür den Dienst im Initsystem einzutragen.