Master 2, Bases de données avancées, année 2023

Objectif

Rappels sur bash et l’environnement

Dans bash (le terminal, pour faire simple), il est possible de fixer certaines variables à certaines valeurs:

a=42

Donne à 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: $a

retourne

La variable a vaut: 42

Certaines 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

env

Pour exporter la variable locale a en une variable d’environnement il suffit d’utiliser

export a

Cet 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 passe

Il est possible de charger ces variables à l’aide de la commande source chemin/fichierchemin/fichier est le chemin vers votre fichier.

Dans les TP, j’utilise ces notations pour les commandes. Vous pouvez:

  1. Soit substituer à la main les variables (avec $) présentes dans les commandes fournies
  2. Soit fixer les variables dans le shell ou dans l’environnement une fois et faire des copier/coller

Installation Redis

  1. 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.

  1. Installez Redis et à 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 redis

Vérifiez que redis est installé à l’aide de la commande

redis-cli --version

  1. Modifier le fichier de config /etc/redis/redis.conf pour:

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)

  1. À l’aide de redis-cli faite les commandes suivante:

À 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"] = 42

Vous 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] = 0

Alors 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:

  1. Continuez le TP sur les middleware (Redis et API)
  2. 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.

  1. Déployez une nouvelle VM nommée middleware et installez lighttpd.

Dans la suite on note $middleware_ip l’adresse ip de cette VM.

  1. Vérifiez que lighttpd fonctionne en créant une page html contenant Hello World! dans /var/www/html/ et en exécutant localement:
curl $middleware_ip

Vous devriez avoir Hello World! comme réponse.

  1. Activez le mode mod_cgi à l’aide de la commande lighty-enable-mod et 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.

  1. Créez un script Bash nommé hello.cgi dans le dossier /var/www/html contenant
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.

  1. Créez un script env.cgi dans le dossier /var/www/html contenant la commande printenv. Que retourne curl $middleware_ip/env.cgi ?

Voici des erreurs courantes:

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:

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).

  1. Ajoutez cat <& 0 à env.cgi et exécutez curl --data "Holla Mundo!" $middleware_ip/env.cgi?halloWelt. En conclure comment les données GET et POST sont 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 lighttpd

CGI avec Python

Pour ceux d’entre vous qui le souhaitent, il est possible d’utiliser Java.

  1. Enlever les règles de réécriture précédentes, nous allons avoir besoin d’en construire de nouvelles.

  2. Modifier /etc/lighttpd/conf-available/10-cgi.conf pour n’autoriser que les fichiers Python

cgi.assign      = (
        ".py"  => "/usr/bin/python3",
)
  1. Modifier rewrite pour rediriger l’URL env vers le fichier /env.py

  2. Créer un fichier env.py dans /var/www/html/ avec le contenu suivant:

import os, sys
print("\n".join(f"{key}:{value}" for key,value in os.environ.items()))
  1. 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:

On va assigner à des bases de données des rôles différents:

Pour communiquer avec l’application, nous allons utiliser le protocole HTTP de manière standard.

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

  1. Créez un fichier redis_connect.py qui propose une fonction connection_redis prend en argument un entier qui retourne un connecteur redis pour la base de donnée passée en argument. Pour ne pas l’exposer à l’extérieur, rediriger l’url redis_connect.py vers /.

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
  1. Créez un fichier create.py (ou create.cgi ou autre si autre langage) et redirigez l’url create vers lui. Faites en sortes que le scripte create.py:

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

  1. Faites une redirection de l’url /connect.py vers /connect

  2. Créez un fichier connect.py qui:

Pour la durée limitée, on doit utiliser la commande expire de Redis

Avoir des informations sur le compte

  1. Faites une redirection de l’url / vers /info.py

  2. Créez un fichier info.py qui va:

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: mar. 19 mars 2024 16:13:10 CET