• At chevron_right

      Let's Encrypt sur HAProxy (Partie 2)

      raspbeguy · pubsub.gugod.fr / atomtest · Friday, 10 November, 2017 - 23:00 · 8 minutes

    Le fonctionnement de la Terre, avant qu'un certain Galilée y mette son grain de sel, a toujours déchaîné les passions des Hommes et a consommé beaucoup d'encre. Parmi les théories les plus folles, Terry Pratchett nous enseigne dans sa série de livres du Disque Monde que le monde repose sur le dos de...

    Le fonctionnement de la Terre, avant qu'un certain Galilée y mette son grain de sel, a toujours déchaîné les passions des Hommes et a consommé beaucoup d'encre. Parmi les théories les plus folles, Terry Pratchett nous enseigne dans sa série de livres du Disque Monde que le monde repose sur le dos de quatre gigantesques éléphants, eux-même reposant sur la carapace d'une tortue encore plus gigantesque appelée A'Tuin. Les quatre éléphants (Bérilia, Tubul, Ti-Phon l'Immense et Jérakine) se répartissent la charge que représente le poids du disque terrestre grâce à la rotation quotidienne de ce dernier. Si maintenant vous ne voyez pas le rapport entre une cosmologie impliquant des animaux au moins aussi grands que des continents capable de tenir en apnée pendant des milliards d'années et le sujet d'aujourd'hui, alors je ne sais plus quoi faire.

    Nous avons vu il y a quelques jours les principes de base d'un répartiteur de charge et exposé brièvement les concepts fondamentaux de HAProxy. Nous avons également mené une réflexion sur la manière de gérer un certificat sur une architecture avec répartiteur de charge et la voix du bon sens nous a susurré que le certificat devait être porté par HAProxy. Bon, mettons ça en pratique.

    Rangez vos affaires, sortez une feuille, interro surprise. Rappelez-moi ce qu'est le principe de base du protocole ACME. Et comme vous suivez bien et que vous buvez les paroles de l'instituteur comme un breton boit du cidre, vous saurez donc me répondre que l'agent de certification crée un challenge ACME en générant des fichiers de test auxquels le serveur ACME devra pouvoir accéder via HTTP, ces fichiers étant soit servis par l'agent via un serveur web intégré, soit mis à disposition du serveur de notre choix qui servira ces fichiers en statique. C'est en pratique presque toujours la deuxième solution qui est utilisée, sachant que justement, un serveur web tourne déjà sur les ports web standard (80 et 443). On ne veut pas d'interruption de service, non mais.

    Serveur statique

    Nous avons un petit problème ici. Les ports HTTP sont utilisés par HAProxy, qui n'est pas un serveur web, mais un répartiteur de charge. Souvenez-vous, il ne peut pas générer de lui même un flux web, pas même servir des fichiers statiques, il se contente de relayer des flux web créés par d'autres serveurs. Conclusion : il nous faut un serveur web tournant sur la machine hébergeant HAProxy capable de servir des fichiers statiques. L'idée, c'est qu'on va faire écouter ce serveur uniquement en local, et HAProxy se chargera de le propulser sur le réseau publique.

    On n'a que l'embarras du choix pour trouver un serveur web sachant servir du bon vieux statique. On pourrait choisir Nginx ou Lighttpd par exemple, mais comme nous sommes sur OpenBSD, le choix se portera sur le serveur web déjà préinstallé, communément appelé openhttpd.

    Première étape, il faut dire à OpenBSD d'activer le service.

    echo 'httpd_flags=""' >> /etc/rc.conf.local

    Ainsi on pourra démarrer le service, et il sera démarré à chaque démarrage de l'OS.

    Ensuite, il va nous falloir créer un dossier qui va accueillir les fichiers du challenge ACME. Comme le processus httpd est chrooté dans /var/www (sur OpenBSD, on appelle ça un jail), ce dossier sera donc /var/www/htdocs/webroot.

    La configuration de notre httpd sera on ne peut plus simple :

    server "default" { 
        listen on 127.0.0.1 port 1375 #ça fait "let's" en l33t
        root "/htdocs/webroot"
    }

    Démarrons le service :

    /etc/rc.d/httpd start

    Notre serveur statique est prêt.

    Configuration HAProxy

    Il faut dire à HAProxy de rediriger les requêtes ACME vers notre serveur statique fraîchement configuré.

    Pour ça, on va définir un backend dédié à Let's Encrypt, vers lequel seront redirigés tous les flux ACME reçus sur les frontends qui nous intéressent.

    backend letsencrypt-backend
            server letsencrypt 127.0.0.1:1375

    Propre. Syntaxe explicite, pas besoin d'expliquer je pense. Je fais juste remarquer qu'en plus de nommer le backend globalement, je donne un nom au serveur du backend même si ici il n'a aucune forme d'importance, il ne sera jamais appelé ailleurs dans le fichier, c'est néanmoins obligatoire sur HAProxy. Enfin, un nom clair est toujours profitable, car il est éventuellement utilisé dans ses logs, ses statistiques et il rend la lecture du fichier de conf plus facile.

    Le backend est prêt, c'est excellent, mais encore faut-il l'utiliser. Pour être exact, on ne va l'utiliser que si l'URL demandée correspond au chemin classique demandé par le validateur ACME, à savoir /.well-known/acme-challenge/. Reprenons l'exemple de la dernière fois, modifions-le de la sorte :

    frontend mon_super_site
        bind *:80
        mode http
    
        acl url-back-office path_dir /admin
        acl letsencrypt-acl path_beg /.well-known/acme-challenge/
    
        use_backend back-office if url-back-office
        use_backend letsencrypt-backend if letsencrypt-acl
        default_backend mes-frontaux
    
    backend back-office
        server backoffice 192.168.0.14:80
    
    backend mes-frontaux
        balance roundrobin
        server backoffice1 192.168.0.11:80
        server backoffice2 192.168.0.12:80
        server backoffice3 192.168.0.13:80
    
    backend letsencrypt-backend
        server letsencrypt 127.0.0.1:1375

    On a ajouté une ACL et une directive use_backend. Rappelez-vous, une ACL est tout simplement une condition, ici elle porte sur le chemin de l'URL. Rien de nouveau, on a déjà expliqué le concept.

    On recharge le schmilblick :

    /etc/rc.d/haproxy reload

    On est prêt à générer notre premier certificat. Mais on ne va pas le faire tout de suite. Comme vous vous en souvenez, HAProxy veut ses certificats sous une forme un peu spécifique, il nous faut donc traiter le certificat dès qu'il a été généré, et pour ça on va faire un peu de scripting. On pourrait déjà générer le certificat sans le script de post-traitement, mais si on faisait ça, on devrait ensuite éditer la configuration d'acme.sh et c'est un peu barbant, sachant que la commande qui génère le premier certificat génère également par magie la configuration associée pour les renouvellements. Donc ce serait stupide.

    Script de post traitement

    Rien de bien compliqué, je vous rassure. Le principe est simple : HAProxy n’accepte des certificats que sous la forme d'un fichier contenant la chaîne complète de certification et la clef privée, dans cet ordre.

    Le script est très simple. Vous pouvez bien entendu le mettre où bon bous semble, pour ma part j'ai décidé de le mettre dans /etc/haproxy/generate-ssl.sh.

    #!/bin/sh
    
    SITE=$1
    LE_CERT_LOCATION=/root/.acme.sh/$SITE
    HA_CERT_LOCATION=/etc/haproxy/ssl
    
    cat $LE_CERT_LOCATION/fullchain.cer $LE_CERT_LOCATION/$SITE.key > $HA_CERT_LOCATION/$SITE.haproxy.pem
    
    /etc/rc.d/haproxy reload

    LE_CERT_LOCATION représente l'endroit où seront générés les certificats par acme.sh (il s'agit ici de l'emplacement par défaut, mais vous pouvez changer ce chemin à condition d'utiliser l'option adéquate lors de la génération du premier certificat).

    HA_CERT_LOCATION est l'emplacement où seront créés les certificats au format HAProxy. D'ailleurs, n'oubliez pas de créer le dossier en question.

    Le script devra être appelé avec en paramètre le domaine complet principal du certificat. N'oubliez pas de le rendre exécutable.

    Génération du premier certificat

    On y est, maintenant on va enfin pouvoir générer ce satané certificat. Partons du principe que vous avez correctement installé acme.sh, le README du projet est très simple à suivre.

    acme.sh --issue -d mon-super-site.com -w /var/www/htdocs/webroot/ --renew-hook "/etc/haproxy/generate-ssl.sh mon-super-site.com" --debug

    Remarquez que vous pouvez générer un certificat couvrant plusieurs domaines à la fois, il suffit d'ajouter -d mon-domaine-secondaire.com entre le domaine principal et l'option -w. Remarquez l'option --renew-hook, elle permet d'appeler le script qu'on vient de définir à chaque fois qu'un renouvellement est effectué.

    Une fois la commande effectuée avec succès (ça peut prendre une minute ou deux), votre certificat est généré, il vous faut encore ajouter le certificat dans la configuration du frontend comme il suit :

    bind *:443 ssl cert /etc/haproxy/ssl/mon-super-site.com.haproxy.pem

    Comme il s'agit de la première génération et non d'un renouvellement, il est nécessaire d'appliquer le script à la main.

    /etc/haproxy/generate-ssl.sh mon-super-site.com

    Vérifiez, et pleurez de joie. Votre certificat est déployé et fonctionnel. C'est magnifique.

    Automatisation du renouvellement

    Ultime étape, celle qui fait prendre tout son sens à la révolution Let's Encrypt, celle qui fait qu'on peut oublier sans scrupule que tel ou tel site a un certificat proche de l'expiration, il s'agit du renouvellement automatique. Pas de subtilités, une simple ligne dans la crontab fait l'affaire. Et en plus, si je ne dis pas de bêtises, acme.sh s'occupe tout seul d'ajouter cette ligne.

    26 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

    La commande sera lancée tous les jours à minuit vingt-six, libre à vous de changer cet horaire. acme.sh sera assez intelligent pour savoir si votre certificat a besoin d'un coup de jeune ou non, donc rassurez vous, il n'y aura pas un renouvellement par jour.

    Conclusion

    Le protocole ACME peut s'appliquer à toutes les architectures web pour peu qu'on puisse traiter les requêtes du challenge correctement. À moins de disposer d'un stockage partagé (NSF, iSCSI, Samba...), il est indispensable que le challenge ACME soit créé par la machine capable de le traiter.

    Illustration : The Great A'Tuin par Paul Kidby

    • At chevron_right

      Let's Encrypt sur HAProxy (Partie 1)

      raspbeguy · pubsub.gugod.fr / atomtest · Sunday, 5 November, 2017 - 23:00 · 6 minutes

    Vous vous en souvenez peut-être, nous avions effectué un tutoriel Let's Encrypt quelques jours après sa mise en bêta publique. D'ailleurs, il mériterait un petit relooking, vu que quelques détails ont un peu changé, et que d'autres clients ACME ont vu le jour. Par exemple on a honteusement passé sou...

    Vous vous en souvenez peut-être, nous avions effectué un tutoriel Let's Encrypt quelques jours après sa mise en bêta publique. D'ailleurs, il mériterait un petit relooking, vu que quelques détails ont un peu changé, et que d'autres clients ACME ont vu le jour. Par exemple on a honteusement passé sous silence l'excellent acme.sh qui a le mérite de ne pas demander une ribambelle de dépendance en temps que simple script Bash. De plus, le client principal (dont le nom est désormais Certbot) a désormais implémenté des extensions Apache et Nginx qui fonctionnent (presque) comme on le souhaite, à savoir modifier tout seul les configurations des sites pour fonctionner avec Let's Encrypt. Cependant je reste plus enclin à utiliser la méthode manuelle.

    À l'époque, je vous parlais d'une infrastructure simple, à savoir un unique serveur frontal : c'est lui qui interceptait les requêtes et qui les traitait, point barre. L'infra d'un site avec une audience restreinte, car tout doit être traité par une unique machine. Bonjour l'indispo surprise.

    Parlons à présent d'une architecture un peu plus ambitieuse, le genre de topologie qu'on peut trouver en production pour des sociétés importantes (et les technophiles enthousiastes). Cette première partie sera dédiée à l'explication de cette mystérieuse architecture et à la présentation d'une manière de la mettre en œuvre. Dans une seconde partie, on apprendra à y déployer ses certificats Let's Encrypt.

    Souvent donc, on ne se contente pas d'un unique frontal, mais de plusieurs frontaux, sous la coupe d'un répartiteur de charge, (load-balancer en anglais) qui se chargera de réceptionner les requêtes des utilisateurs et de les distribuer aux frontaux, selon une stratégie définie. Cette architecture a quatre avantages :

    • On gagne en charge maximale supportée, donc en nombre de visiteurs.
    • Multiplier les frontaux amoindrit fortement les risques de crash du site, car en cas de panne d'un frontal, les autres frontaux se partagent le trafic.
    • La mise en production d'une nouvelle version d'un site web peut se faire sans aucune interruption de service : il suffit de déployer sur un frontal à la fois.
    • Les frontaux sont sur un réseau privés et non directement accessible sur le réseau global, donc la sécurité du site est renforcée.

    load-balancer

    On constate que le répartiteur de charge est un potentiel goulot d'étranglement et il nous faut donc un programme optimisé pour la haute disponibilité afin que le flux des requêtes ne soit pas le facteur limitant.

    Disons maintenant que l'on souhaite apposer un certificat sur cette architecture. Question à dix sesterces : qui parmi ces serveurs doit porter ce certificat ?

    Si on choisit de faire porter le certificat par les frontaux, cela signifie qu'il faut que tous les frontaux doivent le porter, et qu'il faudra déployer le certificat sur toutes ces machine à chaque renouvellement. Mais surtout, cela signifie que, le trafic étant chiffré jusqu'aux frontaux, le répartiteur de charge ne pourra pas prendre en compte le contenu de la requête dans sa stratégie de répartition, ce qui est fort pénalisant. En effet, il est coutume de configurer le répartiteur de charge pour distribuer certaines URL sur un pool de frontaux différent du pool normal, par exemple le back-office (la page d'administration et de saisie de contenu du site). Enfin dernier point, si vous vous souvenez du fonctionnement du protocole ACME comme décrit dans notre tutoriel, vous saurez que le certificat doit être généré sur la machine qui en fait la demande et qui répondra au challenge ACME. Ceci implique qu'un des frontaux doit être désigné pour la génération des certificat, et donc que le répartiteur de charge doit rediriger toutes les requêtes de type ACME vers ce frontal, ce qui est à la fois moche et justement impossible car notre répartiteur de charge ne peut pas comprendre ce qu'il redirige à cause du chiffrement.

    La solution la plus sensée, qui maintient également le plus l'isométrie des frontaux, est de faire porter le certificat par le répartiteur de charge.

    Zoomons alors sur cette fameuse machine, le répartiteur de charge. On va partir d'un socle OpenBSD. Pourquoi pas un Linux ? Parce qu'on utilise souvent la même machine pour la répartition de charge et pour le pare-feu, et qu'OpenBSD est le système de prédilection pour faire un routeur libre. Et en plus j'aime bien cet OS, ça change un peu les idées.

    Sur ce socle, on va utiliser le répartiteur de charge HAProxy. Les seules tâches de HAProxy sont de porter le certificat du ou des sites (on parle de SSL-offloading) et d'aiguiller le trafic web sur les frontaux. Il ne peut pas générer de flux web par lui-même, il peut seulement relayer une requête à un autre service. Et d'ailleurs il est très bon pour ça.

    HAProxy est organisé en frontends et en backends : un frontend est la façade présentée au monde extérieur qui va recevoir les requêtes des internautes. Un backend est un pool de serveurs frontaux qui va traiter la requête. HAProxy doit donc choisir sur quel backend envoyer une requête, mais il doit également gérer le pool de frontaux que représente ce backend.

    Prenons un exemple de configuration simple. On passera les directives globales HAProxy qui ne sont pas intéressantes et présentent peu de valeur ajoutée. Et bien entendu, on part du principe que vos frontaux sont configurés, écoutent sur des adresses locales et tout le toutim.

    frontend mon_super_site
        bind *:80
        mode http
    
        acl url-back-office path_dir /admin
    
        use_backend back-office if url-back-office
        default_backend mes-frontaux
    
    backend back-office
        server backoffice 192.168.0.14:80
    
    backend mes-frontaux
        balance roundrobin
        server backoffice 192.168.0.11:80
        server backoffice 192.168.0.12:80
        server backoffice 192.168.0.13:80

    Dans cette configuration, le frontend écoute sur toutes les adresses IP attribuées au serveur, sur le port 80 (donc pour le moment, uniquement du trafic en clair). Le mode HTTP signifie qu'on utilise ce frontend pour répartir du flux web, et non pas comme le mode TCP ou l'on redirige un flux TCP intouché.

    On a écrit une ACL, autrement dit une condition. Retenez cette notion car les ACL sont au cœur du principe de HAProxy. Ici notre ACL est vraie si le chemin de l'URL commence par /admin et fausse sinon. On utilisera donc le backendback-office si l'ACL est vraie et sinon, on utilisera le backend par défaut mes-frontaux.

    Le backendback-office est très simple car il ne comporte qu'un serveur. Le backendmes-frontaux comporte trois serveurs répartis en round-robin.

    Et voilà, vous avez un répartiteur de charge. Si vous avez un certificat SSL, vous pouvez ajouter une ligne au frontend :

    bind *:443 ssl cert /chemin/vers/mon/certificat.pem

    Sauf que ça ne marchera pas. En tout cas pas tant que vous aurez mis votre certificat sous la forme attendue par HAProxy, à savoir la clef privée et le certificat dans le même fichier. Nous verrons cela dans la seconde partie, qui vous expliquera également (et finalement) comment diable utiliser Let's Encrypt avec ce bidule. En tout cas, si vous êtes perspicace, vous vous doutez que ça tournera en partie autour des ACL, car il y a bien une raison pour laquelle c'est le seul élément en gras de l'article.

    (Source de l'image d'en-tête : Great A'tuin par Schiraki)

    • At chevron_right

      Une Flask de Django

      motius · pubsub.gugod.fr / atomtest · Sunday, 24 September, 2017 - 22:00 · 17 minutes

    Bonjour à tous ! Pour ceux qui ont déjà fait du développement web avec Django le titre est évident, pour les autres, bienvenue sur mon introduction à Django ! Django est un excellent framework Python qui permet de développer rapidement des applications web, et qui est très complet. Il est plus long ...

    Bonjour à tous !

    Pour ceux qui ont déjà fait du développement web avec Django le titre est évident, pour les autres, bienvenue sur mon introduction à Django !

    Django est un excellent framework Python qui permet de développer rapidement des applications web, et qui est très complet. Il est plus long à prendre en main que Flask, un autre microframework Python que je vais utiliser comme exemple afin d'introduire quelques concepts de Django, et de montrer que ces idées bizarres ne sortent pas de nulle part.

    Parés ? C'est parti !

    Prérequis

    Ce tutoriel va référencer des tags du dépôt git à cette adresse. Je vous conseille aussi cet excellent tutoriel qui pourra compléter ce que j'écris ci-dessous. Enfin, la documentation de Django est complète et facile à utiliser.

    Je vous conseille donc de cloner le dépôt ci-dessus :

    git clone https://git.hashtagueule.fr/motius/django-tutorial.git

    et de suivre les tags que j'ai indiqués au fur et à mesure de l'avancement du tutoriel grâce à la commande :

    git checkout <tagname>

    Je préfixe les noms de tous les tags git de ce tutoriel par un "_", afin que ceux-ci ne polluent pas le projet si vous en réutilisez le dépôt git par la suite (git tag admet l'option -d pour ceux qui veulent vraiment se débarasser de mes tags).

    La quasi-totalité de ce tutoriel n'impose pas d'accès root sur le système de développement, je suppose tout de même que vous avez python3 et pip3 d'installés, un accès au réseau pour les packages Python supplémentaires, ainsi que le gestionnaire de versions de code git.

    J'utilise une debian GNU/Linux 9.1 Stretch pour le développement, mais ce tutoriel doit facilement être adaptable à d'autres distributions.

    Commençons en douceur avec Flask.

    Flask, en deux mots

    Je vais me faire battre, je vais parler de Flask alors que je l'ai utilisé en tout et pour tout 7 minutes 24 secondes dans ma courte vie.

    Installation

    Prenons l'exemple le plus basique avec Flask. La documentation du projet se trouve ici.

    Elle indique qu'il suffit de taper la commande suivante pour installer Flask :

    pip3 install --user Flask

    Le projet fil d'ariane

    Si vous avez suivi l'étape précédente, vous savez qu'il faut revenir à la version du tag "_v0_flask" comme ceci :

    git checkout _v0_flask

    pour cette partie du tutoriel. Vous pouvez alors faire tourner le serveur de test à l'aide de la commande :

    make

    Je vais faire une utilisation intensive des Makefiles, que j'affectionne, mais je vous encourage à les parcourir pour voir ce qu'ils exécutent.

    Ici, la commande est simplement :

    python3 ma_flasque.py

    Vous pouvez alors vous rendre à l'url suivante : http://localhost:5000 dans mon cas.

    Le pourquoi du comment

    La partie intéressante que je voulais mettre en avant avec cet exemple concerne la structure du code Flask : j'ai divisé le source en trois parties, les imports, les routes Flask, et le code pour faire tourner l'application. La deuxième partie concernant les routes et le code métier est celle qui nous intéresse.

    Les routes sont indiquées par un :

    @app.route('/', methods=["GET"])

    tandis que le code à exécuter dans le cas où l'utilisateur demande ('GET') la page par défaut de l'application ('/') se situe dans le code de la fonction Python index, qui dans notre cas retourne la chaîne de caractères "Hello World!" (tradition oblige) :

    def index():
        return "Hello World!"

    Ainsi, Flask pose les bases de la programmation avec le motif MVC, que Django utilise : on décrit de manière séparée le code de routage des URL et le code permettant d'obtenir le résultat demandé.

    Django utilisera des fichiers séparés pour le routage (Vues) et pour les fonctions Python du Contrôleur, mais le principe est le même.

    Il va falloir un peu de travail pour arriver au même résultat avec Django, mais n'aillez pas peur, on y arrivera. L'essentiel est de garder les yeux sur l'objectif : avoir une séparation entre le code de routage et le code métier.

    Django va un peu plus loin car il permet de s'interfacer très facilement avec une base de données et en propose une abstraction très propre, il possède un moteur de template pour prémâcher du HTML, permet d'installer des modules, mais tout ça viendra plus tard, je vous mets l'eau à la bouche...

    gâteau au chocolat

    Un gâteau au chocolat pour vous mettre l'eau à la bouche si ma description des fonctionnalités de Django n'a pas suffi.

    Même résultat avec Django

    Installation

    La page de téléchargement de Django indique, à l'heure actuelle, qu'il suffit de taper la commande suivante pour installer la dernière version de Django :

    pip3 install Django==1.11.5

    Si vous utilisez Django de manière professionnelle ou amateure, il faut consulter la page de téléchargement pour mettre à jour la version de Django utilisée.

    Vous pouvez aussi installer la version de votre distribution, par exemple à l'aide de

    su -c 'apt-get install python3-django'

    si vous utilisez une debian ou dérivée (Ubuntu, Mint...)

    On utilisera Django dans un virtualenv Python pour ce projet.

    Virtualenv

    Python est un langage de programmation modulaire dont le cœur des fonctionnalités est assez réduit, mais qui reste très puissant grâce au grand écosystème disponible de bibliothèques logicielles, officielles ou tierces.

    Celles-ci peuvent être importées dans un script par une primitive import du type :

    import os, sys
    import re
    import numpy as np
    from django.http import HttpResponse

    etc.

    Les virtualenv Python servent à gérer pour chaque projet ses dépendances exactes, à l'inclusion des numéros de version des bibliothèques utilisées, et ce sans interférer avec d'autres projets.

    On utilisera donc les virtualenv pour des questions de facilité, et parce qu'ils permettent de s'assurer de la pérennité d'un développement.

    Vous pouvez installer virtualenv à l'aide de la commande :

    sudo apt-get install python3-virtualenv

    ou en tapant :

    pip3 install --user virtualenv

    Premier projet Django dans un Virtualenv

    La version "_v1_django" permet d'installer Django dans un virtualenv Python. Si vous avez installé Django et Virtualenv comme indiqué précédemment, l'installation devrait se faire sans retélécharger des bibliothèques Python depuis internet.

    Elle suppose que vous installez toutes les bibliothèques avec pip, si ce n'était pas le cas, il faut éventuellement changer la variable VENV du Makefile, ou installer une version avec pip3.

    Pour installer le virtualenv, Django, créer un projet et une application, tapez simplement la commande :

    make

    Pour l'arrêter, tapez la combinaison de touches CTRL-C.

    Détaillons les étapes du Makefile :

    • création du répertoire d'installation de l'environnement de développement, représenté par la variable INSTALL_DIR dans le Makefile ;
    • création du virtualenv Python dans l'environnement de développement ;
    • mise à jour de pip3 dans l'environnement de développement ;
    • installation de Django ;
    • création du projet, dont le nom est représenté par la variable PROJECT_NAME ;
    • création de l'application APP_NAME.

    Vous remarquerez que chacune des étapes ne modifie que le répertoire d'installation situé à l'intérieur du répertoire git, jamais des bibliothèques système.

    Utilisation de Django

    Jusqu'ici, on n'a toujours pas écrit de code correspondant à notre application, seulement décrit une manière générale d'obtenir un projet Django d'équerre pour commencer. Il reste quelques étapes à connaître pour avancer dans le développement d'applications web avec Django sans que celui-ci se mette en travers de notre chemin.

    Migrations

    Lors de la création d'un projet Django, certaines applications sont installées pour nous, par exemple l'interface d'administration. Celle-ci n'est pas migrée, opération qu'il faut réaliser pour pouvoir l'utiliser.

    On a par ailleurs créé notre propre application, qui n'est pas non plus migrée.

    Il est nécessaire d'effectuer les migrations des applications, car celles-ci peuvent entraîner une modification du modèle en base de données.

    Obtenez le code de la version _v2_migration du projet git pour pouvoir migrer les applications du projet, et lancer le serveur Django de développement.

    La commande :

    make

    permet d'effectuer toutes les étapes décrites jusqu'ici :

    • installation de virtualenv, mise à jour de pip3, installation de django, etc. ;
    • création d'un projet django ;
    • création d'une application django ;
    • migrations du projet et de l'application ;
    • lancement du serveur de développement.

    Rendez-vous  à l'URL suivante pour la page par défaut de Django : http://localhost:8000

    Organisation du dépôt

    Le dépôt est organisé de la manière suivante :

    .
    +-- doc
    │   \-- README.md
    +-- exec\_django.sh
    +-- LICENSE
    +-- Makefile
    +-- mon\_django
    │   +-- Makefile
    │   +-- mon\_app
    │   \-- mon\_django
    +-- README.md
    \-- sandbox
        +-- bin
        +-- db.sqlite3
        +-- include
        +-- lib
        +-- Makefile -> ../mon\_django/Makefile
        +-- manage.py
        +-- mon\_app -> ../mon\_django/mon\_app
        +-- mon\_django -> ../mon\_django/mon\_django
        +-- pip-selfcheck.json
        +-- static
        \-- templates -> ../mon\_django/templates
    • les sources sont dans le répertoire mon_django (nom du projet) ;
    • la production est dans le répertoire sandbox ;
    • les sources ont des liens sympboliques vers la production ;
    • à la racine du projet se retrouvent éventuellement :
      • la documentation ;
      • la licence ;
      • les tests (unitaires, intégration).

    Cette organisation a pour but de simplifier le développement :

    • le Makefile (et les scripts qu'il appelle) permet de construire et reconstruire le projet à l'aide d'une seule commande ;
    • à chaque enregistrement d'une modification d'un fichier source sur disque, le serveur de développement Django redémarre avec la mise à jour, grâce aux liens symboliques vers les sources ;
    • la production est séparée des sources ;
    • le dépôt git peut être organisé en suivant les bonnes pratiques de développement Python :
      • disposition de la documentation ;
      • disposition des tests unitaires et intégration...

    Comparaison Django et Flask

    Ça y est, on a enfin tout un environnement prêt pour le développement. Vous allez me dire "était-ce bien la peine de faire tout ça pour en arriver au même point ?" Je crois que oui.

    Avantages de Django

    Pour plusieurs raisons :

    • le code de Django est plus organisé que celui de Flask, ce qui procure un avantage sur le long terme :
      • les vues et le contrôleur sont séparés ;
      • Django incite à faire du développement de petites applications réutilisables au sein de plusieurs projets
    • Django va permettre de s'interfacer facilement avec une base de données, ce qui fait qu'on a en fait de l'avance par rapport au cas où l'on serait restés avec Flask
    • j'ai mis en place un système de construction avec les Makefiles qui permet de tester son application à l'aide d'une seule commande, comme pour Flask.

    Je ne détaille pas les inconvénients de Django, il y en a un principal selon moi, la relative difficulté de le mettre en place, ce qui est fait par le système de construction logicielle avec Makefile.

    Comprendre le code de Django en comparaison avec celui de Flask

    Django fonctionne fondamentalement de la même manière que Flask pour le routage, mais il sépare les vues et le contrôleur :

    • on utilise généralement le fichier urls.py pour décrire la liste des urls proposées par une applications :
      • ces URL ne doivent pas se chevaucher, autrement une seule des URL serait prise en compte (la première rencontrée par Django) ;
      • la liste des URL de tous les fichiers urls.py de toutes les applications constitue l'API backend offerte par votre projet Django ;
    • on utilise le fichier views.py pour le code métier qui va recevoir les requêtes GET/POST HTTP ;
    • on peut éventuellement créer d'autres modules Python pour du code spécifique ;
    • on utilise le fichier models.py pour décrire les modèles des données de l'application, tels que ces données seront sauvegardées en base de données.

    Le développement avec Django

    Maintenant que vous avez vu l'installation de Django en virtualenv Python, ainsi que la théorie sur la manière dont il faut organiser le code Python, allons voir quelques exemples simples.

    Hello World!

    Obtenez la version _v3_application du dépôt. Elle contient un simple "Hello World!" si le client demande la page http://localhost:8000/, et une simple indication "Not found" pour toutes les autres pages. Comme pour chanque étape de ce tutoriel, la commande :

    make

    permet de faire le café (enfin presque). Par rapport à l'étape précédente, on dispose désormais d'une application Django dont le code Python est importé.

    café au lait

    Le café au lait que mon ordinateur ne sait toujours pas faire... Ça viendra.

    En lisant le code,on s'aperçoit que son organisation est similaire à celle de Flask : d'un côté on route les urls, on utilise un ou plusieurs fichiers urls.py pour ce faire, et dans les vues du fichier views.py de chaque application du projet, on peut écrire le code à exécuter pour chaque requête.

    urlpatterns = [
        url('^$', views.index),
        url(r'^admin/', admin.site.urls),
        url('^.*', appviews.p404),
    ]

    Le routage des URL du projet (et de chacune des applications) est défini à l'aide de regexPython. Je fait correspondre les URL suivantes :

    • http://localhost:8000/
    • http://localhost:8000

    au code ci-dessous :

    def index(request):
        return HttpResponse("Hello World!")

    à l'aide de la première règle de routage.

    La page suivante de la documentation permet une utilisation avancée des URL de routage de l'application. On peut par exemple utiliser un champ de l'URL comme paramètre.

    Je ne vous ai pas promis la lune mais presque, allons plus loin dans les fonctionnalités qui sont un des grands avantages de Django.

    Mon premier gabarit

    La version _v4_template permet d'utiliser les templates de Django. Je n'en fait qu'un usage très limité, je me limite à indiquer à Django où il doit aller chercher les fichiers de template correspondant au code HTML/CSS/JavaScript, servis par le serveur de développement, mais ce moteur de gabarits possède de nombreuses fonctionnalités, comme les variables, les tags, des boucles, des conditions...

    La page accessible à l'URL par défaut http://localhost:8000/ affiche le code du template index.html :

    def index(request):
        return render(request, "index.html", {})

    J'ai rajouté de manière explicite un paramètre optionnel : le dictionnaire vide {}. Il sert à préciser des variables Python à transmettre au moteur de gabarits.

    Toutes les autres requêtes redirigent vers la page 404.

    Un accès à la base de données

    Une grande quantité d'applications web nécessite une base de données. Django propose un moyen simple pour s'interfacer avec une base de données sans nécessairement en connaître les subtilités, fournit des garanties qui permettent éventuellement de changer de moteur de base de données, et permet au développeur de manipuler les objets en base facilement.

    La version _v5_bdd présente un exemple simple d'utilisation d'une base de données. Les URL suivantes sont valides :

    • http://localhost:8000/create/ : crée un objet en base de données ;
    • http://localhost:8000/delete/ : supprime tous les objets ;
    • http://localhost:8000/get_nb_objects/ : affiche le nombre d'objets en base de données.

    Attention, celles-ci ne le sont pas, il manque un / à la fin :

    • http://localhost:8000/create ;
    • http://localhost:8000/delete ;
    • http://localhost:8000/get_nb_objects.

    Si vous vous demandez pourquoi l'exemple marche bien que tous les objets aient le même nom et la même valeur de booléen, c'est parce qu'ils se voient attribuer un identifiant entier automatique croissant si on ne définit pas d'attribut ayant une clef primaire, cf. la documentation.

    Découpage en modules

    Vous remarquerez l'utilisation de la primitive Django include dans le code, par rapport à la version _v4_template, qui permet de découper le routage vers les URL d'une application.

    Utilisation d'un constructeur personnalisé

    Django utilise la fonction __init__ qui sert à construire des objets Python. La documentation explique comment attacher un constructeur aux objets sérialisés en base de données, plutôt que de recourir à une méthode build comme je m'y suis pris dans l'exemple _v5_bdd. L'exemple se trouve dans le code de la version  _v6_constructeur.

    Pour aller plus loin

    Je ferai peut-être une suite à ce tutoriel Django. Dans tous les cas, sachez que le point auquel nous sommes arrivés n'est pas suffisant ! En effet, on utilise toujours le serveur de développement de Django, qui n'est pas fait pour être utilisé en production, entre autres :

    "(notre métier est le développement d’environnements Web, pas de serveurs Web)."

    Mise en production

    D'autres éléments sont à considérer durant et après la phase de développement :

    • la mise en place d'un mécanisme de journalisation de l'application ;
    • la mise en place d'un chien de garde permettant de s'assurer que le serveur (nginx, apache, ou autre) sert toujours l'application que vous avez écrite ;
    • la mise en place d'une sauvegarde des données en base...

    Il s'agit d'un travail d'administration système indispensable. À ce travail s'ajoute éventuellement celui de développer une interface web ou autres, si besoin est.

    Modules

    Je n'ai pas parlé des modules de Django. Dans la version _v4_bdd, on utilise la fonction Django render, qui permet de retourner une chaîne de caractères en temps que réponse HTTP. Cela diffère de Flask, qui permet de simplement retourner une chaîne de caractères. L'explication tient au fait que Django utilise l'objet request pour le passer aux modules Django, dans la fonction render.

    Si vous êtes intéressés par le sujet, je vous conseille cette page, qui décrit les interactions possibles sur une requête, ainsi que la base de leur fonctionnement.

    Réutilisation

    Voilà un bon début de projet Django. Pour le réutiliser, n'oubliez pas :

    • de changer la clef API située dans le fichier mon_django/settings.py ;
    • de respecter la licence GPLv3.

    Vous pouvez également créer un répertoire git de zéro à partir d'un des tags du dépôt exemple. Pour cela, obtenez les sources du dépôt au tag que vous souhaitez :

    git checkout <tagname>

    Puis supprimez le dépôt git sans toucher au fichiers existant en enlevant l'arborescence sous le répertoire .git :

    rm -rf ./.git

    Enfin, créez un nouveau dépôt :

    git init

    Vous pouvez aussi garder le dépôt git en l'état, le script del_tags.sh permet de supprimer les tags commençant par "_".

    Conclusion

    Mon but ici n'est pas de prétendre que Django est meilleur que Flask, comme je l'ai indiqué au début de ce tutoriel, j'ai très peu touché à Flask. Flask est un prétexte pour moi pour montrer un des différents aspects de Django (le routage des requêtes). Par ailleurs, en lisant un peu sur le sujet, on s'aperçoit que beaucoup de modules peuvent être utilisés pour donner à Flask des capacités similaires à celles qui viennent avec Django, par exemple :

    • une interface d'administration ;
    • un module ORM pour s'interfacer avec une base de données...

    Il y a donc des arguments en faveur de Flask par rapport à Django, par exemple :

    • le code de Flask est composé de moins de lignes de code  ;
    • on peut choisir ce dont on a réellement besoin parmi des fonctionnalités offertes par la bibliothèque qu'on utilise...

    Django, a de son côté :

    • l'organisation générale d'un projet web :
      • Django encourage à structurer son code ;
      • Django suggère la division du code en applications ;
      • il permet aux développeurs de travailler sur des applications différentes, ce qui facilite la collaboration ;
    • il vient avec un moteur de templates ;
    • il vient avec un ORM...

    Cette comparaison n'est évidemment pas exhaustive, mais permet de se rendre compte des convergences et divergences de philosophie de ses deux projets.

    Vous voilà parés pour démarrer ! J'espère que ce tutoriel vous a été utile. Pour ma part, je trouve que Django est très sympathique à utiliser, sa documentation est facile à prendre en main pour un usage débutant, même si elle est touffue. Enfin sachez que raspbeguy et moi-même avons quelques projets web (parmi lesquels clickstart, et Hashtagueule Express) qui utiliseront peut-être cette technologie.

    Bonne journée et bon développement Python/Django !

    Motius

    PS : après avoir commencé cet article, je suis tombé sur celui-ci, en anglais. Je tiens à le mentionner car la majorité des articles Flask vs Django sont théoriques (souvent des descriptions fonctionnelles, assez intéressantes pour se familiariser rapidement avec les deux projets), mais je n'avais pas vu (ni vraiment cherché) d'article les comparant en mettant la main dans le code.

    • Ha chevron_right

      Une Flask de Django

      motius · pubsub.gugod.fr / hashtagueule · Sunday, 24 September, 2017 - 22:00 · 17 minutes

    Bonjour à tous ! Pour ceux qui ont déjà fait du développement web avec Django le titre est évident, pour les autres, bienvenue sur mon introduction à Django ! Django est un excellent framework Python qui permet de développer rapidement des applications web, et qui est très complet. Il est plus long ...

    Bonjour à tous !

    Pour ceux qui ont déjà fait du développement web avec Django le titre est évident, pour les autres, bienvenue sur mon introduction à Django !

    Django est un excellent framework Python qui permet de développer rapidement des applications web, et qui est très complet. Il est plus long à prendre en main que Flask, un autre microframework Python que je vais utiliser comme exemple afin d'introduire quelques concepts de Django, et de montrer que ces idées bizarres ne sortent pas de nulle part.

    Parés ? C'est parti !

    Prérequis

    Ce tutoriel va référencer des tags du dépôt git à cette adresse. Je vous conseille aussi cet excellent tutoriel qui pourra compléter ce que j'écris ci-dessous. Enfin, la documentation de Django est complète et facile à utiliser.

    Je vous conseille donc de cloner le dépôt ci-dessus :

    git clone https://git.hashtagueule.fr/motius/django-tutorial.git

    et de suivre les tags que j'ai indiqués au fur et à mesure de l'avancement du tutoriel grâce à la commande :

    git checkout <tagname>

    Je préfixe les noms de tous les tags git de ce tutoriel par un "_", afin que ceux-ci ne polluent pas le projet si vous en réutilisez le dépôt git par la suite (git tag admet l'option -d pour ceux qui veulent vraiment se débarasser de mes tags).

    La quasi-totalité de ce tutoriel n'impose pas d'accès root sur le système de développement, je suppose tout de même que vous avez python3 et pip3 d'installés, un accès au réseau pour les packages Python supplémentaires, ainsi que le gestionnaire de versions de code git.

    J'utilise une debian GNU/Linux 9.1 Stretch pour le développement, mais ce tutoriel doit facilement être adaptable à d'autres distributions.

    Commençons en douceur avec Flask.

    Flask, en deux mots

    Je vais me faire battre, je vais parler de Flask alors que je l'ai utilisé en tout et pour tout 7 minutes 24 secondes dans ma courte vie.

    Installation

    Prenons l'exemple le plus basique avec Flask. La documentation du projet se trouve ici.

    Elle indique qu'il suffit de taper la commande suivante pour installer Flask :

    pip3 install --user Flask

    Le projet fil d'ariane

    Si vous avez suivi l'étape précédente, vous savez qu'il faut revenir à la version du tag "_v0_flask" comme ceci :

    git checkout _v0_flask

    pour cette partie du tutoriel. Vous pouvez alors faire tourner le serveur de test à l'aide de la commande :

    make

    Je vais faire une utilisation intensive des Makefiles, que j'affectionne, mais je vous encourage à les parcourir pour voir ce qu'ils exécutent.

    Ici, la commande est simplement :

    python3 ma_flasque.py

    Vous pouvez alors vous rendre à l'url suivante : http://localhost:5000 dans mon cas.

    Le pourquoi du comment

    La partie intéressante que je voulais mettre en avant avec cet exemple concerne la structure du code Flask : j'ai divisé le source en trois parties, les imports, les routes Flask, et le code pour faire tourner l'application. La deuxième partie concernant les routes et le code métier est celle qui nous intéresse.

    Les routes sont indiquées par un :

    @app.route('/', methods=["GET"])

    tandis que le code à exécuter dans le cas où l'utilisateur demande ('GET') la page par défaut de l'application ('/') se situe dans le code de la fonction Python index, qui dans notre cas retourne la chaîne de caractères "Hello World!" (tradition oblige) :

    def index():
        return "Hello World!"

    Ainsi, Flask pose les bases de la programmation avec le motif MVC, que Django utilise : on décrit de manière séparée le code de routage des URL et le code permettant d'obtenir le résultat demandé.

    Django utilisera des fichiers séparés pour le routage (Vues) et pour les fonctions Python du Contrôleur, mais le principe est le même.

    Il va falloir un peu de travail pour arriver au même résultat avec Django, mais n'aillez pas peur, on y arrivera. L'essentiel est de garder les yeux sur l'objectif : avoir une séparation entre le code de routage et le code métier.

    Django va un peu plus loin car il permet de s'interfacer très facilement avec une base de données et en propose une abstraction très propre, il possède un moteur de template pour prémâcher du HTML, permet d'installer des modules, mais tout ça viendra plus tard, je vous mets l'eau à la bouche...

    gâteau au chocolat

    Un gâteau au chocolat pour vous mettre l'eau à la bouche si ma description des fonctionnalités de Django n'a pas suffi.

    Même résultat avec Django

    Installation

    La page de téléchargement de Django indique, à l'heure actuelle, qu'il suffit de taper la commande suivante pour installer la dernière version de Django :

    pip3 install Django==1.11.5

    Si vous utilisez Django de manière professionnelle ou amateure, il faut consulter la page de téléchargement pour mettre à jour la version de Django utilisée.

    Vous pouvez aussi installer la version de votre distribution, par exemple à l'aide de

    su -c 'apt-get install python3-django'

    si vous utilisez une debian ou dérivée (Ubuntu, Mint...)

    On utilisera Django dans un virtualenv Python pour ce projet.

    Virtualenv

    Python est un langage de programmation modulaire dont le cœur des fonctionnalités est assez réduit, mais qui reste très puissant grâce au grand écosystème disponible de bibliothèques logicielles, officielles ou tierces.

    Celles-ci peuvent être importées dans un script par une primitive import du type :

    import os, sys
    import re
    import numpy as np
    from django.http import HttpResponse

    etc.

    Les virtualenv Python servent à gérer pour chaque projet ses dépendances exactes, à l'inclusion des numéros de version des bibliothèques utilisées, et ce sans interférer avec d'autres projets.

    On utilisera donc les virtualenv pour des questions de facilité, et parce qu'ils permettent de s'assurer de la pérennité d'un développement.

    Vous pouvez installer virtualenv à l'aide de la commande :

    sudo apt-get install python3-virtualenv

    ou en tapant :

    pip3 install --user virtualenv

    Premier projet Django dans un Virtualenv

    La version "_v1_django" permet d'installer Django dans un virtualenv Python. Si vous avez installé Django et Virtualenv comme indiqué précédemment, l'installation devrait se faire sans retélécharger des bibliothèques Python depuis internet.

    Elle suppose que vous installez toutes les bibliothèques avec pip, si ce n'était pas le cas, il faut éventuellement changer la variable VENV du Makefile, ou installer une version avec pip3.

    Pour installer le virtualenv, Django, créer un projet et une application, tapez simplement la commande :

    make

    Pour l'arrêter, tapez la combinaison de touches CTRL-C.

    Détaillons les étapes du Makefile :

    • création du répertoire d'installation de l'environnement de développement, représenté par la variable INSTALL_DIR dans le Makefile ;
    • création du virtualenv Python dans l'environnement de développement ;
    • mise à jour de pip3 dans l'environnement de développement ;
    • installation de Django ;
    • création du projet, dont le nom est représenté par la variable PROJECT_NAME ;
    • création de l'application APP_NAME.

    Vous remarquerez que chacune des étapes ne modifie que le répertoire d'installation situé à l'intérieur du répertoire git, jamais des bibliothèques système.

    Utilisation de Django

    Jusqu'ici, on n'a toujours pas écrit de code correspondant à notre application, seulement décrit une manière générale d'obtenir un projet Django d'équerre pour commencer. Il reste quelques étapes à connaître pour avancer dans le développement d'applications web avec Django sans que celui-ci se mette en travers de notre chemin.

    Migrations

    Lors de la création d'un projet Django, certaines applications sont installées pour nous, par exemple l'interface d'administration. Celle-ci n'est pas migrée, opération qu'il faut réaliser pour pouvoir l'utiliser.

    On a par ailleurs créé notre propre application, qui n'est pas non plus migrée.

    Il est nécessaire d'effectuer les migrations des applications, car celles-ci peuvent entraîner une modification du modèle en base de données.

    Obtenez le code de la version _v2_migration du projet git pour pouvoir migrer les applications du projet, et lancer le serveur Django de développement.

    La commande :

    make

    permet d'effectuer toutes les étapes décrites jusqu'ici :

    • installation de virtualenv, mise à jour de pip3, installation de django, etc. ;
    • création d'un projet django ;
    • création d'une application django ;
    • migrations du projet et de l'application ;
    • lancement du serveur de développement.

    Rendez-vous  à l'URL suivante pour la page par défaut de Django : http://localhost:8000

    Organisation du dépôt

    Le dépôt est organisé de la manière suivante :

    .
    +-- doc
    │   \-- README.md
    +-- exec\_django.sh
    +-- LICENSE
    +-- Makefile
    +-- mon\_django
    │   +-- Makefile
    │   +-- mon\_app
    │   \-- mon\_django
    +-- README.md
    \-- sandbox
        +-- bin
        +-- db.sqlite3
        +-- include
        +-- lib
        +-- Makefile -> ../mon\_django/Makefile
        +-- manage.py
        +-- mon\_app -> ../mon\_django/mon\_app
        +-- mon\_django -> ../mon\_django/mon\_django
        +-- pip-selfcheck.json
        +-- static
        \-- templates -> ../mon\_django/templates
    • les sources sont dans le répertoire mon_django (nom du projet) ;
    • la production est dans le répertoire sandbox ;
    • les sources ont des liens sympboliques vers la production ;
    • à la racine du projet se retrouvent éventuellement :
      • la documentation ;
      • la licence ;
      • les tests (unitaires, intégration).

    Cette organisation a pour but de simplifier le développement :

    • le Makefile (et les scripts qu'il appelle) permet de construire et reconstruire le projet à l'aide d'une seule commande ;
    • à chaque enregistrement d'une modification d'un fichier source sur disque, le serveur de développement Django redémarre avec la mise à jour, grâce aux liens symboliques vers les sources ;
    • la production est séparée des sources ;
    • le dépôt git peut être organisé en suivant les bonnes pratiques de développement Python :
      • disposition de la documentation ;
      • disposition des tests unitaires et intégration...

    Comparaison Django et Flask

    Ça y est, on a enfin tout un environnement prêt pour le développement. Vous allez me dire "était-ce bien la peine de faire tout ça pour en arriver au même point ?" Je crois que oui.

    Avantages de Django

    Pour plusieurs raisons :

    • le code de Django est plus organisé que celui de Flask, ce qui procure un avantage sur le long terme :
      • les vues et le contrôleur sont séparés ;
      • Django incite à faire du développement de petites applications réutilisables au sein de plusieurs projets
    • Django va permettre de s'interfacer facilement avec une base de données, ce qui fait qu'on a en fait de l'avance par rapport au cas où l'on serait restés avec Flask
    • j'ai mis en place un système de construction avec les Makefiles qui permet de tester son application à l'aide d'une seule commande, comme pour Flask.

    Je ne détaille pas les inconvénients de Django, il y en a un principal selon moi, la relative difficulté de le mettre en place, ce qui est fait par le système de construction logicielle avec Makefile.

    Comprendre le code de Django en comparaison avec celui de Flask

    Django fonctionne fondamentalement de la même manière que Flask pour le routage, mais il sépare les vues et le contrôleur :

    • on utilise généralement le fichier urls.py pour décrire la liste des urls proposées par une applications :
      • ces URL ne doivent pas se chevaucher, autrement une seule des URL serait prise en compte (la première rencontrée par Django) ;
      • la liste des URL de tous les fichiers urls.py de toutes les applications constitue l'API backend offerte par votre projet Django ;
    • on utilise le fichier views.py pour le code métier qui va recevoir les requêtes GET/POST HTTP ;
    • on peut éventuellement créer d'autres modules Python pour du code spécifique ;
    • on utilise le fichier models.py pour décrire les modèles des données de l'application, tels que ces données seront sauvegardées en base de données.

    Le développement avec Django

    Maintenant que vous avez vu l'installation de Django en virtualenv Python, ainsi que la théorie sur la manière dont il faut organiser le code Python, allons voir quelques exemples simples.

    Hello World!

    Obtenez la version _v3_application du dépôt. Elle contient un simple "Hello World!" si le client demande la page http://localhost:8000/, et une simple indication "Not found" pour toutes les autres pages. Comme pour chanque étape de ce tutoriel, la commande :

    make

    permet de faire le café (enfin presque). Par rapport à l'étape précédente, on dispose désormais d'une application Django dont le code Python est importé.

    café au lait

    Le café au lait que mon ordinateur ne sait toujours pas faire... Ça viendra.

    En lisant le code,on s'aperçoit que son organisation est similaire à celle de Flask : d'un côté on route les urls, on utilise un ou plusieurs fichiers urls.py pour ce faire, et dans les vues du fichier views.py de chaque application du projet, on peut écrire le code à exécuter pour chaque requête.

    urlpatterns = [
        url('^$', views.index),
        url(r'^admin/', admin.site.urls),
        url('^.*', appviews.p404),
    ]

    Le routage des URL du projet (et de chacune des applications) est défini à l'aide de regexPython. Je fait correspondre les URL suivantes :

    • http://localhost:8000/
    • http://localhost:8000

    au code ci-dessous :

    def index(request):
        return HttpResponse("Hello World!")

    à l'aide de la première règle de routage.

    La page suivante de la documentation permet une utilisation avancée des URL de routage de l'application. On peut par exemple utiliser un champ de l'URL comme paramètre.

    Je ne vous ai pas promis la lune mais presque, allons plus loin dans les fonctionnalités qui sont un des grands avantages de Django.

    Mon premier gabarit

    La version _v4_template permet d'utiliser les templates de Django. Je n'en fait qu'un usage très limité, je me limite à indiquer à Django où il doit aller chercher les fichiers de template correspondant au code HTML/CSS/JavaScript, servis par le serveur de développement, mais ce moteur de gabarits possède de nombreuses fonctionnalités, comme les variables, les tags, des boucles, des conditions...

    La page accessible à l'URL par défaut http://localhost:8000/ affiche le code du template index.html :

    def index(request):
        return render(request, "index.html", {})

    J'ai rajouté de manière explicite un paramètre optionnel : le dictionnaire vide {}. Il sert à préciser des variables Python à transmettre au moteur de gabarits.

    Toutes les autres requêtes redirigent vers la page 404.

    Un accès à la base de données

    Une grande quantité d'applications web nécessite une base de données. Django propose un moyen simple pour s'interfacer avec une base de données sans nécessairement en connaître les subtilités, fournit des garanties qui permettent éventuellement de changer de moteur de base de données, et permet au développeur de manipuler les objets en base facilement.

    La version _v5_bdd présente un exemple simple d'utilisation d'une base de données. Les URL suivantes sont valides :

    • http://localhost:8000/create/ : crée un objet en base de données ;
    • http://localhost:8000/delete/ : supprime tous les objets ;
    • http://localhost:8000/get_nb_objects/ : affiche le nombre d'objets en base de données.

    Attention, celles-ci ne le sont pas, il manque un / à la fin :

    • http://localhost:8000/create ;
    • http://localhost:8000/delete ;
    • http://localhost:8000/get_nb_objects.

    Si vous vous demandez pourquoi l'exemple marche bien que tous les objets aient le même nom et la même valeur de booléen, c'est parce qu'ils se voient attribuer un identifiant entier automatique croissant si on ne définit pas d'attribut ayant une clef primaire, cf. la documentation.

    Découpage en modules

    Vous remarquerez l'utilisation de la primitive Django include dans le code, par rapport à la version _v4_template, qui permet de découper le routage vers les URL d'une application.

    Utilisation d'un constructeur personnalisé

    Django utilise la fonction __init__ qui sert à construire des objets Python. La documentation explique comment attacher un constructeur aux objets sérialisés en base de données, plutôt que de recourir à une méthode build comme je m'y suis pris dans l'exemple _v5_bdd. L'exemple se trouve dans le code de la version  _v6_constructeur.

    Pour aller plus loin

    Je ferai peut-être une suite à ce tutoriel Django. Dans tous les cas, sachez que le point auquel nous sommes arrivés n'est pas suffisant ! En effet, on utilise toujours le serveur de développement de Django, qui n'est pas fait pour être utilisé en production, entre autres :

    "(notre métier est le développement d’environnements Web, pas de serveurs Web)."

    Mise en production

    D'autres éléments sont à considérer durant et après la phase de développement :

    • la mise en place d'un mécanisme de journalisation de l'application ;
    • la mise en place d'un chien de garde permettant de s'assurer que le serveur (nginx, apache, ou autre) sert toujours l'application que vous avez écrite ;
    • la mise en place d'une sauvegarde des données en base...

    Il s'agit d'un travail d'administration système indispensable. À ce travail s'ajoute éventuellement celui de développer une interface web ou autres, si besoin est.

    Modules

    Je n'ai pas parlé des modules de Django. Dans la version _v4_bdd, on utilise la fonction Django render, qui permet de retourner une chaîne de caractères en temps que réponse HTTP. Cela diffère de Flask, qui permet de simplement retourner une chaîne de caractères. L'explication tient au fait que Django utilise l'objet request pour le passer aux modules Django, dans la fonction render.

    Si vous êtes intéressés par le sujet, je vous conseille cette page, qui décrit les interactions possibles sur une requête, ainsi que la base de leur fonctionnement.

    Réutilisation

    Voilà un bon début de projet Django. Pour le réutiliser, n'oubliez pas :

    • de changer la clef API située dans le fichier mon_django/settings.py ;
    • de respecter la licence GPLv3.

    Vous pouvez également créer un répertoire git de zéro à partir d'un des tags du dépôt exemple. Pour cela, obtenez les sources du dépôt au tag que vous souhaitez :

    git checkout <tagname>

    Puis supprimez le dépôt git sans toucher au fichiers existant en enlevant l'arborescence sous le répertoire .git :

    rm -rf ./.git

    Enfin, créez un nouveau dépôt :

    git init

    Vous pouvez aussi garder le dépôt git en l'état, le script del_tags.sh permet de supprimer les tags commençant par "_".

    Conclusion

    Mon but ici n'est pas de prétendre que Django est meilleur que Flask, comme je l'ai indiqué au début de ce tutoriel, j'ai très peu touché à Flask. Flask est un prétexte pour moi pour montrer un des différents aspects de Django (le routage des requêtes). Par ailleurs, en lisant un peu sur le sujet, on s'aperçoit que beaucoup de modules peuvent être utilisés pour donner à Flask des capacités similaires à celles qui viennent avec Django, par exemple :

    • une interface d'administration ;
    • un module ORM pour s'interfacer avec une base de données...

    Il y a donc des arguments en faveur de Flask par rapport à Django, par exemple :

    • le code de Flask est composé de moins de lignes de code  ;
    • on peut choisir ce dont on a réellement besoin parmi des fonctionnalités offertes par la bibliothèque qu'on utilise...

    Django, a de son côté :

    • l'organisation générale d'un projet web :
      • Django encourage à structurer son code ;
      • Django suggère la division du code en applications ;
      • il permet aux développeurs de travailler sur des applications différentes, ce qui facilite la collaboration ;
    • il vient avec un moteur de templates ;
    • il vient avec un ORM...

    Cette comparaison n'est évidemment pas exhaustive, mais permet de se rendre compte des convergences et divergences de philosophie de ses deux projets.

    Vous voilà parés pour démarrer ! J'espère que ce tutoriel vous a été utile. Pour ma part, je trouve que Django est très sympathique à utiliser, sa documentation est facile à prendre en main pour un usage débutant, même si elle est touffue. Enfin sachez que raspbeguy et moi-même avons quelques projets web (parmi lesquels clickstart, et Hashtagueule Express) qui utiliseront peut-être cette technologie.

    Bonne journée et bon développement Python/Django !

    Motius

    PS : après avoir commencé cet article, je suis tombé sur celui-ci, en anglais. Je tiens à le mentionner car la majorité des articles Flask vs Django sont théoriques (souvent des descriptions fonctionnelles, assez intéressantes pour se familiariser rapidement avec les deux projets), mais je n'avais pas vu (ni vraiment cherché) d'article les comparant en mettant la main dans le code.

    • Ha chevron_right

      Ça va devenir RAID dans votre serveur

      raspbeguy · pubsub.gugod.fr / hashtagueule · Sunday, 20 August, 2017 - 22:00 · 13 minutes

    Récemment j'ai du intervenir sur ma tour parce que j'avais plus tellement de place. J'ai du vite acheter un autre disque dur pour y remédier. Ce qui me donne l'occasion de parler de cette belle technologie sur laquelle je m'étais promis de faire un petit tuto un de ces jours. En théorie RAID est l'a...

    Récemment j'ai du intervenir sur ma tour parce que j'avais plus tellement de place. J'ai du vite acheter un autre disque dur pour y remédier. Ce qui me donne l'occasion de parler de cette belle technologie sur laquelle je m'étais promis de faire un petit tuto un de ces jours.

    En théorie

    RAID est l'acronyme de Redundant Array of Independent Disks (Alignement redondant de disques indépendants), et comme son nom l'indique, il va permettre de regrouper plusieurs disques durs et d'y instaurer une politique de stockage pour faire de la redondance, de l’agrégation de taille, enfin ça répond à plein de besoins différents. En fait on ne devrait pas vraiment parler d'un seul RAID, mais bel et bien de la famille des RAIDs, car en effet il y a autant de RAIDs que de besoins différent.

    On va présenter les assemblages RAID les plus utilisés. Pour plus de détails, je vous conseille la page Wikipédia qui est très claire et qui contient des schémas très explicites.

    Dans la suite, on va beaucoup parler de bandes de stockage : il s'agit d'une quantité de stockage de valeur prédéfinie, qui correspond à une "section" contiguë du disque qui la contient. Dans ce modèle, lors de l'écriture sur le volume, une bande est d’abord remplie avant d'en entamer une suivante. Il s'agit en fait d'abstraire le stockage et de pouvoir mieux comprendre la répartition des données sur les différents disques.

    RAID 0

    Les bandes sont tout bonnement distribuées sur les disques, toujours dans le même ordre. On remplit donc une bande par disque, puis une fois qu'on a touché à tous les disques, on repart du premier disque et on recommence une couche de bandes. Tous les disques contiennent des données différentes. Ainsi, si vous avez n disques durs de capacité exploitable x, la capacité totale du montage sera nx.

    • Avantages : Ce montage permet d'utiliser le plus grand espace possible de votre assemblage. De plus, en moyenne, les accès disques parallèles seront plus rapide, pour peu que ces accès disques se fassent dans des bandes différentes et sur des disques différents.
    • Inconvénients : Il suffit de perdre un seul disque pour corrompre l'intégralité des données. Or, statistiquement, plus on a de disques, plus on a de chances d'en perdre.

    RAID 1

    Les bandes sont écrites à l’identique sur tous les disques. Tous les disques contiennent exactement les même données. Ainsi, si vous avez n disques durs de capacité exploitable x, la capacité totale du montage sera x.

    • Avantages : C'est le montage qui permet la plus grande sécurité des données faces aux pannes : tant qu'il vous reste un disque fonctionnel, vos données sont intactes et complètes. Les accès disques sont aussi nettement améliorés, avec une vitesse théorique multipliée par le nombre de disques dans le montage.
    • Inconvénients : Il est frustrant d'avoir beaucoup de disques durs pour avoir au final l'espace d'un seul disque. Évidemment les données sont très sûres mais quand même, faut pas pousser...

    RAID 5

    Les bandes sont réparties sur tous les disques sauf un, qui reçoit une bande de parité. La bande de parité est tout simplement une bande dont chaque bit est obtenu en appliquant le OU exclusif (XOR) à l'ensemble des bits correspondants dans les bandes alignées des autres disques. Cette bande de parité est attribuée à chaque disque alternativement au fil des couches de bandes remplies. Donc si un disque vient à tomber en panne, pour chaque couche de bande, si la bande du disque en panne est la bande de parité, alors les données de la couche sont intactes. Si la bande contient de la donnée, alors elle peut être recalculée à partir des autres couches de données et celle de parité. C'est du calcul logique, et c'est la force du XOR. Ainsi, si vous avez n disques durs de capacité exploitable x, la capacité totale du montage sera (n-1)x.

    • Avantages : Un bon compromis entre place exploitable et sécurité des données. On se donne le droit de perdre un disque dur dans la bataille. Pour retrouver l'usage des données, il suffit de remplacer le disque défaillant et les données sont reconstruites.
    • Inconvénients : Ça dépends des cas, mais un droit à un unique disque dur en panne, ça peu être peu. Pour certain ça peut être insuffisant.

    RAID 6

    Il s'agit d'une extension du RAID 5 dans laquelle on se permet de pouvoir perdre plus d'un disque. La solution étant d'attribuer plus de bandes de "parité" par couche, une par nombre de pannes maximum permises. Ici, j'utilise les guillemets car il ne s'agit plus vraiment de la parité simple de la logique de base, mais d'un truc chelou reposant sur un principe tout autre dont je ne pas trop vous parler parce que j'en ignore tout. Donc si vous avez n disques durs de capacité exploitable x, et si vous mettez en place k bandes de redondance par couche, la capacité totale du montage sera (n-k)x.

    • Avantages : On se sent un peu plus à l’abri des intempéries matérielles, au pris d'un peu de place de stockage.
    • Inconvénients : Le principe chelou dont je vous ai parlé, enfin dont je ne vous ai pas parlé pour être honnête, eh bien il est tellement chelou qu'il pète pas mal les performances. Et pour couronner le tout, la plupart des implémentations de ce type d'assemblage est bien moins stable que les autres.

    Il est également possible de combiner les RAIDs. Une combinaison fréquente est le RAID 10 (RAID 1 + RAID 0). Par exemple si on a 10 disques, on peu monter 2 RAID 1 de 5 disques chacun, puis assembler ces deux volumes en RAID 0. On a également aussi le RAID 50, combinaison dur RAID 5 et du RAID 0 selon le même principe.

    La gestion du RAID

    Je vous parlais de différentes implémentations du RAID. En fait, le RAID n'est pas un programme en tant que soi, juste un ensemble de spécifications. Du coup chacun y va de sa sauce : il existe des implémentations libres et propriétaires, logicielles et matérielles. Les solutions matérielles se basent sur du matériel dédié (un contrôleur RAID), le système d'exploitation n'a donc pas à s'occuper de ça et le plus souvent ces solutions marchent out of the box, sans avoir besoin de configurer quoi que ce soit. Mais elles ont l'immense inconvénient d'être propriétaire dans leur quasi-totalité, et pour le coup il n'existe aucun standard. Dans certains cas, si votre contrôleur RAID rend l'âme, il vous faudra acquérir exactement le même modèle de contrôleur, sinon, peu importe que vos disques soient tous intacts, plus rien ne pourra les lire, et vous serez alors en légitime position d'être fort contrarié. À mon humble avis, le principal argument des RAIDs matériel auparavant était les meilleures performances. Aujourd'hui cet avantage est de plus en plus obsolète, car désormais les machines sont bien plus puissantes qu'il y a 15 ans et les programmes de RAID ont été nettement optimisés et stabilisés. Cette différence de performance est aujourd'hui presque intangible.

    En pratique

    J'ai hérité par un ami d'une tour de bureau. Elle est bien cool car elle contient une grande baie de disques et pas mal de ports SATA (bien entendu, vous pouvez faire votre RAID avec des disques branchés en USB si vous le souhaitez). J'ai donc décidé d'en faire un NAS fait main.

    Choix des armes

    J'ai donc initialement acheté 3 disques de 3 To chacun (des Western Digital Red, gamme dédiée justement aux usage en RAID) et j'ai opté pour un RAID 5 logiciel. Et sur Linux, le programme de RAID par excellence est mdadm. J'ai décidé de garder branché le disque dur d'origine (500 Go) mais de ne pas l'intégrer au RAID, il sert uniquement de disque de démarrage. Notez que si j'avais voulu optimiser les performances, j'aurais pu troquer ce disque pour un petit SSD, mais bon, je suis déjà content comme ça.

    Par ailleurs, pour faire de la place dans la baie de disques, j'ai du déménager le disque de démarrage dans la baie frontale, celle destinée à accueillir les périphériques comme le lecteur optique.

    Le 4ème disque du RAID, on en parlera plus tard. Je l'ai acheté bien après.

    Pour ça j'ai utilisé un adaptateur 3,5 pouces vers 5,25 pouces bien pratique, qui m'a permis de fixer bien solidement le disque dur au lieu de le laisser se balader dans la tour.

    Partitionnement et formatage

    Du côté software, je me retrouve avec un disque sda de démarrage, et sdb, sdc et sdd des disques vierges prêts à être assemblés en RAID. Avant ça, j'ai besoin de les partitionner. J'utilise donc fdisk pour les trois disques à tour de rôle, je choisis une table de partitions GPT et j'y crée une unique partition de type "Linux RAID" (code 29 dans fdisk).

    Création de l'assemblage RAID

    C'est tout bête, ça tient en une seule ligne de commande :

    mdadm --create --verbose /dev/md0 --level=5 --raid-devices=3 /dev/sdb1 /dev/sdc1 /dev/sdd1

    Dans cette commande, j'indique que le nom du volume abstrait sera /dev/md0, que ce sera un RAID 5, qu'il comporte 3 disques et je lui précise les partitions que je veux utiliser.

    Pour suivre l'évolution de la création de l'assemblage, jetez un coup d'oeil au fichier /proc/mdstat. Ce fichier sera votre nouvel ami, c'est lui qu'il faudra consulter dès que vous ferez une opération qui touche au RAID.

    Puis j'ajoute l'assemblage à la configuration de mdadm pour que celui-ci me l'assemble au démarrage :

    mdadm --detail --scan >> /etc/mdadm.conf

    Puis il ne vous reste plus qu'à mettre votre système de fichier préféré sur le volume créé !

    mkfs.ext4 /dev/md0

    Pour que votre assemblage soit monté au démarrage, j'ajoute à mon /etc/fstab :

    /dev/md0 /mnt/data ext4 defaults 0 0

    Il ne faut pas confondre assemblage et montage, il s'agit de deux étapes lors de la connexion d'un RAID. On doit assembler un RAID afin qu'il soit reconnu comme un volume propre, puis on doit monter notre volume pour pouvoir lire et écrire dessus.

    Enfin on monte le volume :

    mkdir /mnt/data
    mount /mnt/data

    Ayé vous avez un RAID 5 qui fonctionne !

    Il est recommandé de procéder régulièrement à des reconstruction de RAID, opération qui va vérifier la cohérence de l'assemblage. Il s'agit d'une opération transparente mais qui dure plusieurs heures et qui va se traduire par une forte augmentation de l'activité des disques. Pour ça il vous faudra lancer un petit programme sans arguments, raid-check, à un intervalle raisonnable, disons, toutes les semaines. Choisissez un horaire de faible activité, comme la nuit du samedi au dimanche à 3h du matin. Pour ça, faites vous plaisir, entre la crontab et les timers de systemd, on manque pas de solutions.

    Ajout d'un nouveau disque à l'assemblage

    Mon RAID 5 était constitué de 3 disques de 3 To ce qui nous fait 6 To exploitables, si vous avez bien suivi. 6 To, c'est un grand espace, il m'a suffi pendant presque 2 ans... Puis j'ai eu besoin de plus de place. J'ai donc racheté un autre disque (même modèle, même taille) que j'ai fourré dans ma tour. Je vous avoue que sur le coup, j'étais encore puceau au niveau extension de RAID, j'ai donc flippé énormément et j'ai fait une sauvegarde de mes 6 To sur divers disques externes que j'ai pu trouver dans ma caverne. Je vous raconte pas comme c'était pénible...

    Petites observations à ce niveau :

    1. Remarquez que l'ordre des disques dans le nommage des périphériques physiques (sdX) et tout sauf fixe. En théorie, il faut être préparé à ce que vos disques changent de noms. Votre petit sdb chéri que vous avez bordé et vu s'endormir dans le creux douillet de son lit un soir peut très bien vous trahir et s'appeler sdc au petit matin dès le prochain redémarrage. Bon c'est un peu exagéré, mais une chose est sure, il ne faut jamais faire confiance au nommage des périphériques physiques (c'est pour ça entre autres qu'il faut privilégier la désignation par UUID dans la fstab).
    2. mdadm se fiche complètement du nommage de ses périphériques. Il utilise sa propre magie noire pour déterminer les membres d'un assemblages. En conclusion, ne vous tracassez pas pour retrouver le nom de vos disques de votre assemblage, mdadm, lui, s'y retrouvera toujours.

    Cependant, faites quand même gaffe lorsque vous allez ajoutez votre nouveau disque dur. Par exemple chez moi, le nouveau disque s'est vu nommé sdb, tandis que les autres s'étaient vu relégués en sdc, sdd et sde. Faut juste le savoir et traiter les disques pour ce qu'ils sont, et non pas des étiquettes à trois lettres.

    Tout ce que j'ai eu à faire, en définitive, a été de partitionner-formater mon nouveau disque ainsi que l'ont été ses aînés, puis j'ai lancé les deux commandes suivantes :

    mdadm --add /dev/md0 /dev/sdb1
    mdadm --grow --raid-devices=4 --backup-file=/root/grow\_md0.bak /dev/md0

    La première va juste indiquer à mdadm qu'on lui ajoute un disque, mais sans plus. C'est la deuxième commande qui va lancer l'opération de reconstruction (rééquilibrage de l'assemblage). Opération qui dure plusieurs heures, donc suivez son déroulement dans /proc/mdstat.

    Une fois que c'est terminé, il faut maintenant procéder à l'extension du système de fichier lui même. Et là vous priez pour avoir choisi un système de fichier pas trop stupide qui accepte ce genre d'opération. Rassurez-vous, ext4 le supporte très bien.

    Il le supporte même tellement bien que vous pouvez le faire directement à chaud, sans démonter votre volume. Pour ça, premièrement, si vous utilisez une connexion SSH, ouvrez une session tmux. Il serait bête qu'un incident réseau de rien du tout vienne corrombre tout votre système de fichire tout de même. Puis il vous faudra exécuter :

    resize2fs -p /dev/md0

    Il va vous mettre en garde et ronchonner un tantinet que votre disque est toujours monté, mais écoutez, il s'en remettra, dans la vie on fait pas toujours ce qu'on veut. Les minutes passent, vous vous rongez les ongles, vous prenez un café, vous videz un pot de glace, et normalement, si tout se passe bien, le système de fichier aura été étendu à tout l'espace disponible sur l'assemblage. Félicitations !

    Conclusion

    J'ai rencontré beaucoup de monde m'ayant dit qu'ils préféraient rester éloigner autant que possible de RAID parce que ça leur faisait peur. En réalité, il n'y a rien d'effrayant là dedans. mdadm est un outil ma foi fort bien codé et les années ont prouvé qu'il était fiable. Il ne faut pas avoir peur du RAID. Enfin un peu quand même, faut pas faire n'importe quoi.

    • At chevron_right

      Ça va devenir RAID dans votre serveur

      raspbeguy · pubsub.gugod.fr / atomtest · Sunday, 20 August, 2017 - 22:00 · 13 minutes

    Récemment j'ai du intervenir sur ma tour parce que j'avais plus tellement de place. J'ai du vite acheter un autre disque dur pour y remédier. Ce qui me donne l'occasion de parler de cette belle technologie sur laquelle je m'étais promis de faire un petit tuto un de ces jours. En théorie RAID est l'a...

    Récemment j'ai du intervenir sur ma tour parce que j'avais plus tellement de place. J'ai du vite acheter un autre disque dur pour y remédier. Ce qui me donne l'occasion de parler de cette belle technologie sur laquelle je m'étais promis de faire un petit tuto un de ces jours.

    En théorie

    RAID est l'acronyme de Redundant Array of Independent Disks (Alignement redondant de disques indépendants), et comme son nom l'indique, il va permettre de regrouper plusieurs disques durs et d'y instaurer une politique de stockage pour faire de la redondance, de l’agrégation de taille, enfin ça répond à plein de besoins différents. En fait on ne devrait pas vraiment parler d'un seul RAID, mais bel et bien de la famille des RAIDs, car en effet il y a autant de RAIDs que de besoins différent.

    On va présenter les assemblages RAID les plus utilisés. Pour plus de détails, je vous conseille la page Wikipédia qui est très claire et qui contient des schémas très explicites.

    Dans la suite, on va beaucoup parler de bandes de stockage : il s'agit d'une quantité de stockage de valeur prédéfinie, qui correspond à une "section" contiguë du disque qui la contient. Dans ce modèle, lors de l'écriture sur le volume, une bande est d’abord remplie avant d'en entamer une suivante. Il s'agit en fait d'abstraire le stockage et de pouvoir mieux comprendre la répartition des données sur les différents disques.

    RAID 0

    Les bandes sont tout bonnement distribuées sur les disques, toujours dans le même ordre. On remplit donc une bande par disque, puis une fois qu'on a touché à tous les disques, on repart du premier disque et on recommence une couche de bandes. Tous les disques contiennent des données différentes. Ainsi, si vous avez n disques durs de capacité exploitable x, la capacité totale du montage sera nx.

    • Avantages : Ce montage permet d'utiliser le plus grand espace possible de votre assemblage. De plus, en moyenne, les accès disques parallèles seront plus rapide, pour peu que ces accès disques se fassent dans des bandes différentes et sur des disques différents.
    • Inconvénients : Il suffit de perdre un seul disque pour corrompre l'intégralité des données. Or, statistiquement, plus on a de disques, plus on a de chances d'en perdre.

    RAID 1

    Les bandes sont écrites à l’identique sur tous les disques. Tous les disques contiennent exactement les même données. Ainsi, si vous avez n disques durs de capacité exploitable x, la capacité totale du montage sera x.

    • Avantages : C'est le montage qui permet la plus grande sécurité des données faces aux pannes : tant qu'il vous reste un disque fonctionnel, vos données sont intactes et complètes. Les accès disques sont aussi nettement améliorés, avec une vitesse théorique multipliée par le nombre de disques dans le montage.
    • Inconvénients : Il est frustrant d'avoir beaucoup de disques durs pour avoir au final l'espace d'un seul disque. Évidemment les données sont très sûres mais quand même, faut pas pousser...

    RAID 5

    Les bandes sont réparties sur tous les disques sauf un, qui reçoit une bande de parité. La bande de parité est tout simplement une bande dont chaque bit est obtenu en appliquant le OU exclusif (XOR) à l'ensemble des bits correspondants dans les bandes alignées des autres disques. Cette bande de parité est attribuée à chaque disque alternativement au fil des couches de bandes remplies. Donc si un disque vient à tomber en panne, pour chaque couche de bande, si la bande du disque en panne est la bande de parité, alors les données de la couche sont intactes. Si la bande contient de la donnée, alors elle peut être recalculée à partir des autres couches de données et celle de parité. C'est du calcul logique, et c'est la force du XOR. Ainsi, si vous avez n disques durs de capacité exploitable x, la capacité totale du montage sera (n-1)x.

    • Avantages : Un bon compromis entre place exploitable et sécurité des données. On se donne le droit de perdre un disque dur dans la bataille. Pour retrouver l'usage des données, il suffit de remplacer le disque défaillant et les données sont reconstruites.
    • Inconvénients : Ça dépends des cas, mais un droit à un unique disque dur en panne, ça peu être peu. Pour certain ça peut être insuffisant.

    RAID 6

    Il s'agit d'une extension du RAID 5 dans laquelle on se permet de pouvoir perdre plus d'un disque. La solution étant d'attribuer plus de bandes de "parité" par couche, une par nombre de pannes maximum permises. Ici, j'utilise les guillemets car il ne s'agit plus vraiment de la parité simple de la logique de base, mais d'un truc chelou reposant sur un principe tout autre dont je ne pas trop vous parler parce que j'en ignore tout. Donc si vous avez n disques durs de capacité exploitable x, et si vous mettez en place k bandes de redondance par couche, la capacité totale du montage sera (n-k)x.

    • Avantages : On se sent un peu plus à l’abri des intempéries matérielles, au pris d'un peu de place de stockage.
    • Inconvénients : Le principe chelou dont je vous ai parlé, enfin dont je ne vous ai pas parlé pour être honnête, eh bien il est tellement chelou qu'il pète pas mal les performances. Et pour couronner le tout, la plupart des implémentations de ce type d'assemblage est bien moins stable que les autres.

    Il est également possible de combiner les RAIDs. Une combinaison fréquente est le RAID 10 (RAID 1 + RAID 0). Par exemple si on a 10 disques, on peu monter 2 RAID 1 de 5 disques chacun, puis assembler ces deux volumes en RAID 0. On a également aussi le RAID 50, combinaison dur RAID 5 et du RAID 0 selon le même principe.

    La gestion du RAID

    Je vous parlais de différentes implémentations du RAID. En fait, le RAID n'est pas un programme en tant que soi, juste un ensemble de spécifications. Du coup chacun y va de sa sauce : il existe des implémentations libres et propriétaires, logicielles et matérielles. Les solutions matérielles se basent sur du matériel dédié (un contrôleur RAID), le système d'exploitation n'a donc pas à s'occuper de ça et le plus souvent ces solutions marchent out of the box, sans avoir besoin de configurer quoi que ce soit. Mais elles ont l'immense inconvénient d'être propriétaire dans leur quasi-totalité, et pour le coup il n'existe aucun standard. Dans certains cas, si votre contrôleur RAID rend l'âme, il vous faudra acquérir exactement le même modèle de contrôleur, sinon, peu importe que vos disques soient tous intacts, plus rien ne pourra les lire, et vous serez alors en légitime position d'être fort contrarié. À mon humble avis, le principal argument des RAIDs matériel auparavant était les meilleures performances. Aujourd'hui cet avantage est de plus en plus obsolète, car désormais les machines sont bien plus puissantes qu'il y a 15 ans et les programmes de RAID ont été nettement optimisés et stabilisés. Cette différence de performance est aujourd'hui presque intangible.

    En pratique

    J'ai hérité par un ami d'une tour de bureau. Elle est bien cool car elle contient une grande baie de disques et pas mal de ports SATA (bien entendu, vous pouvez faire votre RAID avec des disques branchés en USB si vous le souhaitez). J'ai donc décidé d'en faire un NAS fait main.

    Choix des armes

    J'ai donc initialement acheté 3 disques de 3 To chacun (des Western Digital Red, gamme dédiée justement aux usage en RAID) et j'ai opté pour un RAID 5 logiciel. Et sur Linux, le programme de RAID par excellence est mdadm. J'ai décidé de garder branché le disque dur d'origine (500 Go) mais de ne pas l'intégrer au RAID, il sert uniquement de disque de démarrage. Notez que si j'avais voulu optimiser les performances, j'aurais pu troquer ce disque pour un petit SSD, mais bon, je suis déjà content comme ça.

    Par ailleurs, pour faire de la place dans la baie de disques, j'ai du déménager le disque de démarrage dans la baie frontale, celle destinée à accueillir les périphériques comme le lecteur optique.

    Le 4ème disque du RAID, on en parlera plus tard. Je l'ai acheté bien après.

    Pour ça j'ai utilisé un adaptateur 3,5 pouces vers 5,25 pouces bien pratique, qui m'a permis de fixer bien solidement le disque dur au lieu de le laisser se balader dans la tour.

    Partitionnement et formatage

    Du côté software, je me retrouve avec un disque sda de démarrage, et sdb, sdc et sdd des disques vierges prêts à être assemblés en RAID. Avant ça, j'ai besoin de les partitionner. J'utilise donc fdisk pour les trois disques à tour de rôle, je choisis une table de partitions GPT et j'y crée une unique partition de type "Linux RAID" (code 29 dans fdisk).

    Création de l'assemblage RAID

    C'est tout bête, ça tient en une seule ligne de commande :

    mdadm --create --verbose /dev/md0 --level=5 --raid-devices=3 /dev/sdb1 /dev/sdc1 /dev/sdd1

    Dans cette commande, j'indique que le nom du volume abstrait sera /dev/md0, que ce sera un RAID 5, qu'il comporte 3 disques et je lui précise les partitions que je veux utiliser.

    Pour suivre l'évolution de la création de l'assemblage, jetez un coup d'oeil au fichier /proc/mdstat. Ce fichier sera votre nouvel ami, c'est lui qu'il faudra consulter dès que vous ferez une opération qui touche au RAID.

    Puis j'ajoute l'assemblage à la configuration de mdadm pour que celui-ci me l'assemble au démarrage :

    mdadm --detail --scan >> /etc/mdadm.conf

    Puis il ne vous reste plus qu'à mettre votre système de fichier préféré sur le volume créé !

    mkfs.ext4 /dev/md0

    Pour que votre assemblage soit monté au démarrage, j'ajoute à mon /etc/fstab :

    /dev/md0 /mnt/data ext4 defaults 0 0

    Il ne faut pas confondre assemblage et montage, il s'agit de deux étapes lors de la connexion d'un RAID. On doit assembler un RAID afin qu'il soit reconnu comme un volume propre, puis on doit monter notre volume pour pouvoir lire et écrire dessus.

    Enfin on monte le volume :

    mkdir /mnt/data
    mount /mnt/data

    Ayé vous avez un RAID 5 qui fonctionne !

    Il est recommandé de procéder régulièrement à des reconstruction de RAID, opération qui va vérifier la cohérence de l'assemblage. Il s'agit d'une opération transparente mais qui dure plusieurs heures et qui va se traduire par une forte augmentation de l'activité des disques. Pour ça il vous faudra lancer un petit programme sans arguments, raid-check, à un intervalle raisonnable, disons, toutes les semaines. Choisissez un horaire de faible activité, comme la nuit du samedi au dimanche à 3h du matin. Pour ça, faites vous plaisir, entre la crontab et les timers de systemd, on manque pas de solutions.

    Ajout d'un nouveau disque à l'assemblage

    Mon RAID 5 était constitué de 3 disques de 3 To ce qui nous fait 6 To exploitables, si vous avez bien suivi. 6 To, c'est un grand espace, il m'a suffi pendant presque 2 ans... Puis j'ai eu besoin de plus de place. J'ai donc racheté un autre disque (même modèle, même taille) que j'ai fourré dans ma tour. Je vous avoue que sur le coup, j'étais encore puceau au niveau extension de RAID, j'ai donc flippé énormément et j'ai fait une sauvegarde de mes 6 To sur divers disques externes que j'ai pu trouver dans ma caverne. Je vous raconte pas comme c'était pénible...

    Petites observations à ce niveau :

    1. Remarquez que l'ordre des disques dans le nommage des périphériques physiques (sdX) et tout sauf fixe. En théorie, il faut être préparé à ce que vos disques changent de noms. Votre petit sdb chéri que vous avez bordé et vu s'endormir dans le creux douillet de son lit un soir peut très bien vous trahir et s'appeler sdc au petit matin dès le prochain redémarrage. Bon c'est un peu exagéré, mais une chose est sure, il ne faut jamais faire confiance au nommage des périphériques physiques (c'est pour ça entre autres qu'il faut privilégier la désignation par UUID dans la fstab).
    2. mdadm se fiche complètement du nommage de ses périphériques. Il utilise sa propre magie noire pour déterminer les membres d'un assemblages. En conclusion, ne vous tracassez pas pour retrouver le nom de vos disques de votre assemblage, mdadm, lui, s'y retrouvera toujours.

    Cependant, faites quand même gaffe lorsque vous allez ajoutez votre nouveau disque dur. Par exemple chez moi, le nouveau disque s'est vu nommé sdb, tandis que les autres s'étaient vu relégués en sdc, sdd et sde. Faut juste le savoir et traiter les disques pour ce qu'ils sont, et non pas des étiquettes à trois lettres.

    Tout ce que j'ai eu à faire, en définitive, a été de partitionner-formater mon nouveau disque ainsi que l'ont été ses aînés, puis j'ai lancé les deux commandes suivantes :

    mdadm --add /dev/md0 /dev/sdb1
    mdadm --grow --raid-devices=4 --backup-file=/root/grow\_md0.bak /dev/md0

    La première va juste indiquer à mdadm qu'on lui ajoute un disque, mais sans plus. C'est la deuxième commande qui va lancer l'opération de reconstruction (rééquilibrage de l'assemblage). Opération qui dure plusieurs heures, donc suivez son déroulement dans /proc/mdstat.

    Une fois que c'est terminé, il faut maintenant procéder à l'extension du système de fichier lui même. Et là vous priez pour avoir choisi un système de fichier pas trop stupide qui accepte ce genre d'opération. Rassurez-vous, ext4 le supporte très bien.

    Il le supporte même tellement bien que vous pouvez le faire directement à chaud, sans démonter votre volume. Pour ça, premièrement, si vous utilisez une connexion SSH, ouvrez une session tmux. Il serait bête qu'un incident réseau de rien du tout vienne corrombre tout votre système de fichire tout de même. Puis il vous faudra exécuter :

    resize2fs -p /dev/md0

    Il va vous mettre en garde et ronchonner un tantinet que votre disque est toujours monté, mais écoutez, il s'en remettra, dans la vie on fait pas toujours ce qu'on veut. Les minutes passent, vous vous rongez les ongles, vous prenez un café, vous videz un pot de glace, et normalement, si tout se passe bien, le système de fichier aura été étendu à tout l'espace disponible sur l'assemblage. Félicitations !

    Conclusion

    J'ai rencontré beaucoup de monde m'ayant dit qu'ils préféraient rester éloigner autant que possible de RAID parce que ça leur faisait peur. En réalité, il n'y a rien d'effrayant là dedans. mdadm est un outil ma foi fort bien codé et les années ont prouvé qu'il était fiable. Il ne faut pas avoir peur du RAID. Enfin un peu quand même, faut pas faire n'importe quoi.

    • At chevron_right

      Quand Debian me gonfle : Stretch et OpenVPN

      raspbeguy · pubsub.gugod.fr / atomtest · Saturday, 1 July, 2017 - 22:00 · 5 minutes

    Comme vous le savez sûrement, la nouvelle version stable de Debian, nom de code Stretch, est sortie. Je vous conseille à tous de faire la mise à jour les yeux fermés, ça va bien se passer, aucun accrochage à déplorer, un simple apt dist-upgrade et c'est une affaire qui roule. ... Nan j'déconne. Enfi...

    Comme vous le savez sûrement, la nouvelle version stable de Debian, nom de code Stretch, est sortie. Je vous conseille à tous de faire la mise à jour les yeux fermés, ça va bien se passer, aucun accrochage à déplorer, un simple apt dist-upgrade et c'est une affaire qui roule.

    ...

    Nan j'déconne.

    Enfin je vous conseille quand même de faire la mise à jour bien entendu, pour des raisons évidentes de sécurité, prévoyez de le faire au moins à long terme. Parce que bon, la oldstable est toujours supportée à travers des mises à jour de sécurité, mais après y a plus rien, et votre serveur est livré à lui même et aux attaquants qui sauront profiter de failles non corrigées.

    Ce que je veux dire, c'est qu'en mettant à jour votre distribution, attendez-vous à des trucs qui vont péter sévère. Faites-ça quand vous n'avez rien d'autre à faire et quand un minimum de choses vitales sont attendues de votre serveur par vos utilisateurs. Va y avoir du vilain.

    Et hop c'est cassé

    Donc oui j'aime Debian, mais Debian me gonfle parfois. Notamment ce comportement très fâcheux que je viens de découvrir aujourd'hui même concernant OpenVPN.

    OpenVPN, j'en ai déjà parlé et je ne m'étendrai pas plus là dessus, est un outil permettant de construire des réseaux virtuels privés (VPN), utiles pour abstraire des réseaux entre des machines géographiquement distantes et/ou pour déporter son point de sortie pour "sembler" se connecter d'ailleurs. Il y a donc une machine qui crée le VPN, dite serveur ou tête de pont, et des machines qui s'y connectent à distance, dites clients.

    J'ai donc une tête de pont que j'ai récemment mis à jour sur Stretch. Tout se passa bien entendu parfaitement en apparence, et tout le monde il était beau. Je me retrouvai avec une jolie Debian Stretch toute mignonne dont le cœur et l'âme n'aurait en aucun cas pu être mis en doute par quiconque. Sauf que sous ses jupons, elle avait dissimulé des trucs moins fun. Et en conséquence, quelques jours après, je me rendis compte que mes clients OpenVPN ne se voyaient plus. Je ne mis pas longtemps à soupçonner la belle Stretch d'avoir des aveux à me faire.

    L'élément fautif : le service Systemd a changé bien en profondeur.  Sous Jessie, les fichiers nécessaires pour l'établissement du VPN (configurations, certificats, clefs...) se trouvaient alors modestement dans /etc/openvpn, et ne faisait pas la distinction entre configurations clients et configurations serveurs. Le service utilisé pour démarrer OpenVPN était nommé tout simplement openvpn.service, et son boulot était uniquement de lancer une instance d'OpenVPN par fichier de configuration (dont le nom se terminait par .conf) dans ce dossier. Pour lancer openvpn, il suffisait donc de la commande simple suivante :

    systemctl start openvpn

    Sous Stretch, tout a été changé. Premièrement, le service a changé de nom très salement. Par salement, je veux dire que l'ancien nom existe toujours, mais a été remplacé par un service qui ne fait absolument rien (il lance le programme /bin/true, programme dont l'utilité crève les yeux), ce qui fait que ce changement de service implique non seulement une rupture de ce service si aucune action n'est prise par la suite, mais également que c'est très difficile de s'en rendre compte, le service ancien openvpn.service étant diagnostiqué par le système comme actif et bien portant. Donc, la nouvelle forme de ce service est en fait un couple de service, openvpn-client@.service et openvpn-server@.service, le premier se chargeant de lancer OpenVPN en temps que client, et le second en temps que serveur. Mais ce n'était pas fini : les configurations VPN devaient être déplacées aux nouveaux endroits adéquats, et la ségrégation entre clients et serveurs a été instaurée : désormais, les configurations clients doivent aller dans /etc/openvpn/client et les configurations serveurs dans /etc/openvpn/server. Mais ce n'est encore pas tout : ces nouveaux services ne s'invoquent plus de la même manière. On doit maintenant spécifier le nom de la configuration dans la commande qui démarre le service. Par exemple, pour une configuration serveur qui s'appelle hashtagueule.conf (dûment placée dans /etc/openvpn/server), la commande pour démarrer le service associé est donc :

    systemctl start openvpn-server@hashtagueule

    Pour couronner le tout, un bug de configuration, oui un bug, et non une modification de comportement, s'est glissée dans le fichier de service openvpn-serveur@.service. Ce fichier comporte la ligne suivante :

    LimitNPROC=10

    Cette directive est utile pour contrôler le nombre maximum de processus exécutés par un service, pour éviter dans certain cas l'explosion du load du serveur. Sauf qu'ici, cette valeur est beaucoup trop petite. On se retrouve alors avec l'erreur suivante consignée dans les logs :

    openvpn_execve: unable to fork: Resource temporarily unavailable

    Ce qui veut dire que le système refuse la création d'un nouveau processus pour ce service. Je me résolus donc à réparer salement et de manière non safisfaisante en changeant la valeur 10 par 100 (aucune idée si c'est carrément trop), et enfin, le service a accepté de fonctionner.

    Quelle leçon tirer de cette aventure ? Premièrement, les mises à jour sont rarement simple, surtout dans le cas de Debian. Deuxièmement, même si le nouveau fonctionnement d'OpenVPN est désormais plus propre et correspond à l'usage conventionné sur les systèmes les plus récents, on peut déplorer la manière dont elle a été mise en place. Le bon usage aurait été d'instaurer une période de transition pendant laquelle les deux workflows auraient coexisté, plutôt que de casser brutalement une configuration existante et fonctionnelle. Et je ne parle même pas de la mauvaise limite du nombre de processus...

    En espérant que cette histoire aura été utile à des gens, je vous souhaite une bonne mise à jour.

    • Ha chevron_right

      Quand Debian me gonfle : Stretch et OpenVPN

      raspbeguy · pubsub.gugod.fr / hashtagueule · Saturday, 1 July, 2017 - 22:00 · 5 minutes

    Comme vous le savez sûrement, la nouvelle version stable de Debian, nom de code Stretch, est sortie. Je vous conseille à tous de faire la mise à jour les yeux fermés, ça va bien se passer, aucun accrochage à déplorer, un simple apt dist-upgrade et c'est une affaire qui roule. ... Nan j'déconne. Enfi...

    Comme vous le savez sûrement, la nouvelle version stable de Debian, nom de code Stretch, est sortie. Je vous conseille à tous de faire la mise à jour les yeux fermés, ça va bien se passer, aucun accrochage à déplorer, un simple apt dist-upgrade et c'est une affaire qui roule.

    ...

    Nan j'déconne.

    Enfin je vous conseille quand même de faire la mise à jour bien entendu, pour des raisons évidentes de sécurité, prévoyez de le faire au moins à long terme. Parce que bon, la oldstable est toujours supportée à travers des mises à jour de sécurité, mais après y a plus rien, et votre serveur est livré à lui même et aux attaquants qui sauront profiter de failles non corrigées.

    Ce que je veux dire, c'est qu'en mettant à jour votre distribution, attendez-vous à des trucs qui vont péter sévère. Faites-ça quand vous n'avez rien d'autre à faire et quand un minimum de choses vitales sont attendues de votre serveur par vos utilisateurs. Va y avoir du vilain.

    Et hop c'est cassé

    Donc oui j'aime Debian, mais Debian me gonfle parfois. Notamment ce comportement très fâcheux que je viens de découvrir aujourd'hui même concernant OpenVPN.

    OpenVPN, j'en ai déjà parlé et je ne m'étendrai pas plus là dessus, est un outil permettant de construire des réseaux virtuels privés (VPN), utiles pour abstraire des réseaux entre des machines géographiquement distantes et/ou pour déporter son point de sortie pour "sembler" se connecter d'ailleurs. Il y a donc une machine qui crée le VPN, dite serveur ou tête de pont, et des machines qui s'y connectent à distance, dites clients.

    J'ai donc une tête de pont que j'ai récemment mis à jour sur Stretch. Tout se passa bien entendu parfaitement en apparence, et tout le monde il était beau. Je me retrouvai avec une jolie Debian Stretch toute mignonne dont le cœur et l'âme n'aurait en aucun cas pu être mis en doute par quiconque. Sauf que sous ses jupons, elle avait dissimulé des trucs moins fun. Et en conséquence, quelques jours après, je me rendis compte que mes clients OpenVPN ne se voyaient plus. Je ne mis pas longtemps à soupçonner la belle Stretch d'avoir des aveux à me faire.

    L'élément fautif : le service Systemd a changé bien en profondeur.  Sous Jessie, les fichiers nécessaires pour l'établissement du VPN (configurations, certificats, clefs...) se trouvaient alors modestement dans /etc/openvpn, et ne faisait pas la distinction entre configurations clients et configurations serveurs. Le service utilisé pour démarrer OpenVPN était nommé tout simplement openvpn.service, et son boulot était uniquement de lancer une instance d'OpenVPN par fichier de configuration (dont le nom se terminait par .conf) dans ce dossier. Pour lancer openvpn, il suffisait donc de la commande simple suivante :

    systemctl start openvpn

    Sous Stretch, tout a été changé. Premièrement, le service a changé de nom très salement. Par salement, je veux dire que l'ancien nom existe toujours, mais a été remplacé par un service qui ne fait absolument rien (il lance le programme /bin/true, programme dont l'utilité crève les yeux), ce qui fait que ce changement de service implique non seulement une rupture de ce service si aucune action n'est prise par la suite, mais également que c'est très difficile de s'en rendre compte, le service ancien openvpn.service étant diagnostiqué par le système comme actif et bien portant. Donc, la nouvelle forme de ce service est en fait un couple de service, openvpn-client@.service et openvpn-server@.service, le premier se chargeant de lancer OpenVPN en temps que client, et le second en temps que serveur. Mais ce n'était pas fini : les configurations VPN devaient être déplacées aux nouveaux endroits adéquats, et la ségrégation entre clients et serveurs a été instaurée : désormais, les configurations clients doivent aller dans /etc/openvpn/client et les configurations serveurs dans /etc/openvpn/server. Mais ce n'est encore pas tout : ces nouveaux services ne s'invoquent plus de la même manière. On doit maintenant spécifier le nom de la configuration dans la commande qui démarre le service. Par exemple, pour une configuration serveur qui s'appelle hashtagueule.conf (dûment placée dans /etc/openvpn/server), la commande pour démarrer le service associé est donc :

    systemctl start openvpn-server@hashtagueule

    Pour couronner le tout, un bug de configuration, oui un bug, et non une modification de comportement, s'est glissée dans le fichier de service openvpn-serveur@.service. Ce fichier comporte la ligne suivante :

    LimitNPROC=10

    Cette directive est utile pour contrôler le nombre maximum de processus exécutés par un service, pour éviter dans certain cas l'explosion du load du serveur. Sauf qu'ici, cette valeur est beaucoup trop petite. On se retrouve alors avec l'erreur suivante consignée dans les logs :

    openvpn_execve: unable to fork: Resource temporarily unavailable

    Ce qui veut dire que le système refuse la création d'un nouveau processus pour ce service. Je me résolus donc à réparer salement et de manière non safisfaisante en changeant la valeur 10 par 100 (aucune idée si c'est carrément trop), et enfin, le service a accepté de fonctionner.

    Quelle leçon tirer de cette aventure ? Premièrement, les mises à jour sont rarement simple, surtout dans le cas de Debian. Deuxièmement, même si le nouveau fonctionnement d'OpenVPN est désormais plus propre et correspond à l'usage conventionné sur les systèmes les plus récents, on peut déplorer la manière dont elle a été mise en place. Le bon usage aurait été d'instaurer une période de transition pendant laquelle les deux workflows auraient coexisté, plutôt que de casser brutalement une configuration existante et fonctionnelle. Et je ne parle même pas de la mauvaise limite du nombre de processus...

    En espérant que cette histoire aura été utile à des gens, je vous souhaite une bonne mise à jour.

    • Ha chevron_right

      Un jukebox à partir d'un Raspberry Pi

      raspbeguy · pubsub.gugod.fr / hashtagueule · Wednesday, 15 March, 2017 - 23:00 · 7 minutes

    Ça y est ! Enfin un tutoriel Raspberry Pi. Depuis le temps qu'on m'en demande, vous êtes servi. Le Raspberry Pi (et les autres ordinateurs monocartes de la même farine) est remarquable du fait de la multitude d'usages qu'on peut lui attribuer. On le vois dans tous les domaines, Domotique, informatiq...

    Ça y est ! Enfin un tutoriel Raspberry Pi. Depuis le temps qu'on m'en demande, vous êtes servi.

    Le Raspberry Pi (et les autres ordinateurs monocartes de la même farine) est remarquable du fait de la multitude d'usages qu'on peut lui attribuer. On le vois dans tous les domaines, Domotique, informatique embarquée, serveur écologique, ordinateur de bureau low-cost, Internet des objets, montage électronique, cluster de calcul réparti, pour ne citer que ces exemples. Cet article va se concentrer sur l'aspect média-center du Raspberry Pi et plus précisément la lecture de bibliothèque musicale.

    Dans un premier temps, je vais vous expliquer comment vous monter un serveur MPD de base, chose que vous pourrez en grande partie trouver en quelques clics sur n'importe quel moteur de recherche. La deuxième partie présentera une manière très amusante de contrôler votre serveur de musique via un écran LCD, un projet en python qui a occupé mes soirées depuis ce dimanche (panne d'internet, vous comprenez) et dont je suis par conséquent assez fier, parce qu'il marche d'une part, et qu'en plus vous pouvez épater la galerie lorsque vous invitez des amis à l'heure de l'apéro et que vous voulez mettre un fond sonore.

    Le serveur MPD

    Si vous fréquentez des milieux barbus parlant entre autres d'usage de Linux au quotidien, de lecteurs multimédia, qui lancent des guerres sanglantes ayant pour motif par exemple le meilleur environnement graphique, qu'un environnement graphique en fait c'est pourri, que Arch Linux c'est bien mieux que Gentoo, bref, de tout ce qui relève de l'UNIX porn, vous avez très certainement déjà entendu parler de MPD (Music Player Daemon). Il s'agit d'une référence dans le domaine des lecteurs de bibliothèques musicales du type client-serveur (vous avez une machine qui lis la musique, et vous pouvez vous connecter dessus avec un client pour la contrôler).

    Ce qui fait la force de MPD, c'est sa légèreté (un Raspberry Pi peut l'exécuter sans trembler des genoux) ainsi que la flexibilité de sa configuration vis-à-vis des sorties audio, mais également et surtout la grande diversité des programmes clients, reflétant une communauté considérable, ce qui en fait en quelques sortes un standard de la lecture musicale sur UNIX.

    Nous partirons du principe que vous disposez d'un Raspberry Pi (même la première génération convient), et que vous avez installé une distribution dessus (par exemple Raspbian). Il est également conseillé de disposer d'un DAC (Digital to Analog Converter) externe, la carte son intégrée su RPi étant médiocre pour rester poli. Le DAC, pour faire court, est l'élément qui convertit le flux numérique audio en signal électrique analogique que vous pouvez brancher sur un ampli. Par exemple, la carte son de n'importe quel PC est un DAC. Je vous conseille également d'utiliser un DAC USB et non un DAC branché sur GPIO, sachant qu'ils est appréciable de pouvoir utiliser le GPIO pour autre chose (par exemple la deuxième partie du tutoriel...). Si vous cherchez une bonne affaire mais que vous pouvez quand même y mettre un certain prix, je vous conseille le Cambridge DacMagic 100, qui est d'excellente qualité, qui fonctionne sur le RPi avec une configuration de base et que j'utilise moi-même en étant satisfait.

    Niveau installation, il vous suffit d'installer MPD, présent sur la très grande majorité des distributions. Chose à noter, si vous avez des morceaux au format ALAC dans votre bibliothèque, vous devres en plus installer les plugins gstreamer adaptés. Sur Raspbian, il s'agit du paquet gstreamer1.0-plugins-bad. Puis vous êtes bon pour le décollage.

    Il est important d'ajouter le support de votre bibliothèque musicale dans le fichier /etc/fstab du RPi, cela permettra bien entendu de monter votre disque dur USB ou votre volume NFS dès le démarrage de l'appareil. Dans le cas d'un partage NFS, vous pouvez par exemple écrire :

    192.168.0.5:/chemin/vers/dossier/musique    /data   nfs    rsize=8192,wsize=8192,timeo=14,intr

    En remplaçant bien sûr l'IP de votre serveur NFS et le chemin du dossier musique sur ce serveur.

    Ensuite vous aurez besoin de configurer MPD. Direction le fichier /etc/mpd.conf pour dans un premier temps dire au lecteur où se trouve votre musique.

    music_directory "/mnt"

    Le fichier de configuration de MPD est assez bien documenté via le texte commenté. Ainsi vous pourrez personnaliser l'emplacement de vos futures playlists et de la base de donnée par exemple si ça vous chante.

    Partie également importante de ce fichier, la configuration des sorties audio. Si vous n'utilisez pas de DAC externe, vous pouvez laisser sans toucher. Si vous avec un DAC externe supportant de bonnes fréquences d'échantillonnage et que vous avez de la musique haute qualité, il sera intéressant d'y faire figurer :

    audio_output {
        type                "alsa"
        name                "My ALSA Device"
        device              "hw:CARD=C1,DEV=0"
        mixer_type          "none"
        auto_channels       "no"
        auto_format         "no"
        auto_resample       "no"
    }

    Le nom de la carte correspond pour le Cambridge DacMagic 100. Pour trouver le nom de votre DAC, vous pouvez par exemple faire usage de la commande aplay -L, qui vous listera l'ensemble des périphériques audio et leurs caractéristiques.

    Cette configuration impose à MPD de laisser le flux intouché (pas de traitement a posteriori) et de laisser le DAC révéler son potentiel en traitant du son à des fréquences échantillonnages élevées.

    Normalement c'est tout bon, vous pouvez démarrer ou redémarre MPD et initialiser la base de donnée. Pour ça,  vous allez avoir besoin d'un client, à installer sur les machines qui contrôleront le serveur. Par exemple sonata si vous voulez une interface graphique, ou l'excellent ncmpcpp si vous voulez du joli ncurses dans votre console. Astuce : il est pratique d'utiliser directement sur le RPi un client en ligle de commande minimaliste mpc, qui vous permettra rapidement de faires certaines actions, par exemple mettre la musique en pause parce que le voisin vous attend à votre porte avec une batte de baseball (mpc pause) ou bien mettre à jour la base de donnée (mpc update). Attention, la création de la base de donnée peut être très longue en fonction de la taille de votre bibliothèque. Pendant cette période, le CPU du RPi sera très solicité. Du coup vous saurez vite quand il aura fini...

    L'utilisation de MPD se base sur une liste de lecture centrale, dans laquelle vous ajoutez les morceaux dans l'ordre souhaité de lecture. Pour alimenter cette liste, vous aurez besoin de piocher dans la banque de musique qui a été créée par MPD. Je reste volontairement vague à ce sujet car la présentation de ces concepts varie en fonction du client utilisé.

    Il est également très amusant de contrôler son serveur via une interface web. Si vous désirez le faire, je vous conseille vivement de jeter un œil à ympd, une interface web propre et très légère, et dont il existe une version pour RPi justement.

    Bon, vous l'aurez compris, toute cette première partie n'était qu'un prétexte pour vous présenter le fruit de mon temps libre, mon projet du mois.

    Utiliser un écran LCD

    Il y a quelques années, je cherchais un écran LCD à contrôler par le RPi. J'ai craqué pour le Display-O-Tron 3000, qui dispose d'un rétroéclairage sous stéroïdes et multicolore, d'une série de diodes lumineuses supplémentaires à contrôler et d'un joystick de contrôle. Tout est contrôlable et personnalisable, et le fabriquant a développé une bibliothèque très facile d'usage, utilisable en Python, en C/C++ et en NodeJS. J'avais eu l'idée d'en faire un panneau de contrôle MPD mais je n'avais jamais pris le temps de le coder. C'est maintenant chose faite.

    Ce programme en python donc permet d'afficher les informations de la musique en cours sur MPD, avec les fonctionnalités suivantes :

    • Affichage du titre, de l'artiste et de l'album
    • Défilement du texte quand celui ci est trop long pour tenir sur une ligne
    • Changemment de la couleur du rétroéclairage en fonction de l'état du lecteur
    • Contrôle du lecteur grâce au joystick
    • Killer feature : affichage en temps réel de la puissance sonore via les diodes supplémentaires.

    Mieux que des paroles, voici une vidéo de démonstration

    Je vous invite à allez voir mon code commenté en détail (en anglais).

    Pour le mettre en application, il vous suffira d'installer la bibliothèque Python (voir le lien vers la page Github du fabricant) ainsi que la bibliothèque python-mpd2 utilisée pour contrôler MPD.

    Si vous trouvez des fonctionnalités à ajouter ou des améliorations possibles, n'hésitez pas à me le faire savoir, je serais heureux de prendre toutes les remarques en considération.