Progetto

Generale

Profilo

Uso di Supervisor per controllare più istanze di Django

Supervisor è presente in Debian, e permette il controllo (avvio, stop, riavvio automatico, logging, eccetera) di programmi che devono essere monitorati nel loro funzionamento. In questa guida si avvieranno e si gestiranno quattro istanze del devserver di un'applicazione Django in locale.

Per ogni approfondimento riguardo a supervisor, si rimanda alla documentazione ufficiale.

NB: Supervisor non funziona con Python 3.

Prerequisiti

  • # apt-get install supervisor
  • Progetto django in /home/utente/projects/django/

Configurazione di supervisor

Debian installa supervisor e avvia il demone supervisord con il file /etc/supervisor/supervisord.conf che contiene queste direttive:

; supervisor config file

[unix_http_server]
file=/var/run/supervisor.sock   ; (the path to the socket file)
chmod=0700                       ; sockef file mode (default 0700)

[supervisord]
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
childlogdir=/var/log/supervisor            ; ('AUTO' child log dir, default $TEMP)

; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL  for a unix socket

; The [include] section can just contain the "files" setting.  This
; setting can list multiple files (separated by whitespace or
; newlines).  It can also contain wildcards.  The filenames are
; interpreted as relative to this file.  Included files *cannot*
; include files themselves.

[include]
files = /etc/supervisor/conf.d/*.conf

e come si vede il demone risponde alla unix socket /var/run/supervisor.sock, e supervisorctl (software che presenta una shell di controllo) usa questa socket per inviare comandi al demone (che serviranno per monitorare, avviare, riavviare, fermare i servizi configurati).

Grazie alla direttiva [include] supervisor carica file di configurazione aggiuntivi dalla directory /etc/supervisor/conf.d/, quindi questo file può essere lasciato così (a meno di aver bisogno di settaggi particolari).

Creazione del file django.conf

Come si vedrà, i file di configurazione di supervisor supportano la sintassi python per fare output di valori come se si scrivesse in una stringa (qui la pagina che spiega approfonditamente, con esempi, questa funzionalità).
Si crei il file /etc/supervisor/conf.d/django.conf per la gestione delle istanze di django (in questo esempio si useranno i devserver):

[program:django]
numprocs=4
directory=/home/utente/projects/django
command=python manage.py runserver 80%(process_num)02d
process_name=%(program_name)s_%(process_num)02d
user=utente
redirect_stderr=true
stdout_logfile=/home/utente/projects/django/supervisor_stdout_%(process_num)02d.log
stopsignal=KILL
stopasgroup=true
killasgroup=true

La sezione [program:django] contiene quindi le impostazioni di base per gestire 4 istanze di django. Le singole righe significano:

  • Numero di processi da avviare.
  • Directory in cui entrare prima di eseguire il comando.
  • Comando da eseguire per ogni processo avviato (dato che numprocs è stato specificato, ed è diverso da 1, bisogna che in command sia presente %(process_num)02d per differenziare le righe di comando). In questo esempio, con numprocs che va da 0 a 3, verranno avviati 4 processi alle porte 8000, 8001, 8002, 8003.
  • Nome del processo i-esimo. In questo esempio si chiameranno django_00, django_01, django_02, django_03.
  • Utente da usare per lanciare i comandi. In questo esempio supervisord tenta di passare all'utente utente, in modo da non lanciare i comandi come utente di supervisord (che potrebbe essere root). Se questo tentativo fallisce, il comando non viene eseguito.
  • Redirigere gli stderr dei processi creati nei rispettivi stdout.
  • File in cui loggare stdout.
  • Le ultime tre righe sono necessarie per far sì che supervisord riesca a killare correttamente i devserver di django senza lasciare processi orfani né bloccare le porte in caso di riavvio di un'istanza.

Di default i processi vengono avviati all'avvio di supervisord, ma si può impedire (autostart=false), si può richiedere che i processi siano sempre riavviati in caso di segnali di KILL (autorestart), si possono configurare i timeout di attesa quando un processo cambia stato, il tipi di exit code attesi per discriminare quando un processo esce correttamente o per qualche errore/malfunzionamento (exit_codes), eccetera....

Uso di supervisorctl

Questo è un programma che può essere usato per inviare comandi una tantum:

# supervisorctl start all

oppure avviato senza parametri perché fornisca una shell da cui poter interrogare supervisord:

# supervisorctl 
django:django_00                   RUNNING    pid 9633, uptime 0:41:34
django:django_01                   RUNNING    pid 9634, uptime 0:41:34
django:django_02                   RUNNING    pid 9648, uptime 0:41:33
django:django_03                   RUNNING    pid 9659, uptime 0:41:32
supervisor> help

default commands (type help <topic>):
=====================================
add    clear  fg        open  quit    remove  restart   start   stop  update 
avail  exit   maintail  pid   reload  reread  shutdown  status  tail  version

supervisor> stop all
django_00: stopped
django_01: stopped
django_02: stopped
django_03: stopped
supervisor> start all
django_00: started
django_01: started
django_02: started
django_03: started

Come si vede qui è possibile interrogare il demone e vedere i processi attivi, fermarli, avviarli, ed è anche presente un help in linea.

Se supervisord è stato avviato da root, e nel file supervisord.conf il setting chmod è 0700, ovviamente un utente non privilegiato non potrà accedere alla socket di interrogazione, e supervisorctl andrà lanciato da root (come nell'esempio qui sopra).

Impostazione di invio di email di avviso e di allarme via supervisor.

Il pacchetto superlance consente agganciare a supervisor degli utili script che consentono di osservarne gli eventi per eseguire opportune azioni. Due di questi sono particolarmente utili, il primo per notificare crash di un servizio osservato, il secondo per avvisare di un servizio fermo per un fallimento fatale. Per poterlo utilizzare è anzitutto necessario installare il relativo pacchetto python con:

easy_install superlance

che instellerà tutto il necessario, compresi gli script che ci interessano che verranno posti sotto /usr/local/bin/.

A questo punto basterà registrare sull'eventlistner di supervisor i due script che ci interessano; il primo è crashmail, che spedisce una email tutte le volte che un servizio esce inaspettatamente con un errore, contenente il nome del servizio e le relative informazioni, per questo basta aggiungere a /etc/supervisor/supervisord.conf qualcosa del tipo:

[eventlistener:crashmail]
command=/usr/local/bin/crashmail -a -m sviluppatore@dominio.it
events=PROCESS_STATE

in questo caso in genere il servizio viene riavviato automaticamente da supervisor.

Il secondo script, fatalmailbatch, tiene invece sotto ossevazione gli eventi ed invia una email di allarme (su un periodo di un minuto) quando rileva che un servizio si ferma nello stato FATAL, segno che supervisor ha fallito nel tentativo di farlo ripartire, in questo caso occorre aggiungere a /etc/supervisor/supervisord.conf qualcosa del tipo:

[eventlistener:fatalmailbatch]
command=/usr/local/bin/fatalmailbatch --toEmail="sistemista@dominio.it" --fromEmail="supervisor@dominio.it" --subject="Alarm for a fatal crash" 
events=PROCESS_STATE,TICK_60

Si possono ovviamente attivare entrambe le notifiche aggiungendo entrambe le sezioni indicate.