Déployer une application Django sur un VPS

Découvrez le guide complet pour déployer facilement votre application Django sur un VPS.

Publié le par Gabriel Trouvé (mis à jour le )
110 minutes

Dans cet article, je vous explique étape par étape comment mettre en production un projet Django sur un serveur privé virtuel (VPS) sur Hostinger.

Un VPS est une solution d'hébergement qui permet une grande flexibilité à vos projets à un coût raisonnable. Il existe plusieurs types d'hébergements :

  • L'hébergement mutualisé, peu cher, mais ses ressources (CPU, bande passante, etc.) sont partagées et vous avez peu de contrôle sur l'environnement. Vous pouvez voir cela comme une colocation où tout est partagé

  • Le serveur dédié, assez cher, demande beaucoup de gestion, mais c'est comme avoir sa propre maison (voire son propre immeuble)

  • Le VPS (notre choix), coût raisonnable, vous avez des ressources dédiées, vous êtes libre de le gérer avec votre OS et de configurer votre environnement. Si un serveur dédié est un immeuble, e VPS, c'est comme avoir son propre appartement

Pour ce guide, nous utiliserons un VPS sur Hostinger. Cependant, les compétences que vous allez acquérir sont universelles, car un VPS reste un VPS, peu importe le fournisseur.

Vous êtes prêt ?

Attention

Pour la suite, il est préférable d'être suffisamment à l'aise avec les commandes Bash et Git, ainsi qu'avec Django.

Projet Django et Git/GitHub

Création du projet Django

Commençons par créer notre projet Django avec son environnement virtuel :

Préparation de l'environnement virtuel

Préparation de l'environnement virtuel

Création du projet

Création du projet

À noter

Pensez aussi à installer Pillow, Django en a besoin pour le traitement des fichiers médias : pip install pillow

Nous allons créer un projet simple, qui va utiliser une base de données MySQL en production et SQLite en développement, ainsi que des fichiers médias et statiques :

  • Création d'une application avec un modèle Post qui permet de publier du texte avec un fichier média (penser à l'enregistrer dans admin.py)
Application Blog et son modèle

Application Blog et son modèle

  • Création d'une vue pour afficher les articles, avec son template associé dans blog/templates/blog/index.html
Vue et templates pour notre blog

Vue et templates pour notre blog

  • Insertion d'un fichier statique dans le template, en l'ajoutant dans l'application blog à l'emplacement suivant : static/blog/logo/logo.png
Ajout d'un fichier statique

Ajout d'un fichier statique

  • Configurer les paramètres pour les fichiers statiques et médias
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'


MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "mediafiles"
  • Configurer le fichier d'URL du projet pour gérer les médias en local et ajouter la vue d'index
from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from blog.views import index

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index, name='index'),
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  • Utilisation d'un fichier .env pour plus de flexibilité et ne pas inclure dans Git les données sensibles

Pour cette partie, j'utilise django-environ qui permet de récupérer des variables d'environnement. Nous allons créer le fichier .env, y insérer des variables, et les récupérer depuis les paramètres.

Fichier .env

Fichier .env

Dans la capture d'écran ci-dessus, la clé Django n'est pas sécurisée, pensez à en générer une via le shell :

python manage.py shell
BASH
from django.core.management.utils import get_random_secret_key

get_random_secret_key()
PYTHON

Nous ajouterons les lignes de code pour utiliser MySQL et des mesures de sécurité plus tard dans le guide.

Il est maintenant temps de faire les migrations :

python manage.py makemigrations
python manage.py migrate
SHELL

Testez votre application pour voir si tout fonctionne bien en local.

Il reste une dernière chose à faire avant de versionner notre projet sur GitHub : lister les dépendances en générant le fichier requirements.txt. Ce fichier garantit que nous aurons les mêmes dépendances sur le VPS en les installant toutes en même temps.

Voici la commande pour générer le fichier :

pip freeze > requirements.txt
SHELL

Versionner votre projet avec Git et GitHub

Assurez-vous que Git soit bien installé sur votre système : git --version.

Créons le fichier .gitignore qui permet de spécifier à Git les fichiers et dossiers à ignorer. Par exemple, on ne voudrait pas versionner le fichier .env avec nos clés secrètes et nos mots de passe, et le dossier venv avec notre environnement virtuel qui n'a d'intérêt que localement.

Voici un exemple de fichier .gitignore basique :

