Sommaire
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.
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_gunicornuser_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
deactivateet 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.socketsudo 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.