https://tssr.nolan.ovh by Flo.

    Sommaire

# 14/03/2025

Python: Framework pour le Web Django

Ici, je prend quelques notes sur le Framework. Dans un premier temps son déploiement.

Context

Forcément venant de PHP, j'ai des attentes assez hautes. Je n'utilise pas les Frameworks Symfony ou Laravel, mais de temps en temps, j'explore ces derniers via un projet fictif.
Mais surtout en plus de 10ans de php, j'ai expérimenté l'élaboration d'un framework "Maison", car oui j'ai commencé le php bien avant l'existence même de Symfony et Laravel.

Et pour le Web PHP est très puissant, ne serait-ce que pdo où simplement le fait qu'un script PHP peut nativement être la page web. Le déploiement est donc facile.

Ici, j'ai commencé le cour OpenClassRoom: Débutez avec le framework Django.
Je me suis arrêté à la partie 2 pour le moment. Mes premières impressions sont positive, je trouve le Framework bien pensé, je m'y retrouve facilement.

Je me suis arrêté sur la partie des fichiers statiques, en effet pour moi c'est important de pouvoir comprendre par exemple comment je peux reprendre ma personnalisation de BootstrapCSS via ViteJS et l'adapté à Django.

Si on trouve une pléthore de tutos sur le fonctionnement de Django, concernant sont déploiement ce n'est pas la même. Il existe bien un cour OpenClassRoom: Déployez une application Django, mais sa date un peu (supervisor).

Bref, j'ai passé quasiment deux jours pour comprendre comment faire, du moins une méthode qui fonctionne et qui semble correspondre à mon besoin.

Déploiement de Django

Loin de l'univers php, ici Django demande à mon sens, un peu plus de connaissance en Système.

Point positif Django est parfaitement pensé pour gérer le DEV et la PROD.

Python implémente un serveur WSGI (Web Serveur Gateway Interface), un pour le dev et un autre plus robuste pour la prod. il s'agit de Gunicorn, qui est un serveur WSGI HTTP. Il sert de passerelle (via un socket) entre Django et les requêtes HTTP.

On se sert également de NGINX comme reverse proxy et pour délivrer les fichiers statiques. Sa tombe bien, on a l'habitude de l'utiliser.

Pour lier tout ça, on va utiliser Systemd qui permet d'initialiser et de gérer des services sous Linux. C'est cette partie, où pour le moment je suis le moins à l'aise.

On va donc mettre en place des sockets (point de communication entre deux processus), Gunicorn étant capable de configurer un socket Unix (plus sécurisé).


Voici un petite explication réalisée par le chat (mistral.ai):

Imagine que tu as une boîte aux lettres (le socket) où les lettres (les requêtes web) arrivent.:

  • Gunicorn est le facteur qui vient chercher les lettres dans la boîte et les livre à ton application Django.
  • Systemd est comme le gardien de l'immeuble qui s'assure que la boîte aux lettres est toujours en place et que le facteur (Gunicorn) fait bien son travail. Si le facteur tombe malade (Gunicorn plante), le gardien (Systemd) en appelle un nouveau pour continuer à livrer les lettres.

En utilisant un socket Unix, c'est comme si la boîte aux lettres était à l'intérieur de l'immeuble (le système local), ce qui est plus sûr et plus rapide que si elle était à l'extérieur (sur un réseau).

Ainsi, Systemd facilite la gestion des sockets et assure que Gunicorn est toujours prêt à traiter les requêtes, améliorant ainsi la fiabilité et la performance de ton application Django.

...
Infrastructure pour Django (schéma en cour...)

J'ai suivi différents tutoriels, peu sont vraiment fonctionnel.

Pour configurer Gunicorn, j'ai repris ce tuto: digitalocean.com - How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu

C'était important pour moi de comprendre comment déployer Django pour mettre en place la personnalisation de BootstrapCSS vie ViteJS, afin de savoir où placer les assets et comment implémenter la lecture du manifest.json.

Je me suis posé la question suivante. En php, j'install qu'un seul php-fpm. Ici à chaque fois on configurer un serveur WSGI par site. Est-ce normal? Consommation des ressources? Car en php si un script "bug/plante", la mémoire est vite libérée aucun risque de débordement de par sa nature.

La réponse du chat (mistal.ai):

La configuration de Gunicorn pour chaque projet Django est une pratique courante et recommandée dans le monde Python. Voici pourquoi cela est considéré comme normal et comment cela fonctionne en termes de ressources :

Pourquoi un Gunicorn par projet ?:

  • Isolation des Environnements :
    Chaque projet Django peut avoir ses propres dépendances et versions de bibliothèques. En utilisant un environnement virtuel et un Gunicorn dédié, vous assurez que chaque projet fonctionne avec les versions spécifiques dont il a besoin, sans interférence avec d'autres projets.
  • Gestion des Ressources :
    Gunicorn est léger et conçu pour être efficace en termes de ressources. Vous pouvez configurer le nombre de workers (processus de travail) en fonction des besoins spécifiques de chaque application, ce qui permet une utilisation optimale des ressources du serveur.
  • Sécurité et Stabilité : Si un projet rencontre un problème ou est compromis, cela n'affecte pas les autres projets sur le même serveur. Cela améliore la sécurité et la stabilité globale de votre infrastructure.

Comparaison avec PHP et Nginx (php):

  • Nginx peut directement servir des scripts PHP via PHP-FPM (FastCGI Process Manager). Cela signifie que Nginx peut gérer plusieurs sites PHP sans avoir besoin d'un serveur d'application dédié pour chaque site.
  • Cependant, PHP-FPM fonctionne de manière similaire à Gunicorn en ce sens qu'il gère des pools de processus pour chaque site, assurant ainsi l'isolation et la gestion des ressources.