# Ignore les fichiers de journalisation (logs) qui peuvent contenir des informations sensibles.
*.log
# Ignore le cache de bytecode généré par Python pour améliorer les performances.
__pycache__/
# Ignore la base de données SQLite utilisée pour le développement local.
db.sqlite3
# Ignore le dossier contenant les fichiers téléversés par les utilisateurs (images, documents...).
mediafiles/

# --- Variables d'environnement ---
# Ignore le fichier contenant les clés d'API, mots de passe et autres secrets. CRUCIAL pour la sécurité.
.env

# --- Environnements virtuels ---
# Ignore les dossiers d'environnement virtuel qui contiennent les dépendances du projet.
.venv/
env/
venv/
ENV/

# --- Fichiers des IDEs / Éditeurs de code ---
# Ignore les fichiers de configuration spécifiques à l'éditeur de code (ici, PyCharm et VS Code).
.idea/
.vscode/

# --- Système d'exploitation (macOS) ---
# Ignore le fichier système invisible créé par le Finder de macOS dans les dossiers.
.DS_Store

# --- Fichiers compilés Python ---
# Une règle plus générale pour ignorer les fichiers Python compilés (.pyc, .pyo, .pyd).
*.py[cod]
SHELL
Fichier .gitignore

Fichier .gitignore

Maintenant, il est temps de versionner notre code avec Git et GitHub :

  • Créez un dépôt GitHub
Créer un dépôt sur GitHub

Créer un dépôt sur GitHub

Vous pouvez choisir de créer un projet public ou privé, comme vous le souhaitez, dans mon cas, je laisse toutes les options par défaut.

  • Ensuite, sur la page suivante vous n'avez plus qu'à copier dans le terminal de votre projet le code donné par GitHub
Code donné par GitHub

Code donné par GitHub

  • Vous n'avez plus qu'à faire un commit de vos fichiers puis un push vers GitHub, si besoin, voici un bref rappel des commandes de base :

Personnellement, j'utilise l'interface graphique de VS Code pour mon cas 😊.

Git et GitHub avec VsCode

Git et GitHub avec VsCode

  • Une fois cela fait, vous pourrez voir votre projet sur GitHub
Projet GitHub

Projet GitHub

Déployer sur un VPS

Acheter un VPS

Créez-vous un compte sur Hostinger, puis cliquez sur VPS dans le panel :

Panel Hostinger

Panel Hostinger

