structure & meta
This commit is contained in:
parent
da6d838193
commit
b25ce65735
284 changed files with 461 additions and 1000 deletions
203
posts/2014/2014-05-25-supervisor.md
Executable file
203
posts/2014/2014-05-25-supervisor.md
Executable file
|
|
@ -0,0 +1,203 @@
|
|||
<!-- title: Supervisor, gestion de processus -->
|
||||
<!-- category: GNU/Linux -->
|
||||
<!-- tag: planet -->
|
||||
|
||||
Quand il s'agit de déployer des programmes de son cru sur un serveur
|
||||
GNU/Linux, on réalise généralement deux actions :
|
||||
|
||||
- l'écriture d'un script de démarrage et arrêt du programme
|
||||
- la *démon-isation* du programme
|
||||
<!-- more -->
|
||||
Le premier point n'est pas complexe mais il peut être contraignant. Si on
|
||||
envisage de déployer sur Debian, Ubuntu et Fedora, il faut prévoir trois
|
||||
scripts différents : un pour les scripts à la saucce Sys V, un pour Upstart
|
||||
et un autre pour systemd. L'avantage c'est qu'on peut gérer finement les
|
||||
dépendances à d'autres services.
|
||||
|
||||
Le second point consiste à veiller à ne pas bloquer le script d'init en
|
||||
lançant le programme. On peut le gérer dans le code de notre programme en
|
||||
prévoyant deux modes de lancement de notre programme : *daemon* et
|
||||
interactif. Python, par exemple, propose [la librairie
|
||||
daemonize](https://pypi.python.org/pypi/daemonize) pour réaliser cela. JAVA
|
||||
propose des outils comme JAVA Service Wrapper pour gérer le lancement et
|
||||
garantir l'arrêt du processus. On peut aussi le gérer de manière externe au
|
||||
code, de manière rustique avec un
|
||||
[nohup](https://en.wikipedia.org/wiki/Nohup), auquel cas il faut gérer
|
||||
l'arrêt fiable du processus en manipulant son PID.
|
||||
|
||||
Voici un exemple de script d'init à la sauce Debian pour un programme
|
||||
JAVA :
|
||||
|
||||
``` shell
|
||||
### BEGIN INIT INFO
|
||||
# Provides: monprog
|
||||
# Required-Start: $local_fs $remote_fs $network $syslog
|
||||
# Required-Stop: $local_fs $remote_fs $network $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# X-Interactive: true
|
||||
# Short-Description: Start/stop monprog
|
||||
### END INIT INFO
|
||||
BOOT_LOG=/var/log/monprog-boot.log
|
||||
PID_FILE=/opt/monprog/pid
|
||||
start_java () {
|
||||
nohup java -cp "/opt/monprog/lib/*" fr.yax.monprog.Main >$BOOT_LOG 2>&1 &
|
||||
echo $! > $PID_FILE
|
||||
echo "Monprog started ..."
|
||||
}
|
||||
do_start () {
|
||||
echo "Starting Monprog ..."
|
||||
if [ ! -f $PID_FILE ]; then
|
||||
start_java
|
||||
else
|
||||
PID=$(cat $PID_FILE)
|
||||
if [ -d /proc/$PID ]; then
|
||||
echo "Monprog is already running ..."
|
||||
else
|
||||
start_java
|
||||
fi
|
||||
fi
|
||||
}
|
||||
do_stop() {
|
||||
echo "Stopping Monprog ..."
|
||||
if [ -f $PID_FILE ]; then
|
||||
PID =$(cat $PID_FILE);
|
||||
kill $PID 2>/dev/null
|
||||
echo "Monprog stopped ..."
|
||||
rm -f $PID_FILE
|
||||
else
|
||||
echo "Monprog seems not running ..."
|
||||
fi
|
||||
}
|
||||
case $1 in
|
||||
start)
|
||||
do_start
|
||||
;;
|
||||
stop)
|
||||
do_stop
|
||||
;;
|
||||
restart)
|
||||
do_stop
|
||||
sleep 1
|
||||
do_start
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
C'est perfectible. Il faudrait tenter l'arrêt avec un signal moins violent
|
||||
que SIGKILL de façon à l'intercepter dans le code et faire un arrêt propre.
|
||||
Si cette méthode ne fonctionne pas au bout de plusieurs secondes, le script d'init pourrait alors opter pour un arrêt radical.
|
||||
|
||||
Cette méthode fonctionne bien mais elle nécessite une connaissance système
|
||||
pour écrire et maintenir les scripts en fonction des déploiements cible. Si
|
||||
on veut donner la possibilité à un utilisateur standard (non *root*) de
|
||||
démarrer ou arrêter un programme, il faut aussi maîtriser un peu la gestion
|
||||
des droits UNIX (avec sudo par exemple).
|
||||
|
||||
Une alternative simple pour la plupart des systèmes UNIX (GNU/Linux, FreeBSD,
|
||||
Solaris et Mac OS X) est le [programme Supervisor](http://supervisord.org).
|
||||
C'est écrit en Python (comme la plupart des programmes de qualité ! **Troll
|
||||
inside** ) et de même que MongoDB permet au développeur de reprendre la main
|
||||
au DBA sur l'administration de base de donnée, Supervisor permet au
|
||||
développeur de reprendre un peu la main à l'admin sys sur le déploiement de
|
||||
ses applications.
|
||||
|
||||
Supervisor est fourni sur la plupart des distributions. On peut l'installer
|
||||
avec le système de paquet de sa distribution ou bien opter pour une
|
||||
installation à partir de PIP, l'installeur de programmes Python. Ce dernier
|
||||
permet d'obtenir la version la plus récente de Supervisor.
|
||||
|
||||
Supervisor est composé de deux parties :
|
||||
|
||||
- un service **Supervisord**
|
||||
- un client en mode console : **Supervisorctl**
|
||||
|
||||
Le client permet d'interagir avec les programmes gérés : démarrer, stopper.
|
||||
Une interface RPC permet aussi de piloter Supervisord programmatiquement ; je
|
||||
n'ai pas encore testé cet aspect.
|
||||
|
||||
La configuration principale est dans un fichier supervisord.conf qui décrit la
|
||||
connexion du client supervisorctl (section unix_http_server), la config RPC
|
||||
(section rpcinterface) et le répertoire des configuration des programmes à
|
||||
gérer (section includes).
|
||||
|
||||
Voici une configuration type :
|
||||
|
||||
#### /etc/supervisor/supervisord.conf
|
||||
|
||||
[unix_http_server]
|
||||
file=/var/run//supervisor.sock ; (the path to the socket file)
|
||||
chmod=0770 ; sockef file mode (default 0700)
|
||||
chown=root:supervisor
|
||||
|
||||
[supervisord]
|
||||
logfile=/var/log/supervisor/supervisord.log
|
||||
pidfile=/var/run/supervisord.pid
|
||||
childlogdir=/var/log/supervisor
|
||||
|
||||
[rpcinterface:supervisor]
|
||||
supervisor.rpcinterface_factory = supervisorrpcinterface:make_main_rpcinterface
|
||||
|
||||
[supervisorctl]
|
||||
serverurl=unix:///var/run//supervisor.sock
|
||||
|
||||
[include]
|
||||
files = /etc/supervisor/conf.d/*.conf
|
||||
|
||||
Les droits sur la socket UNIX sont important. En donnant l'accès à ROOT et au
|
||||
groupe supervisor, on peut facilement donner l'accès à supervisorctl en
|
||||
ajoutant un utilisateur dans le groupe supervisor. Attention donner l'accès à
|
||||
supervisorctl c'est donner le droit de stopper n'importe quel programme géré
|
||||
par supervisor. C'est un privilège important.
|
||||
|
||||
Le reste de la configuration consiste à créer des configurations
|
||||
additionnelles décrivant des programmes à lancer simplement :
|
||||
|
||||
- par défaut un programme démarre en même temps que le service donc au
|
||||
démarrage du serveur. C'est configurable.
|
||||
- on peut définir si un programme doit être relancé automatiquement et définir sous quelle condition, par exemple en fonction du code de sortie du programme.
|
||||
- on peut opter pour l'envoi d'un signal au programme afin de demander au programme de s'arrêter proprement plutôt que de forcer un arrêt brutal.
|
||||
- on peut regrouper des programmes pour manipuler des
|
||||
groupes, faire des démarrages groupés et des arrêts groupés. C'est utile si on a beaucoup de programmes.
|
||||
- au sein d'un groupe on peut définir des
|
||||
priorités pour ordonner le lancement des programmes du groupe.
|
||||
|
||||
Voici un exemple qui définit un groupe mesprogrammes composé de 2 programmes correspondant au même binaire.
|
||||
|
||||
#### /etc/supervisor/conf.d/mesprogrammes.conf
|
||||
|
||||
[group:mesprogrammes]
|
||||
programs=monprog1,monprog2
|
||||
|
||||
[program:monprog1]
|
||||
directory=/opt/monprogram
|
||||
command=/usr/bin/java -DrunDir=/opt/monprog -cp "lib/*" fr.yax.monprog.Main --port 1234
|
||||
stopsignal=INT
|
||||
priority=500
|
||||
|
||||
[program:monprog2]
|
||||
directory=/opt/monprogram
|
||||
command=/usr/bin/java -DrunDir=/opt/monprog -cp "lib/*" fr.yax.monprog.Main --port 1235
|
||||
stopsignal=INT
|
||||
priority=501
|
||||
|
||||
Dans cet exemple, on envoie un signal SIGINT à monprog pour lui demander un arrêt propre. Voici un snippet de code JAVA pour intercepter le signal :
|
||||
|
||||
#### Interception d'un signal SIGINT en JAVA
|
||||
|
||||
``` java
|
||||
// register a shutdown hook
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("Shutting down has been requested");
|
||||
stopCleanly();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
En conclusion, **Supervisor** est un bon outil de gestion de programmes :
|
||||
fiable, facile à installer et à configurer. En complément d'un outil de
|
||||
déploiement comme [Fabric](http://www.fabfile.org) un développeur peut
|
||||
facilement automatiser le déploiement de son programme sur une ou plusieurs
|
||||
machines cibles.
|
||||
Loading…
Add table
Add a link
Reference in a new issue