Master 2, Bases de données avancées, année 2025
Objectif
- Se familiariser avec le key-value store Redis
- Avoir une idée des performances de Redis sur la plateforme OpenStack
- Mettre en place un cluster Redis et évaluer l’impact sur les performances
Rappels sur
bash et l’environnement
Dans bash (le terminal, pour faire simple), il est possible de fixer certaines variables à certaines valeurs:
a=42Donne à la variable a la valeur 42.
Il est possible d’utiliser cette valeur dans toute
commande ce qui est bien pratique pour éviter de se souvenir de
certaines informations. Pour utiliser une variable, on utilise la
syntaxe $nom_variable. Donc par exemple:
echo La variable a vaut: $aretourne
La variable a vaut: 42Certaines variables sont locales au processus bash en cours et d’autres sont des variables d’environnement qui contiennent des informations que les programmes utilisent pour personnaliser leur interaction avec l’utilisateur.
Pour afficher les variables d’environnement, il suffit d’utiliser
envPour exporter la variable locale a en une variable
d’environnement il suffit d’utiliser
export aCet export n’est valable que pour le processus (et les sous-processus) qui ont réalisé l’export.
Pour ne pas avoir à se rappeler des variables comme les adresse IP, les mot de passes, etc, il est possible de mettre ces informations dans un fichier texte:
vm1=192.100.00.11 # une adresse ip
pass1=1234 # un très mauvais mot de passeIl est possible de charger ces variables à l’aide de la commande
source chemin/fichier où chemin/fichier est le
chemin vers votre fichier.
Dans les TP, j’utilise ces notations pour les commandes. Vous pouvez:
- Soit substituer à la main les variables (avec $) présentes dans les commandes fournies
- Soit fixer les variables dans le shell ou dans l’environnement une fois et faire des copier/coller
Installation Redis
- Créez une nouvelle VM dédiée à Redis que vous nommez
redis.
Dans la suite, on nommera $redis_ip l’adresse IP de
cette VM.
- Installez
Rediset à l’aide de la documentation configurez les accès distants pour autoriser les requêtes
Redis est packagé dans les dépôts et peut donc être installé très simplement (et rapidement):
Oubliez pas de rafraichir les dépôts avec
sudo apt update
$ sudo apt install redisVérifiez que redis est installé à l’aide de la
commande
redis-cli --version
- Modifier le fichier de config
/etc/redis/redis.confpour:
- autoriser les connexions distantes
- ajouter un mot de passe (
$mot_de_passe) que vous m’indiquez dans le rendu.
Il est possible d’avoir une gestion plus fine des accès, mais ce n’est pas la peine de rentrer dans ce genre de détail ici (pour les curieux)
- À l’aide de
redis-clifaite les commandes suivante:
SET big_answer "42"GET big_answer(ça doit retourner 42)SELECT 1GET big_answer(ça doit retourner une erreur)
À quoi sert la commande SELECT ?
Utilisation simple de Redis
Redis fournit une interface simple. On peut s’y connecter en TCP/IP et envoyer des messages et avoir la réponse. Il est donc possible de bricoler un client très rapidement, quelque soit l’environnement.
Par exemple, en ligne de commande il est possible d’utiliser
telnet qui offre une connexion (non chiffrée).
Ne faites jamais ça dans des vrais cas d’usage, un trafic réseau non chiffré implique que les mots de passe ne sont plus sécurisés. Il vaut mieux utiliser le client fourni.
À l’aide de telnet, connectez vous à l’adresse IP de
redis au port par défaut (6379).
telnet $redis_ip 6379
Vous pouvez alors écrire des commandes simples. Vérifiez que ça marche correctement.
Pour quitter, utilisez la commande redis QUIT.
n’utilisez plus telnet, c’est juste pour montrer la simplicité du protocole
Utilisation applicative de Redis
La plupart du temps, un langage de programmation fournira un
connecteur à Redis. On a vu, il est pas trop dur d’en
bricoler un si ce n’est pas le cas, mais ça reste un bricolage à ne pas
utiliser en production. En Python, on peut utiliser le module
redis.
Pour l’installer, pip3 install --user redis devrait
marcher quelque soit la machine, même sans les droits administrateurs.
Attention, pour installer Redis sur tout le système
utilisez la commande sudo pip3 install redis. Cela peut
être utile pour avoir Redis sur une VM.
import redis
r = redis.Redis(host="100.42.42.42", port=6379, password="chocolat", db=1) # changer l'adresse ip et le mot de passe
r["test_python"] = 42Vous pouvez vérifier que la clef test_python est bien
arrivé dans la base de donnée.
À propos du type des clefs et des valeurs
Dans redis, les clefs et les valeurs sont des suites de bytes et non typés.
Les clients souvent encodent ça sous forme d’un type textuel ce qui peut entraîner des clash indésirables.
Par exemple, dans le client Python
r["42"] = "La réponse à la grande question"
r[42] = 0Alors r["42"] retournera 0, car le client a
transformé l’entier 42 en une chaîne de caractères.
Il est tout à fait possible d’utiliser des fichiers binaires comme valeurs (et même comme clef, même si c’est déconseillé). Ainsi on peut stocker des images facilement:
with open("mon_logo.png", "rb") as f:
r["mon_logo"] = f.read()AU CHOIX:
- Continuez le TP sur les middleware (Redis et API)
- Basculez sur un TP sur l’élasticité (Python et table de hashage distribuée)
Les intergiciels (ou middleware)
La majorité du temps la base de données n’est pas exposée directement sur le réseau mais accessible via un intergiciel.
Le rôle de ce dernier est de faire le lien entre les clients et les bases de données, de distribuer la charge quand ces dernières sont distribuées. Un intergiciel peut être dépendant d’un SGBD ou être fait à la main en fonction des besoins architecturaux. La plupart des SGBD proposent des connecteurs dans toutes les langages de programmation donc il est aisé d’en construire dans votre langage de prédilection.
Par défaut, Python fait bien l’affaire, mais le plus commun est en
fait PHP. Il s’interconnecte facilement avec les serveurs
web standards (comme Apache) et possède un gros vivier de développeurs.
Ici nous allons allez au plus simple:
On va construire un petit mécanisme CGI
en bash, mais c’est simple d’adapter ça au langage de programmation de
votre choix.
CGI est très simple à mettre en place, mais très peu
efficace car nécessite un processus par requête HTTP.
fastCGI permet de contourner ce goulot d’étranglement en
ajoutant un serveur intermédiaire qui s’occupe d’exécuter le code dans
des processus qui tourne en permanence. En Python, il est aussi possible
d’utiliser WSGI.
- Déployez une nouvelle
VMnomméemiddlewareet installezlighttpd.
Dans la suite on note $middleware_ip l’adresse ip de
cette VM.
- Vérifiez que
lighttpdfonctionne en créant une page html contenantHello World!dans/var/www/html/et en exécutant localement:
curl $middleware_ipVous devriez avoir Hello World! comme réponse.
- Activez le mode
mod_cgià l’aide de la commandelighty-enable-modet ajoutez les lignes suivantes au fichier de configuration du module (qui est/etc/lighttpd/conf-available/10-cgi.conf.
cgi.assign = (
".cgi" => "/usr/bin/bash",
)Ainsi vous indiquez à lighttpd quel interpréteur
utiliser pour exécuter les fichiers .cgi. Rechargez la
configuration à l’aide de
service lighttpd force-reload.
- Créez un script
Bashnomméhello.cgidans le dossier/var/www/htmlcontenant
echo This is CGI!Vérifiez à l’aide de curl $middleware_ip/hello.cgi que
le fichier est bien exécuté par lighttpd. Vous devriez
avoir uniquement This is CGI comme réponse et pas
echo.
- Créez un script
env.cgidans le dossier/var/www/htmlcontenant la commandeprintenv. Que retournecurl $middleware_ip/env.cgi?
Voici des erreurs courantes:
Le serveur web ne démarre pas car le fichier de configuration a une erreur. Pour vérifier que le serveur web fonctionne:
systemctl show lighttpdLe fichier CGI ne retourne rien, cela peut déclencher une erreur 500.
Les API REST
Une API Rest est une API dédiée au web qui respecte le protocole HTTP et offre des garanties supplémentaires.
Les méthodes HTTP s’appuient sur trois composantes:
- La requête:
GET,POST,PUT,PATCHetDELETE. Cela désigne l’action à effectuer sur le serveur. - La ressource (
uri): typiquement le lien
Dans la requête, il est possible d’ajouter des données qui sont
transmise via stdin au script cgi.
Il est possible de communiquer avec un serveur, simplement en ligne
de commande avec la commande curl. À l’aide de l’option
-X (ou sa version longue --request on peut
ainsi faire une requête POST, PUT,
PATCH et DELETE).
- Ajoutez
cat <& 0àenv.cgiet exécutezcurl --data "Holla Mundo!" $middleware_ip/env.cgi?halloWelt. En conclure comment les donnéesGETetPOSTsont transmises à l’application en CGI.
Les intergiciels en Bash sont a éviter à tout prix pour
les projets importants, tout comme CGI qui souffre de vrais
problèmes de performance. En pratique, il faudrait utiliser
FastCGI ou une autre technologie récente et un langage de
programmation adapté. Typiquement Python propose de
nombreux framework pour déployer des intergiciels (voir par
exemple flask)
Réécriture d’URL
Il est très souvent utile de permettre à un utilisateur d’utiliser des url simples et de les rediriger.
Activez le module mod_rewrite et ajoutez au fichier de
configuration /etc/lighttpd/conf-available/10-rewrite.conf
la ligne url.rewrite-once = ( "^/(.*)" => "/env.cgi" )
qui redirige toutes les requêtes vers le fichier cgi.
Exécutez la commande
curl $middleware_ip/une/url/foireuse?avec=donnée. En
analysant le retour, indiquez quelle variable d’environnement stock
l’URL initiale avec réécriture par lighttp.
N’oubliez de recharger lighttpd après les modification des fichiers de configurations
sudo systemctl reload lighttpdCGI avec Python
Pour ceux d’entre vous qui le souhaitent, il est possible d’utiliser Java.
Enlever les règles de réécriture précédentes, nous allons avoir besoin d’en construire de nouvelles.
Modifier
/etc/lighttpd/conf-available/10-cgi.confpour n’autoriser que les fichiers Python
cgi.assign = (
".py" => "/usr/bin/python3",
)Modifier
rewritepour rediriger l’URLenvvers le fichier/env.pyCréer un fichier
env.pydans/var/www/html/avec le contenu suivant:
import os, sys
print("\n".join(f"{key}:{value}" for key,value in os.environ.items()))- Que se passe t’il quand on consulte la page
curl $middleware_ip/env?
Un intergiciel et Redis pour une petit application web
Nous allons utiliser un intergiciel et Redis pour une petite application permettant:
- de créer un utilisateur avec un mot de passe
- de lui permettre de se connecter avec un token de session à durée limitée
- de lui donner des informations de connexion
- de lui offrir un stockage de fichier simple sur le serveur
- de supprimer un utilisateur et toutes ses données
On va assigner à des bases de données des rôles différents:
- La base de donnée 0 ne contient rien d’autre que ce qu’on a ajouté dans le TP et des informations statistiques (nombres d’utilisateurs, nombre de fichiers).
- La base de donnée 1 contient en clefs les
user_nameet en valeur son identifiantuuid - La base de donnée 2 contient en clefs les
uuid(d’utilisateur) et en valeur des informations sur l’utilisateur (user_nameassocié, mots de passe, nombres de fichiers, log de connexion) - La base de donnée 3 contient en clefs les
uuidde fichier et associe les différentes versions du fichier stocké sous forme de liste. - La base de donnée 4 contient en clefs les
uuidd’utilisateurs et retourne une association de noms de fichiers à leur uuid. - La base de donnée 5 contient en clefs des token (
uuid) à durée limitée et en valeur l’uuidassocié à l’utilisateur.
Pour communiquer avec l’application, nous allons utiliser le protocole HTTP de manière standard.
- Pour créer un compte, on envoie via les données
POSTà$middleware_ip/createavec le nom d’utilisateur et le mot de passe. - Pour se connecter, on envoie via les données
POSTà$middleware_ip/connectavec le nom d’utilisateur et le mot de passe et on obtient un token en retour. - Pour avoir des informations sur le compte, une requête
GETà$middleware_ipavec le token de connexion en cookie. - Pour ajouter un fichier, on envoie le fichier via les données
POSTà `$middleware_ip/files/nom_de_fichier avec le token en cookie - Pour récupérer la liste des fichiers, on envoie une requête
GETà$middleware_ip/files/le token associé. - Pour télécharger un fichier, on utilise l’URL
$middleware_ip/files/nom_de_ficheravec le token en cookie.
Nous allons créer un intergiciel qui orchestre tout ça. Vous pouvez
utiliser le langage de programmation de votre choix, mais
Python est recommandé.
La création de compte
- Créez un fichier
redis_connect.pyqui propose une fonctionconnection_redisprend en argument un entier qui retourne un connecteurredispour la base de donnée passée en argument. Pour ne pas l’exposer à l’extérieur, rediriger l’urlredis_connect.pyvers/.
Attentions, vous devez installer redis sur l’ensemble du système du
midleware et pas uniquement en local. Utilisez pour ça apt
et pas pip (qui install le packet uniquement pour
l’utilisateur local et donc pas l’utilisateur www-data qui execute les
scripts via CGI).
sudo apt install python3-redis- Créez un fichier
create.py(oucreate.cgiou autre si autre langage) et redirigez l’urlcreatevers lui. Faites en sortes que le scriptecreate.py:
- Lise sur
stdinle nom d’utilisateur et le mot de passe. Si c’est mal formaté, retourne une erreur HTTP. - Vérifie si le nom d’utilisateur est dans la base de donnée 1, si Oui, retourne une erreur HTTP.
- Encrypte le mot de passe à l’aide du module
hashlibet calcule un uuid pour l’utilisateur à l’aide du moduleuuid - Dans la base de donnée 1, associez le nom d’utilisateur à l’uid calculé
- Dans la base de donnée 2, associez un hashmap contenant le nom d’utilisateur, le hash du mot de passe (éventuellement des timestamps de connexion, et de création de compte).
Les commandes Redis pour manipuler les
hashmap sont hset, hget,
hmget, hmset et hgetall. En
python, les binding sont identiques et permettent de stocker un
dictionnaire (dont les clefs valeurs sont des types simples). Pour plus
de détails, voir la documentation de Redis.
Connexion au compte
Faites une redirection de l’url
/connect.pyvers/connectCréez un fichier
connect.pyqui:
- lit sur
stdinun formulaire de connexion (user=password), - récupère l’uid qui correspond ainsi que le hash du mot de passe stocké dans la base de donnée
- vérifie que les hash des deux mots de passes coincident.
- Si le nom d’utilisateur n’existe pas, retourne une erreur “User unknown”
- Si les hash des mots de passe de correspondent pas, retourne une erreur “Wrong password”
- Dans le cas contraire, génère un uuid temporaire, associe cet uuid à l’uuid utilisateur pour une durée limitée (10minutes) dans la base de donnée 5.
- retourne l’uuid temporaire sous la forme d’un cookie
sessionIdà l’aide de la commandeHTTPSet-Cookie - met à jour dans les informations la date dernière connexion.
Pour la durée limitée, on doit utiliser la commande expire de
Redis
Avoir des informations sur le compte
Faites une redirection de l’url
/vers/info.pyCréez un fichier
info.pyqui va:
- soit ne retourne rien s’il n’y a pas de cookie de connexion temporaire
- soit retourne un message d’erreur si le cookie n’est plus valable
- soit retourne des informations associées au compte si le cookie est valide. Vous pouvez prolonger de 10 minutes la session.
- Les cookies sont passés via la variable d’environnement
HTTP_COOKIEque vous pouvez aisément récupérer. - Vous pouvez retourner les informations au format
json. Pour respecter le standard vous pouvez utiliser:content-type: application/jsonpour indiquer au client que vous envoyez du JSON.
API d’upload et de download de fichier
Faites une redirection de toutes les URL de la forme
/files/\* vers /gestion_fichier.py et créez le
fichier gestion_fichier.py
La gestion de fichier va être gérée par le même fichier (éventuellement décomposé en sous fichier importé pour plus de clarté). La liste des fichiers de chaque utilisateur doit être maintenue à l’aide d’un type de donnée list. Vous pouvez lire la liste des commandes ici.
En fonction du type de requête HTTP on va réaliser une
action différente. Les 4 requêtes à prendre en compte sont:
GET, PUT, PATCH et
DELETE. La requête GET va par ailleurs
retourner la liste de fichiers pour les requêtes de la forme
$remote_ip/files/ (c’est-à-dire, sans nom de fichiers
supplémentaires).
Attention pour envoyer des fichiers avec curl, utiliser l’option
--data-binary, l’option --data supprime les
retours à la ligne…
Compiled the: lun. 20 oct. 2025 09:55:53 CEST