Mon premier déploiement Django... une catastrophe
Mars 2018. Mon premier déploiement Django en production. J'avais passé trois mois à développer une belle application web, avec des modèles propres, des vues bien structurées, et une interface qui claquait. Le jour du déploiement arrive, je lance le serveur de développement Django (python manage.py runserver) sur le serveur de prod, j'ouvre le port 8000, et bam, l'application est "en ligne".
Deux heures plus tard, l'application était à genoux. 20 utilisateurs simultanés suffisaient à la faire planter. Le serveur de dev Django, c'est fait pour développer, pas pour gérer de vraies charges. J'ai appris à la dure que Gunicorn + Nginx, ce n'est pas une option en prod, c'est une nécessité. Voici ce que j'aurais aimé savoir ce jour-là.
Pourquoi Gunicorn plutôt que uWSGI ?
Je vais être franc : Gunicorn est plus simple que uWSGI. Point. uWSGI est ultra-puissant, ultra-configurable, mais aussi ultra-compliqué à configurer correctement. Gunicorn, c'est du plug-and-play. Vous lancez une commande, ça marche. Pour 95% des cas d'usage, Gunicorn est parfaitement adapté et bien plus facile à maintenir.
Gunicorn (Green Unicorn) est un serveur WSGI HTTP pour Python. Il est basé sur le modèle pre-fork, ce qui signifie qu'il lance plusieurs processus workers qui peuvent gérer des requêtes en parallèle. C'est exactement ce dont Django a besoin en production.
1. Préparer l'environnement virtuel
Première chose : utilisez toujours un environnement virtuel. Pas seulement parce que c'est la "bonne pratique", mais parce que ça vous évitera des cauchemars de dépendances. Imaginez devoir maintenir trois applications Django sur le même serveur avec des versions différentes de Django. Sans virtualenv, vous êtes mort.
# Installer virtualenv
sudo apt-get install python3-venv
# Créer un environnement virtuel
cd /var/www/monsite
python3 -m venv venv
# Activer l'environnement
source venv/bin/activate
# Installer Django et Gunicorn
pip install django gunicorn psycopg2-binary
Pourquoi un virtualenv ?
Le virtualenv isole vos dépendances Python. Chaque projet a son propre environnement avec ses propres versions de bibliothèques. Vous pouvez avoir Django 4.2 sur un projet et Django 5.0 sur un autre, sur le même serveur, sans conflit. C'est propre, c'est sûr, et c'est maintenable.
2. Configuration de Gunicorn
Maintenant, parlons de la partie intéressante : configurer Gunicorn. La question que tout le monde se pose : combien de workers ? La formule classique, c'est (2 × nombre_de_CPU) + 1. Pourquoi ? Parce que ça permet d'avoir des workers qui traitent des requêtes pendant que d'autres attendent des I/O (base de données, fichiers, etc.).
# Tester Gunicorn manuellement
gunicorn --bind 0.0.0.0:8000 monprojet.wsgi:application
# Configuration recommandée pour production
gunicorn --bind 127.0.0.1:8000 \
--workers 5 \
--timeout 60 \
--access-logfile /var/log/gunicorn/access.log \
--error-logfile /var/log/gunicorn/error.log \
monprojet.wsgi:application
Le calcul des workers expliqué
Sur un serveur avec 2 CPU, ça donne (2 × 2) + 1 = 5 workers. Chaque worker est un processus Python séparé qui peut traiter une requête. Avec 5 workers, vous pouvez gérer 5 requêtes simultanément. Si vous mettez trop de workers, vous surchargerez la RAM. Pas assez, et vous sous-utiliserez votre serveur. Cette formule est un bon point de départ.
gunicorn_config.py avec vos paramètres, puis lancez gunicorn -c gunicorn_config.py monprojet.wsgi. C'est plus maintenable que des lignes de commande à rallonge.3. Configuration de Nginx en proxy inverse
Gunicorn, c'est bien, mais il ne gère pas les fichiers statiques efficacement. Et surtout, vous voulez du SSL, de la compression, du caching, du rate limiting... Bref, vous voulez Nginx devant Gunicorn. Nginx gère les requêtes HTTP, sert les fichiers statiques, et passe les requêtes dynamiques à Gunicorn via proxy_pass.
# /etc/nginx/sites-available/monsite
server {
listen 80;
server_name monsite.fr www.monsite.fr;
# Logs
access_log /var/log/nginx/monsite_access.log;
error_log /var/log/nginx/monsite_error.log;
# Fichiers statiques et media
location /static/ {
alias /var/www/monsite/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /var/www/monsite/media/;
expires 7d;
}
# Proxy vers Gunicorn
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
}
}
# Activer le site
sudo ln -s /etc/nginx/sites-available/monsite /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Pourquoi proxy_pass ?
Nginx écoute sur le port 80 (HTTP) ou 443 (HTTPS) et reçoit toutes les requêtes. Pour les fichiers statiques (/static/ et /media/), Nginx les sert directement depuis le disque. Pour tout le reste, Nginx transmet la requête à Gunicorn via proxy_pass. Gunicorn traite la requête Django et renvoie la réponse à Nginx, qui la transmet au client. C'est rapide, efficace, et scalable.
4. Daemoniser Gunicorn avec systemd
Vous ne voulez pas lancer Gunicorn manuellement à chaque redémarrage du serveur. Vous voulez qu'il démarre automatiquement, se relance en cas de crash, et se gère tout seul. C'est exactement le job de systemd. Créez un service systemd pour Gunicorn, et votre vie devient plus simple.
# /etc/systemd/system/gunicorn.service
[Unit]
Description=Gunicorn instance pour monsite
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/monsite
Environment="PATH=/var/www/monsite/venv/bin"
ExecStart=/var/www/monsite/venv/bin/gunicorn \
--workers 5 \
--bind 127.0.0.1:8000 \
--timeout 60 \
--access-logfile /var/log/gunicorn/access.log \
--error-logfile /var/log/gunicorn/error.log \
monprojet.wsgi:application
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
# Activer et démarrer le service
sudo systemctl daemon-reload
sudo systemctl enable gunicorn
sudo systemctl start gunicorn
sudo systemctl status gunicorn
www-data doit avoir les droits de lecture sur les fichiers du projet et d'écriture sur les logs. Vérifiez bien les permissions, sinon Gunicorn ne démarrera pas. sudo chown -R www-data:www-data /var/www/monsite5. Debugging : Les erreurs courantes
Erreur 502 Bad Gateway
C'est l'erreur classique. Nginx ne peut pas joindre Gunicorn. Vérifiez que Gunicorn tourne bien : sudo systemctl status gunicorn. Vérifiez que l'adresse dans proxy_pass correspond bien au --bind de Gunicorn. Et surtout, vérifiez les logs : tail -f /var/log/gunicorn/error.log.
Les fichiers statiques ne chargent pas
Avez-vous lancé python manage.py collectstatic ? Django collecte tous les fichiers statiques dans STATIC_ROOT. Si ce n'est pas fait, Nginx cherchera des fichiers qui n'existent pas. Et vérifiez que le chemin alias dans Nginx correspond bien à votre STATIC_ROOT.
Application lente ou qui timeout
Pas assez de workers. Augmentez le nombre de workers Gunicorn. Ou alors, vous avez des requêtes SQL lentes. Utilisez django-debug-toolbar en dev pour identifier les requêtes problématiques. Pensez aussi au cache Redis pour alléger la charge.
Ma checklist avant la mise en prod
Après plusieurs déploiements Django, voici ma checklist systématique. Vérifiez que DEBUG = False dans settings.py. Configurez ALLOWED_HOSTS avec votre nom de domaine. Lancez collectstatic pour les fichiers statiques. Configurez une vraie base de données (PostgreSQL, pas SQLite). Configurez les logs Gunicorn et Django pour tracer les erreurs. Testez avec quelques workers, puis ajustez selon la charge. Activez HTTPS avec Let's Encrypt (Certbot + Nginx). Configurez un pare-feu (UFW) pour n'autoriser que les ports 80, 443 et SSH. Et mettez en place un monitoring (Sentry, Prometheus, ou simplement Glances).
Ce qu'il faut retenir
Déployer Django en production, ce n'est pas sorcier, mais il faut comprendre pourquoi chaque couche existe. Django gère la logique métier. Gunicorn transforme Django en serveur WSGI capable de gérer des requêtes concurrentes. Nginx gère les fichiers statiques, le SSL, et fait office de reverse proxy. Et systemd s'assure que tout tourne tout le temps.
Cette stack (Django + Gunicorn + Nginx + systemd) est devenue le standard de fait. Elle est simple, éprouvée, et scale très bien. J'ai des applications Django avec cette stack qui tournent depuis des années sans souci. Commencez simple, mesurez les performances, et ajustez si nécessaire. Et surtout, testez en conditions réelles avant de lancer en prod. Vous vous remercierez plus tard.