Consommation de Ressources (gunicorn) :

  • Bien que chaque instance de Gunicorn consomme des ressources, vous pouvez optimiser cela en ajustant le nombre de workers et en utilisant des configurations adaptées à chaque projet.
  • Les ressources utilisées par Gunicorn sont généralement proportionnelles à la charge de travail de l'application, ce qui signifie que des applications moins sollicitées consommeront moins de ressources.

Conclusion : Utiliser un Gunicorn par projet est une pratique normale et recommandée dans l'écosystème Python/Django. Cela permet une meilleure isolation, sécurité, et gestion des ressources. Bien que cela puisse sembler plus complexe que la configuration PHP avec Nginx, cela offre des avantages significatifs en termes de flexibilité et de contrôle sur chaque application.

Déployer avec Git grâce au Hook

Vu que l'on doit intervenir coté serveur via ssh, je peux ici utiliser git et ses hook pour déployer en CI/CD l'application Django.

Quelques étapes/rappels:

  • git remote add prod user_name@domain.tld:PORT/path/to/repo/git
  • dans repo de dev local, ne pas oublié le .gitingore et d'y inscrire les fichiers de dev
  • sur le serveur, on config la branche par défaut pour le repo git: git config --global init.defaultBranch main
  • On créer le repo: git init --bare
  • On créer le hook: post-receive et on le rend executable

Exemple de hook post-receive:

nano hooks/post-receive

#!/bin/bash
while read oldrev newrev ref
do
if [[ $ref =~ .*/main$ ]];
then
echo "Master ref received.  Deploying master branch to production..."
git --work-tree=/var/www/html --git-dir=/var/repo/test checkout -f

# Collect static files
/var/www/project_name/venv/bin/python /var/www/project_name/manage.py collectstatic --noinput

# Restart Gunicorn
sudo systemctl restart project_name_gunicorn

else
echo "Ref $ref successfully received.  Doing nothing: only the master branch may be deployed on this server."
fi
done

Pense à ajouter à l'utilisateur git le droit d'executer cette commande en sudo sans demander le mote de passe:
sudo visudo
On ajoute les lignes suivantes (adapter suivant le user):

  • user_name ALL=(ALL) NOPASSWD: /bin/systemctl restart upon_gunicorn
  • user_name ALL=(ALL) NOPASSWD: /var/www/project_name/venv/bin/python /var/www/project_name/manage.py collectstatic --noinput

On n'oublie pas de le rendre executable: chmod +x hooks/post-receive

Inspiré par: digitalocean.com - How To Use Git Hooks To Automate Development and Deployment Tasks

  • on créer le répertoire pour le projet /var/www/project_name on n'oublie pas de chmoder
  • Une fois le repo pusher, deployer Django
  • python3 -m venv venv
  • source venv/bin/activate
  • pip install -r requirements.txt
  • pip install gunicorn
  • est-ce que ça se lance? python3 manage.py runserver 0.0.0.0:8000
  • python3 manage.py collectstatic
  • on test via gunicorn: gunicorn --bind 0.0.0.0:8000 myproject.wsgi
  • Si sa fonctionne on sort de l'environnement virtuel deactivate et on va créer le socket
  • sudo nano /etc/systemd/system/project_name_gunicorn.socket

La config:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/project_name_gunicorn.sock

[Install]
WantedBy=sockets.target
    

On créer ensuite le service sudo nano /etc/systemd/system/project_name_gunicorn.service

[Unit]
Description=gunicorn daemon for project: project_name
Requires=project_name_gunicorn.socket
After=network.target

[Service]
User=username
Group=www-data
WorkingDirectory=/path/to/your/project
ExecStart=/path/to/your/project/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \project_name_gunicorn.sock \
          project_name.wsgi:application

[Install]
WantedBy=multi-user.target
    

  • On active ensuite le socket:
  • sudo systemctl start project_name_gunicorn.socket
  • sudo systemctl enable project_name_gunicorn.socket
  • vérifie le socket: sudo systemctl status project_name_gunicorn.socket
  • vérifier le sock file: file /run/project_name_gunicorn.sock
  • tester le service : sudo systemctl status project_name_gunicorn
  • Indication disable est normal si aucun connexion n'a été reçue
  • Faire une requête: curl --unix-socket /run/project_name_gunicorn.sock localhost

On créer le fichier de config pour nginx (pour le moment que du http):

server {
    listen 80;
    server_name domain.tld;

    location = /favicon.ico { access_log off; log_not_found off; }

    location /static/ {
        alias /var/www/project_name;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/project_name_gunicorn.sock;
    }
}        
    

On n'oublie pas le lien symbolique:
sudo ln -s /etc/nginx/sites-available/domain.tld /etc/nginx/sites-enabled

sudo nginx -t

sudo systemctl restart nginx

Du 80 au 443 -> le SSl

Ici, je vais reprendre ce que je fais en php. Vu qu'il s'agit de la partie avec nGinx.

J'edit le fichier de config dans nginx:

server {
    listen 80;
    listen [::]:80;
    server_name domain.tld;

    # ACME Challenge - renew certificate
    location /.well-known {
        root /usr/share/nginx/html;
        try_files \$uri \$uri/ =404;
    }


    location = /favicon.ico { access_log off; log_not_found off; }

    location /static/ {
        alias /var/www/project_name;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/project_name_gunicorn.sock;
    }
}        
    

On test si tout est ok via: sudo nginx -t

Je vais passer par snap pour installer Certbot. snapcraft - installing
En ce qui me concerne: sudo apt install snapd

Tester si tout est ok: sudo snap install hello-world

Après suivre ces instructions: certbot.eff.org - nginx - snap

Du coté de Django, j'ai aucune modif à faire. Nice.