Ensuite, vous pouvez suivre la procédure (voir les captures d'écran ci-dessous) :

  • Choisir un serveur KVM

  • Choisir la localisation du serveur (dans mon cas, la France)

  • Choisir l'OS (pour ce guide, je vais opter pour Ubuntu, la dernière version LTS)

  • Créer le mot de passe (ignorez l'option de clé SSH)

  • Cliquer sur suivant. Le scanner de logiciels est gratuit

  • Choisir la formule KVM1

  • Passer à la caisse

Après toutes ces étapes, il suffit de patienter quelques minutes et le VPS est créé.

Choix d'un VPS KVM

Choix d'un VPS KVM

Choix de la localisation du serveur

Choix de la localisation du serveur

Choix du système d'exploitation

Choix du système d'exploitation

Création d'un mot de passe

Création d'un mot de passe

Scanner de logiciels malveillants gratuit

Scanner de logiciels malveillants gratuit

Choix du plan

Choix du plan

Après toutes ces étapes, votre VPS est disponible depuis votre panel (ne faites pas attention, pour ma part j'ai deux KVM2) :

Panel VPS

Panel VPS

Cliquez sur Gérer pour afficher les informations de votre VPS :

Information sur le VPS

Information sur le VPS

Pour ce guide, nous allons nous concentrer sur deux éléments principaux :

  • L'adresse IP (1)

  • Le bouton pour accéder au terminal (2)

En cliquant sur Terminal du navigateur, vous êtes directement connecté en tant que l'utilisateur root :

Terminal du VPS

Terminal du VPS

Mettre à jour et installer les éléments essentiels sur le VPS

Une fois dans le terminal du VPS, mettons-le à jour :

sudo apt update
sudo apt upgrade
SHELL

Il faut ensuite installer tous les outils nécessaires :

sudo apt install python3 python3-venv python3-pip git nginx
SHELL

À noter

Lorsqu'on utilise sudo apt upgrade ou sudo apt install, le système vous demande de confirmer l'opération en appuyant sur la touche Y puis sur la touche Entrée.

Créer un utilisateur administrateur

À ce stade nous allons créer un utilisateur avec les droits sudo (super utilisateur), ce qui est plus sécurisé que de tout faire avec l'utilisateur root.

Voici la procédure à suivre :

  • Ajouter l'utilisateur avec adduser tonnom (1)

  • Choisir un mot de passe (2)

  • Vous pouvez renseigner vos informations ... ou non (3)

  • Acceptez (4)

  • Donnez les droits sudo à l'utilisateur avec usermod -aG sudo tonnom (5)

Création d'un utilisateur

Création d'un utilisateur

Donner les droits sur le répertoire et cloner le projet

Maintenant, en tant que root, donnons les droits à l'utilisateur que nous venons de créer (pour ma part je l'ai appelé gabriel) :

chown -R gabriel:www-data /var/www/
SHELL
su - gabriel
SHELL
cd /var/www/
SHELL
git clone https://github.com/ton-repo/ton-projet.git
SHELL
ls -la
SHELL

Nous devons mettre en place le code de notre application. La première étape consiste à configurer les permissions du répertoire /var/www/ avec la commande chown, afin que votre utilisateur (gabriel) puisse y écrire et que le groupe du serveur web (www-data) puisse y lire. Par mesure de sécurité, basculez ensuite sur votre utilisateur non-privilégié avec su - gabriel. Une fois dans le bon répertoire (cd /var/www/), clonez votre projet depuis votre dépôt Git. Un rapide ls -la vous permettra de vérifier que le code source est bien en place avant de passer à la suite.

Dossier du projet Django

Dossier du projet Django

Le projet est cloné, mais il appartient uniquement à vous. Le problème est que le serveur nginx tourne sous l'utilisateur www-data. Changeons le groupe propriétaire :

sudo chgrp -R www-data /var/www/votreprojetdjango/ # pour ma part votreprojetdjango sera docvps
SHELL

Pourquoi utiliser sudo ?
L'action de changer le groupe propriétaire pour un groupe système comme www-data nécessite d'être administrateur. Utiliser sudo, c'est comme mettre notre casquette d'administrateur 🧢.

À noter

Pourquoi créer un utilisateur sudo alors qu'il y a root ?
Un utilisateur qui a les droits sudo peut exécuter les mêmes commandes que root. La différence n'est pas dans le pouvoir final, mais dans la procédure pour l'obtenir.

En tant que root, vous avez les pleins pouvoirs en permanence. Pas besoin d'utiliser sudo, chaque commande est exécutée comme si on faisait sudo, une simple faute de frappe peut endommager le système.

En tant qu'utilisateur sudo (donc ici gabriel), l'état par défaut est inoffensif. Pour effectuer une action "dangereuse" il faut délibérément taper sudo avant la commande.

L'utilisateur root est la cible numéro 1 des pirates, donc en désactivant la connexion de root (ce que nous allons voir juste après) et en utilisant un nom d'utilisateur personnel vous les obligez à deviner le nom d'utilisateur et le mot de passe.

Désactiver la connexion directe de root

Une fois que notre utilisateur est créé, et qu'il fonctionne avec les droits sudo, nous allons interdire la connexion SSH à l'utilisateur root.

Attention

Assurez-vous que la commande fonctionne avec votre utilisateur : sudo whoami doit afficher root.

À partir de maintenant, nous utiliserons exclusivement notre utilisateur, et non plus l'utilisateur root.

Vérifiez si vous avez les droits root

Vérifiez si vous avez les droits root

Éditons le fichier de configuration SSH :

  • sudo nano /etc/ssh/sshd_config

  • Défilez tout en bas avec la flèche du bas pour modifier la ligne : PermitRootLogin yes en PermitRootLogin no puis ctrl + x, y et Entrée

  • Redémarrez le service ssh : sudo systemctl restart ssh

À noter

Vous remarquerez qu'après cette configuration, vous pouvez toujours vous connecter en root via la console accessible depuis le navigateur de Hostinger, c'est normal.

La console du navigateur n'est pas une connexion SSH, c'est un accès direct à votre serveur.

La connexion SSH est la porte d'entrée de votre serveur depuis internet. C'est elle qui est sécurisée avec PermitRootLogin no.

Utiliser la connexion ssh

Maintenant, utilisons une méthode de travail standard : la connexion ssh.

Commençons par créer une clé ssh sur votre ordinateur

ssh-keygen -t ed25519 -C "[email protected]"
BASH

ssh-keygen c'est l'outil de création, et l’algorithme utilisé est géré par ed. L'adresse mail sert d'étiquette. Appuyez sur Entrée pour l'emplacement par défaut, et créez un mot de passe pour plus de sécurité.

Voir la clé publique
Vous pouvez visualiser la clé publique avec la commande :

cat ~/.ssh/id_ed25519.pub
# Puis copiez la clé
SHELL

Coller la clé sur le vps

  • Retournez sur votre vps, connectez-vous avec votre utilisateur (pas en root !), et faites mkdir -p ~/.ssh pour créer le dossier .ssh s'il n'existe pas

  • Ouvrez le fichier nano ~/.ssh/authorized_keys et copiez l'intégralité de votre clé publique (adresse mail comprise). ctrl + x et y puis Entrée pour sauvegarder

  • Lancez ces deux commandes : chmod 700 ~/.ssh et chmod 600 ~/.ssh/authorized_keys.

Ces deux dernières commandes appliquent des permissions restrictives pour protéger les clés SSH. Même si je suis le seul utilisateur humain du serveur, de nombreux processus système tournent avec leurs propres comptes utilisateurs. De plus, SSH lui-même pourrait refuser de fonctionner si les permissions ne sont pas suffisamment restrictives, c'est un mécanisme de sécurité intégré et une bonne pratique.

Se connecter via ssh
Vous pouvez maintenant fermer le terminal du navigateur et utiliser votre terminal pour vous connecter via SSH :

  • Ouvrez un terminal sur votre ordinateur

  • Puis exécutez ssh monutilisateur@adresseip, pour ma part : ssh [email protected] (voir les captures ci-dessous)

  • Si vous avez créé un mot de passe au moment de la création de la clé ssh, celui-ci vous sera demandé

Exemple pour mon vps

Exemple pour mon vps

Exemple avec mon terminal

Exemple avec mon terminal

Nous allons pouvoir entrer dans le vif du sujet, et faire tourner notre projet Django !

Installation et configuration de MySQL

Installation et configuration

Une fois connecté en ssh sur votre terminal avec votre utilisateur, installez MySQL :

sudo apt install mysql-server
SHELL

Une fois installé, exécutez la commande :

sudo mysql_secure_installation
SHELL

Le script va vous poser une série de questions :

  1. Le script vous demande d'activer ou non un composant qui force la création de mots de passe pour les utilisateurs MySQL
    y est une bonne pratique pour empêcher de créer un utilisateur de base de données avec un mot de passe faible. Même s'il n'y a que vous sur le VPS, c'est une bonne pratique.

  2. Le niveau de politique du mot de passe
    2 (fort) est le meilleur choix, ça va de soi.

  3. Supprimer les utilisateurs anonymes
    y pour supprimer l'utilisateur anonyme, important pour éviter les attaques.

  4. Interdire la connexion root à distance
    Dans la capture d'écran ci-dessous, j'ai mis n par erreur, mais mettez y (j'ai recommencé la configuration de mon côté). On n'a pas besoin de se connecter à la base de données en tant que root depuis l'extérieur.

À noter

Quand nous parlons de root pour la configuration MySQL, il ne s'agit pas de root du VPS Ubuntu. L'utilisateur root de MySQL est le super-administrateur uniquement pour les bases de données, tandis que le root d'Ubuntu est le super-administrateur pour l'ensemble du serveur.

  1. Supprimer la base de données de test
    y, tout comme l'utilisateur anonyme, elle n'a pas sa place en production

  2. Recharger les tables de privilèges maintenant
    y pour appliquer les règles de sécurité maintenant

Configuration de MySQL

Configuration de MySQL

Configuration de MySQL partie 2

Configuration de MySQL partie 2

  1. Maintenant connectons-nous à MySQL : sudo mysql. Nous sommes directement connectés, et aucun mot de passe n'a été demandé

  2. Attribuons un mot de passe à notre utilisateur root : ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'MonMotDePasse';

  3. Appliquons les changements : FLUSH PRIVILEGES;

  4. Quittons MySQL avec exit;

  5. Essayons de se connecter comme avant : sudo mysql. Ce qui ne fonctionne plus

  6. Connectez-vous avec sudo mysql -u root -p

  7. Renseignez votre mot de passe

Attribuer un mot de passe à root

Attribuer un mot de passe à root

Création de la base de données

Une fois MySQL lancé, lancez cette commande pour créer la base de données : CREATE DATABASE docvps CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

  • docvps correspond au nom de la base de données

  • CHARACTER SET et COLLATE permettent de dire à la base qu'elle peut stocker tous les types de caractères (accents, symboles, emojis...). C'est l'option qui est normalement par défaut mais ça ne coûte rien d'être explicite 😊

Ensuite, vous pouvez exécuter la commande SHOW DATABASES; pour voir s'afficher votre nouvelle base de données.

Affichage de la base de données

Affichage de la base de données

Maintenant, créons un utilisateur que Django utilisera. Appelons-le docvps_user :

CREATE USER 'docvps_user'@'localhost' IDENTIFIED BY 'MonMotDePasse';
SHELL

L'utilisateur est créé, mais il n'a aucun droit. Attribuons-lui les droits de gérer notre nouvelle base de données :

GRANT ALL PRIVILEGES ON docvps.* TO 'docvps_user'@'localhost';
SHELL

Pourquoi docvps.* ? On spécifie le nom de la base, puis après le . le nom de la table. Comme je veux donner les droits à l'utilisateur de gérer toutes les tables, on utilise *.

Il n'y a plus qu'à recharger les tables de privilèges, et nous pouvons quitter MySQL :

  • FLUSH PRIVILEGES;

  • EXIT;

Notre base de données est fin prête !

Finaliser le projet Django

Il est temps de finaliser le projet Django. En production, nous utilisons toujours DEBUG=False. En sachant cela, modifions la partie base de données de notre fichier settings.py :

if DEBUG:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': BASE_DIR / 'db.sqlite3',
        }
    }
else:
    DATABASES = {
        "default": {
            # Indique à Django quel "moteur" utiliser. Chaque SGBD (MySQL, PostgreSQL, etc.) a le sien.
            "ENGINE": "django.db.backends.mysql",

            # Le nom exact de la base de données que nous avons créée dans MySQL.
            "NAME": "docvps",

            # L'utilisateur que nous avons créé dans MySQL et qui a les droits sur cette base de données.
            "USER": "docvps_user",

            # Le mot de passe de l'utilisateur. Ici, on le charge depuis une variable d'environnement pour ne pas l'écrire en clair (bonne pratique de sécurité).
            "PASSWORD": env("DB_PASSWORD"),

            # L'adresse du serveur de base de données. Comme MySQL tourne sur le même VPS que Django, on utilise "localhost".
            "HOST": "localhost",

            # Le port de communication de MySQL. 3306 est le port par défaut.
            "PORT": "3306",

            # Permet de passer des options spécifiques à MySQL. "STRICT_TRANS_TABLES" est une bonne pratique qui rend MySQL plus strict.
            "OPTIONS": {
                "sql_mode": "STRICT_TRANS_TABLES",
            },
        }
    }
PYTHON

De cette manière, en développement, nous utiliserons une base SQlite, et en production la base MySQL.

Enfin, générez le fichier qui liste vos dépendances si ce n'est pas encore fait :

pip freeze > requirements.txt
SHELL

Il est maintenant temps de faire un commit de nos changements et de faire un push vers GitHub :

Pousser les changements sur GitHub

Pousser les changements sur GitHub

Retour sur le VPS

Mettre à jour le projet Django

De retour sur le VPS, rendez-vous dans le dossier du projet Django :

Retour dans le projet Django

Retour dans le projet Django

Exécutez la commande git pull pour récupérer les changements sur le VPS afin d'intégrer la configuration pour la base de données. Il faut aussi penser à créer le fichier .env tout comme on l'a fait en développement, avec les configurations de production.

Tout d'abord, générez une clé secrète sécurisée pour Django depuis le shell si ça n'a pas été fait avant :

python manage.py shell
SHELL
from django.core.management.utils import get_random_secret_key
print(get_random_secret_key())
PYTHON

Ensuite, dans le dossier du projet Django, au même niveau que manage.py, nous allons créer le .env : nano .env.

.env sur le VPS

.env sur le VPS

Pour la SECRET_KEY, j'utilise la clé générée précédemment, DEBUG est bien à False, je laisse ALLOWED_HOSTS vide pour le moment, et j'ai renseigné le mot de passe pour l'utilisateur de la base de données.

ALLOWED_HOSTS est vide pour le moment, car nous allons acheter un nom de domaine 😎 ! En effet, on va faire les choses bien : nom de domaine, et sécurité SSL.

Le but est d'avoir une mise en production complète :

  • Base de données solide

  • Nom de domaine

  • HTTPS

Allons acheter notre nom de domaine.

Acheter un nom de domaine

De retour sur votre panel Hostinger, allez dans Noms de domaine et Ajouter un nom de domaine.

Hostinger : Nom de domaine

Hostinger : Nom de domaine

Vous pouvez rechercher un nom de domaine disponible. Pour ma part, je tape pythonworkshop, vous pouvez mettre ce que vous voulez tant que c'est disponible. Pour vous entraîner, prenez l'extension la moins chère si vous le souhaitez.

Une fois l'achat terminé, il faut attendre une minute afin que le processus d'enregistrement se termine. Cliquez ensuite sur DNS/Serveurs de noms.

Domaine DNS

Domaine DNS

Tout en bas de la page, allez effacer la dernière ligne comme sur la capture ci-dessous :

Effacer le dernier enregistrement

Effacer le dernier enregistrement

Maintenant, revenez un peu plus haut pour ajouter l'adresse IP du VPS :

Ajout de l'adresse IP du VPS

Ajout de l'adresse IP du VPS

Nous en avons terminé avec le nom de domaine. Facile non ?

Revenons sur notre VPS pour finaliser la configuration et enfin voir notre application Django fonctionner !

Finaliser le .env

nano .env dans le dossier du projet Django, et ajoutez le domaine à ALLOWED_HOSTS.

.env sur le VPS

.env sur le VPS

Sécurisons le fichier pour que seul votre utilisateur puisse le lire et écrire : chmod 600 .env.

Installer les dépendances

Tout d'abord, il faut créer un environnement virtuel sur le VPS à la racine de votre projet : python3 -m venv venv.
Activez-le avec la commande source venv/bin/activate et installez les dépendances depuis le fichier requirements.txt :

pip install -r requirements.txt
SHELL
Installation des dépendances

Installation des dépendances

Pour l'environnement de production, il reste deux dépendances supplémentaires à installer :

pip install gunicorn mysqlclient
SHELL

gunicorn est le serveur d'application et mysqlclient le connecteur qui permet à Django de communiquer avec la base MySQL.

À noter

Si jamais une erreur se produit, désactivez l'environnement virtuel et installez ces dépendances : sudo apt install pkg-config python3-dev default-libmysqlclient-dev build-essential.

Ensuite, vous pouvez de nouveau activer l'environnement virtuel et installer les dépendances pour notre projet avec pip install gunicorn mysqlclient.

Configuration de Gunicorn

Comme expliqué précédemment, Gunicorn est le serveur d'application. Nous allons devoir créer deux fichiers :

  • Un fichier gunicorn.service qui définit comment lancer notre application

  • Un fichier gunicorn.socket qui servira de canal de communication entre Gunicorn et Nginx

gunicorn.service

Pour créer le fichier au bon emplacement, lancez la commande :

sudo nano /etc/systemd/system/gunicorn.service
SHELL

Pour ma part, voici à quoi il ressemble :

[Unit]
Description=Gunicorn service for docvps project
Requires=gunicorn.socket
After=network.target

[Service]
User=gabriel
Group=www-data
WorkingDirectory=/var/www/docvps
ExecStart=/var/www/docvps/venv/bin/gunicorn \
          --workers 3 \
          --access-logfile - \
          --error-logfile - \
          --bind unix:/run/gunicorn.sock \
          project.wsgi:application

[Install]
WantedBy=multi-user.target
BASH
Fichier gunicorn.service

Fichier gunicorn.service

Ce fichier explique à Linux comment gérer Gunicorn :

  • Description=Gunicorn service for docvps project, une description simple

  • Requires=gunicorn.socket, précise que ce fichier a besoin du fichier gunicorn.socket

  • After=network.target, une sécurité banale pour que le service ne démarre qu'après que le réseau du serveur soit entièrement fonctionnel

  • User=gabriel, le processus gunicorn sera lancé par l'utilisateur gabriel, ce qui est obligatoire pour lire les fichiers du projet

  • Group=www-data, Nginx en aura besoin pour accéder aux fichiers (car Nginx est dans le groupe www-data)

  • WorkingDirectory=/var/www/docvps, spécifie le dossier de notre projet

  • ExecStart=/var/www/docvps/venv/bin/gunicorn, le chemin complet vers l'exécutable gunicorn qui est dans notre environnement virtuel

    • --workers 3 le nombre de processus Gunicorn à lancer, la règle générale c'est (2 * nombre de cœurs CPU) + 1

    • --access-logfile - et --error-logfile - indiquent à gunicorn où écrire ses logs, tous les logs seront consultables dans sudo journalctl -u gunicorn.service

    • --bind unix:/run/gunicorn.sock, crée une écoute sur le fichier socket Unix /run/gunicorn.sock

    • project.wsgi:application, utilise le fichier wsgi.py

  • WantedBy=multi-user.target, permet de lancer le service automatiquement à chaque démarrage du serveur

gunicorn.socket

Ce fichier, plus simple, sert à créer le point d'écoute. Créons-le :

sudo nano /etc/systemd/system/gunicorn.socket
SHELL
[Unit]
Description=gunicorn socket for docvps project

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target
SHELL
gunicorn.socket

gunicorn.socket

  • Description=gunicorn socket for docvps project, une description simple

  • ListenStream=/run/gunicorn.sock, écoute les connexions qui arrivent à cet endroit

  • WantedBy=sockets.target, active le socket au démarrage du serveur

Nous pouvons maintenant configurer Nginx.

Nginx

Nginx est en première ligne et reçoit toutes les requêtes. C'est aussi lui qui sera chargé de servir les fichiers statiques. Créons-le fichier :

sudo nano /etc/nginx/sites-available/docvps
SHELL
# Bloc 1: Redirection de WWW vers non-WWW
server {
    listen 80;
    server_name www.pythonworkshop.fr;
    return 301 http://pythonworkshop.fr$request_uri;
}

# Bloc 2: Configuration principale du site
server {
    listen 80;
    server_name pythonworkshop.fr; # Le domaine principal

    # Emplacement pour les fichiers statiques gérés par Nginx
    location /static/ {
        # 'alias' est correct ici si STATIC_ROOT est bien /var/www/docvps/staticfiles
        alias /var/www/docvps/staticfiles/;
    }

    # Emplacement pour les fichiers média (uploadés par les utilisateurs)
    location /media/ {
        alias /var/www/docvps/mediafiles/;
    }

    # Pour toutes les autres requêtes, on les passe à Gunicorn via le socket
    location / {
        proxy_pass http://unix:/run/gunicorn.sock;
        include proxy_params; # Contient la plupart des headers utiles
    }
}
BASH
  • Le premier bloc sert uniquement à rediriger vers la version sans le www

  • Le bloc serveur principal (dans le bloc 2) gère le trafic pour pythonworkshop.fr

  • Ensuite, il faut spécifier les chemins vers les dossiers mediafiles et staticfiles

  • On spécifie à la fin le chemin vers le socket

nginx

nginx

Activer les services

Maintenant, il est temps d'activer les services gunicorn et nginx :

sudo systemctl daemon-reload
SHELL
sudo systemctl start gunicorn.socket
SHELL
sudo systemctl enable gunicorn.socket
SHELL

Pour finir, activez le service Gunicorn via systemd. Rechargez la configuration avec sudo systemctl daemon-reload, puis démarrez et activez le socket avec sudo systemctl start gunicorn.socket et sudo systemctl enable gunicorn.socket pour le rendre opérationnel immédiatement et à chaque redémarrage du serveur

Pour gunicorn, tout est en ordre :

Le service gunicorn fonctionne

Le service gunicorn fonctionne

Pour nginx :

sudo ln -s /etc/nginx/sites-available/docvps /etc/nginx/sites-enabled/
SHELL
sudo nginx -t
SHELL
sudo systemctl restart nginx
SHELL

Pour finaliser la configuration du serveur web, activez votre site Nginx avec sudo ln -s. Validez la syntaxe de vos fichiers avec sudo nginx -t, et si tout est bon, redémarrez Nginx avec sudo systemctl restart nginx.

Configuration nginx

Configuration nginx

Activez votre environnement virtuel et exécutez la commande :

python manage.py collectstatic
SHELL

Ensuite, exécutez les migrations :

python manage.py migrate
SHELL

Tout fonctionne, créons un super utilisateur :

python manage.py createsuperuser
SHELL

Lorsque je me rend sur le site (en http pour le moment), j'ai bien mon image statique :

Le site fonctionnant en HTTP

Le site fonctionnant en HTTP

Allons écrire un article via l'administration. Pour ma part, l'adresse est la suivante : http://pythonworkshop.fr/admin/.

Administration django

Administration django

Tout fonctionne comme prévu :

Notre site est en ligne

Notre site est en ligne

Activer les sécurités SSL/TLS avec Certbot et Django

Pour le moment, nous accédons à notre application via HTTP, mais ce que l'on veut c'est impérativement du HTTPS.

Ajoutons quelques sécurités pour la production. D'ailleurs, si vous faites cette commande : python manage.py check --deploy, vous aurez quelques avertissements 😅.

python manage.py check --deploy
SHELL

Pour la production, il faut donc ajouter ces lignes dans le fichier de settings :

if not DEBUG:
    # HTTPS settings
    SESSION_COOKIE_SECURE = True  # Force les cookies de session à être transmis uniquement via HTTPS
    CSRF_COOKIE_SECURE = True     # Force les cookies CSRF à être transmis uniquement via HTTPS
    SECURE_SSL_REDIRECT = True    # Redirige automatiquement toutes les requêtes HTTP vers HTTPS
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')  # Détecte HTTPS derrière un proxy

    # HSTS settings (HTTP Strict Transport Security)
    SECURE_HSTS_SECONDS = 31536000        # Durée (1 an) pendant laquelle le navigateur doit utiliser HTTPS uniquement
    SECURE_HSTS_PRELOAD = True            # Permet l'inclusion dans la liste de préchargement HSTS des navigateurs
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True # Applique la politique HSTS à tous les sous-domaines
PYTHON

Nous allons de nouveau faire un commit pour cette modification, pousser les changements sur GitHub, et sur notre VPS exécuter git pull :

Pousser les changements sur GitHub

Pousser les changements sur GitHub

Récupérer les changements sur le VPS

Récupérer les changements sur le VPS

Maintenant, installons et activons Certbot pour notre application. Désactivez votre environnement virtuel si ce n'est pas déjà fait.

Nous allons suivre cette partie de la documentation sur Hostinger : https://www.hostinger.com/fr/support/6865487-comment-installer-un-certificat-ssl-sur-un-vps-a-l-aide-de-certbot-chez-hostinger/.

Voici la liste des commandes :

sudo apt install python3 python3-venv libaugeas0
SHELL
sudo python3 -m venv /opt/certbot/
SHELL
sudo /opt/certbot/bin/pip install --upgrade pip
SHELL
sudo /opt/certbot/bin/pip install certbot certbot-nginx
SHELL
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
SHELL
sudo certbot --nginx # Cette dernière commande va vous demander quelques informations
SHELL
Demander un certificat SSL

Demander un certificat SSL

  • Entrez votre adresse email

  • Acceptez les conditions

  • Partagez (ou pas) votre adresse email

  • Appuyez sur Entrée en laissant blanc afin de demander votre certificat SSL pour votre domaine

Le certificat SSL est active pendant 90 jours, mais vous pouvez le renouveller automatiquement avec cette commande :

echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew -q" | sudo tee -a /etc/crontab > /dev/null
SHELL

Maintenant, si je retourne sur mon site http://pythonworkshop.fr/, automatiquement je suis redirigé vers la version sécurisé https://pythonworkshop.fr/.

Sécuriser le VPS avec un Pare-feu

Pour davantage de sécurité, activons le pare-feu UFW qui est déjà installé sur le VPS :

sudo apt update
SHELL
sudo apt install ufw
SHELL
sudo ufw default deny incoming
SHELL
sudo ufw default allow outgoing
SHELL
sudo ufw allow ssh
SHELL
sudo ufw allow 'Nginx Full'
SHELL
sudo ufw enable
SHELL

Pour sécuriser le serveur avec UFW, on établit d'abord une politique par défaut : on bloque tout ce qui tente d'entrer (deny incoming) et on laisse sortir ce qui doit sortir (allow outgoing). Puis, on crée des exceptions pour les services nécessaires (allow ssh, allow 'Nginx Full'). Un sudo ufw enable finalise et active la protection.

Vous pouvez ensuite vérifier le statut de votre pare-feu :

sudo ufw status verbose
SHELL
Pare-feu configuré

Pare-feu configuré

Bonne nouvelle, vous avez terminé ! 🧑‍💻😎

Bravo, tu es prêt à passer à la suite

Rechercher sur le site

Formulaire de contact

Inscris-toi à Docstring

Pour commencer ton apprentissage.

Tu as déjà un compte ? Connecte-toi.