Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

⚙️ Supervisor – Gestion de Processus (supervisord & supervisorctl)

Guide complet IDEO-Lab sur le gestionnaire de processus (Client/Serveur) en Python.

1.1

Concept : Gestionnaire de Processus

Garder les processus "vivants" (Keep Alive), Monitoring.

Supervisor Process Python
1.2

Architecture Client/Serveur

supervisord (Serveur) vs supervisorctl (Client).

supervisord supervisorctl
1.3

vs. systemd / init.d

Niveau OS (systemd) vs Niveau Application (supervisor).

systemd init.d Comparatif
2.1

Config : [supervisord]

Le bloc principal (nodaemon=true, pidfile, logfile).

supervisord.conf Global
2.2

Config : [program:x]

Définition d'un programme (command, directory).

[program] command
2.3

Config : Auto-Restart

autostart, autorestart (true, unexpected).

autostart autorestart
3.1

Config : Logs (stdout/stderr)

stdout_logfile, stderr_logfile, logrotate.

stdout stderr Logs
3.2

Config : Sécurité (User)

user (Exécuter en tant que non-root).

user Sécurité
3.3

Config : [include]

Gestion (files = /etc/supervisor/conf.d/*.conf).

[include] conf.d
4.1

Outil : supervisorctl

Le client CLI (Shell interactif).

supervisorctl CLI
4.2

supervisorctl : status

Afficher l'état (RUNNING, STOPPED, FATAL).

status RUNNING
4.3

supervisorctl : start/stop/restart

Gérer les processus (start myapp, restart all).

start stop restart
5.1

supervisorctl : reread/update

Appliquer les changements de config (.conf).

reread update
5.2

Interface Web (inet_http_server)

Activer l'interface web ([inet_http_server]).

Web UI inet_http_server
5.3

Events (Événements)

[eventlistener]. Réagir (ex: PROCESS_STATE_FATAL).

Events EventListener
6.1

Cas d'Usage : Gunicorn / uWSGI

Gérer les workers d'applications (Python/Django/Flask).

Gunicorn uWSGI
6.2

Cas d'Usage : Queue Workers

Gérer les workers (Celery, RabbitMQ, Redis).

Celery Queue
6.3

Cas d'Usage : Docker

Le "mauvais" usage (CMD ["supervisord"]).

Docker Anti-pattern
1.1 Concept : Gestionnaire de Processus
Qu'est-ce que supervisord ?

Supervisor (ou supervisord) est un système de contrôle de processus (Process Control System) écrit en Python.

Son unique objectif est de contrôler et de surveiller d'autres processus (ex: vos applications, vos scripts, vos workers). Il s'assure qu'ils sont "vivants" (running) et les redémarre s'ils crashent.

Caractéristiques Clés
  • Garder "Vivants" (Keep Alive) : (Rôle n°1) Redémarre automatiquement les processus qui se terminent (autorestart).
  • Gestion (Control) : Offre un client (supervisorctl) pour démarrer, arrêter, redémarrer les processus (sans kill manuel).
  • Centralisation (Logs) : Capture stdout et stderr de tous les processus enfants et les gère (rotation, taille).
  • Web UI : (Optionnel) Fournit une interface web simple pour voir/gérer les processus.
  • Événements (Events) : Peut émettre des événements (ex: "Processus X est entré en état FATAL") pour déclencher d'autres scripts (ex: une alerte Slack).
1.2 Architecture Client/Serveur

Supervisor utilise une architecture Client/Serveur (sur la même machine).

+--------------------------------+
| [ Serveur : supervisord ]      | (Daemon/Service Principal)
| (Écrit en Python)              |
|                                |
| - Lit /etc/supervisord.conf    |
| - Lance les [program:x]        |
| - Surveille les PIDs           |
| - Capture stdout/stderr        |
| - Écoute (Socket ou TCP)       |
+--------------------------------+
     ▲
     │ (Socket UNIX: /tmp/supervisor.sock)
     │ (ou TCP: localhost:9001)
     │
+--------------------------------+
| [ Client : supervisorctl ]     | (Outil CLI)
|                                |
| (Envoie des commandes :)       |
|  - "status"                    |
|  - "restart myapp"             |
|  - "reread"                    |
+--------------------------------+
  • supervisord : Le serveur (daemon) qui tourne en arrière-plan. C'est lui qui gère réellement les processus enfants.
  • supervisorctl : Le client (interface de commande) qui se connecte (via un socket UNIX ou un port TCP) à supervisord pour lui donner des ordres.
1.3 Comparaison : Supervisor vs. systemd

Problème : "systemd (le standard Linux moderne) peut aussi redémarrer (Restart=on-failure) des services. Pourquoi utiliser Supervisor ?"

Réponse : Ils n'opèrent pas au même niveau. systemd est pour les services système (OS), supervisor est pour les processus applicatifs (User).

Critèresystemd (Init System)supervisor (Process Manager)
NiveauNiveau OS (PID 1)Niveau Application (User-space)
UsageGérer les services "système" (ex: sshd, nginx, docker, supervisord lui-même).Gérer les processus "applicatifs" (ex: 8 workers gunicorn, 4 workers celery).
ConfigurationFichiers .service (/etc/systemd/system/). Nécessite sudo.Fichiers .conf (/etc/supervisor/conf.d/). Peut tourner en non-root.
Gestionsystemctl (Lent, lourd, root).supervisorctl (Rapide, léger, non-root, Web UI).
PortabilitéLinux (Spécifique systemd)Portable (Python) (Linux, macOS, *BSD).
Cas d'UsageDémarrer supervisord au boot.Démarrer/Gérer 10 copies de mon_app.py (scaling).

Bonne Pratique : Utiliser systemd pour démarrer (et garder "vivant") supervisord. Puis utiliser supervisor pour gérer (plus finement) l'application (ex: Gunicorn).

2.1 Config : [supervisord] (Global)

La configuration principale (ex: /etc/supervisord.conf) est un fichier .ini.

Le bloc [supervisord] définit le comportement du daemon (serveur) lui-même.

[supervisord]
; (Chemin du fichier de log principal du serveur)
logfile=/var/log/supervisord/supervisord.log

; (Taille max avant rotation)
logfile_maxbytes=50MB

; (Nombre de backups à garder)
logfile_backups=10

; (Niveau de log)
loglevel=info

; (Chemin du fichier PID)
pidfile=/var/run/supervisord.pid

; (Si 'true', tourne en "foreground".
;  Indispensable pour Docker !)
nodaemon=false

; (Utilisateur (si 'nodaemon=false'))
user=root

; (Dossier pour les fichiers de config 'include')
childlogdir=/var/log/supervisord/
2.2 Config : [program:x] (Le Programme)

C'est la définition du processus (Job) à superviser. (Généralement dans un fichier séparé, ex: /etc/supervisor/conf.d/myapp.conf).

[program:myapp_web]
; --- 1. L'Identité ---

; (Le nom (ID) utilisé dans supervisorctl)
; (Doit être unique)
; program_name=myapp_web (auto-déduit)

; --- 2. La Commande (Le Quoi) ---

; (La commande à exécuter.
;  Chemins absolus recommandés.)
command=/var/www/myenv/bin/gunicorn --workers 4 myapp:app

; (Optionnel : Démarrer dans ce dossier)
directory=/var/www/myapp/

; (Optionnel : S'exécuter en tant que cet utilisateur)
user=www-data

; --- 3. La Gestion (Le Comment) ---

; (Démarrer automatiquement au lancement de supervisord)
autostart=true

; (Redémarrer automatiquement si le process crash)
autorestart=true

; (Priorité (999 = défaut))
priority=999

; (Temps d'attente (sec) après le lancement
;  avant de le considérer comme "RUNNING")
startsecs=5

; (Nombre de tentatives de redémarrage)
startretries=3

; (Codes de sortie "attendus" (OK).
;  Ne pas redémarrer si exit code = 0 ou 2)
exitcodes=0,2

; (Logs - Voir 3.1)
stdout_logfile=/var/log/myapp/app.log
stderr_logfile=/var/log/myapp/app.err.log
2.3 Config : autostart & autorestart

Ces deux directives (dans [program:x]) sont cruciales pour la HA (Haute Disponibilité) du processus.

autostart

Type : Booléen (true / false).

Défaut : true.

Action : Si true, supervisord (le serveur) démarre ce programme automatiquement lors de son propre lancement (ex: au boot du serveur).

autorestart

Type : true, false, ou unexpected.

Défaut : unexpected.

Action : Redémarrer le processus s'il s'arrête ?

  • false : Ne jamais redémarrer (si le processus est un "one-shot" script).
  • true : Toujours redémarrer (même si le processus s'arrête "proprement" avec un exit code 0).
  • unexpected (Recommandé) : Redémarrer uniquement si le processus s'arrête avec un code de sortie (exit code) non-attendu.
    • (Par défaut, les codes "attendus" (exitcodes) sont 0 et 2).
    • (Si le script crash (exit 1), il sera redémarré. S'il se termine (exit 0), il ne le sera pas).
3.1 Config : Logs (stdout/stderr)

Une fonction clé de Supervisor est de capturer les sorties "standard" (stdout) et "erreur" (stderr) de vos processus (qui, sinon, seraient perdues).

[program:myapp]
command=python myapp.py

; --- Logs ---

; (Recommandé) Envoyer la sortie standard (echo)
; dans ce fichier
stdout_logfile=/var/log/myapp/app.log

; (Recommandé) Envoyer la sortie erreur (exceptions)
; dans ce fichier
stderr_logfile=/var/log/myapp/app.err.log

; --- Rotation (Logrotate intégré) ---

; (Taille max du fichier avant rotation)
stdout_logfile_maxbytes=10MB
stderr_logfile_maxbytes=10MB

; (Nombre de backups à garder)
stdout_logfile_backups=5

Alternative (stdout) : Si vous préférez que vos logs aillent dans le syslog principal (ou soient gérés par journald/systemd), vous pouvez utiliser :

; (Ne pas capturer, laisser hériter du parent)
stdout_logfile=NONE
stderr_logfile=NONE

; (Ou (legacy) rediriger vers le syslog)
; stdout_logfile=syslog
; stderr_logfile=syslog
3.2 Config : Sécurité (user)

Par défaut, supervisord est souvent lancé en tant que root (via systemd). Si user n'est pas spécifié, les programmes ([program:x]) seront aussi lancés en tant que root.

Risque : C'est une faille de sécurité majeure (Violation du Moindre Privilège). Si votre application (ex: Gunicorn) est compromise, l'attaquant obtient un shell root.

Bonne Pratique

Toujours spécifier un utilisateur non-privilégié (ex: www-data, myuser) pour exécuter le processus.

[program:my_gunicorn_app]
command=/var/www/myenv/bin/gunicorn myapp:app

; (Sécurité) Lancer le processus Gunicorn
; en tant que l'utilisateur 'www-data'
user=www-data

; (Le dossier doit appartenir à www-data)
directory=/var/www/myapp
3.3 Config : [include] (Modularité)

Ne mettez pas vos définitions [program:x] dans le fichier principal /etc/supervisord.conf.

La bonne pratique (similaire à Nginx/Apache) est d'utiliser le bloc [include] pour charger des configurations modulaires (ex: 1 fichier .conf par application).

/etc/supervisord.conf (Principal)
[supervisord]
...
(Configuration globale...)
...

[inet_http_server]
...

[supervisorctl]
...

; --- Inclure tous les fichiers .conf ---
[include]
files = /etc/supervisor/conf.d/*.conf
/etc/supervisor/conf.d/app1.conf
[program:my_app_1]
command=/usr/bin/python /srv/app1/run.py
user=app1
autostart=true
autorestart=true
...
/etc/supervisor/conf.d/app2.conf
[program:my_app_2]
command=/usr/bin/python /srv/app2/run.py
user=app2
...
4.1 Outil : supervisorctl

supervisorctl est l'interface client (CLI) pour contrôler le daemon supervisord.

Mode Interactif (Shell)

Lancer supervisorctl sans argument ouvre un shell interactif (le plus courant).

$ sudo supervisorctl
myapp:worker_01              RUNNING   pid 1234, uptime 1:10:05
myapp:worker_02              RUNNING   pid 1235, uptime 1:10:05
my_other_app                 FATAL     Exited too quickly

supervisor> status
(Affiche la même chose)

supervisor> restart myapp:worker_01
myapp:worker_01: stopped
myapp:worker_01: started

supervisor> help
(Affiche toutes les commandes)

supervisor> exit
Mode Commande (Direct)

Permet de passer une commande directement (utile pour les scripts).

$ sudo supervisorctl status
$ sudo supervisorctl restart all
4.2 supervisorctl : status

La commande status est la commande de base. Elle affiche l'état de tous les processus gérés.

supervisor> status
app:worker-01   RUNNING   pid 1120, uptime 0:02:15
app:worker-02   RUNNING   pid 1121, uptime 0:02:15
celery          STARTING  pid 1122, uptime 0:00:03
db_backup       STOPPED   Jan 05 10:00 AM
gunicorn_web    FATAL     Exited too quickly
nginx           EXITED    Jan 05 10:15 AM (exit status 0)
État (State)Description
RUNNINGNominal. (Tourne depuis plus longtemps que startsecs).
STARTINGNominal. (En cours de démarrage, moins que startsecs).
STOPPEDArrêté (volontairement via stop, ou autostart=false).
EXITEDArrêté (proprement). (autorestart=false ou autorestart=unexpected et exit code 0).
FATALPanne. Le processus a (crashé) trop de fois (startretries) trop vite (startsecs). (autorestart a échoué).
BACKOFFEn attente avant de retenter un démarrage (après un FATAL).
4.3 supervisorctl : start / stop / restart

Commandes pour gérer le cycle de vie d'un programme ([program:x]).

# Syntaxe (Shell)
supervisor> [commande] [nom_programme]
supervisor> [commande] [groupe]:*
supervisor> [commande] all
  • start [nom] : Démarrer un processus (ex: start myapp).
  • stop [nom] : Arrêter un processus (envoie SIGTERM, puis SIGKILL après stopwaitsecs).
  • restart [nom] : Équivaut à stop puis start.
Gestion de Groupe

Si vous utilisez des groupes (ex: [group:myworkers]) ou des processus numérotés (ex: process_name=%(program_name)s_%(process_num)02d), vous pouvez utiliser le : (ou :*).

# Redémarrer un seul worker
supervisor> restart myapp:worker_01

# Redémarrer TOUS les workers de l'app "myapp"
supervisor> restart myapp:*

# Redémarrer TOUS les programmes
supervisor> restart all
5.1 supervisorctl : reread & update (Appliquer Config)

Le Piège : Si vous modifiez un fichier .conf, supervisord ne le détecte pas automatiquement. Un restart ne suffit pas.

Vous devez forcer le rechargement de la configuration (en 2 étapes).

Workflow (Mise à jour de .conf)
(Vous venez de modifier /etc/supervisor/conf.d/myapp.conf)

$ sudo supervisorctl

# 1. REREAD
# (Demande à supervisord de scanner les fichiers .conf
#  et de voir les *différences* (nouveaux/modifiés))
supervisor> reread
myapp: changed
my_new_app: available

# 2. UPDATE
# (Applique les changements : démarre les "available",
#  redémarre les "changed")
supervisor> update
myapp: stopped
myapp: started
my_new_app: added process group

# (Alternative)
# supervisor> reload
# (Fait reread + update, mais redémarre TOUT)
5.2 Interface Web (inet_http_server)

Supervisor inclut une interface web (très basique, mais fonctionnelle) pour voir/gérer les processus (Start/Stop/Restart).

Configuration (supervisord.conf)

Elle est désactivée par défaut. Pour l'activer, ajoutez ce bloc :

[inet_http_server]
; (Écoute sur TCP 9001, accessible à tous)
port=*:9001

; (BONNE PRATIQUE : Sécuriser l'accès)
;port=127.0.0.1:9001
;username=admin
;password=mon_pass_hash

Après un supervisorctl reload, l'interface est accessible sur http://[IP_Serveur]:9001.

5.3 Events (Événements)

Supervisor peut émettre des événements (Events) sur son "Event Bus" interne lorsque l'état d'un processus change. Un [eventlistener] est un [program] spécial qui s'abonne à ces événements.

Usage (Ex: Alerte Slack si crash)

Étape 1 : Créer un script (ex: /usr/local/bin/notify_slack.py) qui lit (stdin) et envoie à Slack.

Étape 2 : Configurer le Listener (.conf).

[eventlistener:slack_alerter]
; (Le script à lancer)
command=/usr/bin/python /usr/local/bin/notify_slack.py

; (Les événements à écouter)
; (PROCESS_STATE_FATAL = État 'FATAL')
; (PROCESS_STATE_EXITED = Si 'autorestart=unexpected')
events=PROCESS_STATE_FATAL,PROCESS_STATE_EXITED

Flux :

  1. [program:myapp] passe en état FATAL (voir 4.2).
  2. supervisord émet l'événement PROCESS_STATE_FATAL.
  3. [eventlistener:slack_alerter] (qui écoute) reçoit l'événement.
  4. supervisord envoie les infos (Payload) au stdin du script notify_slack.py.

6.1 Cas d'Usage : Gunicorn / uWSGI (Python Web)

C'est le cas d'usage le plus courant pour Supervisor. Gérer les "workers" (processus) d'une application web Python (Django, Flask) servie par Gunicorn ou uWSGI (derrière Nginx).

Supervisor ne remplace pas Gunicorn. Supervisor gère Gunicorn.

# Fichier: /etc/supervisor/conf.d/my_django_app.conf

[program:django_gunicorn]

; Commande (ex: 4 workers, bind sur un socket UNIX)
command=/path/to/myenv/bin/gunicorn \
            --workers 4 \
            --bind unix:/tmp/gunicorn.sock \
            myproject.wsgi:application

; S'exécuter dans le bon dossier
directory=/path/to/myproject/

; S'exécuter en tant que 'www-data'
user=www-data

; (Gestion des logs stdout/stderr)
stdout_logfile=/var/log/gunicorn/access.log
stderr_logfile=/var/log/gunicorn/error.log

; (Keep Alive)
autostart=true
autorestart=true
startsecs=10
startretries=3
6.2 Cas d'Usage : Queue Workers (Celery)

Autre cas d'usage majeur : gérer les workers (consommateurs) de tâches asynchrones (Queues).

Ex: Celery (Python), RabbitMQ Consumers, Laravel Horizon (PHP).

# Fichier: /etc/supervisor/conf.d/celery_workers.conf

; (On utilise le "numprocs" pour lancer 4 copies)
[program:celery_worker]
command=/path/to/myenv/bin/celery -A myapp worker --loglevel=INFO
directory=/path/to/myapp
user=www-data
autostart=true
autorestart=true

; --- Scaling ---
; (Lancer 4 instances de ce 'command')
numprocs=4

; (Donner des noms uniques : worker_00, worker_01...)
process_name=%(program_name)s_%(process_num)02d

; (Arrêter (SIGTERM) gracieusement (laisser finir la tâche))
stopwaitsecs=600
6.3 Cas d'Usage : Docker (L'Anti-Pattern)

Un "anti-pattern" (mauvaise pratique) courant est d'utiliser Supervisor à l'intérieur d'un conteneur Docker pour gérer plusieurs services (ex: Nginx + Gunicorn + Redis) dans 1 seul conteneur.

Le Problème

La philosophie de Docker est : "1 Conteneur = 1 Processus (service)". Docker (le moteur) est *déjà* un gestionnaire de processus (il gère le CMD/ENTRYPOINT).

# Mauvais (Anti-Pattern)
# (Le conteneur gère 3 services)
CMD ["/usr/bin/supervisord", "-n"]

Pourquoi c'est mauvais :

  • Signaux : docker stop envoie SIGTERM à supervisord (PID 1). supervisord doit alors (correctement) relayer le SIGTERM à ses enfants (Nginx, Gunicorn...). (Compliqué).
  • Logs : Docker est conçu pour lire stdout/stderr (du PID 1). Supervisor *capture* (détourne) stdout/stderr et les écrit dans des *fichiers* (.log) *à l'intérieur* du conteneur (docker logs est vide).
  • Santé (Healthcheck) : Si Gunicorn (enfant) crash, supervisord (PID 1) continue de tourner. Docker pense que le conteneur est "sain" (HEALTHY).
La "Bonne" Façon (Docker Compose)

Utiliser 3 conteneurs séparés (Nginx, Gunicorn, Redis) et les orchestrer avec docker-compose.yml.