Mise à jour : j'ai finalement décidé d'utiliser supervisor pour gérer/monitorer ce genre de services,voyez ce billet.
Je continue l'optimisation de mon serveur web (cf le premier épisode) avec cette fois-ci mes projets Django que j'ai migré vers Nginx et Gunicorn. (veuillez noter que Gunicorn permet également de faire tourner d'autres types d'applications Python que Django)
Virtualenv
Pour gérer mes projets Django, j'utilise virtualenv et toute cette doc part donc du principe que vous l'utilisez également. Si ce n'est pas le cas 1. je vous encourage à le faire ;) 2. vous devrez adapter cette doc en conséquence.
Gunicorn
On commence par installer Gunicorn dans chacun des environnements virtuels hébergeant vos projets Django (cf plus bas pour les détails)
$ pip install gunicorn
ou
$ easy_install gunicorn
Ensuite pour chaque projet Django qui sera propulsé par Gunicorn, le plus simple est de créer un fichier de configuration gunicorn.conf.py pour indiquer les options du serveur à son lancement. Voici un exemple de fichier :
backlog = 2048 bind = "unix:/var/run/gunicorn/monprojet.sock" pidfile = "/var/run/gunicorn/monprojet.pid" daemon = True debug = False workers = 2 logfile = "/opt/django/www_domain_tld/log/gunicorn.log" loglevel = "info"
Pour bind vous pouvez soit spécifier un socket Unix soit une adresse et un port TCP/IP. Veuillez consulter la doc configuration pour les détails.
Vous pouvez alors lancer gunicorn avec la commande suivante :
$ gunicorn_django -c gunicorn.conf.py
Bien sûr, comme pour l'article précédent que se passe-t-il en cas de redémarrage du serveur ? Il faut relancer les processus gunicorn un par un. Pour éviter ce genre de désagréments j'ai donc créé un script destiné au système de démarrage sysvinit de Debian.
Ce script a comme prérequis :
- d'avoir pour chaque projet Django
- un virtualenv dédié dans un répertoire
www_domaine_tld - dans ce virtualenv un répertoire nommé
monprojetavec les sources de celui-ci et notamment les fichiers importants pour Django (settings.py, urls.py...)
- un virtualenv dédié dans un répertoire
- d'avoir un répertoire
/etc/gunicorn/sites - dans ce répertoire vous faites un lien vers le virtualenv de chaque projet en lui donnant le nom du répertoire du projet, exemple :
# cd /etc/gunicorn/sites # ln -s /opt/django/www_domaine_tld monprojet
- le fichier
gunicorn.conf.pyest situé dans le virtualenv (donc dans le répertoire parent de celui des sources du projet) - il faut que le pid spécifié dans ce fichier soit de la forme
monprojet.pidpuisque son nom est ainsi extrapolable par rapport au nom du lien dans /etc/gunicorn/sites
Ce qui donne (pour éclaircir tout ça) une arborescence comme suit :
/etc/gunicorn/sites/
monprojet1 -> /opt/django/www_domaine1_tld # lien vers /opt/django/www_domaine1_tld
monprojet2 -> /opt/django/www_domaine2_tld # lien vers /opt/django/www_domaine2_tld
/opt/django/
www_domaine1_tld/
bin/ #
include/ # répertoires créés par virtualenv
lib/ #
monprojet1/ # répertoire contenant les sources du projet
gunicorn.conf.py # fichier de configuration de gunicorn
www_domaine2_tld/
bin/ #
include/ # répertoires créés par virtualenv
lib/ #
monprojet2/ # répertoire contenant les sources du projet
gunicorn.conf.py # fichier de configuration de gunicorn
Et voici le script /etc/init.d/gunicorn
#!/bin/sh
### BEGIN INIT INFO
# Provides: gunicorn
# Required-Start: $local_fs $syslog
# Required-Stop: $local_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Gunicorn processes
### END INIT INFO
USER=www-data
NAME="gunicorn"
DAEMON="gunicorn_django"
CONFDIR=/etc/gunicorn/sites
PIDDIR=/var/run/gunicorn
VENV_ACTIVATION="source ../bin/activate"
CONFFILE="../gunicorn.conf.py"
OPTIONS="-c $CONFFILE"
RETVAL=0
# source function library
. /lib/lsb/init-functions
# pull in default settings
[ -f /etc/default/gunicorn ] && . /etc/default/gunicorn
start()
{
echo $"Starting $NAME."
cd $CONFDIR;
for d in *; do
echo -n $d;
PIDFILE=$PIDDIR/$d.pid
if [ -f $PIDFILE ]; then
echo ": already started!"
else
cd $d;
cd $d;
su -c "$VENV_ACTIVATION; $DAEMON $OPTIONS" $USER && echo ": OK";
fi
done
echo "done"
}
stop()
{
echo $"Stopping $NAME:"
cd $CONFDIR
for d in *; do
echo -n $d;
if [ -f $PIDDIR/$d.pid ]
then kill -QUIT `cat $PIDDIR/$d.pid` && echo ": OK" || echo ": failed";
fi
done
echo "done"
}
reload()
{
echo $"Reloading $NAME:"
cd $CONFDIR
for d in *; do
echo -n $d;
if [ -f $PIDDIR/$d.pid ]
then kill -HUP `cat $PIDDIR/$d.pid` && echo ": OK" || echo ": failed";
fi
done
echo "done"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
reload
;;
reload)
reload
;;
force-reload)
stop && start
;;
*)
echo $"Usage: $0 {start|stop|restart}"
RETVAL=1
esac
exit $RETVAL
Nginx
Pour Nginx, on retrouve un fichier de configuration semblable à celui-ci :
upstream www_domaine_tld {
server unix:/var/run/gunicorn/monprojet.sock fail_timeout=0;
}
server {
listen 80;
server_name www.domaine.tld;
access_log /var/log/nginx/www.domaine.tld.access.log;
error_log /var/log/nginx/www.domaine.tld.error.log;
location /media {
root /opt/django/www.domaine.tld/lib/python2.5/site-packages/django/contrib/admin/;
expires 30d;
}
root /opt/django/www.domaine.tld/monprojet/media;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://www_domaine_tld;
break;
}
}
}
Et voili.
