Aller au contenu
medium

Supervisor : gérer et superviser vos processus

21 min de lecture

Supervisor est un gestionnaire de processus pour systèmes Unix qui démarre, surveille et redémarre automatiquement vos programmes en arrière-plan. Il remplace les scripts personnalisés et les solutions fragiles basées sur cron par un système centralisé, simple à configurer et à opérer. Vous apprendrez dans ce guide à installer Supervisor, configurer des programmes, utiliser supervisorctl pour les contrôler, et exploiter les fonctionnalités avancées comme les groupes de processus et l’interface web.

Supervisor est un système client/serveur qui surveille et contrôle des processus sur des systèmes Unix. Il a été créé pour résoudre un problème concret : garantir que des services critiques restent actifs, sans écrire de scripts rc.d complexes ni vérifier manuellement des fichiers PID.

Analogie : Supervisor est comme un chef d’équipe dans une usine. Il s’assure que chaque ouvrier (processus) est à son poste, le remplace automatiquement s’il tombe malade (crash), et tient un registre précis de ce qui se passe (logs). Le chef d’équipe ne fait pas le travail lui-même, il supervise.

Supervisor se compose de quatre éléments :

ComposantRôle
supervisordLe démon (serveur) qui démarre et surveille les processus
supervisorctlLe client en ligne de commande pour contrôler supervisord
Interface webInterface graphique optionnelle pour monitorer les processus
API XML-RPCInterface programmatique pour intégrer Supervisor à d’autres outils

Les deux outils gèrent des processus, mais ils n’ont pas le même positionnement :

CritèreSupervisorsystemd
ComplexitéFichier INI simpleUnit files + directives avancées
PortabilitéLinux, macOS, FreeBSD, SolarisLinux uniquement
Droits requisFonctionne sans rootSouvent root pour les services système
Cas d’usageApplications utilisateur, conteneursServices système, boot OS
Interface webIntégréeNécessite un outil tiers
Gestion des logsCentralisée par processusJournald

En résumé : utilisez systemd pour les services système, et Supervisor pour vos applications, vos environnements de développement, ou vos conteneurs multi-processus.

Fenêtre de terminal
sudo apt-get update
sudo apt-get install -y supervisor

Le paquet crée automatiquement le fichier /etc/supervisor/supervisord.conf et le dossier /etc/supervisor/conf.d/ pour les configurations par programme. Le service est activé au boot.

Vérification :

Fenêtre de terminal
supervisord --version
# 4.3.0

Supervisor utilise un fichier de configuration au format INI (style Windows-INI). Le fichier principal est recherché dans cet ordre :

  1. ../etc/supervisord.conf (relatif à l’exécutable)
  2. ../supervisord.conf (relatif à l’exécutable)
  3. $CWD/supervisord.conf
  4. $CWD/etc/supervisord.conf
  5. /etc/supervisord.conf
  6. /etc/supervisor/supervisord.conf

Un fichier de configuration complet contient plusieurs sections. Voici les sections essentielles avec leurs options les plus utiles :

