• 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

      Less is more, partie 2 : les contenus multimédia

      raspbeguy · pubsub.gugod.fr / hashtagueule · Friday, 22 September, 2017 - 22:00 · 7 minutes

    Je me relance dans cette série d'articles que j'ai trop souvent tendance à oublier alors qu'au final, j'ai passé (et j'en passe toujours) une énorme partie de mon temps à améliorer mon environnement visuel. Pour rappel, dans cette série d'articles, j'essaye d'expliquer comment se passer le plus poss...

    Je me relance dans cette série d'articles que j'ai trop souvent tendance à oublier alors qu'au final, j'ai passé (et j'en passe toujours) une énorme partie de mon temps à améliorer mon environnement visuel. Pour rappel, dans cette série d'articles, j'essaye d'expliquer comment se passer le plus possibles d'environnement graphique et les bienfaits de la simplicité pure d'un bon vieux terminal. Ça peut même être joli, faites-moi confiance.

    Aujourd'hui je vais me concentrer sur les outils de lecture et de gestion de contenus multimédias : images, films, et surtout musique, ce dernier média étant celui que je bichonne le plus avec une bibliothèque de plusieurs mois de longueurs cumulées et presque 500 Go de taille. Ça commence à faire gros et automatiser la gestion d'un machin pareil devient assez nécessaire.

    D'aucuns pourraient se dire que visionner du contenu multimédia sur un poste qui veut se passer d'environnement graphique, c'est un peu contradictoire. Mais rassurez-vous, il n'en est rien, c'est tout à fait possible, et même conseillé dans certains cas (histoire de réserver de la ressource pour lire le média lui-même et non pour gérer un ensemble d'effets de fenêtres débile qui fait prout quand on les ferme). Je vais donc vous exposer des outils KISS, faisant le travail demandé, ni plus, ni moins.

    Images

    Pour ouvrir des images, feh fait très bien le travail. Ça ouvre les jpg, png, gif, et bien d'autres, en fait tous les formats supportés par Imlib2. Il suffit de l'invoquer de la sorte :

    feh rem\_is\_best\_waifu.png

    À l'aide d'ImageMagick, ça peut même convertir d'autres formats afin de les ouvrir, par exemple le svg. Il suffit de donner une valeur de timeout à ImageMagick :

    feh --magick-timeout 5 logo\_hashtagueule.svg

    Astuce, si vous avez des images de grande dimension et que vous souhaitez adapter automatiquement la taille à la fenêtre de feh, ajoutez l'option suivante :

    feh -. playboy\_special\_tshirt\_mouille.jpg

    On peut même s'en servir pour le fond d'écran :

    feh --bg-scale windowsXP\_wallpaper.jpg

    Cette commande va créer par la même occasion un petit script ~/.fehbg afin de faciliter l'instauration du fond d'écran. Il suffira donc de dire à X11 ou à votre gestionnaire de fenêtre préféré de lancer ce script à chaque début de session (pour i3 on avait vu que ça se faisait avec la directive exec).

    Enfin, sachez que feh peut également ouvrir des images distantes via HTTP.

    Vidéos

    La plupart des gens, si on leur dit "vidéo sur linux", ils répondront à la microseconde et le plus fort possible "VLC!!!". Sachez qu'il n'y a pas que VLC dans la vie, mes enfants. Mon avis est que, même si VLC c'est très bien pour amener de l'open-source en terrain ennemi (ça tourne partout, y compris Windows et Mac), ça a une interface intuitive (c'est du QT, portable sur toutes les plateformes) et relativement facile à mettre en œuvre grâce à là nucléarité de ses fichiers d’exploitation (La plupart des bibliothèques utilisées par VLC sont des bibliothèques maison, et il y en a très peu), ben ces avantages apparents sont en partie des inconvénients pour les utilisateurs avancés. L'interface agréable, la belle affaire quand on veut se passer d'environnement graphique. La nucléarité de VLC également est un problème : ce sont des bibliothèques que seul VLC utilise ; résultat, personne n'y comprends plus rien et ça fait doublon avec d'autres bibliothèques plus communes et à mon sens plus propre.

    Bref, pour toutes ces raisons j'ai renoncé à VLC. Quand on y réfléchit, les raisons sont presque les même que les arguments contre GNOME.

    À la place, j'utilise mpv, qui a vraiment une interface minimale (et par conséquent élégante), et qui est plus léger que VLC, et ça se voit quand on lis des vidéos sur des machines pas super récentes.

    mpv a un système de raccourcis clavier hautement configurable et possède les même fonctionnalités que VLC. Il possède également un mécanisme d'accélération graphique.

    Bon, vu que je m'y connais pas vraiment côté vidéo, je vous laisse avec une capture d'écran.

    Rem is Best Waifu Rem is best waifu

    Musique

    Bon, là c'est du sérieux. Je ne gère pas de photothèque, et mes bibliothèques de films et de séries ne nécessitent pas beaucoup d'effort pour rester rangé. Mais la musique c'est une autre paire de manche.

    Gestionnaire de musique

    Pour ranger ma musique, jusqu'il n'y a pas si longtemps, j'utilisais l'utilitaire graphique Ex Falso, livré avec le lecteur de musique Quod Libet. Ces deux programmes sont très bons, c'est ceux que j'utilisait quand j'étais encore sous Cinnamon. Ex Falso est très puissant et permettait de faire des opérations sur des fichiers audio : modification des tags manuellement, par nom de fichier, ou encore et surtout l'opération inverse, à savoir changer l'emplacement et le nom de fichier en fonction des tags. L'ennui c'est que c'est du GTK, et pas d'interface en ligne de commande donc pas scriptable du tout.

    Je me suis finalement mis à beets, un outil full CLI destiné à maintenir une bibliothèque de musique rigoureusement organisée, que j'exécute directement sur le NAS. Beets peut à peu près tout faire et répond à toutes les demandes, pour peu qu'on passe du temps à comprendre toutes ses options. Et Dieu sait qu'il y en a beaucoup.

    Beets intègre un autotaggeur, c'est à dire que par défaut, il va chercher les tags des nouveaux fichiers. Sachant qu'en général les fichiers que j'importe sont déjà correctement taggés, j'ai choisi de désactiver cette fonctionnalité (et j'ai trop peur de ce qui arriverai si beets se plantait). Par contre, je l'utilise pour qu'il intègre mes nouveaux fichiers dans l'arborescence correcte. Je tiens à ce que ma bibliothèque soit rangé ainsi (pour faire simple) : racine/artiste/album/numdisque_numpiste-titre.flac

    Voici mon fichier de configuration (il est ridiculement petit et n'exploite qu'un fragment des possibilités de beets) :

    directory: /mnt/data/multimedia/Musique
    
    import:
        copy: yes
        #incremental: yes
        log: ~/.config/beets/beets.log
        autotag: no
    
    paths:
        default: %if{$albumartist,$albumartist,%if{$artist,%artist,unknown}}/%if{$album,$album,unknown}/%if{$disc,$disc-}%if{$track,$track - }$title

    Maintenant, quand je veux importer de nouveaux fichiers, il me suffit d'invoquer beets de la sorte :

    beet import /chemin/vers/dossier/contenant/nouveaux/morceaux

    Et paf, ça fait le taf. Bon je vous laisse lire la signification des instructions sur la (très bonne) documentation de beets. Vous allez vous en sortir, je crois en vous.

    Lecteur de musique

    C'est là que le bât blesse. En fait je n'ai pas encore trouvé le lecteur qui couvre exactement % de mes besoins. Je suis partagé entre deux programmes, qui ont chacun des avantages.

    MPD

    Mon rêve c'est de ne pouvoir tourner qu'avec MPD. Je vous en ai déjà parlé sur l'article Un jukebox à partir d'un Raspberry Pi en vous disant que ça déchire, et c'est effectivement le cas. Ça ne consomme quasiment aucune ressource, ça peut lire plein de formats, ça se contrôle avec un protocole simple (même avec MPRIS2, le protocole universel de contrôle de lecteur multimédia sur Linux, grâce à l'outil mpDris2) et ça vous fout la paix. Le problème, c'est que tous les clients MPD en ligne de commande que j'ai trouvé ont à mon sens une interface peu ergonomique. Le meilleur candidat étant ncmpcpp (déjà il à un nom à coucher dehors), l'ergonomie de son interface (pourtant en très joli ncurse, et vous savez à quel point j'aime ncurse) ne m'a pas emballé. Naviguer dans sa bibliothèque est assez douloureux, la répartition des différentes vues du programme sont pour le moins folkloriques, les raccourcis clavier par défaut sont assez déroutant... Dommage.

    cmus

    On parlait d'interface, et cmus en a une très réussie et intelligemment pensée. Les utilisateurs de lecteurs de musique graphiques comme Rhythmbox, Quod Libet ou encore iTunes s'y retrouveront assez facilement. Et depuis la version 2.8, encore au stade de release candidate, on peut également le contrôler nativement par MPRIS2. Le problème est qu'il s'agit uniquement d'un lecteur local, pas prévu pour être contrôlé depuis une autre machine que celle sur laquelle il est exécuté (ou alors on le contrôle depuis une session tmux sur la machine distante, mais c'est sale, et de toute façon, on perd les avantages de MPD mentionnés). Là encore, c'est dommage.

    mcmpcpp à gauche, cmus à droite. Et pour rajouter du kikoolol, l'utilitaire cava en bas.

    L'idéal pour moi serait donc un cmus qui contrôlerait MPD, mais à en croire la réponse à une feature request, c'est loin d'être à l'ordre du jour hélas.

    Je me suis donc résolu à utiliser MPD pour le seul usage sur mon installation audio dédiée, et cmus quand j'ai besoin de travailler avec des écouteurs, au boulot comme à la maison.

    N'hésitez pas à me dire votre avis, notamment si vous avez un client MPD préféré, et nous dire qui est votre waifu. Rem c'est la mienne alors pas touche.

    • At chevron_right

      Less is more, partie 2 : les contenus multimédia

      raspbeguy · pubsub.gugod.fr / atomtest · Friday, 22 September, 2017 - 22:00 · 7 minutes

    Je me relance dans cette série d'articles que j'ai trop souvent tendance à oublier alors qu'au final, j'ai passé (et j'en passe toujours) une énorme partie de mon temps à améliorer mon environnement visuel. Pour rappel, dans cette série d'articles, j'essaye d'expliquer comment se passer le plus poss...

    Je me relance dans cette série d'articles que j'ai trop souvent tendance à oublier alors qu'au final, j'ai passé (et j'en passe toujours) une énorme partie de mon temps à améliorer mon environnement visuel. Pour rappel, dans cette série d'articles, j'essaye d'expliquer comment se passer le plus possibles d'environnement graphique et les bienfaits de la simplicité pure d'un bon vieux terminal. Ça peut même être joli, faites-moi confiance.

    Aujourd'hui je vais me concentrer sur les outils de lecture et de gestion de contenus multimédias : images, films, et surtout musique, ce dernier média étant celui que je bichonne le plus avec une bibliothèque de plusieurs mois de longueurs cumulées et presque 500 Go de taille. Ça commence à faire gros et automatiser la gestion d'un machin pareil devient assez nécessaire.

    D'aucuns pourraient se dire que visionner du contenu multimédia sur un poste qui veut se passer d'environnement graphique, c'est un peu contradictoire. Mais rassurez-vous, il n'en est rien, c'est tout à fait possible, et même conseillé dans certains cas (histoire de réserver de la ressource pour lire le média lui-même et non pour gérer un ensemble d'effets de fenêtres débile qui fait prout quand on les ferme). Je vais donc vous exposer des outils KISS, faisant le travail demandé, ni plus, ni moins.

    Images

    Pour ouvrir des images, feh fait très bien le travail. Ça ouvre les jpg, png, gif, et bien d'autres, en fait tous les formats supportés par Imlib2. Il suffit de l'invoquer de la sorte :

    feh rem\_is\_best\_waifu.png

    À l'aide d'ImageMagick, ça peut même convertir d'autres formats afin de les ouvrir, par exemple le svg. Il suffit de donner une valeur de timeout à ImageMagick :

    feh --magick-timeout 5 logo\_hashtagueule.svg

    Astuce, si vous avez des images de grande dimension et que vous souhaitez adapter automatiquement la taille à la fenêtre de feh, ajoutez l'option suivante :

    feh -. playboy\_special\_tshirt\_mouille.jpg

    On peut même s'en servir pour le fond d'écran :

    feh --bg-scale windowsXP\_wallpaper.jpg

    Cette commande va créer par la même occasion un petit script ~/.fehbg afin de faciliter l'instauration du fond d'écran. Il suffira donc de dire à X11 ou à votre gestionnaire de fenêtre préféré de lancer ce script à chaque début de session (pour i3 on avait vu que ça se faisait avec la directive exec).

    Enfin, sachez que feh peut également ouvrir des images distantes via HTTP.

    Vidéos

    La plupart des gens, si on leur dit "vidéo sur linux", ils répondront à la microseconde et le plus fort possible "VLC!!!". Sachez qu'il n'y a pas que VLC dans la vie, mes enfants. Mon avis est que, même si VLC c'est très bien pour amener de l'open-source en terrain ennemi (ça tourne partout, y compris Windows et Mac), ça a une interface intuitive (c'est du QT, portable sur toutes les plateformes) et relativement facile à mettre en œuvre grâce à là nucléarité de ses fichiers d’exploitation (La plupart des bibliothèques utilisées par VLC sont des bibliothèques maison, et il y en a très peu), ben ces avantages apparents sont en partie des inconvénients pour les utilisateurs avancés. L'interface agréable, la belle affaire quand on veut se passer d'environnement graphique. La nucléarité de VLC également est un problème : ce sont des bibliothèques que seul VLC utilise ; résultat, personne n'y comprends plus rien et ça fait doublon avec d'autres bibliothèques plus communes et à mon sens plus propre.

    Bref, pour toutes ces raisons j'ai renoncé à VLC. Quand on y réfléchit, les raisons sont presque les même que les arguments contre GNOME.

    À la place, j'utilise mpv, qui a vraiment une interface minimale (et par conséquent élégante), et qui est plus léger que VLC, et ça se voit quand on lis des vidéos sur des machines pas super récentes.

    mpv a un système de raccourcis clavier hautement configurable et possède les même fonctionnalités que VLC. Il possède également un mécanisme d'accélération graphique.

    Bon, vu que je m'y connais pas vraiment côté vidéo, je vous laisse avec une capture d'écran.

    Rem is Best Waifu Rem is best waifu

    Musique

    Bon, là c'est du sérieux. Je ne gère pas de photothèque, et mes bibliothèques de films et de séries ne nécessitent pas beaucoup d'effort pour rester rangé. Mais la musique c'est une autre paire de manche.

    Gestionnaire de musique

    Pour ranger ma musique, jusqu'il n'y a pas si longtemps, j'utilisais l'utilitaire graphique Ex Falso, livré avec le lecteur de musique Quod Libet. Ces deux programmes sont très bons, c'est ceux que j'utilisait quand j'étais encore sous Cinnamon. Ex Falso est très puissant et permettait de faire des opérations sur des fichiers audio : modification des tags manuellement, par nom de fichier, ou encore et surtout l'opération inverse, à savoir changer l'emplacement et le nom de fichier en fonction des tags. L'ennui c'est que c'est du GTK, et pas d'interface en ligne de commande donc pas scriptable du tout.

    Je me suis finalement mis à beets, un outil full CLI destiné à maintenir une bibliothèque de musique rigoureusement organisée, que j'exécute directement sur le NAS. Beets peut à peu près tout faire et répond à toutes les demandes, pour peu qu'on passe du temps à comprendre toutes ses options. Et Dieu sait qu'il y en a beaucoup.

    Beets intègre un autotaggeur, c'est à dire que par défaut, il va chercher les tags des nouveaux fichiers. Sachant qu'en général les fichiers que j'importe sont déjà correctement taggés, j'ai choisi de désactiver cette fonctionnalité (et j'ai trop peur de ce qui arriverai si beets se plantait). Par contre, je l'utilise pour qu'il intègre mes nouveaux fichiers dans l'arborescence correcte. Je tiens à ce que ma bibliothèque soit rangé ainsi (pour faire simple) : racine/artiste/album/numdisque_numpiste-titre.flac

    Voici mon fichier de configuration (il est ridiculement petit et n'exploite qu'un fragment des possibilités de beets) :

    directory: /mnt/data/multimedia/Musique
    
    import:
        copy: yes
        #incremental: yes
        log: ~/.config/beets/beets.log
        autotag: no
    
    paths:
        default: %if{$albumartist,$albumartist,%if{$artist,%artist,unknown}}/%if{$album,$album,unknown}/%if{$disc,$disc-}%if{$track,$track - }$title

    Maintenant, quand je veux importer de nouveaux fichiers, il me suffit d'invoquer beets de la sorte :

    beet import /chemin/vers/dossier/contenant/nouveaux/morceaux

    Et paf, ça fait le taf. Bon je vous laisse lire la signification des instructions sur la (très bonne) documentation de beets. Vous allez vous en sortir, je crois en vous.

    Lecteur de musique

    C'est là que le bât blesse. En fait je n'ai pas encore trouvé le lecteur qui couvre exactement % de mes besoins. Je suis partagé entre deux programmes, qui ont chacun des avantages.

    MPD

    Mon rêve c'est de ne pouvoir tourner qu'avec MPD. Je vous en ai déjà parlé sur l'article Un jukebox à partir d'un Raspberry Pi en vous disant que ça déchire, et c'est effectivement le cas. Ça ne consomme quasiment aucune ressource, ça peut lire plein de formats, ça se contrôle avec un protocole simple (même avec MPRIS2, le protocole universel de contrôle de lecteur multimédia sur Linux, grâce à l'outil mpDris2) et ça vous fout la paix. Le problème, c'est que tous les clients MPD en ligne de commande que j'ai trouvé ont à mon sens une interface peu ergonomique. Le meilleur candidat étant ncmpcpp (déjà il à un nom à coucher dehors), l'ergonomie de son interface (pourtant en très joli ncurse, et vous savez à quel point j'aime ncurse) ne m'a pas emballé. Naviguer dans sa bibliothèque est assez douloureux, la répartition des différentes vues du programme sont pour le moins folkloriques, les raccourcis clavier par défaut sont assez déroutant... Dommage.

    cmus

    On parlait d'interface, et cmus en a une très réussie et intelligemment pensée. Les utilisateurs de lecteurs de musique graphiques comme Rhythmbox, Quod Libet ou encore iTunes s'y retrouveront assez facilement. Et depuis la version 2.8, encore au stade de release candidate, on peut également le contrôler nativement par MPRIS2. Le problème est qu'il s'agit uniquement d'un lecteur local, pas prévu pour être contrôlé depuis une autre machine que celle sur laquelle il est exécuté (ou alors on le contrôle depuis une session tmux sur la machine distante, mais c'est sale, et de toute façon, on perd les avantages de MPD mentionnés). Là encore, c'est dommage.

    mcmpcpp à gauche, cmus à droite. Et pour rajouter du kikoolol, l'utilitaire cava en bas.

    L'idéal pour moi serait donc un cmus qui contrôlerait MPD, mais à en croire la réponse à une feature request, c'est loin d'être à l'ordre du jour hélas.

    Je me suis donc résolu à utiliser MPD pour le seul usage sur mon installation audio dédiée, et cmus quand j'ai besoin de travailler avec des écouteurs, au boulot comme à la maison.

    N'hésitez pas à me dire votre avis, notamment si vous avez un client MPD préféré, et nous dire qui est votre waifu. Rem c'est la mienne alors pas touche.

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