[unix_http_server]
file=/var/run/supervisor.sock ; socket Unix pour supervisorctl
chmod=0700 ; permissions du socket
[supervisord]
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
childlogdir=/var/log/supervisor
nodaemon=false ; true = premier plan (utile dans Docker)
loglevel=info ; critical, error, warn, info, debug, trace
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[include]
files = /etc/supervisor/conf.d/*.conf

La section [include] est importante : elle permet de séparer la configuration de chaque programme dans son propre fichier, ce qui facilite la maintenance.

Chaque programme supervisé est déclaré dans une section [program:nom]. Créez un fichier /etc/supervisor/conf.d/mon-app.conf :

[program:mon-app]
command=/usr/bin/python3 /opt/mon-app/main.py
directory=/opt/mon-app
autostart=true
autorestart=true
startsecs=3
startretries=3
user=appuser
stdout_logfile=/var/log/supervisor/mon-app.log
stderr_logfile=/var/log/supervisor/mon-app-error.log
environment=APP_ENV="production",LOG_LEVEL="info"

Voici le rôle de chaque directive :

DirectiveValeur par défautDescription
command(requis)Commande à exécuter. Le programme ne doit pas se daemoniser
directory(hérite de supervisord)Répertoire de travail avant exécution
autostarttrueDémarre automatiquement avec supervisord
autorestartunexpectedtrue = toujours, false = jamais, unexpected = si code de sortie inattendu
startsecs1Durée minimale en secondes avant de considérer le démarrage réussi
startretries3Nombre de tentatives avant l’état FATAL
user(utilisateur de supervisord)Compte Unix sous lequel le programme tourne
stdout_logfileAUTOFichier de logs stdout (AUTO, NONE, ou un chemin)
stderr_logfileAUTOFichier de logs stderr
environment(vide)Variables d’environnement au format CLÉ="valeur"
priority999Ordre de démarrage (les petits chiffres démarrent en premier)
stopsignalTERMSignal envoyé pour arrêter le processus
stopwaitsecs10Temps d’attente avant SIGKILL

Après avoir créé ou modifié un fichier dans /etc/supervisor/conf.d/, rechargez la configuration :

Fenêtre de terminal
supervisorctl reread
# mon-app: available

La commande reread lit les fichiers de configuration mais n’applique rien. Pour appliquer les changements :

Fenêtre de terminal
supervisorctl update
# mon-app: added process group

La commande update démarre les nouveaux programmes, arrête ceux qui ont été supprimés et redémarre ceux dont la configuration a changé.

supervisorctl est l’outil principal pour interagir avec Supervisor au quotidien. Il peut fonctionner en mode interactif (shell) ou en mode commande unique.

Fenêtre de terminal
# Voir l'état de tous les processus
supervisorctl status
# mon-app RUNNING pid 12345, uptime 0:05:23
# worker_00 RUNNING pid 12346, uptime 0:05:23
# Démarrer un processus
supervisorctl start mon-app
# mon-app: started
# Arrêter un processus
supervisorctl stop mon-app
# mon-app: stopped
# Redémarrer un processus
supervisorctl restart mon-app
# mon-app: stopped
# mon-app: started
# Redémarrer tous les processus
supervisorctl restart all
# Consulter les logs d'un processus
supervisorctl tail mon-app
supervisorctl tail -f mon-app # suivi en temps réel
supervisorctl tail mon-app stderr # logs d'erreur

Il existe trois niveaux de rechargement :

CommandeEffet
rereadRelit les fichiers de configuration, affiche les changements détectés, ne touche à rien
updateApplique les changements : ajoute les nouveaux programmes, retire les supprimés, redémarre les modifiés
reloadRedémarre complètement le démon supervisord (arrête puis relance tous les processus)

En fonctionnement normal, utilisez le couple reread + update. Réservez reload aux cas où une modification de la section [supervisord] elle-même est nécessaire.

Lancer supervisorctl sans argument ouvre un shell interactif :

Fenêtre de terminal
supervisorctl
# supervisor> status
# mon-app RUNNING pid 12345, uptime 0:05:23
# supervisor> help
# ... liste des commandes disponibles
# supervisor> quit

Supervisor permet de démarrer plusieurs instances identiques d’un même programme grâce à la directive numprocs. C’est utile pour les workers, les consommateurs de file d’attente, ou les traitements parallèles.

[program:worker]
command=/usr/bin/python3 /opt/app/worker.py
process_name=%(program_name)s_%(process_num)02d
numprocs=3
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/worker_%(process_num)02d.log
stderr_logfile=/var/log/supervisor/worker_%(process_num)02d-error.log

Quand numprocs est supérieur à 1, la directive process_name doit contenir %(process_num)s pour que chaque instance ait un nom unique. Supervisor crée alors les processus worker_00, worker_01 et worker_02.

Fenêtre de terminal
supervisorctl status
# worker_00 RUNNING pid 12350, uptime 0:02:10
# worker_01 RUNNING pid 12351, uptime 0:02:10
# worker_02 RUNNING pid 12352, uptime 0:02:10

Les expressions Python disponibles dans les chemins et commandes sont : %(program_name)s, %(process_num)02d, %(group_name)s, %(host_node_name)s et %(here)s (répertoire du fichier de configuration).

Les groupes permettent de regrouper plusieurs programmes pour les contrôler ensemble. C’est utile quand des processus sont liés fonctionnellement.

[program:api]
command=/usr/bin/python3 /opt/app/api.py
priority=10
autostart=true
autorestart=true
[program:worker]
command=/usr/bin/python3 /opt/app/worker.py
process_name=%(program_name)s_%(process_num)02d
numprocs=3
priority=20
autostart=true
autorestart=true
[group:mon-app]
programs=api,worker
priority=999

Avec cette configuration, les processus sont regroupés sous le préfixe mon-app: :

Fenêtre de terminal
supervisorctl status
# mon-app:api RUNNING pid 12345, uptime 0:01:30
# mon-app:worker_00 RUNNING pid 12346, uptime 0:01:30
# mon-app:worker_01 RUNNING pid 12347, uptime 0:01:30
# mon-app:worker_02 RUNNING pid 12348, uptime 0:01:30
# Arrêter tout le groupe
supervisorctl stop 'mon-app:*'
# mon-app:api: stopped
# mon-app:worker_00: stopped
# mon-app:worker_01: stopped
# mon-app:worker_02: stopped

La directive priority contrôle l’ordre de démarrage et d’arrêt. Les programmes avec une priorité basse démarrent en premier et s’arrêtent en dernier.

Un processus supervisé passe par différents états au cours de sa vie. Comprendre ces états permet de diagnostiquer rapidement les problèmes.

ÉtatDescription
STOPPEDLe processus est arrêté (manuellement ou jamais démarré)
STARTINGLe processus est en cours de démarrage
RUNNINGLe processus fonctionne depuis au moins startsecs secondes
BACKOFFLe processus a démarré mais s’est arrêté trop vite (avant startsecs). Supervisor réessaye
STOPPINGLe processus est en cours d’arrêt
EXITEDLe processus s’est arrêté (volontairement ou non) depuis l’état RUNNING
FATALLe processus n’a pas pu démarrer après startretries tentatives
UNKNOWNÉtat inconnu (erreur interne de Supervisor)

Le cycle complet :

  1. STOPPED → STARTING : vous lancez start ou autostart=true au boot
  2. STARTING → RUNNING : le processus tourne depuis startsecs secondes
  3. STARTING → BACKOFF : le processus s’arrête avant startsecs
  4. BACKOFF → STARTING : Supervisor réessaye (délai croissant entre chaque tentative)
  5. BACKOFF → FATAL : startretries tentatives échouées
  6. RUNNING → EXITED : le processus se termine
  7. EXITED → STARTING : autorestart est activé et le code de sortie correspond

Un processus en état FATAL nécessite une intervention manuelle. Corrigez le problème (commande incorrecte, permissions, dépendances manquantes) puis relancez-le avec supervisorctl start.

Supervisor intègre une interface web pour surveiller et contrôler les processus depuis un navigateur. Pour l’activer, ajoutez la section [inet_http_server] :

[inet_http_server]
port=127.0.0.1:9001
username=admin
password=secret

Accédez ensuite à http://127.0.0.1:9001 dans votre navigateur. L’interface permet de voir l’état de chaque processus, de les démarrer, arrêter ou redémarrer, et de consulter leurs logs.

Supervisor réagit à plusieurs signaux Unix :

SignalEffet
SIGTERMArrête supervisord et tous ses processus
SIGINTIdentique à SIGTERM
SIGQUITIdentique à SIGTERM
SIGHUPArrête tous les processus, relit la configuration, puis redémarre tout
SIGUSR2Ferme et rouvre les fichiers de logs (rotation)

Pour un arrêt propre de vos programmes, configurez stopsignal et stopwaitsecs :

[program:api]
command=/usr/bin/python3 /opt/app/api.py
stopsignal=TERM
stopwaitsecs=30
stopasgroup=true
killasgroup=true

Les directives stopasgroup et killasgroup sont importantes pour les programmes qui créent des sous-processus (Flask en mode debug, applications multiprocessing). Elles envoient le signal à tout le groupe de processus et pas uniquement au processus parent.

Supervisor centralise les logs de chaque processus supervisé. Chaque programme peut avoir un fichier dédié pour stdout et stderr.

[program:mon-app]
command=/usr/bin/python3 /opt/app/main.py
stdout_logfile=/var/log/supervisor/mon-app.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
stderr_logfile=/var/log/supervisor/mon-app-error.log
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=10
redirect_stderr=false
DirectiveEffet
stdout_logfileChemin du fichier de logs stdout. AUTO = automatique, NONE = pas de log
stdout_logfile_maxbytesTaille max avant rotation (défaut : 50 MB). 0 = pas de rotation
stdout_logfile_backupsNombre de fichiers de rotation conservés (défaut : 10)
redirect_stderrSi true, stderr est redirigé vers stdout (un seul fichier de logs)

Pour forcer la rotation des logs sans redémarrer, envoyez un signal SIGUSR2 à supervisord :

Fenêtre de terminal
kill -USR2 $(supervisorctl pid)
Fenêtre de terminal
# Dernières lignes du log stdout
supervisorctl tail mon-app
# Suivi en temps réel (comme tail -f)
supervisorctl tail -f mon-app
# 500 derniers octets
supervisorctl tail -500 mon-app
# Logs stderr
supervisorctl tail mon-app stderr

Supervisor est souvent utilisé dans des conteneurs Docker quand un conteneur doit gérer plusieurs processus. Bien que la bonne pratique recommande un processus par conteneur, certains cas justifient l’exception : environnements de développement, applications legacy, ou conteneurs intégrant un processus principal et un sidecar.

Prenons un cas concret : une application web qui a besoin d’un serveur HTTP et d’un worker de traitement.

Structure du projet :

mon-projet/
├── Dockerfile
├── supervisord.conf
├── app.py
└── worker.py

Dockerfile :

FROM python:3.12-slim
RUN pip install --no-cache-dir supervisor
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisord.conf
COPY app.py /opt/app/app.py
COPY worker.py /opt/app/worker.py
EXPOSE 8000
CMD ["supervisord", "-n", "-c", "/etc/supervisord.conf"]

Le flag -n (ou --nodaemon) est indispensable dans Docker : il force Supervisor à rester au premier plan. Sans ce flag, supervisord se daemonise, Docker considère que le processus principal est terminé, et le conteneur s’arrête.

supervisord.conf :

[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log
logfile_maxbytes=0
pidfile=/var/run/supervisord.pid
[unix_http_server]
file=/var/run/supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[program:web]
command=python3 /opt/app/app.py
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
priority=10
[program:worker]
command=python3 /opt/app/worker.py
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
priority=20

Dans un contexte Docker, redirigez les logs vers /dev/stdout et /dev/stderr avec logfile_maxbytes=0 (désactivation de la rotation, obligatoire pour les fichiers non seekable). Cela permet à docker logs de capturer la sortie de tous les processus.

Construction et exécution :

Fenêtre de terminal
docker build -t mon-app-supervisor .
docker run -d --name mon-app mon-app-supervisor
# Vérifier les processus
docker exec mon-app supervisorctl status
# web RUNNING pid 8, uptime 0:00:15
# worker RUNNING pid 9, uptime 0:00:15
# Consulter les logs
docker logs mon-app
SymptômeCause probableSolution
FATAL après démarrageCommande invalide ou dépendance manquanteVérifiez la commande manuellement : exécutez-la dans un terminal
BACKOFF puis FATALLe processus démarre puis crash avant startsecsAugmentez startsecs=0 temporairement pour diagnostiquer, consultez les logs stderr
unix:///var/run/supervisor.sock no such filesupervisord n’est pas démarréLancez supervisord ou vérifiez le chemin du socket
Permission denied: supervisor.sockMauvais droits sur le socketVérifiez chmod dans [unix_http_server] ou lancez avec les bons droits
SPAWNERR: can't find commandChemin de la commande incorrectUtilisez un chemin absolu dans command=
Logs videsredirect_stderr=true mais logs lus sur stderrConsultez stdout_logfile, ou séparez stdout et stderr
Processus zombieSous-processus non récupérésAjoutez stopasgroup=true et killasgroup=true
Fenêtre de terminal
# Vérifier que supervisord tourne
supervisorctl pid
# 12340
# Voir les logs détaillés de supervisord
tail -50 /var/log/supervisor/supervisord.log
# Voir les logs d'un processus en erreur
supervisorctl tail mon-app stderr
# Tester la commande manuellement
/usr/bin/python3 /opt/app/main.py
# Vérifier la configuration
supervisord -c /etc/supervisord.conf --test 2>&1 || echo "Erreur de configuration"
  • Un fichier par programme dans /etc/supervisor/conf.d/ plutôt que tout dans supervisord.conf
  • Chemins absolus pour command, stdout_logfile et directory
  • Utilisateur dédié avec user= pour ne pas exécuter les processus en root
  • Variables d’environnement avec environment= plutôt que des scripts wrapper
  • Utilisez reread + update plutôt que reload pour appliquer les changements sans tout redémarrer
  • Surveillez les processus en état FATAL : ils ne redémarreront pas seuls
  • Configurez la rotation des logs (stdout_logfile_maxbytes, stdout_logfile_backups) pour éviter de saturer le disque
  • Ajustez startsecs à une valeur réaliste : trop bas génère de faux positifs, trop haut retarde la détection de problèmes
  • Ne jamais exposer l’interface web (inet_http_server) sur un réseau non sécurisé
  • Protéger le fichier supervisord.conf en lecture seule pour les utilisateurs non privilégiés (il peut contenir des mots de passe en clair)
  • Utiliser le socket Unix (unix_http_server) avec des permissions restreintes (chmod=0700)
  • Préférer le hachage SHA-1 pour les mots de passe : password={SHA}hash_hex
  • Supervisor est un gestionnaire de processus simple et éprouvé, idéal pour les applications utilisateur et les conteneurs multi-processus
  • Le fichier de configuration est au format INI, avec une section [program:x] par programme supervisé
  • Les programmes supervisés ne doivent jamais se daemoniser : Supervisor doit rester le processus parent
  • supervisorctl permet de démarrer, arrêter, redémarrer les processus et de consulter leurs logs
  • La directive numprocs permet de lancer plusieurs instances d’un même programme
  • Les groupes ([group:x]) regroupent des processus pour les contrôler ensemble
  • Dans Docker, utilisez toujours le flag -n (nodaemon) et redirigez les logs vers /dev/stdout
  • Un processus en état FATAL nécessite une intervention manuelle : diagnostic puis supervisorctl start

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn