{"@attributes":{"version":"2.0"},"channel":{"title":"Olivier DOSSMANN","link":"https:\/\/olivier.dossmann.net\/","description":"Contenu r\u00e9cent sur Olivier DOSSMANN","generator":"Hugo -- gohugo.io","language":"fr","copyright":"\u00a9 2014-2022 Olivier DOSSMANN","lastBuildDate":"Sun, 22 Mar 2026 11:42:00 +0100","item":[{"title":"Encore un autre lecteur de flux RSS, Yarr","link":"https:\/\/olivier.dossmann.net\/2026\/03\/encore-un-autre-lecteur-de-flux-rss-yarr\/","pubDate":"Sun, 22 Mar 2026 11:42:00 +0100","guid":"https:\/\/olivier.dossmann.net\/2026\/03\/encore-un-autre-lecteur-de-flux-rss-yarr\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Il y a fort fort longtemps (en 2009), je testais <a href=\"https:\/\/tt-rss.org\/\"><strong>TinyTinyRSS<\/strong>, un lecteur de flux RSS<\/a> avec une interface Web. J&rsquo;en parlais d&rsquo;ailleurs dans un <a href=\"https:\/\/olivier.dossmann.net\/2009\/09\/nouvelles-astuces-sur-rxvt-unicode-et-tinytinyrss\/\" title=\"D\u00e9couvrir mon article sur TinyTinyRSS et rxvt-unicode\">article pour signaler la venue de deux nouveaux tutoriels (dont TinyTinyRSS) dans mon Recueil d&rsquo;astuces<\/a>. Ce lecteur \u00e9tait <strong>agr\u00e9able \u00e0 l&rsquo;usage, rapide<\/strong> et utilisait une base de donn\u00e9es <strong>postgreSQL<\/strong>. Bref, tout ce que je voulais \u00e0 l&rsquo;\u00e9poque. Le temps a pass\u00e9\u00a0; je d\u00e9pensais moins de temps sur les flux RSS. Puis j&rsquo;ai arr\u00eat\u00e9 - qu&rsquo;importe les raisons. \u00c7a fait <strong>bient\u00f4t 10 ans<\/strong> que j&rsquo;ai laiss\u00e9 cela derri\u00e8re moi.<\/p>\n<blockquote>\n<p>Bouh la honte Olivier\u00a0!<\/p>\n<\/blockquote>\n<p>Certes.<\/p>\n<p>R\u00e9cemment, <strong>une mouche m&rsquo;a piqu\u00e9e<\/strong> - ou une <em>lubie<\/em> - et <strong>je me suis renseign\u00e9<\/strong> sur les outils de lecture de <strong>flux RSS<\/strong> fa\u00e7on <strong>auto-h\u00e9berg\u00e9s et minimalistes<\/strong>. Je ne vais pas d\u00e9tailler la liste dans cet article puisque, vous l&rsquo;aurez compris, on parle d\u00e9j\u00e0 dans le titre de <a href=\"https:\/\/github.com\/nkanaev\/yarr\"><strong>Yarr<\/strong><\/a>\u00a0!<\/p>\n<p>Ainsi nous commencerons par pr\u00e9senter l&rsquo;outil, pour ensuite d\u00e9crire comment l&rsquo;utiliser avec docker-compose pour finalement terminer par mon retour d&rsquo;exp\u00e9rience apr\u00e8s quelques jours d&rsquo;utilisation (d\u00e9marrage au 25 f\u00e9vrier 2026) et via un <strong>tour complet de l&rsquo;outil<\/strong> - \u00e7a va \u00eatre long mais il y a des images &#x1f609; .<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/objets\/faisceaux_lumineux.jpg\" alt=\"Image de faisceaux lumineux multiples qui ont s\u00fbrement d\u00e9riv\u00e9\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/www.flickr.com\/photos\/12836528@N00\/\">profil de Kevin Dooley sur Flickr<\/a><\/em> sous licence CC BY 2.0.<\/p>\n<h1 id=\"d\u00e9couverte-de-yarr\">D\u00e9couverte de Yarr<\/h1>\n<p>Avant de d\u00e9couvrir\u00a0: <strong>on cherche<\/strong>\u00a0! J&rsquo;ai donc \u00e9tabli une petite liste de <strong>crit\u00e8res pour la recherche d&rsquo;un lecteur de flux RSS<\/strong>\u00a0:<\/p>\n<ul>\n<li>simple, voire <strong>basique<\/strong>,<\/li>\n<li>avec une <strong>interface Web<\/strong>,<\/li>\n<li><strong>auto-h\u00e9bergeable<\/strong>,<\/li>\n<li><strong>Open Source<\/strong>,<\/li>\n<li><strong>rapide<\/strong>,<\/li>\n<li><strong>l\u00e9ger<\/strong>,<\/li>\n<li>sans forc\u00e9ment une base de donn\u00e9es de type MySQL ou PostgreSQL. SQLite acceptable,<\/li>\n<li>\u00e9crit, \u00e9ventuellement, <strong>en Go<\/strong> (j&rsquo;envisage d&rsquo;apprendre prochainement le Go).<\/li>\n<\/ul>\n<p>J&rsquo;ai donc cherch\u00e9. J&rsquo;ai trouv\u00e9 quelques outils comme\u00a0:<\/p>\n<ul>\n<li><a href=\"https:\/\/miniflux.app\/\">Miniflux<\/a> (en Go),<\/li>\n<li><a href=\"https:\/\/freshrss.org\/index.html\">FreshRSS<\/a> (en PHP),<\/li>\n<li><a href=\"https:\/\/github.com\/LeedRSS\/Leed\">LeedRSS<\/a> (en PHP),<\/li>\n<li><a href=\"https:\/\/github.com\/ncarlier\/feedpushr\">feedpushr<\/a> (en Go),<\/li>\n<li>et <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a> (en Go).<\/li>\n<\/ul>\n<p>Je l&rsquo;ai d\u00e9j\u00e0 annonc\u00e9\u00a0: nous n&rsquo;irons pas plus loin avec cette liste.<\/p>\n<p>Nul besoin de faire la promotion de <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a>, je pense qu&rsquo;<a href=\"https:\/\/lord.re\/posts\/246-yarr-web-rss-pour-remplacer-ttrss\/\">Internet fait d\u00e9j\u00e0 de belles suggestions d&rsquo;utilisation de Yarr comme rempla\u00e7ant de TinyTinyRSS<\/a>. Petite histoire marrante\u00a0: le nom est l&rsquo;<strong>acronyme de Yet Another Rss Reader<\/strong>, litt\u00e9ralement \u00ab\u00a0Encore un autre lecteur RSS\u00a0\u00bb ou \u00ab\u00a0L&rsquo;\u00e9ni\u00e8me lecteur de flux RSS\u00a0\u00bb.<\/p>\n<p>Une fois cette lecture de blog effectu\u00e9e, j&rsquo;ai voulu tester <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a>. Je me suis donc lanc\u00e9 dans l&rsquo;\u00e9criture d&rsquo;un fichier <strong>docker-compose.yml<\/strong>.<\/p>\n<h1 id=\"mon-fichier-docker-composeyaml-pour-yarr\">Mon fichier docker-compose.yaml pour Yarr<\/h1>\n<p>Il aura fallu <strong>moins de 20 minutes<\/strong> pour trouver l&rsquo;image Docker de <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a> sur le <a href=\"https:\/\/github.com\/nkanaev\/yarr\">d\u00e9p\u00f4t officiel de Yarr sur Github<\/a>. Cependant l&rsquo;image \u00e9tant sur Github Actions - que je connais peu - j&rsquo;ai eu plus de mal \u00e0 trouver le nom donn\u00e9 \u00e0 l&rsquo;image pour la r\u00e9cup\u00e9rer depuis Docker. Je vous le donne de suite\u00a0: <strong>ghcr.io\/nkanaev\/yarr:v2.5<\/strong>.<\/p>\n<p>Une fois le nom de l&rsquo;image obtenu, il reste \u00e0 en conna\u00eetre son usage. Heureusement j&rsquo;ai trouv\u00e9 un article sur la cr\u00e9ation d&rsquo;un fichier docker-compose.yaml pour <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a> (Cf. <a href=\"https:\/\/awesome-docker-compose.com\/apps\/rss\/yarr\">https:\/\/awesome-docker-compose.com\/apps\/rss\/yarr<\/a>) dont je me suis, forc\u00e9ment, grandement inspir\u00e9. Et oui je le dis quand j&rsquo;utilise quelque chose qui n&rsquo;est pas de moi &#x1f609; .<\/p>\n<p>Ainsi j&rsquo;ai pu construire le fichier <strong>docker-compose.yaml<\/strong> suivant\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">services<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">yarr<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">ghcr.io\/nkanaev\/yarr:v2.5<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">container_name<\/span>: <span style=\"color:#ae81ff\">yarr<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">restart<\/span>: <span style=\"color:#ae81ff\">always<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">ports<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">7070<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">volumes<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">${DATA_DIR:-.\/data}:\/data<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">command<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - --<span style=\"color:#ae81ff\">addr=0.0.0.0:7070<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - --<span style=\"color:#ae81ff\">db=\/data\/yarr.db<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - --<span style=\"color:#ae81ff\">auth=${Y_USER}:${PASSWORD}<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">environment<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">TZ=Europe\/Paris<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">labels<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.enable=true&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.${SERVICE}.rule=Host(`$DOMAIN_URL`)&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.${SERVICE}.entrypoints=websecure&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>o\u00f9\u00a0:<\/p>\n<ul>\n<li><strong>DATA_DIR<\/strong>, <strong>Y_USER<\/strong>, <strong>PASSWORD<\/strong>, <strong>SERVICE<\/strong> et <strong>DOMAIN_URL<\/strong> sont \u00e0 renseigner dans un fichier <em>.env<\/em> (avec une valeur),<\/li>\n<li><strong>DATA_DIR<\/strong> est le chemin vers le point de montage o\u00f9 seront stock\u00e9es les donn\u00e9es de <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a>,<\/li>\n<li><strong>Y_USER<\/strong> est le nom d&rsquo;utilisateur que vous souhaitez utiliser comme <em>login<\/em> pour s&rsquo;identifier sur votre <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a>,<\/li>\n<li><strong>PASSWORD<\/strong>\u2026 bah le mot de passe pour s&rsquo;identifier au service <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a>,<\/li>\n<li><strong>SERVICE<\/strong> est un nom (en minuscule, sans espace et sans point) pour le routeur dans Tr\u00e6fik (si vous n&rsquo;utilisez pas Tr\u00e6fik, supprimez la section &ldquo;labels&rdquo; de ce docker-compose.yaml),<\/li>\n<li><strong>DOMAIN_URL<\/strong> est l&rsquo;adresse web pour acc\u00e9der au service, mais seulement si vous utilisez Tr\u00e6fik en frontal. Sinon supprimez la section &ldquo;labels&rdquo; de ce fichier docker-compose.yaml.<\/li>\n<\/ul>\n<p>Exemple de fichier <em>.env<\/em>\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-ini\" data-lang=\"ini\"><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">SERVICE<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">monService<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">DOMAIN_URL<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">domain.tld<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">DATA_DIR<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">\/srv\/http\/yarr_data<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">Y_USER<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">olivier<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">PASSWORD<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">mot2passe<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Vous noterez qu&rsquo;on <strong>autorise<\/strong> volontairement <strong>toutes les IP<\/strong> pour acc\u00e9der au service (0.0.0.0). Ce dernier s&rsquo;ouvrira sur le <strong>port 7070<\/strong> dans le conteneur, mais Docker choisira un port al\u00e9atoire sur ce service (pour \u00e9viter de demander un port d\u00e9j\u00e0 utilis\u00e9). Nous en parlerons ci-apr\u00e8s avec la commande <code>docker compose port<\/code>.<\/p>\n<p>Une fois le fichier <em>.env<\/em> configur\u00e9, on lance et acc\u00e8de rapidement au service (pour tester par exemple)\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Lance le service en arri\u00e8re plan - r\u00e9cup\u00e9ration de la main sur l&#39;invite de commande<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>docker compose up -d\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Affiche les conteneurs lanc\u00e9s<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>docker compose ps\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Affiche l&#39;adresse pour acc\u00e9der au service en local<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>docker compose port yarr <span style=\"color:#ae81ff\">7070<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>On lance un navigateur, on tape l&rsquo;adresse retourn\u00e9e par la derni\u00e8re commande (chez moi <strong>0.0.0.0:32768<\/strong>) et le lecteur de flux RSS appara\u00eet. C&rsquo;est parti pour une p\u00e9riode d&rsquo;essai de plusieurs jours (tests commenc\u00e9s \u00e0 partir du 25 f\u00e9vrier 2026).<\/p>\n<h1 id=\"mon-exp\u00e9rience-avec-yarr---on-fait-le-tour\">Mon exp\u00e9rience avec Yarr - on fait le tour\u00a0!<\/h1>\n<h2 id=\"lauthentification\">L&rsquo;authentification<\/h2>\n<p>Dans le fichier docker-compose.yml pr\u00e9c\u00e9dent nous avons ajout\u00e9 l&rsquo;option <code>--auth<\/code> afin d&rsquo;activer l&rsquo;authentification de <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a>. La <strong>premi\u00e8re page<\/strong> sur laquelle nous tombons est donc celle <strong>pour s&rsquo;identifier<\/strong>\u00a0:<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/logiciels\/yarr_accueil.png\" alt=\"Formulaire d&rsquo;accueil pour s&rsquo;identifier\"><\/p>\n<p><strong>Simple<\/strong>, efficace.<\/p>\n<h2 id=\"linterface-g\u00e9n\u00e9rale\">L&rsquo;interface g\u00e9n\u00e9rale<\/h2>\n<p>L&rsquo;accueil est plut\u00f4t <strong>aust\u00e8re<\/strong> - chouette c&rsquo;est <strong>ce que je voulais<\/strong>\u00a0! - et propose une <strong>disposition en 3 colonnes<\/strong>\u00a0:<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/logiciels\/yarr_3_colonnes.png\" alt=\"Disposition en 3 colonnes sur la page d&rsquo;accueil de Yarr\"><\/p>\n<p>Chaque colonne sert un objectif pr\u00e9cis, de gauche \u00e0 droite\u00a0:<\/p>\n<ul>\n<li>la <strong>liste des flux<\/strong> RSS,<\/li>\n<li>puis la <strong>liste des articles<\/strong>\/\u00e9l\u00e9ments d&rsquo;un flux en particulier,<\/li>\n<li>puis <strong>l&rsquo;article<\/strong> choisi parmi la liste pr\u00e9c\u00e9dente.<\/li>\n<\/ul>\n<p>Donc plus on se dirige \u00e0 droite, plus la lecture se pr\u00e9cise.<\/p>\n<p>Sur t\u00e9l\u00e9phone mobile il n&rsquo;y aura plus qu&rsquo;une seule colonne\u00a0: celle tout \u00e0 gauche. On choisira ainsi un flux, ce qui affichera la liste des articles et si on choisit un article, on verra son contenu. Toujours sur une colonne. <strong>Astucieux<\/strong>, <strong>logique<\/strong> et presque <strong>intuitif<\/strong>.<\/p>\n<p>J&rsquo;avoue que sur ce dernier point, sur t\u00e9l\u00e9phone mobile, j&rsquo;ai \u00e9t\u00e9 totalement perdu\u00a0: je ne savais pas o\u00f9 j&rsquo;\u00e9tais et ce que je faisais. Ce n&rsquo;est qu&rsquo;en comparant l&rsquo;usage entre un grand \u00e9cran et un petit \u00e9cran que j&rsquo;ai saisi ce qu&rsquo;il en \u00e9tait. Sur ce coup-l\u00e0, je suis le couteau le moins aiguis\u00e9 du tiroir\u2026<\/p>\n<h2 id=\"la-configuration-de-linterface\">La configuration de l&rsquo;interface<\/h2>\n<p>Je continue ma d\u00e9couverte avec l&rsquo;<strong>ic\u00f4ne repr\u00e9sentant 3 petits points<\/strong> en haut \u00e0 droite de la premi\u00e8re colonne (ou en haut \u00e0 droite sur un petit \u00e9cran ne pr\u00e9sentant donc qu&rsquo;une seule colonne). Les options de l&rsquo;outil apparaissent\u00a0:<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/logiciels\/yarr_options.png\" alt=\"Image contenant le menu d&rsquo;option de configuration de l&rsquo;outil Yarr\"><\/p>\n<p>C&rsquo;est ici que va se jouer les <strong>seules options disponibles dans <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a><\/strong>\u00a0:<\/p>\n<ul>\n<li><strong>New Feed<\/strong>\u00a0: Permet d&rsquo;<strong>ajouter un nouveau flux<\/strong>. Il faut 2 infos : l&rsquo;adresse URL du flux, et le dossier dans lequel le ranger,<\/li>\n<li><strong>Refresh Feeds<\/strong>\u00a0: <strong>Actualisation<\/strong> des flux,<\/li>\n<li><strong>Theme<\/strong>\u00a0: Pour changer l&rsquo;<strong>apparence de <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a><\/strong>. Nous en parlerons tout \u00e0 l&rsquo;heure,<\/li>\n<li><strong>Auto Refresh<\/strong>\u00a0: Permet de choisir le <strong>laps de temps<\/strong> entre plusieurs <strong>rafra\u00eechissements automatiques<\/strong>. Comprendre\u00a0: on r\u00e9cup\u00e8re les flux tous les X temps (de 0mn \u00e0 4h),<\/li>\n<li><strong>Show first<\/strong>\u00a0: Permet de choisir si on pr\u00e9f\u00e8re afficher les <strong>articles parus r\u00e9cemment<\/strong> en premier (<em>New<\/em>) <strong>ou ceux les plus anciens<\/strong> en premier (<em>Old<\/em>),<\/li>\n<li>Subscriptions\u00a0:\n<ul>\n<li><strong>Import<\/strong>\u00a0: Donne la possibilit\u00e9 d&rsquo;importer une liste de flux (au <strong>format OPML<\/strong>),<\/li>\n<li><strong>Export<\/strong>\u00a0: Permet d&rsquo;exporter sa liste de flux (au <strong>format OPML<\/strong>) pour la partager par exemple,<\/li>\n<li><strong>Shortcuts<\/strong>\u00a0: Affiche un rappel des <strong>raccourcis claviers<\/strong> possibles pour naviguer et lire les flux. Et ils sont <strong>tr\u00e8s utiles<\/strong>,<\/li>\n<li><strong>Log out<\/strong>\u00a0: Appara\u00eetra seulement si l&rsquo;option <code>--auth<\/code> a \u00e9t\u00e9 utilis\u00e9e. C&rsquo;est pour <strong>se d\u00e9connecter<\/strong>.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>J&rsquo;avais peur de tomber sur un lecteur avec des milliers de configurations. Finalement <strong><a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a> va \u00e0 l&rsquo;essentiel<\/strong>. Cela me pla\u00eet - encore une fois.<\/p>\n<h2 id=\"choix-dun-th\u00e8me\">Choix d&rsquo;un th\u00e8me<\/h2>\n<p>Vous aurez remarqu\u00e9 que <strong>mes impressions \u00e9crans<\/strong> pr\u00e9sentent les pages d&rsquo;une <strong>couleur simili jaune<\/strong> (qui fait penser \u00e0 <a href=\"https:\/\/ethanschoonover.com\/solarized\/\">Solarized Light d&rsquo;Ethan Shoonover<\/a>). C&rsquo;est parce que j&rsquo;ai choisi un th\u00e8me. On peut <strong>choisir entre 3 th\u00e8mes<\/strong>\u00a0:<\/p>\n<ul>\n<li><strong>blanc<\/strong>,<\/li>\n<li><strong>jaune<\/strong>,<\/li>\n<li><strong>noir<\/strong>.<\/li>\n<\/ul>\n<p>Je suis content du th\u00e8me jaune car ma vue pose probl\u00e8me avec les th\u00e8mes noirs (l&rsquo;astigmathie a une forme d&rsquo;opposition rebelle au &ldquo;dark theme&rdquo; et peut rendre les textes &ldquo;flous&rdquo;). Certes, la <a href=\"https:\/\/www.nngroup.com\/articles\/dark-mode\/\">performance de lecture est plus importante en mode &ldquo;light&rdquo;<\/a>, mais \u00e7a reste trop lumineux pour moi. Je n&rsquo;aime pas le jaune en tant que tel, mais pour mon efficacit\u00e9 de lecture c&rsquo;est le &ldquo;moins pire&rdquo; &#x1f923; . \u00c9videmment les tendances s&rsquo;inversent la nuit (mais pas pour moi), dans la p\u00e9nombre et\/ou dans un environnement peu lumineux. Mais nous ne sommes pas ici pour lancer le troll \u00ab\u00a0Dark Mode vs. Light Mode\u00a0\u00bb, ou bien\u00a0? Combat\u00a0!<\/p>\n<h2 id=\"les-trois-ic\u00f4nes-principales\">Les trois ic\u00f4nes principales<\/h2>\n<p>Vous avez d\u00e9couvert l&rsquo;usage en 3 colonnes, cette fois nous allons d\u00e9couvrir l&rsquo;usage des 3 ic\u00f4nes tout en haut qui sont, en quelque sorte, le menu principal de <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a>\u00a0:<\/p>\n<ul>\n<li>un rond rempli de noir,<\/li>\n<li>une \u00e9toile,<\/li>\n<li>3 traits horizontaux parall\u00e8les de longueurs diff\u00e9rentes.<\/li>\n<\/ul>\n<p>La premi\u00e8re (le <strong>rond noir<\/strong>) permet d&rsquo;<strong>afficher la liste des flux<\/strong> sur lesquels nous avons encore des articles\/\u00e9l\u00e9ments \u00e0 lire.<\/p>\n<p>La seconde (<strong>\u00e9toile noire<\/strong>) affiche une liste des flux sur lequels vous avez marqu\u00e9s des articles\/\u00e9l\u00e9ments comme <strong>favoris<\/strong>.<\/p>\n<p>Et la derni\u00e8re (les <strong>3 traits horizontaux<\/strong>) affiche la <strong>liste compl\u00e8te des flux<\/strong>, qu&rsquo;importe qu&rsquo;il vous reste ou non des articles\/\u00e9l\u00e9ments \u00e0 lire.<\/p>\n<p>Exemple d&rsquo;affichage en 3 colonnes sur le rond noir avec le choix d&rsquo;un article et son affichage\u00a0:<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/logiciels\/yarr_exemple.png\" alt=\"Affichage en 3 colonnes de Yarr avec le choix du flux MiniMachines.net, d&rsquo;un article pr\u00e9cis et de son affichage dans la derni\u00e8re colonne\"><\/p>\n<p><strong>Simple, basique, sans fioritures<\/strong>. Utilisable <strong>avec des raccourcis<\/strong> clavier. <strong>Parfait<\/strong>\u00a0!<\/p>\n<h2 id=\"les-derniers-petits-d\u00e9tails---et-apr\u00e8s-jarr\u00eate-promis\">Les derniers petits d\u00e9tails - et apr\u00e8s j&rsquo;arr\u00eate promis\u00a0!<\/h2>\n<p>Derni\u00e8re liste d&rsquo;ic\u00f4nes dans l&rsquo;interface\u00a0:<\/p>\n<ul>\n<li>deuxi\u00e8me colonne\u00a0:\n<ul>\n<li>ic\u00f4ne de <strong>loupe<\/strong>\u00a0: Fait une <strong>recherche dans la liste d&rsquo;article<\/strong> de <strong>CE<\/strong> flux choisi,<\/li>\n<li>ic\u00f4ne de <strong>validation<\/strong>\u00a0: <strong>Marque tous les articles<\/strong> de ce flux <strong>comme lus<\/strong>,<\/li>\n<li>ic\u00f4ne de <strong>3 petits points<\/strong> l&rsquo;un \u00e0 c\u00f4t\u00e9 de l&rsquo;autre verticalement\u00a0: c&rsquo;est ici qu&rsquo;on a les <strong>d\u00e9tails du flux<\/strong>. On peut le renommer, le changer de place, le supprimer, aller sur le site Web, etc.,<\/li>\n<\/ul>\n<\/li>\n<li>troisi\u00e8me colonne\u00a0:\n<ul>\n<li>ic\u00f4ne d&rsquo;<strong>\u00e9toile<\/strong> (vide\/remplie)\u00a0: Marque l&rsquo;article en <strong>favori<\/strong> ou au contraire l&rsquo;enl\u00e8ve des favoris,<\/li>\n<li>ic\u00f4ne d&rsquo;un <strong>rond<\/strong> (vide\/rempli)\u00a0: Marque l&rsquo;article comme <strong>lu ou non-lu<\/strong> (suivant de quel \u00e9tat on commence),<\/li>\n<li>ic\u00f4ne de <strong>traits verticaux entrecoup\u00e9s comme de petits pistons<\/strong>\u00a0: Permet de <strong>changer la police<\/strong> d&rsquo;\u00e9criture (parmi sans-serif, serif et monospace) ainsi que <strong>la taille de police<\/strong> (petit, ou grand, plus on appuie plus \u00e7a r\u00e9duit\/aggrandit),<\/li>\n<li>ic\u00f4ne d&rsquo;un <strong>livre ouvert<\/strong>\u00a0: Choisi de <strong>lire l&rsquo;article dans <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a><\/strong> ou bien seulement les informations du flux RSS,<\/li>\n<li>ic\u00f4ne d&rsquo;un <strong>carr\u00e9 avec une fl\u00e8che qui en sort<\/strong>)\u00a0: Va <strong>directement sur le site Web de l&rsquo;article<\/strong> en question.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/logiciels\/yarr_flux_options.png\" alt=\"D\u00e9tail des options disponible pour un flux donn\u00e9 dans la seconde colonne sur l&rsquo;ic\u00f4ne avec les 3 petits points\"><\/p>\n<p>J&rsquo;utilise souvent l&rsquo;ic\u00f4ne de validation de la seconde colonne pour marquer tous les articles comme lus une fois que j&rsquo;ai parcouru la liste des titres et que rien ne m&rsquo;int\u00e9ressait.<\/p>\n<p>Les raccourcis claviers sont l\u00e0 pour faciliter le traitement des articles (lu, non-lu, favori, lire \u00e0 l&rsquo;ext\u00e9rieur, etc.). Parfois j&rsquo;utilise le raccourci &ldquo;o&rdquo; pour lire l&rsquo;article sur le site Web.<\/p>\n<p>Du reste, je n&rsquo;utilise quasiment rien. Apr\u00e8s tout je ne viens que pour lire des titres, trier et lire un article de temps en temps &#x1f937; . Je ne suis pas l\u00e0 pour bosser.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Au d\u00e9but <strong>un peu perdu par l&rsquo;interface<\/strong> afin de <strong>comprendre \u00e0 quoi sert chaque \u00e9l\u00e9ment<\/strong> - surtout entre interface petit \u00e9cran et grand \u00e9cran - j&rsquo;ai finalement <strong>appr\u00e9ci\u00e9 <a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a> \u00e0 l&rsquo;usage<\/strong>.<\/p>\n<p>C&rsquo;est un outil &ldquo;menu&rdquo; (maigre, <strong>l\u00e9ger<\/strong>), simple, <strong>basique, sans fioritures<\/strong>. <strong>Configurable<\/strong> un minimum. Utilisable avec des <strong>raccourcis clavier<\/strong>. Tr\u00e8s efficace d&rsquo;ailleurs avec un clavier quand on s&rsquo;y fait. C&rsquo;est m\u00eame redoutable de rapidit\u00e9 quand on a le coup de main\u00a0!<\/p>\n<p>\u00c0 vrai dire, je ne sais m\u00eame pas de quoi j&rsquo;aurais besoin en plus pour lire des flux. J&rsquo;ajoute rapidement des flux, j&rsquo;<strong>importe et exporte facilement<\/strong> pour partager avec d&rsquo;autres, je vois de nouveaux articles, je clique sur ce qui m&rsquo;int\u00e9resse, je lis, je note le reste des articles comme lus pour ce flux et voil\u00e0.<\/p>\n<p>Ni plus. Ni moins. <strong>Parfait<\/strong> &#x1f604; .<\/p>\n<h1 id=\"liens-utiles\">Liens utiles<\/h1>\n<p>Voici les liens de l&rsquo;article par ordre d&rsquo;apparition\u00a0:<\/p>\n<ul>\n<li><a href=\"https:\/\/tt-rss.org\/\">TinyTinyRSS, un lecteur de flux RSS<\/a>,<\/li>\n<li><a href=\"https:\/\/olivier.dossmann.net\/2009\/09\/nouvelles-astuces-sur-rxvt-unicode-et-tinytinyrss\/\">Mon article pour signaler la venue de deux nouveaux tutoriels (dont TinyTinyRSS) dans mon Recueil d&rsquo;astuces<\/a>,<\/li>\n<li><a href=\"https:\/\/github.com\/nkanaev\/yarr\">Yarr<\/a>,<\/li>\n<li><a href=\"https:\/\/miniflux.app\/\">Miniflux<\/a>,<\/li>\n<li><a href=\"https:\/\/freshrss.org\/index.html\">FreshRSS<\/a>,<\/li>\n<li><a href=\"https:\/\/github.com\/LeedRSS\/Leed\">LeedRSS<\/a>,<\/li>\n<li><a href=\"https:\/\/github.com\/ncarlier\/feedpushr\">feedpushr<\/a>,<\/li>\n<li><a href=\"https:\/\/lord.re\/posts\/246-yarr-web-rss-pour-remplacer-ttrss\/\">Bel article sur  l&rsquo;utilisation de Yarr comme rempla\u00e7ant de TinyTinyRSS<\/a>,<\/li>\n<li><a href=\"https:\/\/awesome-docker-compose.com\/apps\/rss\/yarr\">Exemple de docker-compose pour Yarr<\/a>,<\/li>\n<li><a href=\"https:\/\/ethanschoonover.com\/solarized\/\">Solarized Light d&rsquo;Ethan Shoonover<\/a>,<\/li>\n<li><a href=\"https:\/\/www.nngroup.com\/articles\/dark-mode\/\">La performance de lecture suivant le th\u00e8me light\/dark et nos yeux<\/a>.<\/li>\n<\/ul>\n"},{"title":"RustFS, alternative au service de stockage objet MinIO","link":"https:\/\/olivier.dossmann.net\/2026\/02\/rustfs-alternative-au-service-de-stockage-objet-minio\/","pubDate":"Sun, 15 Feb 2026 17:27:26 +0100","guid":"https:\/\/olivier.dossmann.net\/2026\/02\/rustfs-alternative-au-service-de-stockage-objet-minio\/","description":"<p><a href=\"https:\/\/olivier.dossmann.net\/2025\/11\/mon-labo-k8s-pour-essayer-des-applications\/\">Quand on \u00e9tudie <strong>Kubernetes<\/strong><\/a>, on arrive t\u00f4t ou tard sur la question de la <strong>gestion des sauvegardes<\/strong>. Et avec elles le <strong>sujet du stockage<\/strong> de ces derni\u00e8res. La r\u00e9f\u00e9rence en mati\u00e8re de sauvegarde - dont je parlerais s\u00fbrement dans un prochain article - est <a href=\"https:\/\/velero.io\/\">Velero<\/a>. Cet outil supporte plusieurs fournisseurs de stockage pour ses op\u00e9rations de sauvegarde comme Amazon Web Services, Google Cloud, Microsoft Azure ou VMware Sphere. Principalement des fournisseurs li\u00e9s \u00e0 une entreprise. Que faire dans la situation o\u00f9 nous souhaitons <strong>auto-h\u00e9berger une solution de stockage<\/strong> chez soi ?<\/p>\n<p>C&rsquo;est ce dont je voudrais parler aujourd&rsquo;hui\u00a0: une solution sp\u00e9cifique que j&rsquo;ai - presque - choisie. Surtout <strong>pourquoi cette solution<\/strong> ? Je d\u00e9crirais son <strong>installation sous Docker Compose<\/strong> derri\u00e8re un proxy nomm\u00e9 <a href=\"https:\/\/olivier.dossmann.net\/2022\/12\/utilisation-de-tr%C3%A6fik-pour-publier-ses-conteneurs-docker\/\">Traefik, dont je vous ai d\u00e9j\u00e0 parl\u00e9<\/a>. Et j&rsquo;ajouterais quelques mots sur les ports r\u00e9seau \u00e0 ouvrir, l&rsquo;<strong>initialisation des premiers \u00ab buckets \u00bb<\/strong> (nous y reviendrons) et l&rsquo;usage de noms de domaines.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/logos\/rustfs.png\" alt=\"Logo du projet RustFS, simple \u00e9criture du mot RustFS en bleu\"><\/p>\n<h1 id=\"pourquoi-cette-solution\">Pourquoi cette solution\u00a0?<\/h1>\n<p><strong>Si vous n&rsquo;\u00eates pas fan de l&rsquo;histoire<\/strong> de \u00ab pourquoi choisir cette solution \u00bb, <strong>vous pouvez passer au chapitre suivant<\/strong>, je ne vous en voudrais pas &#x1f609;.<\/p>\n<p>En \u00e9tudiant <a href=\"https:\/\/velero.io\/docs\/\">la documentation de Velero<\/a> on d\u00e9couvre parmi les <strong>fournisseurs de solutions de stockage<\/strong> le c\u00e9l\u00e8bre Amazon Web Services. Et avec lui le service <strong>Amazon S3<\/strong>. Ce service est une <strong>solution de <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Stockage_objet\">stockage objet<\/a><\/strong> qui a d\u00e9velopp\u00e9 un <strong>protocole ouvert<\/strong> du m\u00eame nom\u00a0: <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Amazon_S3\"><strong>S3<\/strong><\/a>. Gr\u00e2ce \u00e0 cela, de nombreux outils compatibles avec ce protocole ont vu le jour ; que ce soit des clients comme des serveurs. Top, c&rsquo;est ce que je recherche\u00a0!<\/p>\n<p>Ayant beaucoup entendu parler de <strong>MinIO<\/strong>, je m&rsquo;enquiers de l&rsquo;installer fin novembre 2025. En Docker Compose, <a href=\"https:\/\/olivier.dossmann.net\/2022\/12\/utilisation-de-tr%C3%A6fik-pour-publier-ses-conteneurs-docker\/\">derri\u00e8re Traefik comme je l&rsquo;explique dans un pr\u00e9c\u00e9dent article sur le sujet<\/a>. Mais <strong>quelques probl\u00e8mes se posent<\/strong> successivement\u00a0:<\/p>\n<ul>\n<li>en ao\u00fbt 2025 les d\u00e9p\u00f4ts de Bitnami, qui fournissaient jusqu&rsquo;alors d&rsquo;excellentes images, chartes Helm et outils en tous genres sont chamboul\u00e9s par <a href=\"https:\/\/github.com\/bitnami\/charts\/issues\/35164\">une <strong>d\u00e9cision drastique prise par Bitnami<\/strong><\/a>. L&rsquo;impact est \u00e9norme sur la communaut\u00e9,<\/li>\n<li>faute de cette image pratique, je me tourne vers l&rsquo;image officielle qui demande des <strong>ajustements pour avoir des \u00ab buckets \u00bb \u00e0 l&rsquo;initialisation<\/strong>,<\/li>\n<li>finalement <strong>le 13 f\u00e9vrier 2026, l&rsquo;entreprise qui g\u00e9rait MinIO d\u00e9cide d&rsquo;abandonner la maintenance<\/strong> de la version communautaire.<\/li>\n<\/ul>\n<p>Coup dur. Je recherche des alternatives. Je tombe sur le <a href=\"https:\/\/github.com\/rustfs\/rustfs\/issues\/555#issuecomment-3620454213\">message de loverustfs qui compare les <strong>solutions alternatives<\/strong> \u00e0 RustFS, dont MinIO, Garage, Ceph, Seaweeedfs, etc.<\/a>.<\/p>\n<p><strong>Edit (24 f\u00e9v. 2026)<\/strong>\u00a0: Un <a href=\"https:\/\/github.com\/pgsty\/minio\">fork de MinIO<\/a> est apparu il y a quelques jours. Si le c\u0153ur vous en dit &#x1f937;.<\/p>\n<p><a href=\"https:\/\/github.com\/rustfs\/rustfs\"><strong>RustFS<\/strong><\/a> n&rsquo;est probablement pas la solution la plus performante (Seaweedfs semble plus performant), mais il se d\u00e9fend bien. Et il semble <strong>proche de MinIO<\/strong> en terme d&rsquo;interface et d&rsquo;utilisation. Allons donc, ne le faisons pas attendre, installons-le\u00a0!<\/p>\n<h1 id=\"rustfs-sous-docker-compose\">RustFS sous Docker Compose<\/h1>\n<p>L&rsquo;id\u00e9e est d&rsquo;installer\/utiliser <strong>RustFS avec Docker Compose<\/strong>. Derri\u00e8re <strong>Tr\u00e6fik<\/strong>. Avec des domaines.<\/p>\n<h2 id=\"installation-minimale\">Installation minimale<\/h2>\n<p>On commence par une installation de base. On a besoin de\u00a0:<\/p>\n<ul>\n<li>une <strong>image Docker<\/strong>. \u00c7a tombe bien, il y a des <a href=\"https:\/\/hub.docker.com\/r\/rustfs\/rustfs\">images officielles de RustFS sur le Hub Docker<\/a>,<\/li>\n<li>ouvrir <strong>2 ports<\/strong>\u00a0:\n<ul>\n<li><strong>9000<\/strong>,<\/li>\n<li><strong>9001<\/strong>,<\/li>\n<\/ul>\n<\/li>\n<li><strong>un volume local<\/strong> pour stocker les donn\u00e9es et pouvoir les r\u00e9utiliser entre 2 red\u00e9marrages de conteneurs,<\/li>\n<li>un <strong>identifiant<\/strong> et un <strong>mot de passe<\/strong> initial.<\/li>\n<\/ul>\n<p><strong>ATTENTION<\/strong>\u00a0: le projet <strong>RustFS est encore jeune<\/strong>, il est en d\u00e9veloppement actif et les versions ne sont pas toutes utilisables telles quelles. Par exemple dans ma situation, la <em>version 1.0.0-alpha.83<\/em> \u00e9tait inutilisable. Ceci d\u00fb \u00e0 un <a href=\"https:\/\/github.com\/rustfs\/rustfs\/issues\/1838\">bug sur l&rsquo;architecture de ma machine comme le montre ce ticket Github<\/a>.<\/p>\n<p>Une premi\u00e8re \u00e9bauche d&rsquo;un fichier docker-compose.yaml pourrait s&rsquo;\u00e9crire de la mani\u00e8re suivante\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">services<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">rustfs<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">rustfs\/rustfs:1.0.0-alpha.82<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">restart<\/span>: <span style=\"color:#ae81ff\">always<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">ports<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">9000<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">9001<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">volumes<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">.\/storage_data:\/data<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">\/etc\/localtime:\/etc\/localtime:ro<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">\/etc\/timezone:\/etc\/timezone:ro<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">environment<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_VOLUMES=\/data<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_ADDRESS=0.0.0.0:9000<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_CONSOLE_ENABLE=true<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_ACCESS_KEY=olivier<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_SECRET_KEY=mot2passe<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_OBS_LOGGER_LEVEL=info<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">healthcheck<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">test<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>        [\n<\/span><\/span><span style=\"display:flex;\"><span>          <span style=\"color:#e6db74\">&#34;CMD&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>          <span style=\"color:#e6db74\">&#34;sh&#34;<\/span>, <span style=\"color:#e6db74\">&#34;-c&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>          <span style=\"color:#e6db74\">&#34;curl -f http:\/\/127.0.0.1:9000\/health &amp;&amp; curl -f http:\/\/127.0.0.1:9001\/rustfs\/console\/health&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>        ]\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">interval<\/span>: <span style=\"color:#ae81ff\">30s<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">timeout<\/span>: <span style=\"color:#ae81ff\">10s<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">retries<\/span>: <span style=\"color:#ae81ff\">3<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">start_period<\/span>: <span style=\"color:#ae81ff\">40s<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Veillez \u00e0 changer les \u00e9l\u00e9ments suivants - au minimum\u00a0:<\/p>\n<ul>\n<li><code>.\/storage_data<\/code> dans la section <em>volumes<\/em>. Renseigner par un <strong>dossier<\/strong> dans lequel vous voulez <strong>stocker les donn\u00e9es<\/strong>,<\/li>\n<li><code>RUSTFS_ACCESS_KEY<\/code> dans la section <em>environment<\/em>. Changez <em>olivier<\/em> par un <strong>identifiant<\/strong> que vous souhaitez utiliser,<\/li>\n<li><code>RUSTFS_SECRET_KEY<\/code> dans la section <em>environment<\/em>. Changez la valeur <em>mot2passe<\/em> par quelque chose de bien plus long et bien plus compliqu\u00e9 que cela (une <strong>cl\u00e9 secr\u00e8te<\/strong> en somme).<\/li>\n<\/ul>\n<p>De cette fa\u00e7on, le service va se lancer avec des ports choisis par Docker Compose. Vous pouvez les conna\u00eetre en tapant les commandes suivantes\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span>docker compose port rustfs <span style=\"color:#ae81ff\">9000<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>docker compose port rustfs <span style=\"color:#ae81ff\">9001<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>On constate sur l&rsquo;interface (port initial 9001), apr\u00e8s s&rsquo;\u00eatre authentifi\u00e9 avec l&rsquo;identifiant et la cl\u00e9 secr\u00e8te, qu&rsquo;il n&rsquo;y aucun bucket.<\/p>\n<h2 id=\"initialisation-des-buckets\">Initialisation des buckets<\/h2>\n<blockquote>\n<p>Qu&rsquo;est un bucket\u00a0?<\/p>\n<\/blockquote>\n<p>C&rsquo;est un <strong>espace qui regroupe plusieurs objets<\/strong> qu&rsquo;on aurait d\u00e9pos\u00e9 \u00e0 cet endroit. Cela permet de se rep\u00e9rer plus facilement dans un espace de stockage objet.<\/p>\n<p>Afin de cr\u00e9er un bucket, soit on utilise l&rsquo;interface (m\u00e9thode manuelle), soit on cr\u00e9\u00e9 un conteneur d&rsquo;initialisation qui fera le job \u00e0 notre place (automatique).<\/p>\n<p>J&rsquo;opte pour le <strong>conteneur d&rsquo;initialisation<\/strong> pour cr\u00e9er 3 buckets. Dans notre fichier docker-compose.yaml on ajoute les lignes suivantes\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">init_buckets<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">minio\/mc:RELEASE.2025-08-13T08-35-41Z<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">depends_on<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">rustfs<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">restart<\/span>: <span style=\"color:#66d9ef\">on<\/span>-<span style=\"color:#ae81ff\">failure<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">entrypoint<\/span>: &gt;<span style=\"color:#e6db74\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/bin\/sh -c &#34;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      sleep 5;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/usr\/bin\/mc alias set s3server http:\/\/rustfs:9000 \\&#34;olivier\\&#34; \\&#34;mot2passe\\&#34;;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/usr\/bin\/mc mb s3server\/default;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/usr\/bin\/mc mb s3server\/bucket1;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/usr\/bin\/mc mb s3server\/bucket2;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      exit 0;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      &#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p><strong>Pensez \u00e0 changer olivier et mot2passe<\/strong> par les m\u00eames identifiants\/mot de passe que renseign\u00e9s dans le service <code>rustfs<\/code> du docker-compose.yaml pr\u00e9c\u00e9dent.<\/p>\n<p>L&rsquo;astuce est donc d&rsquo;utiliser le client <code>mc<\/code> de MinIO pour ajouter les buckets qu&rsquo;on souhaiterait. Dans l&rsquo;exemple pr\u00e9sent\u00e9 ci-avant, le conteneur d&rsquo;initialisation permet de cr\u00e9er 3 buckets\u00a0:<\/p>\n<ul>\n<li><em>default<\/em>,<\/li>\n<li><em>bucket1<\/em>,<\/li>\n<li><em>bucket2<\/em>.<\/li>\n<\/ul>\n<p>Libre \u00e0 vous d&rsquo;adapter ce conteneur pour r\u00e9pondre \u00e0 vos besoins en mati\u00e8re de cr\u00e9ation initiale de bucket.<\/p>\n<p>Il ne reste alors plus que la mise \u00e0 disposition sur Internet via l&rsquo;usage de sous-domaines et de Tr\u00e6fik.<\/p>\n<h2 id=\"configurer-les-labels-pour-tr\u00e6fik\">Configurer les labels pour Tr\u00e6fik<\/h2>\n<p>Tr\u00e6fik est un proxy souvent utilis\u00e9 avec Docker Compose pour se charger des routes. L&rsquo;id\u00e9e de Tr\u00e6fik est d&rsquo;<strong>utiliser le champ <code>labels<\/code><\/strong> pour r\u00e9cup\u00e9rer des instructions utiles \u00e0 la <strong>cr\u00e9ation des routes en lien avec les domaines<\/strong>.<\/p>\n<p>Pour RustFS sous Docker Compose, et avec Tr\u00e6fik en frontal, je sugg\u00e8re les labels suivants\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">labels<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.enable=true&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.api-rustfs.rule=Host(`api.mondomaine.tld`)&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.api-rustfs.entrypoints=websecure&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.api-rustfs.service=api-rustfs&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.services.api-rustfs.loadbalancer.server.port=9000&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.rustfs.rule=Host(`mondomaine.tld`)&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.rustfs.entrypoints=websecure&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.rustfs.service=rustfs&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.services.rustfs.loadbalancer.server.port=9001&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p><strong>Prenez soin de changer les \u00e9l\u00e9ments suivants<\/strong>\u00a0:<\/p>\n<ul>\n<li><code>websecure<\/code> par le <strong>nom de votre routeur<\/strong> permettant d&rsquo;avoir du HTTPS,<\/li>\n<li><code>api.mondomaine.tld<\/code> par le <strong>domaine de votre choix<\/strong> pour acc\u00e9der \u00e0 l&rsquo;API de RustFS,<\/li>\n<li><code>mondomaine.tld<\/code> par le <strong>domaine utilis\u00e9<\/strong> pour acc\u00e9der \u00e0 l&rsquo;<strong>interface Web<\/strong> de RustFS.<\/li>\n<\/ul>\n<p>\u00c9videmment tout un tas d&rsquo;autres options sont possibles, il suffit de lire la documentation de Tr\u00e6fik pour cela.<\/p>\n<h2 id=\"r\u00e9sultat-final-complet\">R\u00e9sultat final complet<\/h2>\n<p>Ce qui nous donne\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">services<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">rustfs<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">rustfs\/rustfs:1.0.0-alpha.82<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">restart<\/span>: <span style=\"color:#ae81ff\">always<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">ports<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">9000<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">9001<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">volumes<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">.\/storage_data:\/data<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">\/etc\/localtime:\/etc\/localtime:ro<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">\/etc\/timezone:\/etc\/timezone:ro<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">environment<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_VOLUMES=\/data<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_ADDRESS=0.0.0.0:9000<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_CONSOLE_ENABLE=true<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_ACCESS_KEY=olivier<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_SECRET_KEY=mot2passe<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">RUSTFS_OBS_LOGGER_LEVEL=info<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">healthcheck<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">test<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>        [\n<\/span><\/span><span style=\"display:flex;\"><span>          <span style=\"color:#e6db74\">&#34;CMD&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>          <span style=\"color:#e6db74\">&#34;sh&#34;<\/span>, <span style=\"color:#e6db74\">&#34;-c&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>          <span style=\"color:#e6db74\">&#34;curl -f http:\/\/127.0.0.1:9000\/health &amp;&amp; curl -f http:\/\/127.0.0.1:9001\/rustfs\/console\/health&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>        ]\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">interval<\/span>: <span style=\"color:#ae81ff\">30s<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">timeout<\/span>: <span style=\"color:#ae81ff\">10s<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">retries<\/span>: <span style=\"color:#ae81ff\">3<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">start_period<\/span>: <span style=\"color:#ae81ff\">40s<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">labels<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.enable=true&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.api-rustfs.rule=Host(`api.mondomaine.tld`)&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.api-rustfs.entrypoints=websecure&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.api-rustfs.service=api-rustfs&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.services.api-rustfs.loadbalancer.server.port=9000&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.rustfs.rule=Host(`mondomaine.tld`)&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.rustfs.entrypoints=websecure&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.routers.rustfs.service=rustfs&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;traefik.http.services.rustfs.loadbalancer.server.port=9001&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">init_buckets<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">minio\/mc:RELEASE.2025-08-13T08-35-41Z<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">depends_on<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">rustfs<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">restart<\/span>: <span style=\"color:#66d9ef\">on<\/span>-<span style=\"color:#ae81ff\">failure<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">entrypoint<\/span>: &gt;<span style=\"color:#e6db74\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/bin\/sh -c &#34;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      sleep 5;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/usr\/bin\/mc alias set s3server http:\/\/rustfs:9000 \\&#34;olivier\\&#34; \\&#34;mot2passe\\&#34;;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/usr\/bin\/mc mb s3server\/default;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/usr\/bin\/mc mb s3server\/bucket1;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      \/usr\/bin\/mc mb s3server\/bucket2;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      exit 0;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">      &#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Avec ce contenu vous devriez pouvoir <strong>d\u00e9marrer une instance RustFS<\/strong> sans probl\u00e8me &#x1f609;.<\/p>\n<h1 id=\"quelle-aventure\">Quelle aventure\u00a0!<\/h1>\n<p>Au bout du compte, avoir cherch\u00e9 \u00e0 lancer un service de stockage objet a \u00e9t\u00e9 une <strong>sacr\u00e9e odyss\u00e9e pour moi<\/strong>. Beaucoup d&rsquo;al\u00e9as li\u00e9s au <strong>changement de politique de Bitnami<\/strong> (pour les images MinIO), \u00e0 l&rsquo;<strong>arr\u00eat de maintenance du code du serveur MinIO<\/strong> par AIStor et aux <strong>effets de bord de RustFS<\/strong> en plein d\u00e9veloppement.<\/p>\n<p>Mes besoins sp\u00e9cifiques en terme d&rsquo;infrastructure (Docker Compose et Tr\u00e6fik) m&rsquo;ont amen\u00e9 \u00e0 devoir retravailler plusieurs fois le fichier docker-compose.yaml. Et j&rsquo;ai essuy\u00e9 quelques pl\u00e2tres avant d&rsquo;avoir une instance fonctionnelle.<\/p>\n<p>Je suis ainsi <strong>content de partager ce fichier docker-compose.yaml<\/strong>. Bien qu&rsquo;il y a mati\u00e8re \u00e0 faire \u00e9voluer ce fichier. On peut imaginer <strong>am\u00e9liorer ce fichier<\/strong> en utilisant des <strong>variables<\/strong> pour le dossier o\u00f9 seront stock\u00e9es les donn\u00e9es, le nom du service, le domaine et bien \u00e9videmment l&rsquo;identifiant et le mot de passe du service RustFS. Et d\u00e9poser les valeurs dans <strong>un fichier .env<\/strong>.<\/p>\n<p>On pourrait \u00e9galement trouver un autre client que <code>mc<\/code> pour <strong>initialiser les buckets<\/strong> au d\u00e9part. Voire trouver un autre outil permettant de cr\u00e9er les buckets plus facilement. Imaginons une nouvelle image Docker ayant un script d&rsquo;initialisation avec des variables d&rsquo;environnement le permettant.<\/p>\n<p>Je laisse votre imagination faire le reste &#x1f60a;.<\/p>\n<h1 id=\"sources-utilis\u00e9es-pour-la-mise-en-place-de-rustfs\">Sources utilis\u00e9es pour la mise en place de RustFS<\/h1>\n<ul>\n<li><a href=\"https:\/\/github.com\/rustfs\/rustfs\/issues\/555\">Benchmarks de plusieurs solutions alternatives \u00e0 Minio<\/a><\/li>\n<li><a href=\"https:\/\/docs.rustfs.com\/concepts\/comparison.html\">Comparaison \u00e0 Ceph et MinIO<\/a><\/li>\n<li><a href=\"https:\/\/docs.rustfs.com\/management\/bucket\/creation.html\">Documentation sur la cr\u00e9ation de buckets<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/rustfs\/rustfs\/blob\/main\/docker-compose.yml\">Fichier docker-compose.yml d&rsquo;exemple sur le d\u00e9p\u00f4t Github du projet RustFS<\/a><\/li>\n<li><a href=\"https:\/\/docs.rustfs.com\/developer\/mc.html\">Documentation sur l&rsquo;utilisation du client MinIO Client (mc) pour RustFS<\/a><\/li>\n<li><a href=\"https:\/\/belginux.com\/installer-rustfs-avec-docker\/\">Tutoriel d&rsquo;installation de RustFS avec Docker<\/a><\/li>\n<\/ul>\n<h1 id=\"liens-utiles\">Liens utiles<\/h1>\n<ul>\n<li><a href=\"https:\/\/github.com\/rustfs\/rustfs\/issues\/1838\">Bug RustFS (ticket Github) version 1.0.0-alpha.83 sur l&rsquo;architecture de ma machine<\/a>,<\/li>\n<li><a href=\"https:\/\/github.com\/bitnami\/charts\/issues\/35164\">D\u00e9cision drastique prise par Bitnami<\/a>,<\/li>\n<li><a href=\"https:\/\/github.com\/rustfs\/rustfs\">D\u00e9p\u00f4t Github de RustFS<\/a>,<\/li>\n<li><a href=\"https:\/\/github.com\/pgsty\/minio\">Fork de MinIO<\/a>,<\/li>\n<li><a href=\"https:\/\/hub.docker.com\/r\/rustfs\/rustfs\">Images docker officielles de RustFS sur le Hub Docker<\/a>,<\/li>\n<li><a href=\"https:\/\/fr.wikipedia.org\/wiki\/Stockage_objet\">Le stockage objet selon Wikip\u00e9dia<\/a>,<\/li>\n<li><a href=\"https:\/\/fr.wikipedia.org\/wiki\/Amazon_S3\">S3 selon Wikip\u00e9dia<\/a>,<\/li>\n<li><a href=\"https:\/\/velero.io\/docs\/\">Velero (documentation)<\/a>,<\/li>\n<li><a href=\"https:\/\/velero.io\/\">Velero<\/a>.<\/li>\n<\/ul>\n"},{"title":"Mon Labo K8S pour essayer des applications","link":"https:\/\/olivier.dossmann.net\/2025\/11\/mon-labo-k8s-pour-essayer-des-applications\/","pubDate":"Sun, 09 Nov 2025 06:25:39 +0100","guid":"https:\/\/olivier.dossmann.net\/2025\/11\/mon-labo-k8s-pour-essayer-des-applications\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Quand on commence \u00e0 \u00e9tudier Kubernetes (k8s), on a <strong>besoin d&rsquo;un environnement o\u00f9 tester<\/strong> ce qu&rsquo;on apprend. Les environnements possibles sont nombreux\u00a0: <a href=\"https:\/\/minikube.sigs.k8s.io\/docs\/\">Minikube<\/a>, <a href=\"https:\/\/kubernetes.io\/fr\/\">Kubernetes (k8s) officiel<\/a>, <a href=\"https:\/\/k0sproject.io\/\">k0s<\/a>, <a href=\"https:\/\/k3s.io\/\">k3s<\/a>, etc. Cela ne s&rsquo;arr\u00eate plus\u00a0!<\/p>\n<p>Entre les <strong>difficult\u00e9s \u00e0 installer l&rsquo;environnement<\/strong> et celles \u00e0 appliquer ce qu&rsquo;on apprend, on ne s&rsquo;en sort plus. Cela devient <strong>vite compliqu\u00e9<\/strong> au point de vouloir tout arr\u00eater ; c&rsquo;\u00e9tait mon cas.<\/p>\n<p>De plus je souhaitais avoir un environnement reproductible et sp\u00e9cifique \u00e0 chacun de mes projets, de quoi travailler unitairement sur un service particulier.<\/p>\n<p>Avant de d\u00e9crocher compl\u00e8tement, j&rsquo;ai fait une pause, <strong>pris du recul<\/strong> et \u00e9tudi\u00e9 la question. Je vous relate ici le r\u00e9sultat de cette petite aventure dans le monde de Kubernetes et des outils envisag\u00e9s pour <strong>cr\u00e9er un labo de d\u00e9veloppement<\/strong> d&rsquo;applications tournant sous Kubernetes.<\/p>\n<p>Dans un premier temps nous poserons le probl\u00e8me, apr\u00e8s quoi nous \u00e9tudierons quelques solutions pour finalement d\u00e9tailler le produit de cette p\u00e9r\u00e9grination.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/objets\/tubes_a_essai.jpg\" alt=\"Quelques tubes \u00e0 essais avec une pipette vus de pr\u00e8s\"><\/p>\n<p><em>Photo trouv\u00e9e sur <a href=\"https:\/\/www.publicdomainpictures.net\/fr\/view-image.php?image=45299&amp;picture=tubes-a-essai\">publicdomainpictures.net<\/a><\/em> sous Licence CC0 Public Domain.<\/p>\n<h1 id=\"le-contexte\">Le contexte<\/h1>\n<p>Venant du <a href=\"https:\/\/olivier.dossmann.net\/tags\/docker\/\">monde de Docker et Docker Compose<\/a>, il me semblait aller de soi d&rsquo;\u00e9tudier ensuite le monde de Kubernetes - qui utilise la conteneurisation. Seulement je ne pensais pas que l&rsquo;installation m\u00eame de <strong>Kubernetes \u00e9tait un cap \u00e0 franchir<\/strong>.<\/p>\n<p>Je souhaite en <strong>profiter pour migrer<\/strong> petit \u00e0 petit <strong>mes services sur Kubernetes<\/strong>. Chacun d&rsquo;eux devient donc un projet en soi. Et comme souvent, <strong>j&rsquo;aime avoir un environnement sp\u00e9cifique de d\u00e9veloppement<\/strong> pour cr\u00e9er les fichiers n\u00e9cessaires \u00e0 Kubernetes et permettre a posteriori d&rsquo;am\u00e9liorer le service. Je cherche donc \u00e0 avoir un simili de docker-compose.yml pour chaque service et permettant de d\u00e9velopper l&rsquo;application dans un environnement Kubernetes propre.<\/p>\n<p>Au d\u00e9but on d\u00e9couvre <a href=\"https:\/\/minikube.sigs.k8s.io\/docs\/\"><strong>Minikube<\/strong><\/a>. Du moins c&rsquo;est un outil tr\u00e8s souvent conseill\u00e9. <strong>Pratique<\/strong> sur une machine bureautique pour triturer rapidement quelques fichiers Kubernetes (des fichiers au format YAML). Il est <strong>bien document\u00e9<\/strong> et poss\u00e8de plusieurs plugins pour faciliter l&rsquo;\u00e9tude du Kubernetes en place ; par exemple un <strong>plugin Dashboard<\/strong> permet de visualiser les m\u00e9triques du serveur et les objets instanci\u00e9s. J&rsquo;ai la sensation que cet outil reste \u00ab trop sp\u00e9cifique \u00bb. Il ne repr\u00e9sente pas totalement un \u00ab vrai \u00bb Kubernetes. \u00c7a peut parfois causer des torts au moment de d\u00e9ployer sur un autre serveur Kubernetes. L&rsquo;outil est \u00e9galement <strong>lourd<\/strong> et relativement <strong>lent<\/strong> pour instancier un nouvel environnement.<\/p>\n<p>Pourquoi ne pas tester un Kubernetes connu pour sa l\u00e9g\u00e8ret\u00e9\u00a0? Par exemple <a href=\"https:\/\/k3s.io\/\"><strong>k3s<\/strong><\/a>. D&rsquo;autant qu&rsquo;il s&rsquo;installe rapidement \u00e0 l&rsquo;aide de <a href=\"https:\/\/github.com\/alexellis\/k3sup\"><strong>k3sup<\/strong><\/a> (qui fonctionne \u00e0 merveille). Sur le papier c&rsquo;est une bonne id\u00e9e\u00a0: <strong>l\u00e9ger, facile \u00e0 installer<\/strong>. En revanche il <strong>ne permet pas d&rsquo;instancier un nouvel environnement<\/strong> ; il faut donc jouer avec les namespaces Kubernetes. Rebelote\u00a0: ce n&rsquo;est pas un outil adapt\u00e9 \u00e0 l&rsquo;apprentissage de Kubernetes. Au d\u00e9part on fait forc\u00e9ment des erreurs dues \u00e0 l&rsquo;incompr\u00e9hension ou l&rsquo;ignorance ; ce qui peut rendre l&rsquo;environnement rapidement inutilisable.<\/p>\n<p>Apr\u00e8s ces deux premi\u00e8res tentatives, je sais d\u00e9sormais ce que je veux\u00a0:<\/p>\n<ul>\n<li>possibilit\u00e9 de <strong>cr\u00e9er un environnement \u00ab propre \u00bb<\/strong>,<\/li>\n<li>encore mieux s&rsquo;il peut cr\u00e9er <strong>plusieurs environnements<\/strong>,<\/li>\n<li>si possible <strong>l\u00e9ger<\/strong> (petit) - ce qui exclue les machines virtuelles,<\/li>\n<li><strong>facile \u00e0 installer<\/strong>,<\/li>\n<li>et <strong>rapide<\/strong>.<\/li>\n<\/ul>\n<p>Nous avons nos crit\u00e8res, il ne reste plus qu&rsquo;\u00e0 chercher le(s) outil(s) permettant de cr\u00e9er rapidement un environnement de d\u00e9veloppement autour de Kubernetes.<\/p>\n<h1 id=\"solutions-\u00e9tudi\u00e9es\">Solutions \u00e9tudi\u00e9es<\/h1>\n<p>\u00c0 la recherche des mots cl\u00e9s \u00ab lightweight \u00bb (similaire au mot l\u00e9ger) et \u00ab kubernetes \u00bb dans un moteur de recherche, on trouve plusieurs outils comme\u00a0:<\/p>\n<ul>\n<li><a href=\"https:\/\/microk8s.io\/\">MicroK8S<\/a>,<\/li>\n<li><a href=\"https:\/\/k0sproject.io\/\">k0s<\/a>,<\/li>\n<li><a href=\"https:\/\/k3s.io\/\">k3s<\/a>,<\/li>\n<li>et <a href=\"https:\/\/kind.sigs.k8s.io\/\">kind<\/a>.<\/li>\n<\/ul>\n<p><strong>k3s<\/strong> a d\u00e9j\u00e0 \u00e9t\u00e9 cit\u00e9 dans cet article. Il ne me convient pas pour un environnement de d\u00e9veloppement\/d&rsquo;\u00e9tude concernant k8s. Il est s\u00fbrement tr\u00e8s <strong>redoutable \u00e0 l&rsquo;installation rapide<\/strong> sur une quantit\u00e9 importante de machines \u00e0 l&rsquo;aide de <a href=\"https:\/\/github.com\/alexellis\/k3sup\">k3sup<\/a>. Mais ce n&rsquo;est pas le sujet de cet article.<\/p>\n<p>La liste mentionne \u00e9galement <a href=\"https:\/\/microk8s.io\/\"><strong>MicroK8S<\/strong><\/a>, le petit rejeton de la famille Ubuntu, qui semble \u00eatre une <strong>alternative \u00e0 <a href=\"https:\/\/minikube.sigs.k8s.io\/docs\/\">Minikube<\/a><\/strong> avec des <strong>plugins<\/strong> (Dashboard, DNS, registry, istio, etc.). Je pense qu&rsquo;il est effectivement int\u00e9ressant pour un usage complet entre des d\u00e9veloppeurs, une int\u00e9gration continue (CI) \/ d\u00e9ploiement continue (CD) et plusieurs environnements allant jusqu&rsquo;\u00e0 des environnements dit \u00ab de production \u00bb. Je sens cependant que la similitude avec Minikube ne puisse me convenir. Je le <strong>laisse de c\u00f4t\u00e9 pour l&rsquo;instant<\/strong>.<\/p>\n<p><a href=\"https:\/\/k0sproject.io\/\"><strong>k0s<\/strong><\/a>, quant \u00e0 lui, semble \u00eatre la <strong>solution id\u00e9ale pour<\/strong> une installation sur <strong>une machine physique<\/strong>. \u00c0 <strong>usage priv\u00e9<\/strong>. Bien qu&rsquo;il semble <strong>l\u00e9ger, facile \u00e0 installer<\/strong> et utiliser, je comprends rapidement qu&rsquo;il est fait pour avoir une installation unique sur une machine physique pr\u00e9cise. Il <strong>ne r\u00e9pond pas \u00e0<\/strong> mon crit\u00e8re de <strong>cr\u00e9ation multiple d&rsquo;environnements<\/strong>.<\/p>\n<blockquote>\n<p>Et un de plus en moins\u00a0!<\/p>\n<\/blockquote>\n<p>Il reste le dernier de cette liste\u00a0: <a href=\"https:\/\/kind.sigs.k8s.io\/\"><strong>kind<\/strong><\/a>. A priori il semble <strong>l\u00e9ger<\/strong>, pr\u00e9vu pour un usage <strong>sp\u00e9cifique au d\u00e9veloppement<\/strong> ou de l&rsquo;<strong>int\u00e9gration continue<\/strong> (CI) et <strong>utilise Docker<\/strong> pour cr\u00e9er un cluster complet \u00e0 chaque fois. Waouh, rien que \u00e7a\u00a0! Sur le papier, cet outil est <strong>tr\u00e8s prometteur<\/strong> car il r\u00e9pond aux crit\u00e8res pr\u00e9c\u00e9demment list\u00e9s\u00a0:<\/p>\n<ul>\n<li>cr\u00e9er des environnements \u00ab propres \u00bb gr\u00e2ce \u00e0 l&rsquo;usage de Docker,<\/li>\n<li>ainsi il permet l&rsquo;usage de plusieurs environnements,<\/li>\n<li>il est l\u00e9ger, petit,<\/li>\n<li>facile \u00e0 installer puisque <a href=\"https:\/\/aur.archlinux.org\/packages\/kind\">kind est disponible dans les d\u00e9p\u00f4ts AUR d&rsquo;ArchLinux<\/a>,<\/li>\n<li>et pour la rapidit\u00e9 nous verrons.<\/li>\n<\/ul>\n<p>\u00c0 l&rsquo;usage, kind (qui veut dire Kubernetes IN Docker) semble <strong>facile \u00e0 prendre en main<\/strong>\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Cr\u00e9er un cluster nomm\u00e9 olivier<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>kind create cluster --name olivier\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Supprime le cluster nomm\u00e9 olivier<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>kind delete cluster --name olivier\n<\/span><\/span><\/code><\/pre><\/div><p>Une fois le cluster cr\u00e9\u00e9, il s&rsquo;agit simplement de jouer avec la commande <code>kubectl<\/code> habituelle pour contacter le cluster. Et la commande \u00e9tant \u00e0 taper, je peux tout \u00e0 fait <strong>scripter la cr\u00e9ation du cluster<\/strong> et sa destruction.<\/p>\n<p>Il serait plus pratique de l&rsquo;utiliser avec <strong>d&rsquo;autres outils pour en faciliter l&rsquo;usage<\/strong>. J&rsquo;ai donc cherch\u00e9 au fur et \u00e0 mesure de nouveaux outils dont voici la liste\u00a0:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/ahmetb\/kubectx\"><strong>kubectx<\/strong><\/a> va permettre de <strong>commuter<\/strong> d&rsquo;<strong>un contexte Kubernetes<\/strong> \u00e0 un autre. Autrement dit passer d&rsquo;un cluster \u00e0 un autre facilement,<\/li>\n<li><a href=\"https:\/\/github.com\/yannh\/kubeconform\"><strong>kubeconform<\/strong><\/a> pour <strong>valider<\/strong> nos fichiers YAML,<\/li>\n<li><a href=\"https:\/\/github.com\/stackrox\/kube-linter\"><strong>kube-linter<\/strong><\/a> pour pousser \u00e0 l&rsquo;usage des <strong>bonnes pratiques<\/strong> en mati\u00e8re de r\u00e9daction de fichiers YAML,<\/li>\n<li><a href=\"https:\/\/skaffold.dev\/\"><strong>skaffold<\/strong><\/a> pour <strong>surveiller<\/strong> la moindre modification (\u00e9quivalent d&rsquo;un \u00ab watcher \u00bb) sur nos fichiers YAML et les d\u00e9ployer directement sur le cluster,<\/li>\n<li>et <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Make\"><strong>make<\/strong><\/a> (avec un <strong>fichier Makefile<\/strong>) pour m\u00e9langer tout \u00e7a.<\/li>\n<\/ul>\n<p>Mon id\u00e9e est d&rsquo;utiliser un fichier Makefile pour fournir plusieurs commandes permettant de cr\u00e9er le cluster, le d\u00e9truire, v\u00e9rifier les fichiers YAML ou encore lancer un d\u00e9ploiement automatique s&rsquo;il y a des changements dans les fichiers en cours de d\u00e9veloppement.<\/p>\n<p>J&rsquo;imagine quelque chose comme\u00a0:<\/p>\n<ul>\n<li><code>make cluster<\/code> pour <strong>cr\u00e9er un cluster<\/strong>,<\/li>\n<li><code>make dev<\/code> pour lancer skaffold et <strong>\u00e9couter le moindre changement<\/strong> sur le dossier de d\u00e9veloppement de l&rsquo;application,<\/li>\n<li><code>make kubeconform<\/code> pour <strong>v\u00e9rifier<\/strong> les fichiers une fois \u00e9crits,<\/li>\n<li><code>make lint<\/code> pour v\u00e9rifier les <strong>bonnes pratiques<\/strong> avec kube-linter,<\/li>\n<li>et <code>make clean<\/code> pour <strong>d\u00e9truire le cluster<\/strong>.<\/li>\n<\/ul>\n<p>Tentons l&rsquo;aventure et voyons le r\u00e9sultat\u00a0!<\/p>\n<h1 id=\"ce-que-jai-finalement-produit\">Ce que j&rsquo;ai finalement produit<\/h1>\n<h2 id=\"en-bref\">En bref<\/h2>\n<p>Le fichier <strong>Makefile<\/strong>\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-Makefile\" data-lang=\"Makefile\"><span style=\"display:flex;\"><span>CLUSTER_NAME <span style=\"color:#f92672\">:=<\/span> k8s-dev-example\n<\/span><\/span><span style=\"display:flex;\"><span>KUBECTL <span style=\"color:#f92672\">:=<\/span> kubectl\n<\/span><\/span><span style=\"display:flex;\"><span>SKAFFOLD <span style=\"color:#f92672\">:=<\/span> skaffold\n<\/span><\/span><span style=\"display:flex;\"><span>KIND <span style=\"color:#f92672\">:=<\/span> kind\n<\/span><\/span><span style=\"display:flex;\"><span>KUBECONFORM <span style=\"color:#f92672\">:=<\/span> kubeconform\n<\/span><\/span><span style=\"display:flex;\"><span>KUSTOMIZE <span style=\"color:#f92672\">:=<\/span> kustomize\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>NAMESPACES <span style=\"color:#f92672\">:=<\/span> dev\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">.PHONY<\/span><span style=\"color:#f92672\">:<\/span> help\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">help<\/span><span style=\"color:#f92672\">:<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t@echo <span style=\"color:#e6db74\">&#34;Commandes disponibles :&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t@echo <span style=\"color:#e6db74\">&#34;  make cluster       - Cr\u00e9e un cluster Kind&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t@echo <span style=\"color:#e6db74\">&#34;  make namespaces    - Cr\u00e9e les namespaces Kubernetes (dev\/staging\/prod)&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t@echo <span style=\"color:#e6db74\">&#34;  make dev           - Lance Skaffold pour l&#39;env dev&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t@echo <span style=\"color:#e6db74\">&#34;  make kubeconform   - Lance kubeconform sur l&#39;env de DEV&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t@echo <span style=\"color:#e6db74\">&#34;  make clean         - Supprime le cluster Kind&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">cluster<\/span><span style=\"color:#f92672\">:<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">$(<\/span>KIND<span style=\"color:#66d9ef\">)<\/span> create cluster --name <span style=\"color:#66d9ef\">$(<\/span>CLUSTER_NAME<span style=\"color:#66d9ef\">)<\/span> <span style=\"color:#f92672\">||<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">namespaces<\/span><span style=\"color:#f92672\">:<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t@for ns in <span style=\"color:#66d9ef\">$(<\/span>NAMESPACES<span style=\"color:#66d9ef\">)<\/span>; <span style=\"color:#66d9ef\">do<\/span> <span style=\"color:#ae81ff\">\\\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#66d9ef\">$(<\/span>KUBECTL<span style=\"color:#66d9ef\">)<\/span> get ns $$ns &gt;\/dev\/null 2&gt;&amp;<span style=\"color:#ae81ff\">1<\/span> <span style=\"color:#f92672\">||<\/span> <span style=\"color:#66d9ef\">$(<\/span>KUBECTL<span style=\"color:#66d9ef\">)<\/span> create ns $$ns; <span style=\"color:#ae81ff\">\\\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">done<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">dev<\/span><span style=\"color:#f92672\">:<\/span> cluster namespaces\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">$(<\/span>SKAFFOLD<span style=\"color:#66d9ef\">)<\/span> dev\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">kubeconform<\/span><span style=\"color:#f92672\">:<\/span> kubeconform-dev\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">kubeconform-dev<\/span><span style=\"color:#f92672\">:<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">$(<\/span>KUSTOMIZE<span style=\"color:#66d9ef\">)<\/span> build overlays\/dev | <span style=\"color:#66d9ef\">$(<\/span>KUBECONFORM<span style=\"color:#66d9ef\">)<\/span> -strict -summary -ignore-missing-schemas\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">clean<\/span><span style=\"color:#f92672\">:<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">$(<\/span>KIND<span style=\"color:#66d9ef\">)<\/span> delete cluster --name <span style=\"color:#66d9ef\">$(<\/span>CLUSTER_NAME<span style=\"color:#66d9ef\">)<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Ce <a href=\"https:\/\/gitlab.com\/odtre\/templates\/k8s-dev-example\/\">fichier Makefile est disponible sur le d\u00e9p\u00f4t Gitlab d&rsquo;ODTRE<\/a>.<\/p>\n<p>Il va utiliser deux dossiers principaux\u00a0:<\/p>\n<ul>\n<li><strong>base<\/strong> qui contient les fichiers YAML de l&rsquo;application qu&rsquo;on souhaite impl\u00e9menter sur Kubernetes,<\/li>\n<li><strong>overlays\/dev<\/strong> qui contient un fichier kustomization.yaml pour surcharger le dossier <em>base<\/em>.<\/li>\n<\/ul>\n<p>Et 2 fichiers de configuration\u00a0:<\/p>\n<ul>\n<li><strong>kube-linter.yaml<\/strong> avec votre configuration de l&rsquo;outil,<\/li>\n<li><strong>skaffold.yaml<\/strong> avec le contenu suivant\u00a0:<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">apiVersion<\/span>: <span style=\"color:#ae81ff\">skaffold\/v4beta13<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">kind<\/span>: <span style=\"color:#ae81ff\">Config<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">metadata<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">k8s-demo-app<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">manifests<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">kustomize<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">paths<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">.\/overlays\/dev<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Exemple de redirection de port sur le service nomm\u00e9 dev-nginx (car l&#39;overlay ajoute dev- devant tout)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">portForward<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  - <span style=\"color:#f92672\">resourceType<\/span>: <span style=\"color:#ae81ff\">service<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">resourceName<\/span>: <span style=\"color:#ae81ff\">dev-nginx<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">namespace<\/span>: <span style=\"color:#ae81ff\">dev<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">port<\/span>: <span style=\"color:#ae81ff\">80<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">localPort<\/span>: <span style=\"color:#ae81ff\">4000<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Cela permet de surveiller le dossier <em>.\/overlays\/dev<\/em> avec <em>kustomize<\/em> pour interpr\u00e9ter les fichiers. Et il va ouvrir le <strong>port 4000<\/strong> sur la machine locale pour <strong>rediriger sur le port 80<\/strong> du service Nginx d&rsquo;exemple (fourni dans le d\u00e9p\u00f4t Gitlab mentionn\u00e9 pr\u00e9c\u00e9demment).<\/p>\n<h2 id=\"\u00e0-lusage\">\u00c0 l&rsquo;usage<\/h2>\n<p>Tout n&rsquo;est pas parfait. Cependant ce fichier Makefile permet de lancer quelques commandes <code>make<\/code> utiles\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Lance l&#39;environnement de DEV. Si le cluster n&#39;existe pas : il le cr\u00e9\u00e9. Sinon il ignore.<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>make dev\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># V\u00e9rifie la conformit\u00e9 des fichiers YAML en utilisant le dossier overlays\/dev<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>make kubeconform\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># D\u00e9truit le cluster de DEV<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>make clean\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Pousse \u00e0 l&#39;usage des bonnes pratiques sur les fichiers contenus dans le dossier .\/base<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>kube-linter lint --config kube-linter.yaml .\/base\n<\/span><\/span><\/code><\/pre><\/div><p>Pour toute aide suppl\u00e9mentaire, faites\u00a0: <code>make help<\/code>.<\/p>\n<p>Ainsi <strong>il devient facile de travailler sur un nouvel environnement<\/strong>\u00a0:<\/p>\n<ul>\n<li>je copie les fichiers disponible sur un <a href=\"https:\/\/gitlab.com\/odtre\/templates\/k8s-dev-example\/\">d\u00e9p\u00f4t template sur Gitlab<\/a>,<\/li>\n<li>j&rsquo;ouvre le fichier Makefile en \u00e9criture,<\/li>\n<li>j&rsquo;\u00e9dite la ligne suivante pour adapter le nom du cluster de travail\u00a0:<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span>CLUSTER_NAME :<span style=\"color:#f92672\">=<\/span> k8s-dev-example\n<\/span><\/span><\/code><\/pre><\/div><p>Et le tour est jou\u00e9\u00a0!<\/p>\n<p>Je suis assez <strong>satisfait de cet environnement<\/strong>. Il <strong>r\u00e9pond aux crit\u00e8res<\/strong> \u00e9dict\u00e9s plusieurs fois dans cet article. Et je me sens plus \u00e0 l&rsquo;aise de pouvoir modifier comme bon me semble cet environnement de d\u00e9veloppement\/travail.<\/p>\n<p>Les outils utilis\u00e9s me font penser \u00e0 <strong>une bo\u00eete \u00e0 outil<\/strong> de base pour le <strong>travail sur Kubernetes<\/strong>. M\u00eame sans utiliser le fichier Makefile je suis amen\u00e9 \u00e0 pratiquer les outils comme kubeconform, kubectl, kube-linter et kustomize.<\/p>\n<p>C&rsquo;est donc une tr\u00e8s bonne chose d&rsquo;avoir regroup\u00e9 ces outils autour de Makefile et de les utiliser r\u00e9guli\u00e8rement sur mes projets Kubernetes.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Nous avons pos\u00e9 le contexte d&rsquo;une <strong>recherche d&rsquo;outil<\/strong> simple, rapide et l\u00e9ger <strong>pour cr\u00e9er de nombreux environnements Kubernetes<\/strong> afin de travailler sur ces environnements dans le cadre d&rsquo;un <strong>d\u00e9veloppement de fichiers YAML<\/strong>.<\/p>\n<p>Apr\u00e8s quelques essais sur Minikube et k3s, mes recherches se sont port\u00e9es sur l&rsquo;<strong>utilisation de kind<\/strong>. Puis j&rsquo;ai utilis\u00e9 make pour le manipuler et regrouper aussi les diff\u00e9rents outils utiles.<\/p>\n<p>La <strong>bo\u00eete \u00e0 outil<\/strong> se compose de\u00a0:<\/p>\n<ul>\n<li><strong>kind<\/strong>,<\/li>\n<li><strong>skaffold<\/strong>,<\/li>\n<li><strong>kubectl<\/strong>,<\/li>\n<li><strong>kubeconform<\/strong>,<\/li>\n<li><strong>kube-linter<\/strong>,<\/li>\n<li>et <strong>make<\/strong>.<\/li>\n<\/ul>\n<p>J&rsquo;aborde d\u00e9sormais de <strong>mani\u00e8re plus sereine<\/strong> le d\u00e9veloppement d&rsquo;une application qui sera h\u00e9berg\u00e9e par un Kubernetes. J&rsquo;ai un template \u00e0 ma disposition pour ces fichiers. Template que je peux faire \u00e9voluer au gr\u00e9 des obstacles rencontr\u00e9s. Il utilise tous les outils que j&rsquo;ai appr\u00e9ci\u00e9s. J&rsquo;en suis <strong>pleinement satisfait<\/strong> &#x1f60e; .<\/p>\n<p>Et vous, <strong>quel environnement Kubernetes utilisez-vous<\/strong> pour vos d\u00e9veloppements de fichiers YAML\u00a0?<\/p>\n<h1 id=\"liens-utiles\">Liens utiles<\/h1>\n<ul>\n<li><a href=\"https:\/\/kubernetes.io\/fr\/\">Kubernetes (k8s) officiel<\/a>,<\/li>\n<li><a href=\"https:\/\/microk8s.io\/\">MicroK8S<\/a>,<\/li>\n<li><a href=\"https:\/\/minikube.sigs.k8s.io\/docs\/\">Minikube<\/a>,<\/li>\n<li><a href=\"https:\/\/k0sproject.io\/\">k0s<\/a>,<\/li>\n<li><a href=\"https:\/\/k3s.io\/\">k3s<\/a>,<\/li>\n<li><a href=\"https:\/\/kind.sigs.k8s.io\/\">kind<\/a>,<\/li>\n<li><a href=\"https:\/\/github.com\/stackrox\/kube-linter\">kube-linter<\/a>,<\/li>\n<li><a href=\"https:\/\/github.com\/yannh\/kubeconform\">kubeconform<\/a>,<\/li>\n<li><a href=\"https:\/\/github.com\/ahmetb\/kubectx\">kubectx<\/a>,<\/li>\n<li><a href=\"https:\/\/fr.wikipedia.org\/wiki\/Make\">make<\/a>,<\/li>\n<li><a href=\"https:\/\/skaffold.dev\/\">skaffold<\/a>,<\/li>\n<li><a href=\"https:\/\/gitlab.com\/odtre\/templates\/k8s-dev-example\/\">d\u00e9p\u00f4t Gitlab du template propos\u00e9 dans cet article pour d\u00e9velopper sur un environnement Kubernetes \u00ab propre \u00bb<\/a>.<\/li>\n<\/ul>\n"},{"title":"Clavier Keychron Q6 Knob ISO version","link":"https:\/\/olivier.dossmann.net\/2025\/09\/clavier-keychron-q6-knob-iso-version\/","pubDate":"Wed, 10 Sep 2025 21:47:52 +0200","guid":"https:\/\/olivier.dossmann.net\/2025\/09\/clavier-keychron-q6-knob-iso-version\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>\u00c0 une \u00e9poque lointaine je m&rsquo;int\u00e9ressais aux <strong>claviers m\u00e9caniques<\/strong>. Notamment le <a href=\"https:\/\/www.daskeyboard.com\/blog\/das-keyboard-ii-105-keys-for-accent-users\/\" title=\"Lire un article sur le Das Keyboard 2\">Das Keyboard 2<\/a>. Aucun article sur le pr\u00e9sent site n&rsquo;avait \u00e9t\u00e9 publi\u00e9. Je l&rsquo;ai pourtant fait lors de l&rsquo;acquisition de mon <a href=\"https:\/\/olivier.dossmann.net\/2017\/04\/clavier-usb-compact-thinkpad-avec-trackpoint\/\">clavier USB Compact Thinkpad avec TrackPoint<\/a> en 2017. Mais ce n&rsquo;est pas un clavier m\u00e9canique.<\/p>\n<p>Aujourd&rsquo;hui j&rsquo;aimerais partager avec vous des \u00e9l\u00e9ments autour des claviers m\u00e9caniques. Et pour cela nous parlerons, entre autre, du clavier <a href=\"https:\/\/keychron.fr\/fr\/products\/keychron-q6-qmk-custom-mechanical-keyboard-iso-layout-collection\">Keychron Q6 Knob ISO Version<\/a> acquis il y a quelques ann\u00e9es d\u00e9j\u00e0\u00a0: en <strong>2022<\/strong>.<\/p>\n<p>Je tiens \u00e0 pr\u00e9ciser que <a href=\"https:\/\/richard-dern.fr\/interets\/informatique\/2022\/01\/11\/a-la-recherche-du-clavier-parfait-un-clavier-100-custom\/\">d&rsquo;autres personnes, comme Richard Dern, parlent mieux de ce sujet sur leur blog<\/a>, voire <a href=\"https:\/\/richard-dern.fr\/interets\/informatique\/2022\/01\/15\/a-la-recherche-du-clavier-parfait-a-propos-de-lorientation-des-switches\/\">d\u00e9taillent mieux les parties concernant les composants de clavier m\u00e9caniques<\/a>.<\/p>\n<p>J&rsquo;aimerais surtout partager les caract\u00e9ristiques de ce clavier, pourquoi je l&rsquo;ai choisi, quels composants permettent de construire son propre clavier, combien cela co\u00fbte et les outils disponibles pour am\u00e9liorer son exp\u00e9rience sur un <strong>clavier personnalis\u00e9 et personnalisable<\/strong>.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/materiel\/clavier_keychron_q6_knob_iso_version.png\" alt=\"\"><\/p>\n<h1 id=\"le-keychron-q6-knob-iso-version\">Le Keychron Q6 Knob ISO Version<\/h1>\n<p>Vous l&rsquo;aurez compris\u00a0: j&rsquo;ai achet\u00e9 un Keychron Q6 Knob ISO Version.<\/p>\n<h2 id=\"mod\u00e8le-et-disposition\">Mod\u00e8le et disposition<\/h2>\n<blockquote>\n<p>Que veut dire Keychron Q6 Knob ISO Version<\/p>\n<\/blockquote>\n<ul>\n<li><strong>Keychron<\/strong> est la <strong>marque<\/strong> du clavier (l&rsquo;entreprise qui fabrique le clavier),<\/li>\n<li><strong>Q6<\/strong> en est le <strong>mod\u00e8le<\/strong>. Il existe plusieurs mod\u00e8les en fonction du nombre de touches, du type de clavier (filaire, bluetooth ou wifi), etc.<\/li>\n<li><strong>Knob<\/strong> est une <strong>alternative<\/strong> poss\u00e9dant un <strong>potentiom\u00e8tre<\/strong>. Bref, un gros bouton qui tourne pour changer le volume &#x1f604;<\/li>\n<li>Et <strong>ISO version<\/strong> signifie que la <strong>disposition<\/strong> du clavier est europ\u00e9enne plut\u00f4t que la version ANSI qui par exemple ne se pr\u00e9sente pas de la m\u00eame mani\u00e8re au niveau de la touche entr\u00e9e (bien plus fine que celle pour la version europ\u00e9enne).<\/li>\n<\/ul>\n<p><strong>Edit<\/strong>\u00a0: En r\u00e9alit\u00e9 <a href=\"https:\/\/en.wikipedia.org\/wiki\/ISO\/IEC_9995\">ISO est un standard nomm\u00e9 aussi IEC 9995<\/a> (merci \u00e0 <a href=\"https:\/\/gitlab.com\/greyxor\/\">GreyXor<\/a> de l&rsquo;avoir signal\u00e9).<\/p>\n<h1 id=\"caract\u00e9ristiques\">Caract\u00e9ristiques<\/h1>\n<p>Ayant pris seulement la base du clavier (sans les touches ni les switches - dont nous parlerons plus tard), voici les caract\u00e9ristiques de ce support de clavier\u00a0:<\/p>\n<ul>\n<li>Clavier avec une <strong>base en aluminium<\/strong>,<\/li>\n<li>Branchement <strong>filaire<\/strong> avec de l&rsquo;USB-C - avec un adaptateur USB-C vers USB Type B,<\/li>\n<li>Bouton pour changer de <strong>disposition Win\/Mac<\/strong>,<\/li>\n<li>Clavier <strong>programmable<\/strong> (avec un <strong>firmware QMK<\/strong> - open source),<\/li>\n<li>Touches <strong>r\u00e9tro\u00e9clair\u00e9es<\/strong>,<\/li>\n<li>Disposition <strong>ISO<\/strong> (europ\u00e9enne),<\/li>\n<li>Couleur \u00ab <strong>Navy Blue<\/strong> \u00bb.<\/li>\n<\/ul>\n<p>Apparence de la base que j&rsquo;ai prise\u00a0:<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/materiel\/clavier_keychron_q6_knob_iso_version_navy_blue.png\" alt=\"\"><\/p>\n<h1 id=\"pourquoi-ce-choix\">Pourquoi ce choix\u00a0?<\/h1>\n<p>\u00c0 <strong>200\u20ac environ - rien que la base<\/strong>, sans touches - on peut se demander ce qui a motiv\u00e9 cet achat. Et curieusement la r\u00e9ponse est\u00a0: financi\u00e8re\u00a0!<\/p>\n<p>Je divague\u00a0? Pas totalement.<\/p>\n<p>En effet ces derni\u00e8res annn\u00e9es, avant l&rsquo;acquisition d&rsquo;un tel objet, j&rsquo;achetais 1 clavier par an. Pas pour \u00eatre \u00e0 la page, ni pour toujours avoir un clavier neuf. Mais parce que <strong>mes claviers tombent en panne<\/strong> environ <strong>1 an apr\u00e8s<\/strong> leur achat.<\/p>\n<p>J&rsquo;utilise <strong>\u00e9norm\u00e9ment<\/strong> la ligne de commande. Et contr\u00f4le mon bureau au clavier. Il a donc <strong>une place cruciale<\/strong> dans mes usages. Et \u00e0 cet effet <strong>il s&rsquo;use aussi tr\u00e8s vite<\/strong>. Avec 12 000 phrases par mois sur un seul salon de discussion sur lequel je suis actif, avec 50 mots par phrase en moyenne, m\u00eame si un mot faisait 3 lettres, cela fait - en arrondissant beaucoup - quelque chose comme <strong>2 millions de touches appuy\u00e9es sur 1 an<\/strong>. Et ceci n&rsquo;est qu&rsquo;un seul de mes - nombreux - usages. J&rsquo;\u00e9cris sur d&rsquo;autres supports.<\/p>\n<p>Au d\u00e9but j&rsquo;achetais des claviers simples, \u00e0 30\u20ac, puis 60\u20ac pour monter en gamme. Et \u00e0 force mes habitudes se tournaient vers des <strong>claviers pratiques, r\u00e9tro\u00e9clair\u00e9s, m\u00e9caniques, avec un potentiom\u00e8tre pour le son<\/strong>, etc. Et les co\u00fbts avoisinaient facilement 200\u20ac. Jusqu&rsquo;\u00e0 ce qu&rsquo;il tombe en panne et que je doive en racheter un.<\/p>\n<p>Autant vous dire qu&rsquo;\u00e0 200\u20ac l&rsquo;ann\u00e9e, \u00e7a commence \u00e0 faire cher. Ainsi j&rsquo;ai d\u00e9cid\u00e9 de me tourner vers les <strong>claviers \u00ab faits-maison \u00bb : des claviers qu&rsquo;on assemble soi-m\u00eame<\/strong>.<\/p>\n<p>L&rsquo;id\u00e9e\u00a0? Si quelque chose tombe en panne je le remplace au prix de la pi\u00e8ce. <strong>Pas au prix d&rsquo;un clavier entier<\/strong>.<\/p>\n<h1 id=\"historique-de-mes-claviers\">Historique de mes claviers<\/h1>\n<p>Pour vous donner un ordre de grandeur des d\u00e9penses, voici un <strong>historique de mes achats<\/strong> et de leur prix approximatif. <strong>Uniquement pour les claviers m\u00e9caniques<\/strong>, sans les claviers interm\u00e9diaires\u00a0:<\/p>\n<ul>\n<li><strong>2009<\/strong> - (~80\u20ac) Das Keyboard 2 (switches Cherry MX blue),<\/li>\n<li><strong>2017<\/strong> - (~120\u20ac) Das Keyboard 4 Ultimate (switches Cherry MX brown),<\/li>\n<li><strong>2019<\/strong> - (~200\u20ac) Corsair K95 RGB platinum (switches Cherry MX brown),<\/li>\n<li><strong>2022<\/strong> - (~70\u20ac) DREVO Calibur V2 Te RGB 60% (switches Cherry MX brown),<\/li>\n<li><strong>2022<\/strong> - (~210\u20ac + switches + touches) Keychron Q6 Knob ISO Version (nu, sans touches, sans switches)<\/li>\n<\/ul>\n<p>Avant 2022, rien qu&rsquo;en clavier m\u00e9canique, sur 10 ans j&rsquo;avais d\u00e9pens\u00e9 400\u20ac. Et quasiment tout autant en clavier &ldquo;non m\u00e9canique&rdquo;. Soit <strong>800\u20ac sur 10 ans<\/strong>, soit <strong>80\u20ac par an<\/strong>.<\/p>\n<p>L&rsquo;<strong>id\u00e9e<\/strong> est donc de <strong>diminuer ce budget pour les 10 ans \u00e0 venir<\/strong> (\u00e0 partir de 2022).<\/p>\n<h1 id=\"les-composants-dun-clavier--fait-maison--assembl\u00e9\">Les composants d&rsquo;un clavier \u00ab fait-maison \u00bb (assembl\u00e9)<\/h1>\n<p>Avoir un clavier assembl\u00e9 veut dire <strong>composer soi-m\u00eame son clavier<\/strong>\u00a0: il faut choisir chacun des composants. Trouver des composants compatibles, comparer les prix, commander, patienter, monter\/assembler.<\/p>\n<p>Nous ne sommes pas l\u00e0 pour faire un cours sur les \u00e9l\u00e9ments n\u00e9cessaires \u00e0 un clavier ni pour d\u00e9tailler chacun des composants, mais voici les \u00e9l\u00e9ments de base\u00a0:<\/p>\n<ul>\n<li>un <strong>bo\u00eetier<\/strong> de clavier (pour mettre la carte m\u00e8re de clavier notamment),<\/li>\n<li><strong>mousse<\/strong> dans le bo\u00eetier pour <strong>r\u00e9duire l&rsquo;\u00e9cho<\/strong> (quand on tape sur une touche),<\/li>\n<li>une <strong>carte m\u00e8re<\/strong> pour g\u00e9rer la ou les dispositions de clavier,<\/li>\n<li>une <strong>plaque<\/strong> \u00e0 mettre par dessus,<\/li>\n<li>des <strong>switches<\/strong> (qui sont le <strong>lien entre un emplacement de touche et la touche<\/strong> elle-m\u00eame),<\/li>\n<li>des <strong>stabilisateurs<\/strong> pour les touches plus grosses comme la touche Entr\u00e9e, la barre espace, etc.,<\/li>\n<li>des <strong>touches<\/strong> pardi\u00a0!<\/li>\n<li>et un <strong>repose-poignet<\/strong> (facultatif).<\/li>\n<\/ul>\n<h1 id=\"combien-cela-a-t-il-co\u00fbt\u00e9\">Combien cela a-t-il co\u00fbt\u00e9\u00a0?<\/h1>\n<p>Faisons le <strong>calcul approximatif<\/strong>. En <strong>2022<\/strong> j&rsquo;ai achet\u00e9\u00a0:<\/p>\n<ul>\n<li><strong>80\u20ac<\/strong> - <strong>switches<\/strong> pour clavier m\u00e9canique,<\/li>\n<li><strong>28\u20ac<\/strong> - une seule <strong>touche personnalis\u00e9e<\/strong> (Toadstool Retainer de Mario Bros),<\/li>\n<li><strong>210\u20ac<\/strong> - la base du <strong>clavier keychron<\/strong> (sans les touches, sans les switches),<\/li>\n<li><strong>40\u20ac<\/strong> - une seule <strong>touche en m\u00e9tal<\/strong> (le point interrogation de Mario Bros),<\/li>\n<li><strong>62\u20ac<\/strong> - ensemble de <strong>touches north-facing<\/strong>,<\/li>\n<li><strong>57\u20ac<\/strong> - <strong>repose-poignet<\/strong> en marbre.<\/li>\n<\/ul>\n<p><strong>Total\u00a0: ~480\u20ac<\/strong>.<\/p>\n<p>\u00c0 ce prix, le <strong>clavier devient \u00e9conomique au bout de 2 ans et demi<\/strong> - pour moi. En effet, ces derni\u00e8res ann\u00e9es je d\u00e9pensais pas moins de 200\u20ac de clavier par an (avant l&rsquo;arriv\u00e9e du Keychron Q6 dans le foyer).<\/p>\n<h1 id=\"retours-dexp\u00e9rience\">Retour(s) d&rsquo;exp\u00e9rience<\/h1>\n<p>\u00c0 l&rsquo;heure o\u00f9 j&rsquo;\u00e9cris ces lignes nous sommes en 2025. Le clavier est <strong>toujours vivant. Toujours robuste. Toujours fonctionnel<\/strong>. J&rsquo;ai d\u00fb <strong>changer 2 switches<\/strong> seulement \u00e0 moindre co\u00fbt. Les touches ont \u00e9t\u00e9 chang\u00e9es pour une raison simple\u00a0: on m&rsquo;en a offertes.\nJe n&rsquo;ai <strong>rien d\u00e9pens\u00e9 de plus pour ce clavier<\/strong>. J&rsquo;ai donc <strong>r\u00e9duit grandement mon budget clavier \u00e0 l&rsquo;ann\u00e9e<\/strong>.<\/p>\n<p><strong>L&rsquo;utilisation est appr\u00e9ciable<\/strong>. Tr\u00e8s appr\u00e9ciable\u00a0! J&rsquo;ai choisi des switches qui me conviennent parfaitement\u00a0: <strong>la pression des touches est adapt\u00e9e \u00e0 ma frappe<\/strong>. Ni trop dure, ni trop molle.\nDe temps en temps, je change les touches (appel\u00e9es \u00ab caps \u00bb en anglais) <strong>pour le plaisir<\/strong>. Comme si c&rsquo;\u00e9tait un rev\u00eatement de saison ou une <strong>nouvelle parure<\/strong>.<\/p>\n<p>En revanche ce clavier est tr\u00e8s lourd. <strong>Il est mastoc<\/strong>. Ce qui le rend <strong>impossible \u00e0 transporter<\/strong> comme on le ferait avec d&rsquo;autres claviers. Si vous pr\u00e9voyez de d\u00e9placer le clavier, mieux vaut en prendre un plus petit - avec moins de touches.\nMais quoi de mieux qu&rsquo;un bon gros clavier pour son bureau\u00a0?<\/p>\n<p>Finalement je me sens <strong>\u00e0 l&rsquo;aise avec ce clavier<\/strong> qui coche mes crit\u00e8res et r\u00e9pond \u00e0 son r\u00f4le \u00e0 merveille\u00a0;\u00a0sans d\u00e9faillir\u00a0!<\/p>\n<h1 id=\"outils-disponibles-pour-un-tel-clavier\">Outils disponibles pour un tel clavier<\/h1>\n<p>La particularit\u00e9 d&rsquo;un tel clavier est de pouvoir \u00eatre <strong>personnalisable<\/strong> non seulement en mati\u00e8re de touches, de r\u00e9action et de pressions, mais aussi en terme de r\u00e9ponse \u00e0 un appui. C&rsquo;est \u00e0 dire de pouvoir d\u00e9finir sous chaque touche l&rsquo;action qui sera entreprise. Si la plupart des claviers r\u00e9pondront par un A \u00e0 l&rsquo;appui de la touche A, nous pouvons tout \u00e0 fait d\u00e9finir que l&rsquo;appui sur la touche A met en pause la vid\u00e9o en cours de lecture. Et c&rsquo;est \u00e7a qu&rsquo;on recherche, croyez-moi\u00a0!<\/p>\n<p>Pour ce faire, nous disposons d&rsquo;une ribambelle d&rsquo;outils, parmi\u00a0:<\/p>\n<ul>\n<li><a href=\"https:\/\/config.qmk.fm\/\"><strong>QMK Configurator<\/strong>, une interface Web de configuration de son clavier<\/a> (utilise le firmware QMK, votre clavier doit donc \u00eatre compatible),<\/li>\n<li><a href=\"https:\/\/launcher.keychron.com\/\"><strong>Keychron Launcher<\/strong>, l&rsquo;outil officiel de Keychron pour configurer son clavier en ligne<\/a> (n\u00e9cessite un navigateur parmi Microsoft Edge, Chrome ou Opera - qui doit surtout g\u00e9rer WebUSB, Cf. <a href=\"https:\/\/caniuse.com\/webusb\">Support de WebUSB sur la plupart des navigateurs courants<\/a>),<\/li>\n<li><a href=\"https:\/\/www.usevia.app\/\"><strong>VIA<\/strong>, une interface conviviale et simple<\/a>.<\/li>\n<\/ul>\n<p>Ces outils ne fonctionneront que si le clavier est reconnu par le syst\u00e8me et le navigateur. Pour savoir comment proc\u00e9der avec le Keychron Q6, je vous renvoie vers <a href=\"https:\/\/olivier.dossmann.net\/wiki\/materiel\/keychron_q6\/\">ma page d&rsquo;astuce concernant le Keychron Q6 ISO Knob version<\/a>.<\/p>\n<p>Vous pouvez \u00e9galement installer sur votre machine certains outils, comme ceux contenus dans <a href=\"https:\/\/qmk.fm\/toolbox\"><strong>QMK Toolbox<\/strong><\/a>.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Apr\u00e8s avoir d\u00e9taill\u00e9 les caract\u00e9ristiques de ce <strong>clavier Keychron Q6 Knob ISO Version<\/strong>, les composants n\u00e9cessaires \u00e0 sa compl\u00e9tude, l&rsquo;historique de mes claviers pour comprendre la baisse \u00e9ventuelle du budget par an et mes retours d&rsquo;exp\u00e9rience, il semble clair qu&rsquo;au bout de 3 ans je suis <strong>pleinement satisfait<\/strong> de ce clavier.\nAvec l&rsquo;ensemble des claviers m\u00e9caniques que j&rsquo;ai achet\u00e9s, je peux d\u00e9sormais dire que ce clavier me convient parfaitement. Il est devenu <strong>LE clavier<\/strong>.<\/p>\n<p>De 2022 \u00e0 2025, ce changement de clavier est <strong>devenu rentable<\/strong>. Le temps nous dira de combien il l&rsquo;est. Rien que pour la maintenance possible par \u00ab petites doses \u00bb (comme <strong>changer seulement les touches us\u00e9es<\/strong>), cela vaut le coup\u00a0! En revanche on s&rsquo;aper\u00e7oit que <strong>changer d&rsquo;apparence peut co\u00fbter cher<\/strong>\u00a0: en fonction de l&rsquo;ensemble de touches choisi les prix varient entre luxe et arnaque (les prix sont parfois ind\u00e9cents)\u2026<\/p>\n<p>Par ailleurs une <strong>large gamme d&rsquo;outils<\/strong> est disponible pour personnaliser les touches et on peut facilement se lancer dans une programmation du clavier tr\u00e8s forte avec une <strong>r\u00e9elle \u00e9volution de l&rsquo;usage<\/strong> que nous en faisons. <strong>Je conseille<\/strong>\u00a0!<\/p>\n<p>Et vous\u00a0? <strong>Quel sera votre prochain clavier m\u00e9canique\u00a0?<\/strong><\/p>\n<h1 id=\"liens-utiles\">Liens utiles<\/h1>\n<ul>\n<li><a href=\"https:\/\/www.daskeyboard.com\/blog\/das-keyboard-ii-105-keys-for-accent-users\/\" title=\"Lire un article sur le Das Keyboard 2\">Clavier <strong>Das Keyboard 2<\/strong><\/a>,<\/li>\n<li><a href=\"https:\/\/keychron.fr\/fr\/products\/keychron-q6-qmk-custom-mechanical-keyboard-iso-layout-collection\">Clavier <strong>Keychron Q6 Knob ISO Version<\/strong><\/a>,<\/li>\n<li><a href=\"https:\/\/richard-dern.fr\/interets\/informatique\/2022\/01\/11\/a-la-recherche-du-clavier-parfait-un-clavier-100-custom\/\">Article de Richard Dern sur la <strong>recherche du clavier parfait<\/strong><\/a>,<\/li>\n<li><a href=\"https:\/\/richard-dern.fr\/interets\/informatique\/2022\/01\/15\/a-la-recherche-du-clavier-parfait-a-propos-de-lorientation-des-switches\/\">Article de Richard Dern concernant l&rsquo;<strong>orientation des switches<\/strong><\/a>,<\/li>\n<li><a href=\"https:\/\/config.qmk.fm\/\"><strong>QMK Configurator<\/strong>, une interface Web de configuration de son clavier<\/a> (utilise le firmware QMK, votre clavier doit donc \u00eatre compatible),<\/li>\n<li><a href=\"https:\/\/launcher.keychron.com\/\"><strong>Keychron Launcher<\/strong>, l&rsquo;outil officiel de Keychron pour configurer son clavier en ligne<\/a> (n\u00e9cessite un navigateur parmi Microsoft Edge, Chrome ou Opera),<\/li>\n<li><a href=\"https:\/\/www.usevia.app\/\"><strong>VIA<\/strong>, une interface conviviale et simple<\/a>,<\/li>\n<li><a href=\"https:\/\/olivier.dossmann.net\/wiki\/materiel\/keychron_q6\/\">Ma <strong>page d&rsquo;astuce<\/strong> concernant le Keychron Q6 ISO Knob version<\/a>.<\/li>\n<\/ul>\n"},{"title":"Ce que j'aurais voulu savoir sur Gitlab CI","link":"https:\/\/olivier.dossmann.net\/2025\/05\/ce-que-jaurais-voulu-savoir-sur-gitlab-ci\/","pubDate":"Sun, 18 May 2025 13:55:39 +0200","guid":"https:\/\/olivier.dossmann.net\/2025\/05\/ce-que-jaurais-voulu-savoir-sur-gitlab-ci\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Il y a quelques mois <a href=\"https:\/\/olivier.dossmann.net\/2024\/10\/mes_conseils_pour_reussir_une_formation_bootcamp\/\">je parlais de ma formation Bootcamp pour devenir Ing\u00e9nieur DevOps<\/a>. J&rsquo;y ai utilis\u00e9 - \u00e0 nouveau - <strong>Gitlab CI<\/strong> (Continuous Integration). Vous savez, cet <strong>outil permettant d&rsquo;ex\u00e9cuter des actions<\/strong> apr\u00e8s avoir envoy\u00e9 votre code\/d\u00e9p\u00f4t sur la plateforme Gitlab\u00a0?<\/p>\n<p>C&rsquo;est tr\u00e8s utile, par exemple pour <strong>lancer les tests<\/strong> sur votre code, v\u00e9rifier que <strong>le code s&rsquo;ex\u00e9cute dans un environnement pr\u00e9cis<\/strong> ou tout simplement v\u00e9rifier que <strong>le code compile<\/strong>. Et si votre d\u00e9p\u00f4t n&rsquo;est pas du code, par exemple un site web, vous pouvez tester les liens morts\u00a0; voire publier votre site\u00a0! <strong>Les possibilit\u00e9s sont - finalement - nombreuses<\/strong>\u00a0!<\/p>\n<p>En revanche, un tel outil est parfois d\u00e9stabilisant. Et des cheveux, j&rsquo;en ai arrach\u00e9s\u00a0! J&rsquo;aurais appr\u00e9ci\u00e9 tomber sur un article explicatif. Avec des astuces. Peut-\u00eatre que le pr\u00e9sent article m&rsquo;aurait convenu, qui sait\u00a0!<\/p>\n<p>J&rsquo;aurai par exemple aim\u00e9 savoir comment <strong>publier facilement un site web<\/strong>, <strong>tester localement<\/strong> le fichier <em>.gitlab-ci.yml<\/em> avant de l&rsquo;envoyer sur Gitlab, conna\u00eetre <strong>plus d&rsquo;astuces<\/strong> pour les mots cl\u00e9s Gitlab, savoir <strong>utiliser Docker dans Gitlab CI<\/strong> et ce que sont les <strong>composants r\u00e9utilisables<\/strong>.<\/p>\n<p>Ce sont ainsi tous les sujets que nous allons aborder aujourd&rsquo;hui.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/nature\/raton_laveur.jpg\" alt=\"Un visage de Raton Laveur\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/www.flickr.com\/photos\/131830853@N05\/\">profil de didier.camus sur Flickr<\/a><\/em> sous Licence \u0152uvre du domaine public.<\/p>\n<h1 id=\"publier-rapidement-un-site-statique\">Publier rapidement un site statique<\/h1>\n<p>Gitlab met \u00e0 disposition des utilisateurs un <strong>espace permettant d&rsquo;h\u00e9berger des sites webs statiques<\/strong>. Incroyable n&rsquo;est-ce pas\u00a0? Cet espace est <a href=\"https:\/\/docs.gitlab.com\/user\/project\/pages\/\" title=\"Voir la documentation de Gitlab concernant Gitlab Pages\">Gitlab Pages<\/a>.<\/p>\n<p>Comment cela fonctionne\u00a0? Il suffit de cr\u00e9er un d\u00e9p\u00f4t Gitlab, d&rsquo;ajouter un fichier <strong>.gitlab-ci.yml<\/strong> contenant la section suivante\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">busybox<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">pages<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">deploy<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">script<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#ae81ff\">echo &#34;The site will be deployed to $CI_PAGES_URL&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">artifacts<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">paths<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">public<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">rules<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#f92672\">if<\/span>: <span style=\"color:#ae81ff\">$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Code r\u00e9cup\u00e9r\u00e9 du projet <a href=\"https:\/\/gitlab.com\/pages\/plain-html\">https:\/\/gitlab.com\/pages\/plain-html<\/a>.<\/p>\n<p>Cela signifie qu&rsquo;au terme de la CI\/CD (dans l&rsquo;\u00e9tape nomm\u00e9e <em>deploy<\/em>), <strong>le contenu du dossier public sera publi\u00e9 sur Gitlab Pages<\/strong>. \u00c0 quelle adresse\u00a0? Imaginons que vous partez d&rsquo;un d\u00e9p\u00f4t dont l&rsquo;adresse est\u00a0: <code>https:\/\/gitlab.com\/blankoworld\/monprojet\/<\/code>, l&rsquo;adresse URL d&rsquo;acc\u00e8s \u00e0 votre site publi\u00e9 sera\u00a0: <code>https:\/\/blankoworld.gitlab.io\/monprojet\/<\/code>. Pratique non\u00a0?<\/p>\n<p>Astuce\u00a0: Si votre d\u00e9p\u00f4t est priv\u00e9, vous pouvez quand m\u00eame rendre votre site public. Il suffit d&rsquo;aller dans <strong>Settings &gt; General<\/strong>, Section \u00ab <strong>Visibility, project features, permissions<\/strong> \u00bb et dans le sous-titre \u00ab <strong>Pages<\/strong> \u00bb, activez la fonctionnalit\u00e9. Et dans le menu d\u00e9roulant \u00e0 c\u00f4t\u00e9, choisissez \u00ab <strong>Everyone<\/strong> \u00bb. N&rsquo;oubliez pas de <strong>sauvegarder<\/strong> cette configuration. Le code sera priv\u00e9, le site web statique public\u00a0!<\/p>\n<p>Pour <strong>plus d&rsquo;exemple<\/strong>, veuillez vous rendre sur la page Gitlab pr\u00e9sentant la <a href=\"https:\/\/gitlab.com\/pages\" title=\"Liste d'exemple de projets utilisant Gitlab Pages\">liste de projets utilisant Gitlab Pages<\/a>.<\/p>\n<h1 id=\"tester-avant-denvoyer-sur-le-d\u00e9p\u00f4t-\">Tester avant d&rsquo;envoyer sur le d\u00e9p\u00f4t ?<\/h1>\n<p>Le truc vraiment emb\u00eatant avec les fichiers <em>.gitlab-ci.yml<\/em>, c&rsquo;est qu&rsquo;<strong>on ne peut pas les tester sur notre machine<\/strong> (localement). Pour avancer il faut\u00a0:<\/p>\n<ul>\n<li>modifier le fichier <em>.gitlab-ci.yml<\/em>,<\/li>\n<li>le valider (faire un commit),<\/li>\n<li>l&rsquo;envoyer sur Gitlab,<\/li>\n<li>patienter que la pipeline se lance (et parfois se termine),<\/li>\n<li>b\u00e9n\u00e9ficier du r\u00e9sultat ou, dans la plupart des cas, recommencer \u00e0 l&rsquo;\u00e9tape 1\u2026<\/li>\n<\/ul>\n<p>\u00c0 une \u00e9poque on pouvait utiliser une commande <code>gitlab-runner exec<\/code> en local. Mais celle-ci a \u00e9t\u00e9 supprim\u00e9e\u00a0: <a href=\"https:\/\/gitlab.com\/gitlab-org\/gitlab-runner\/-\/commit\/f8508c924f80b104ec7353353e6ad0edb5daae66\">https:\/\/gitlab.com\/gitlab-org\/gitlab-runner\/-\/commit\/f8508c924f80b104ec7353353e6ad0edb5daae66<\/a> .<\/p>\n<p>Suivant ce que nous faisons dans la CI\/CD de Gitlab, il est possible d&rsquo;utiliser un outil bien pratique nomm\u00e9 <strong>gitlab-ci-local<\/strong>\u00a0: <a href=\"https:\/\/github.com\/firecow\/gitlab-ci-local\" title=\"Se rendre sur la page Github du projet gitlab-ci-local\">gitlab-ci-local<\/a>.<\/p>\n<p>Vous trouverez quelques d\u00e9tails de <a href=\"https:\/\/olivier.dossmann.net\/wiki\/developpement\/gitlab-ci-local\/\" title=\"Visiter le recueil d'astuces d'Olivier concernant l'outil gitlab-ci-local\">gitlab-ci-local sur mon recueil d&rsquo;astuces<\/a>.<\/p>\n<h1 id=\"g\u00e9n\u00e9ralit\u00e9s\">G\u00e9n\u00e9ralit\u00e9s<\/h1>\n<p>Voici quelques pistes int\u00e9ressantes sur le sujet Gitlab\u00a0:<\/p>\n<ul>\n<li>Une <strong>documentation de d\u00e9part<\/strong> est\u00a0: <a href=\"https:\/\/docs.gitlab.com\/topics\/build_your_application\/\">https:\/\/docs.gitlab.com\/topics\/build_your_application\/<\/a>. Elle permet d&rsquo;en savoir plus sur la <a href=\"https:\/\/docs.gitlab.com\/ci\/yaml\/\" title=\"Lire la documentation officielle de Gitlab concernant la syntaxe du fichier *gitlab-ci.yml*\">syntaxe de gitlab-ci.yaml<\/a>, les <a href=\"https:\/\/docs.gitlab.com\/ci\/variables\/predefined_variables\/\" title=\"En savoir plus sur les variables pr\u00e9-d\u00e9finies dans la CI Gitlab\">variables pr\u00e9-d\u00e9finies<\/a> ou encore le <a href=\"https:\/\/docs.gitlab.com\/ci\/pipelines\/pipeline_architectures\/#basic-pipelines\" title=\"D\u00e9couvrir les pipelines de Gitlab CI\">fonctionnement d&rsquo;un pipeline dans Gitlab CI<\/a>,<\/li>\n<li>Toujours utiles\u00a0: les actions que je nomme comme \u00ab <strong>globales<\/strong> \u00bb. Par exemple les mots cl\u00e9s <code>image<\/code>, <code>variables<\/code> ou <code>before_script<\/code> peuvent <strong>\u00eatre utilis\u00e9s en d\u00e9but du fichier<\/strong> pour d\u00e9finir des actions ou des valeurs qui <strong>seront utilis\u00e9es dans toutes les \u00e9tapes du pipeline<\/strong>. Ce qui peut \u00eatre un avantage, ou un inconv\u00e9nient. Exemple\u00a0: <code>image: ubuntu:latest<\/code> va d\u00e9finir que toutes les \u00e9tapes se lanceront dans un docker partant de l&rsquo;image Ubuntu,<\/li>\n<li>Entr\u00e9es\/sorties\u00a0: <strong>un artefact = une sortie<\/strong>. C&rsquo;est \u00e0 dire des fichiers utilisables\/disponibles pour les \u00e9tapes suivantes (donc en <strong>entr\u00e9e pour les autres \u00e9tapes<\/strong>) en utilisant le mot cl\u00e9 <strong>dependencies<\/strong> (Cf. <a href=\"https:\/\/docs.gitlab.com\/ci\/jobs\/job_artifacts\/#fetching-artifacts)\">https:\/\/docs.gitlab.com\/ci\/jobs\/job_artifacts\/#fetching-artifacts)<\/a>. Sauf si <code>dependencies = []<\/code> est utilis\u00e9.<\/li>\n<li>Utiliser des <strong>variables globales en d\u00e9but du fichier<\/strong> (par exemple pour les num\u00e9ros de versions) fait gagner du temps sur le long terme. Exemple\u00a0:<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">variables<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">DOCKER_VERSION<\/span>: <span style=\"color:#e6db74\">&#34;27.3.1&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">UBUNTU_VERSION<\/span>: <span style=\"color:#e6db74\">&#34;24.04&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><ul>\n<li>Concernant le <strong>mot cl\u00e9 script<\/strong>\u00a0:\n<ul>\n<li>si on veut \u00e9crire plusieurs lignes, utiliser la syntaxe suivante\u00a0:<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">script<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  - <span style=\"color:#ae81ff\">echo &#34;premi\u00e8re \u00e9tape des scripts&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  - |<span style=\"color:#e6db74\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">    echo &#34;premi\u00e8re ligne&#34;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">    echo &#34;seconde ligne&#34;\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">    echo &#34;etc.&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><ul>\n<li><strong>limiter \u00e0 10 lignes max<\/strong>imum,<\/li>\n<li>si on utilise la syntaxe pour \u00e9crire plusieurs lignes, le <strong>debug<\/strong> est plus <strong>difficile<\/strong><\/li>\n<\/ul>\n<\/li>\n<li>Pour <strong>organiser les \u00e9tapes<\/strong>, on peut donner \u00e0 chacune d&rsquo;elle une <strong>\u00e9tape pr\u00e9-requise en utilisant le mot cl\u00e9 \u00ab <a href=\"https:\/\/docs.gitlab.com\/ci\/yaml\/#needs\" title=\"En savoir plus sur le mot cl\u00e9 needs dans la documentation de Gitlab\">needs<\/a> \u00bb<\/strong>, cela permet de cha\u00eener les \u00e9tapes dans un certain ordre. Exemple\u00a0: si on veut que l&rsquo;\u00e9tape \u00ab deploy \u00bb se fasse apr\u00e8s \u00ab compile \u00bb, alors \u00e0 la fin de l&rsquo;\u00e9tape \u00ab deploy \u00bb on met <code>needs: [compile]<\/code>.<\/li>\n<\/ul>\n<h1 id=\"le-cas-docker\">Le Cas Docker<\/h1>\n<h2 id=\"cr\u00e9ation-dune-image-docker-dans-un-conteneur-docker\">Cr\u00e9ation d&rsquo;une image Docker\u2026 dans un conteneur Docker<\/h2>\n<p>Dans la situation o\u00f9 vous voudriez <strong>fabriquer des images Docker<\/strong>, vous allez \u00eatre confront\u00e9s \u00e0 plusieurs probl\u00e9matiques\u00a0:<\/p>\n<ul>\n<li>il faut utiliser des commandes <code>docker<\/code> pour cr\u00e9er des images,<\/li>\n<li>mais la plupart des Gitlab Runner ex\u00e9cutent les \u00e9tapes de Gitlab CI dans un Docker,<\/li>\n<li>donc on se retrouve \u00e0 vouloir ex\u00e9cuter des commandes <code>docker<\/code> dans un environnement Docker.<\/li>\n<\/ul>\n<p>Vous voyez le probl\u00e8me\u00a0?<\/p>\n<p>Pour contourner cette situation il suffit d&rsquo;<strong>utiliser <a href=\"https:\/\/docs.gitlab.com\/ci\/docker\/using_docker_build\/#use-docker-in-docker\" title=\"Se rendre sur la documentation officielle de Gitlab CI au sujet de Docker-in-Docker\">Docker-in-Docker<\/a><\/strong>.<\/p>\n<p>En somme le d\u00e9but du fichier <em>.gitlab-ci.yml<\/em> ressemblera \u00e0\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">variables<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">DOCKER_VERSION<\/span>: <span style=\"color:#e6db74\">&#34;27.4.1&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">docker:$DOCKER_VERSION<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">services<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  - <span style=\"color:#ae81ff\">docker:$DOCKER_VERSION-dind<\/span>\n<\/span><\/span><\/code><\/pre><\/div><h2 id=\"sidentifier\">S&rsquo;identifier<\/h2>\n<p>Pour utiliser les d\u00e9p\u00f4ts de conteneurs de Gitlab, rien de plus simple il suffit d&rsquo;\u00e9crire le code suivant dans le fichier <em>.gitlab-ci.yml<\/em>\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">before_script<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  - <span style=\"color:#ae81ff\">docker info<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  - <span style=\"color:#ae81ff\">echo &#34;$CI_REGISTRY_PASSWORD&#34; | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Nul besoin de renseigner les variables. Elles sont renseign\u00e9es par Gitlab CI.<\/p>\n<h2 id=\"construire-limage\">Construire l&rsquo;image<\/h2>\n<p>Pour construire l&rsquo;image cela va ressembler \u00e0\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">variables<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">CONTAINER_TEST_IMAGE<\/span>: <span style=\"color:#ae81ff\">$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">build-image<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">build<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">script<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#ae81ff\">docker pull &#34;$CONTAINER_TEST_IMAGE&#34; || true<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#ae81ff\">docker build --build-arg BUILDKIT_INLINE_CACHE=1 -t $CONTAINER_TEST_IMAGE .<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#ae81ff\">docker push $CONTAINER_TEST_IMAGE<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>On part du principe que <strong>le fichier Dockerfile est situ\u00e9 \u00e0 la racine<\/strong> de votre projet Gitlab.<\/p>\n<p>CONTAINER_TEST_IMAGE est une variable <strong>globale<\/strong> construite \u00e0 partir des <a href=\"https:\/\/docs.gitlab.com\/ci\/variables\/predefined_variables\/\" title=\"En savoir plus sur les variables pr\u00e9-d\u00e9finies de Gitlab CI dans la documentation officielle de Gitlab\"><strong>variables pr\u00e9-d\u00e9finies<\/strong> de Gitlab CI<\/a>.<\/p>\n<h2 id=\"pousser-limage-sur-le-d\u00e9p\u00f4t\">Pousser l&rsquo;image sur le d\u00e9p\u00f4t<\/h2>\n<p>Dans cette situation nous sommes contents d&rsquo;avoir d\u00e9j\u00e0 ajout\u00e9 l&rsquo;authentification aux d\u00e9p\u00f4ts de conteneurs Gitlab.<\/p>\n<p>Cette fois on fait une \u00e9tape pour r\u00e9cup\u00e9rer l&rsquo;image pr\u00e9c\u00e9dente et lui donner un nouveau nom\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">push-image<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">push<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">script<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#ae81ff\">export IMAGE_TAG=&#34;$(date +&#39;%Y%m%d&#39;)-${CI_COMMIT_SHORT_SHA}&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#ae81ff\">docker pull $CONTAINER_TEST_IMAGE<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#ae81ff\">docker tag $CONTAINER_TEST_IMAGE $CI_REGISTRY_IMAGE:$IMAGE_TAG<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#ae81ff\">docker push $CI_REGISTRY_IMAGE:$IMAGE_TAG<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">rules<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    - <span style=\"color:#f92672\">if<\/span>: <span style=\"color:#ae81ff\">$CI_COMMIT_BRANCH == &#34;main&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>L&rsquo;id\u00e9e est\u00a0:<\/p>\n<ul>\n<li>de <strong>cr\u00e9er un nom de tag<\/strong> pour l&rsquo;image (variable IMAGE_TAG),<\/li>\n<li><strong>r\u00e9cup\u00e9rer l&rsquo;image pr\u00e9c\u00e9dente<\/strong> - puisque les \u00e9tapes d&rsquo;un fichier <em>.gitlab-ci.yml<\/em> s&rsquo;ex\u00e9cutent dans des conteneurs isol\u00e9s les uns des autres,<\/li>\n<li>apposer un <strong>nouveau tag<\/strong> sur l&rsquo;image r\u00e9cup\u00e9r\u00e9e,<\/li>\n<li><strong>publier le r\u00e9sultat<\/strong> en poussant l&rsquo;image ainsi \u00e9tiquet\u00e9e.<\/li>\n<\/ul>\n<p>Ceci ne s&rsquo;ex\u00e9cute que si la branche est nomm\u00e9e <strong>main<\/strong>. Donc chaque commit sur cette branche g\u00e9n\u00e9rera une image Docker et un tag.<\/p>\n<h2 id=\"ordre-dex\u00e9cution-des-\u00e9tapes\">Ordre d&rsquo;ex\u00e9cution des \u00e9tapes<\/h2>\n<p>Afin que les deux \u00e9tapes pr\u00e9c\u00e9dentes s&rsquo;ex\u00e9cutent dans un certain ordre, <strong>ne pas oublier les lignes suivantes<\/strong> dans le fichier <em>.gitlab-ci.yml<\/em>\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">stages<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  - <span style=\"color:#ae81ff\">build<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  - <span style=\"color:#ae81ff\">push<\/span>\n<\/span><\/span><\/code><\/pre><\/div><h2 id=\"comme-si-\u00e7a-ne-suffisait-pas\">Comme si \u00e7a ne suffisait pas\u2026<\/h2>\n<p>En outre, voici d&rsquo;<strong>autres \u00e9l\u00e9ments \u00e0 rajouter<\/strong> dans son fichier <em>.gitlab-ci.yml<\/em>\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">variables<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#75715e\"># Use TLS https:\/\/docs.gitlab.com\/ee\/ci\/docker\/using_docker_build.html#tls-enabled<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">DOCKER_HOST<\/span>: <span style=\"color:#ae81ff\">tcp:\/\/docker:2376<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">DOCKER_TLS_CERTDIR<\/span>: <span style=\"color:#e6db74\">&#34;\/certs&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Ces lignes permettent de renseigner un <strong>autre acc\u00e8s \u00e0 l&rsquo;API Docker<\/strong> pour lancer les commandes <code>docker<\/code>.<\/p>\n<h1 id=\"les-composants-r\u00e9utilisables\">Les composants r\u00e9utilisables<\/h1>\n<p>Gitlab CI offre la possibilit\u00e9 d&rsquo;utiliser des <a href=\"https:\/\/docs.gitlab.com\/ci\/components\/\"><strong>composants r\u00e9utilisables<\/strong><\/a> pour agr\u00e9menter les \u00e9tapes de son fichier <em>.gitlab-ci.yml<\/em>. Ceci \u00e9vite de r\u00e9inventer la roue (en codant soi-m\u00eame).<\/p>\n<p>Plusieurs composants sont disponibles sur la <a href=\"https:\/\/gitlab.com\/components\">page Gitlab components<\/a>.<\/p>\n<p>Exemple d&rsquo;utilisation d&rsquo;un composant\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">include<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  - <span style=\"color:#f92672\">component<\/span>: <span style=\"color:#ae81ff\">gitlab.com\/components\/container-scanning\/container-scanning@5.1.0<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>C&rsquo;est tr\u00e8s utile dans certaines situations comme pour tester des fichiers Terraform (avec <a href=\"https:\/\/gitlab.com\/components\/opentofu\">le composant OpenTofu<\/a>), <a href=\"https:\/\/gitlab.com\/components\/go\">le composant Go<\/a>, <a href=\"https:\/\/gitlab.com\/components\/go\">le composant Rust<\/a> ou m\u00eame utiliser les <a href=\"https:\/\/gitlab.com\/components\/autodevops\">fonctionnalit\u00e9s Autodevops de Gitlab<\/a>.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Au premier abord Gitlab CI para\u00eet complexe et maigre en fonctionnalit\u00e9s. Cependant la lecture de la <strong>documentation (tr\u00e8s fournie<\/strong>\u00a0!) donne rapidement un aper\u00e7u bien plus avantageux de cet outil. On y d\u00e9couvre les composants r\u00e9utilisables, les mots cl\u00e9s <code>image<\/code>, <code>variables<\/code>, <code>before_script<\/code>, les artefacts (sorties), les variables globales, le d\u00e9ploiement de sites statiques et j&rsquo;en passe\u00a0!<\/p>\n<p>Certaines difficult\u00e9s telles que Docker viennent s&rsquo;immiscer dans nos essais. Mais <strong>une fois les obstacles franchis c&rsquo;est un outil tr\u00e8s satisfaisant<\/strong>. Je l&rsquo;appr\u00e9cie.<\/p>\n<p>Je serais ravi de tomber plus souvent sur des articles d\u00e9crivant ses fonctionnalit\u00e9s. J&rsquo;esp\u00e8re participer \u00e0 une diffusion plus grande de la connaissance Gitlab CI et, qui sait, peut-\u00eatre partagerais-je prochainement d&rsquo;autres astuces\u00a0?<\/p>\n"},{"title":"Mes conseils pour r\u00e9ussir une formation Bootcamp","link":"https:\/\/olivier.dossmann.net\/2024\/10\/mes_conseils_pour_reussir_une_formation_bootcamp\/","pubDate":"Sun, 13 Oct 2024 10:14:59 +0200","guid":"https:\/\/olivier.dossmann.net\/2024\/10\/mes_conseils_pour_reussir_une_formation_bootcamp\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Il y a peu je commen\u00e7ais une <strong>formation<\/strong> chez <a href=\"https:\/\/datascientest.com\/\">DataScientest<\/a>. <strong>En Bootcamp<\/strong>. Ce format est l&rsquo;\u00e9quivalent d&rsquo;un <strong>temps complet<\/strong> (lundi \u00e0 vendredi de 8h \u00e0 18h) pass\u00e9 sur le contenu du programme p\u00e9dagogique. Dur\u00e9e pr\u00e9vue dans ma situation\u00a0: 3 mois.<\/p>\n<p>Ce temps est n\u00e9cessaire pour apprendre les diff\u00e9rentes notions. C&rsquo;est \u00e0 la fois long et court\u00a0: <strong>c&rsquo;est dense<\/strong> et le temps accord\u00e9 est parfois \u00ab tout juste \u00bb pour lire. Alors comment tenir ce <strong>Marathon de 3 mois<\/strong> ?<\/p>\n<p>J&rsquo;aborderais dans un premier temps les \u00e9l\u00e9ments qui m&rsquo;ont permis d&rsquo;organiser mon plan de la semaine. Apr\u00e8s quoi j&rsquo;aborderais des sujets proches du contenu des cours comme les exercices et les examens. Finalement je donnerais des astuces diverses que j&rsquo;ai list\u00e9es au fur et \u00e0 mesure de mon avanc\u00e9e dans l&rsquo;aventure. Cette liste risque donc de s&rsquo;allonger avec le temps\u00a0!<\/p>\n<p>\u00c0 noter que <strong>ces conseils sont forc\u00e9ment emprunts de mon exp\u00e9rience<\/strong> de vie, mes comp\u00e9tences et mes particularit\u00e9s (la folie, \u00e7a compte ? &#x1f914;)<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/humains\/deep_work.jpg\" alt=\"Un homme travaillant avec un outil ressemblant \u00e0 un marteau\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/flickr.com\/photos\/adouea\/43082145135\/\">profil de Aur\u00e9lien Adoue sur Flickr<\/a><\/em> sous Licence Attribution 2.0 Generic.<\/p>\n<h1 id=\"organisation\">Organisation<\/h1>\n<p>1 mois que j&rsquo;ai commenc\u00e9 la formation. Une <strong>dur\u00e9e pr\u00e9vue de 3 mois<\/strong>. <strong>Une vie \u00e0 mener \u00e0 c\u00f4t\u00e9<\/strong>. <strong>Des objectifs professionnels<\/strong> en lien avec le r\u00e9sultat de ce Bootcamp. Vous ne pouvez pas vous pointer les mains dans les poches.<\/p>\n<p>Il est \u00e9vident qu&rsquo;<strong>il faut un plan<\/strong>. Tous les conseils pointent sur la m\u00eame chose\u00a0: il faut <strong>compartimenter son temps<\/strong>. Et s&rsquo;y tenir ; oui <strong>s&rsquo;y tenir<\/strong>. Faites-le suivant votre vie et vos possibilit\u00e9s. Mais tenez-y vous\u00a0! Et mettez au courant votre entourage de votre planning.<\/p>\n<h2 id=\"mon-planning\">Mon planning<\/h2>\n<p>Dans mon cas, j&rsquo;ai opt\u00e9 pour le planning suivant\u00a0:<\/p>\n<ul>\n<li>les <strong>matins du lundi au jeudi<\/strong>\u00a0: lecture des <strong>nouveaux cours<\/strong>. Apprentissage et exercices, voire <strong>examens<\/strong> ;<\/li>\n<li>les <strong>apr\u00e8s-midi du lundi au jeudi<\/strong>\u00a0: <strong>r\u00e9vision des cours<\/strong> lus les jours pr\u00e9c\u00e9dents (parfois semaines pr\u00e9c\u00e9dentes), sinon faire des <strong>exercices<\/strong> ;<\/li>\n<li>le <strong>vendredi<\/strong>\u00a0: <strong>mise en pratique<\/strong> des outils\/notions appri(e)s(es) sur des projets personnels.<\/li>\n<\/ul>\n<p>De cette mani\u00e8re <strong>la nouveaut\u00e9 arrive quand je suis frais, c&rsquo;est \u00e0 dire le matin<\/strong>.<\/p>\n<p>Viens ensuite l&rsquo;apr\u00e8s-midi, apr\u00e8s le repas, o\u00f9 je suis moins attentif que le matin. Id\u00e9al pour des r\u00e9visions sur des sujets d\u00e9j\u00e0 abord\u00e9s les jours pr\u00e9c\u00e9dents. Et cela fait une fin de journ\u00e9e plus tranquille que le matin.<\/p>\n<p>Une r\u00e9vision passe par la r\u00e9daction de \u00ab fiches \u00bb de synth\u00e8se pour cr\u00e9er des r\u00e9capitulatifs de ce qui a \u00e9t\u00e9 vu. Parfois je dessine des <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Carte_heuristique\">cartes heuristiques<\/a>\u00a0: plus parlantes pour moi.<\/p>\n<p>Pendant la lecture des cours <strong>je note des id\u00e9es d&rsquo;utilisation des outils d\u00e9couverts<\/strong> (en somme des projets qui germent dans ma t\u00eate). Ce qui me permet d&rsquo;aborder le vendredi un projet r\u00e9fl\u00e9chi pendant la semaine. Exemple\u00a0: \u00e0 la lecture du contenu, j&rsquo;ai not\u00e9 qu&rsquo;il y avait beaucoup d&rsquo;abr\u00e9viations. Par ailleurs, un des modules du cours apprend \u00e0 utiliser un outil de cr\u00e9ation d&rsquo;API. Quoi de mieux que de cr\u00e9er une API pour lister les abr\u00e9viations\u00a0? Et hop!, un projet \u00e0 faire le vendredi.<\/p>\n<p>Autre exemple : on apprend le scripting Bash ? Quoi de mieux que de faire un outil pour automatiser l&rsquo;installation de nos logiciels habituels sur les machines virtuelles fournies par le centre de formation ? Ou bien simplifier la vie en ajoutant des raccourcis (alias) Bash pour les commandes usuelles pendant les cours\u00a0?<\/p>\n<p>On apprend la m\u00e9thode ETL (Extract, Transform, Load) sur des API ? Utilisons notre API des abr\u00e9viations pour extraire les donn\u00e9es\u00a0!<\/p>\n<p>Que faire une fois les donn\u00e9es extraites ? On a appris \u00e0 utiliser - en bonus - SQLAlchemy (biblioth\u00e8que pour faire des requ\u00eates sur une base de donn\u00e9es), pourquoi ne pas l&rsquo;utiliser pour ins\u00e9rer les donn\u00e9es extraites de l&rsquo;API pour les charger dans une base de donn\u00e9es \u00e0 nous\u00a0?<\/p>\n<blockquote>\n<p>Mais en dehors de ces moments, que fais-tu Olivier ?<\/p>\n<\/blockquote>\n<h2 id=\"hors-planning\">Hors planning<\/h2>\n<p>Si le planning existe, c&rsquo;est pour une raison\u00a0: <strong>il est n\u00e9cessaire d&rsquo;avoir une vie \u00e0 c\u00f4t\u00e9\u00a0!<\/strong> Il faut du temps pour\u00a0:<\/p>\n<ul>\n<li>la <strong>famille<\/strong> (conjoint(e), enfants, parents, etc.),<\/li>\n<li><strong>des pauses<\/strong>,<\/li>\n<li>avoir <strong>du temps \u00e0 soi<\/strong>,<\/li>\n<li><strong>dormir<\/strong>,<\/li>\n<li><strong>manger<\/strong>,<\/li>\n<li>etc.<\/li>\n<\/ul>\n<p>R\u00e9ellement, <strong>il FAUT ce temps<\/strong> pour votre famille et vous\u00a0!<\/p>\n<p>La famille apporte un entourage (indirectement \u00e7a peut \u00eatre de la confiance en soi), les pauses un souffle pour votre cerveau, le temps \u00e0 soi pour se retrouver, dormir pour ranger les informations dans votre cerveau et manger pour redonner de l&rsquo;\u00e9nergie \u00e0 votre corps\u00a0!<\/p>\n<p>Chacun de ces \u00e9l\u00e9ments va permettre de franchir plus facilement les obstacles de votre parcours en Bootcamp. D&rsquo;o\u00f9 le c\u00f4t\u00e9 strict des horaires que vous vous donnez.<\/p>\n<p>Ainsi apr\u00e8s une matin\u00e9e de lecture de nouveaux cours, vers midi, je m&rsquo;arr\u00eate. Et je passe \u00e0 autre chose\u00a0: le repas, une pause et un repos <strong>allong\u00e9 sur le dos<\/strong> pendant 10 mn environ. Avant de recommencer sur l&rsquo;apr\u00e8s-midi avec les r\u00e9visions de la veille.<\/p>\n<p>Apr\u00e8s 17h\u00a0? Je m&rsquo;arr\u00eate \u00e9galement. Je passe aux t\u00e2ches quotidiennes, de la famille et du repas du soir. Un peu de d\u00e9tente. Un bon gros sommeil de plus de 8h.<\/p>\n<p><strong>Et la journ\u00e9e recommence.<\/strong><\/p>\n<p><strong>Le week-end\u00a0? Pas de formation.<\/strong> La famille, les amis, du temps pour notre conjoint, pour soi, etc. On fait toutes ces choses qui donnent <strong>de l&rsquo;\u00e9nergie<\/strong> et remplissent <strong>notre c\u0153ur<\/strong>, <strong>notre corps<\/strong> et <strong>notre moral<\/strong>.<\/p>\n<p>La partie organisation est tr\u00e8s importante, elle va d\u00e9finir si vous aurez la t\u00eate sous l&rsquo;eau ou pas. Prenez donc le temps de d\u00e9finir tout \u00e7a. Respectez votre planning et les personnes qui vous entourent. Par ailleurs, ce n&rsquo;est qu&rsquo;une seule cl\u00e9 parmi d&rsquo;autres\u00a0: pour que cela fonctionne il est n\u00e9cessaire d&rsquo;\u00eatre efficace lors de sa p\u00e9riode de travail. C&rsquo;est ce que nous allons voir dans le prochain chapitre.<\/p>\n<h1 id=\"travail-efficace\">Travail efficace<\/h1>\n<p>Il n&rsquo;y a pas de secrets selon moi, <strong>un travail efficace est<\/strong> d\u00e9termin\u00e9 par\u00a0:<\/p>\n<ol>\n<li>un <strong>environnement<\/strong> sp\u00e9cifique de travail,<\/li>\n<li>une <strong>technique d&rsquo;assimilation<\/strong> particuli\u00e8re,<\/li>\n<li>et une fa\u00e7on d&rsquo;agir durant <strong>les examens<\/strong> chronom\u00e9tr\u00e9s.<\/li>\n<\/ol>\n<p>Je vais donc vous parler de chacun des ces points en donnant quelques astuces issues d&rsquo;\u00e9tudes faites sur la question.<\/p>\n<h2 id=\"point-1-lenvironnement-de-travail\">Point 1\u00a0: l&rsquo;environnement de travail<\/h2>\n<p>\u00c0 la lecture du livre <a href=\"https:\/\/calnewport.com\/deep-work-rules-for-focused-success-in-a-distracted-world\/\">Deep work, retrouver la concentration dans un monde de distractions<\/a> \u00e9crit par Cal NEWPORT (un professeur dans les sciences informatiques du MIT - rien que \u00e7a!), j&rsquo;ai bien compris qu&rsquo;il fallait un environnement <strong>sans distractions<\/strong>.<\/p>\n<p>Exit les <strong>t\u00e9l\u00e9phones mobiles<\/strong>\u00a0: on le met <strong>sur mode avion<\/strong>. On s&rsquo;enferme dans une pi\u00e8ce, \u00e0 cl\u00e9. On mobilise toutes nos ressources pour se concentrer sur la t\u00e2che \u00e0 accomplir\u00a0: apprendre les cours.<\/p>\n<p><strong>Toute perturbation est \u00e0 proscrire<\/strong> et malheureusement pour certains\u00a0: la musique en fait partie. Il semble que des \u00e9tudes ont d\u00e9termin\u00e9 que cela activait des zones du cerveau qui emp\u00eachent une concentration compl\u00e8te.<\/p>\n<p>L&rsquo;id\u00e9e du livre \u00ab Deep work \u00bb est que <strong>si vous \u00eates concentr\u00e9s vous prendrez moins de temps \u00e0 travailler<\/strong>. Ce qui me semble tout \u00e0 fait adapt\u00e9 pour une formation en Bootcamp, n&rsquo;est-ce pas\u00a0?<\/p>\n<h2 id=\"point-2-assimiler-les-cours\">Point 2\u00a0: assimiler les cours<\/h2>\n<p>L\u00e0 encore, les scientifiques ont \u00e9tudi\u00e9 la question. Et c&rsquo;est au travers d&rsquo;une <strong><a href=\"https:\/\/scienceetonnante.com\/2023\/09\/01\/mieux-apprendre-etudier-les-vraies-techniques-scientifiques\/\">vid\u00e9o de Science \u00e9tonnante concernant les (vraies) techniques pour mieux apprendre et \u00e9tudier<\/a><\/strong> que j&rsquo;ai pu am\u00e9liorer mon apprentissage.<\/p>\n<p>Je vous conseille de regarder la vid\u00e9o - YouTube - et d&rsquo;imaginer une nouvelle fa\u00e7on d&rsquo;\u00e9tudier, de r\u00e9viser et de travailler.<\/p>\n<p>De mon c\u00f4t\u00e9 j&rsquo;ai opt\u00e9 pour <strong>la relecture r\u00e9guli\u00e8re<\/strong>, la lecture <strong>de plusieurs cours<\/strong> qui font souvent <strong>des liens sur le domaine \u00e9tudi\u00e9<\/strong> et <strong>la mise en pratique<\/strong> par des cas concrets de la vie courante pour s&rsquo;entra\u00eener avec les outils \u00e9tudi\u00e9s. J&rsquo;en parlais tout \u00e0 l&rsquo;heure\u00a0: la cr\u00e9ation d&rsquo;une API, l&rsquo;extraction des donn\u00e9es de cette derni\u00e8re pour les introduire ensuite dans une autre base de donn\u00e9es sous un autre format.<\/p>\n<p>\u00c0 noter que plus loin dans cet article je vous demanderais de ne pas suivre de vid\u00e9os YouTube pendant vos \u00e9tudes Bootcamp, je ne suis pas schizophr\u00e8ne\u00a0: consid\u00e9rez cette vid\u00e9o comme l&rsquo;exception qui confirme la r\u00e8gle :-) .<\/p>\n<h2 id=\"point-3-les-examens\">Point 3\u00a0: les examens<\/h2>\n<p>Certains examens sont chronom\u00e9tr\u00e9s. Ce qui peut tr\u00e8s vite \u00eatre d\u00e9stabilisant\u00a0!<\/p>\n<p>Je propose plusieurs choses pour s&rsquo;en sortir\u00a0:<\/p>\n<ol>\n<li>Rien de mieux que <strong>la pr\u00e9paration<\/strong>\u00a0: si vous avez fait les cours r\u00e9guli\u00e8rement et <strong>que vous avez r\u00e9vis\u00e9<\/strong> 1 jour apr\u00e8s, 2 jours apr\u00e8s, 1 semaine apr\u00e8s, vous retiendrez bien mieux les cours ;<\/li>\n<li><strong>Faites des exercices<\/strong> avant et sentez-vous un minimum \u00e0 l&rsquo;aise, <strong>travaillez les points sensibles<\/strong> ;<\/li>\n<li>Lors des examens\u00a0: <strong>ouvrez en avance les documentations<\/strong> qui vous aident ;<\/li>\n<li>Dans les examens, optez pour <strong>des noms de variables courts<\/strong>\u00a0: plus vous rallongez le nom des fonctions et des variables, plus vous aurez \u00e0 taper du code. Indirectement vous prenez un temps fou pour r\u00e9diger au lieu de r\u00e9fl\u00e9chir\u00a0!<\/li>\n<li>Pour les examens, <strong>pr\u00e9parez<\/strong> d\u00e9j\u00e0 \u00e0 c\u00f4t\u00e9 vos codes sur le sujet\u00a0: c&rsquo;est <strong>votre code, celui qui vous ressemble<\/strong>. Rien n&rsquo;interdit un copier-coller de votre propre travail\u00a0!<\/li>\n<\/ol>\n<p>Avec ces 3 points, vous devriez d\u00e9j\u00e0 avoir <strong>de bonnes bases<\/strong> pour aborder la formation. Il y a d&rsquo;autres astuces diverses que nous allons d\u00e9couvrir maintenant.<\/p>\n<h1 id=\"diverses-astuces\">Diverses astuces<\/h1>\n<p>Ne sachant dans quelles cat\u00e9gories mettre ces astuces, mais consid\u00e9rant ces derni\u00e8res comme tout aussi importantes, je vous les d\u00e9pose l\u00e0. Piochez selon vos envies ;)<\/p>\n<ul>\n<li><strong>Revoyez chaque semaine le planning<\/strong> que vous allez suivre\u00a0: quel cours \u00e9tudier chaque jour. Prenez en compte les sujets sur lesquels vous avez d\u00e9j\u00e0 des connaissances pour passer plus de temps sur ceux que vous ne connaissez pas ;<\/li>\n<li>Ne prenez <strong>des notes qu&rsquo;\u00e0 une seconde lecture<\/strong> (lors de r\u00e9visions par exemple)\u00a0: sinon vous allez passer trop de temps sur un cours ;<\/li>\n<li>Si vous avez un <strong>souci logiciel<\/strong> qui emp\u00eache de travailler\u00a0: <strong>passez \u00e0 autre chose<\/strong>\u00a0!<\/li>\n<li>Quand vous revenez sur un souci logiciel - plus tard - si vous ne r\u00e9ussisez pas mieux, <strong>utilisez <a href=\"https:\/\/chat.openai.com\/\">ChatGPT<\/a> pour d\u00e9crire votre probl\u00e8me<\/strong>. Cet outil est plut\u00f4t efficace pour donner des pistes. Rappelez vous, <strong>ChatGPT est un assistant<\/strong>, vous le dirigez. Ce n&rsquo;est pas lui qui doit vous diriger. ;<\/li>\n<li><strong>N&rsquo;utilisez pas de vid\u00e9os pour apprendre<\/strong> - YouTube ou autre - c&rsquo;est une <strong>perte de temps consid\u00e9rable<\/strong>. Pourquoi\u00a0? Tout simplement car vous ne pourrez pas voir rapidement si la vid\u00e9o vous apporte quelque chose de concret ou non. Et parfois ce qu&rsquo;ils disent est tout bonnement faux\u00a0! Cela vous apporte plus de confusion qu&rsquo;autre chose. En revanche chez DataScientest ils ont des vid\u00e9os de cours. Cela apporte g\u00e9n\u00e9ralement quelque chose de concret et de vrai ;<\/li>\n<li><strong>Lisez de la documentation<\/strong> des outils utilis\u00e9s\u00a0: elles ne mentent pas. Elles d\u00e9crivent. Et peut-\u00eatre retiendrez-vous des options qui vous feront gagner plus tard un temps fou\u00a0!<\/li>\n<li><strong>Restez humbles<\/strong>\u00a0: m\u00eame si vous ma\u00eetrisez un sujet, vous trouverez souvent des nouveaut\u00e9s et des d\u00e9tails que vous ne connaissiez pas dans les cours ;<\/li>\n<li>Si vous avez la possibilit\u00e9, <strong>apprenez \u00e0 utiliser des d\u00e9p\u00f4ts Git rapidement pour enregistrer votre travail<\/strong> sur les machines virtuelles distantes propos\u00e9es par la formation. Cela vous \u00e9vitera de mauvaises surprises quand les machines se r\u00e9initialisent\u2026<\/li>\n<\/ul>\n<p>Si jamais je trouve d&rsquo;autres astuces je les rajouterai ici.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Cette formation m&rsquo;a permis de travailler mon environnement, mes m\u00e9thodes de travail et d\u00e9couvrir des astuces pour <strong>\u00eatre plus efficace en journ\u00e9e<\/strong>. Et cela <strong>sans mettre de c\u00f4t\u00e9 la famille<\/strong> et la maison. Ce qui <strong>donne de l&rsquo;\u00e9nergie pour continuer chaque semaine<\/strong> de travailler.<\/p>\n<p><strong>C&rsquo;est un cercle vertueux<\/strong> que je conseille \u00e0 chacun pour am\u00e9liorer son quotidien et <strong>\u00eatre plus productif<\/strong>.<\/p>\n<p>Je souhaite de tout c\u0153ur <strong>que chacun trouve son \u00e9quilibre<\/strong> dans sa vie et r\u00e9ussisse sa formation en Bootcamp. Bonne chance \u00e0 vous et bon travail\u00a0!<\/p>\n"},{"title":"Cookiecutter-trognon pour initialiser mes scripts Bash","link":"https:\/\/olivier.dossmann.net\/2024\/04\/cookiecutter_pour_initialiser_mes_scripts_bash\/","pubDate":"Sun, 28 Apr 2024 13:20:47 +0200","guid":"https:\/\/olivier.dossmann.net\/2024\/04\/cookiecutter_pour_initialiser_mes_scripts_bash\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Pour la petite histoire, l&rsquo;ann\u00e9e derni\u00e8re je lisais l&rsquo;<a href=\"https:\/\/www.deblan.io\/post\/647\/modele-pour-demarrer-script-shell\">article de deblan sur la cr\u00e9ation d&rsquo;un mod\u00e8le de script Shell<\/a> sur le <a href=\"https:\/\/www.journalduhacker.net\/\">journalduhacker<\/a>. Cela m&rsquo;a rappel\u00e9 que t\u00f4t ou tard dans <strong>la qu\u00eate de l&rsquo;automatisation<\/strong> on arrive toujours \u00e0 faire <strong>nos propres mod\u00e8les de scripts<\/strong>.<\/p>\n<p>La plupart de <strong>mes projets utilisent<\/strong> le mod\u00e8le de script <a href=\"https:\/\/github.com\/blankoworld\/trognon\"><strong>Trognon<\/strong><\/a>.<\/p>\n<p>Comment, en partant de ce mod\u00e8le, pourrais-je automatiser la cr\u00e9ation de nouvelles commandes\u00a0? C&rsquo;est l\u00e0 qu&rsquo;entre en jeu <a href=\"https:\/\/cookiecutter.readthedocs.io\/en\/stable\/\">cookiecutter<\/a>, une commande et une m\u00e9thode permettant de <strong>transformer n&rsquo;importe lequel de vos projet en template<\/strong> pour les suivants\u00a0!<\/p>\n<p>Nous commencerons par voir le r\u00e9sultat de l&rsquo;adaptation d&rsquo;un script avec <a href=\"https:\/\/cookiecutter.readthedocs.io\/en\/stable\/\">cookiecutter<\/a>. Ensuite nous verrons ce qu&rsquo;est <a href=\"https:\/\/cookiecutter.readthedocs.io\/en\/stable\/\">cookiecutter<\/a>. Finalement nous expliquerons comment adapter son projet avec <a href=\"https:\/\/cookiecutter.readthedocs.io\/en\/stable\/\">cookiecutter<\/a> en pr\u00e9sentant le cas pratique de <a href=\"https:\/\/github.com\/blankoworld\/cookiecutter-trognon\">cookiecutter-trognon<\/a>.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/objets\/lapins_chocolat.jpg\" alt=\"Des lapins de P\u00e2ques en chocolat, industrialis\u00e9s et dupliqu\u00e9s \u00e0 l&rsquo;infini\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/flickr.com\/photos\/cogdog\/52726715586\/\">profil de Alan Levine sur Flickr<\/a><\/em> sous Domaine public.<\/p>\n<h1 id=\"le-r\u00e9sultat-cookiecutter-trognon\">Le r\u00e9sultat\u00a0: cookiecutter-trognon<\/h1>\n<p>Pour une fois nous commencerons par la fin, c&rsquo;est \u00e0 dire le r\u00e9sultat\u00a0! <strong>L&rsquo;automatisation de Trognon a donn\u00e9 un d\u00e9p\u00f4t<\/strong>\u00a0: <a href=\"https:\/\/github.com\/blankoworld\/cookiecutter-trognon\">cookiecutter-trognon<\/a>.<\/p>\n<p>Gr\u00e2ce \u00e0 lui, cr\u00e9er un nouveau script Bash suivant le mod\u00e8le de Trognon revient \u00e0 taper les commandes suivantes\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Installe cookiecutter sur notre machine (\u00e0 ne faire que la premi\u00e8re fois)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>pipx install cookiecutter\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Utilise mon d\u00e9p\u00f4t Github de cookiecutter-trognon comme mod\u00e8le<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>cookiecutter gh:blankoworld\/cookiecutter-trognon\n<\/span><\/span><\/code><\/pre><\/div><p>Ensuite 2 questions sont pos\u00e9es pour d\u00e9finir\u00a0:<\/p>\n<ul>\n<li>le <strong>nom du dossier<\/strong> dans lequel ma commande se trouvera<\/li>\n<li>le <strong>nom<\/strong> souhait\u00e9 pour la <strong>commande<\/strong><\/li>\n<\/ul>\n<p>\u00c9videmment j&rsquo;ai d\u00e9fini les questions \u00e0 poser et ce que je faisais des r\u00e9ponses dans le r\u00e9sultat. C&rsquo;est l\u00e0 tout l&rsquo;int\u00e9r\u00eat de cookiecutter\u00a0: <strong>vous d\u00e9finissez les questions \u00e0 poser, les r\u00e9ponses possibles<\/strong> s&rsquo;il en est, quoi faire des r\u00e9ponses et <strong>o\u00f9 les utiliser dans vos templates<\/strong>.<\/p>\n<p>Voyons donc cookiecutter.<\/p>\n<h1 id=\"pr\u00e9sentation-de-cookiecutter\">Pr\u00e9sentation de cookiecutter<\/h1>\n<p><a href=\"https:\/\/cookiecutter.readthedocs.io\/en\/stable\/\">cookiecutter<\/a> est <strong>un outil Python qui duplique un r\u00e9pertoire<\/strong>, vous pose des questions et modifie le duplicat en rempla\u00e7ant certaines cha\u00eenes de caract\u00e8res par les r\u00e9ponses donn\u00e9es aux questions.<\/p>\n<p>Il sait agir de mani\u00e8re conditionnelle\u00a0: c&rsquo;est-\u00e0-dire qu&rsquo;il peut aussi <strong>ajouter\/enlever des pans de code\/texte en fonction de la valeur d&rsquo;une variable<\/strong>. Ceci gr\u00e2ce au <a href=\"https:\/\/jinja.palletsprojects.com\/\">moteur de template Jinja<\/a>.<\/p>\n<p>Autrement dit\u00a0: vous d\u00e9finissez les fichiers dont vous avez besoin (parfois un copier\/coller suffit), puis vous ins\u00e9rez dans vos fichiers des cha\u00eenes de caract\u00e8res que vous souhaitez remplacer dans le projet. Vous pouvez \u00e9galement d\u00e9finir des valeurs conditionnelles. Par exemple\u00a0: si je souhaite avoir une licence MIT, alors le fichier LICENSE contiendra un certain texte. Si je choisis la licence <a href=\"https:\/\/fr.wikipedia.org\/wiki\/WTFPL\">LPR\u00c0B\/WTFPL<\/a>, alors j&rsquo;aurais un autre texte.<\/p>\n<p>Un exemple concret serait plus parlant, essayons avec <a href=\"https:\/\/github.com\/blankoworld\/cookiecutter-trognon\">cookiecutter-trognon<\/a>\u00a0!<\/p>\n<h1 id=\"comment-transformer-trognon-en-cookiecutter-trognon\">Comment transformer Trognon en cookiecutter-trognon\u00a0?<\/h1>\n<h2 id=\"en-rapide\">En rapide<\/h2>\n<p>Pour rendre un projet compatible avec cookiecutter, il va falloir suivre quelques \u00e9tapes cl\u00e9s\u00a0:<\/p>\n<ol>\n<li>Cr\u00e9er un <strong>nouveau r\u00e9pertoire<\/strong> pour votre projet,<\/li>\n<li>Y cr\u00e9er un fichier nomm\u00e9 <strong>cookiecutter.json<\/strong>,<\/li>\n<li>Cr\u00e9er <strong>un r\u00e9pertoire nomm\u00e9 <code>{{ cookiecutter.project_slug }}<\/code><\/strong> (oui oui, <strong>AVEC<\/strong> les accolades),<\/li>\n<li><strong>D\u00e9poser les fichiers n\u00e9cessaires<\/strong> au projet <strong>dans le r\u00e9pertoire <code>{{ cookiecutter.project_slug }}<\/code><\/strong>,<\/li>\n<li>Remplacer dans les fichiers tous les mots \u00e0 adapter \u00e0 chaque nouveau projet\u00a0: pour cela utiliser les mots cl\u00e9s fournis dans le fichier <em>cookiecutter.json<\/em> (<strong>variables<\/strong>).<\/li>\n<\/ol>\n<p>Dans le cas de Trognon, j&rsquo;ai d\u00e9pos\u00e9 les fichiers du projet dans le r\u00e9pertoire <code>{{ cookiecutter.project_slug }}<\/code>.<\/p>\n<h2 id=\"cookiecutterjson-la-configuration-du-projet\">cookiecutter.json, la configuration du projet<\/h2>\n<p>J&rsquo;ai ensuite rempli le fichier <code>cookiecutter.json<\/code> tel que montr\u00e9 ici\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-json\" data-lang=\"json\"><span style=\"display:flex;\"><span>{\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">&#34;project_name&#34;<\/span>: <span style=\"color:#e6db74\">&#34;Mon projet&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">&#34;__project_slug&#34;<\/span>: <span style=\"color:#e6db74\">&#34;{{ cookiecutter.project_name.lower().replace(&#39; &#39;, &#39;_&#39;).replace(&#39;-&#39;, &#39;_&#39;) }}&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">&#34;command_shortname&#34;<\/span>: <span style=\"color:#e6db74\">&#34;CommandePrincipale&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">&#34;__command_slug&#34;<\/span>: <span style=\"color:#e6db74\">&#34;{{ cookiecutter.command_shortname.lower().replace(&#39; &#39;, &#39;_&#39;).replace(&#39;-&#39;, &#39;_&#39;) }}&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">&#34;__prompts__&#34;<\/span>: {\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">&#34;project_name&#34;<\/span>: <span style=\"color:#e6db74\">&#34;Choisissez un nom de projet court&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">&#34;command_shortname&#34;<\/span>: <span style=\"color:#e6db74\">&#34;Que voudriez-vous comme nom court de commande ?&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  }\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p><strong>Je d\u00e9fini des variables<\/strong> comme\u00a0:<\/p>\n<ul>\n<li><strong>project_name<\/strong>\u00a0: le nom du projet, avec comme valeur par d\u00e9faut <code>Mon projet<\/code>,<\/li>\n<li><strong>command_shortname<\/strong>\u00a0: le nom de la commande Bash que nous utiliserons.<\/li>\n<\/ul>\n<p>D&rsquo;autres variables ne demanderont rien \u00e0 l&rsquo;utilisateur car elles commencent par deux espaces soulignes <code>__<\/code>. Je parle bien entendu de <code>__project_slug<\/code>, <code>__command_slug<\/code> et <code>__prompts__<\/code>.<\/p>\n<p>Cependant <code>__prompts__<\/code> a une particularit\u00e9\u00a0: il permet de <strong>personnaliser la question pos\u00e9e<\/strong> pour chaque variable.<\/p>\n<p>Ainsi pour d\u00e9finir <code>project_name<\/code>, l&rsquo;utilisateur verra la question suivante\u00a0: <code>Choisissez un nom de projet court<\/code>.<\/p>\n<h2 id=\"remplacer-des-mots-par-des-variables\">Remplacer des mots par des variables<\/h2>\n<p>Maintenant que le fichier <code>cookiecutter.json<\/code> poss\u00e8de des mots cl\u00e9s sp\u00e9cifiques (des variables), nous allons pouvoir modifier les fichiers de notre projet pour contenir des \u00ab\u00a0mots-cl\u00e9s\u00a0\u00bb. Par exemple voici le d\u00e9but du fichier <em>README.md<\/em> de Trognon (<strong>AVANT<\/strong> modifications)\u00a0:<\/p>\n<pre tabindex=\"0\"><code># Trognon\n\nUn lanceur de commandes personnalis\u00e9es pour votre projet de d\u00e9veloppement.\n\nExemple : \n\n# Affiche l&#39;aide\n.\/agnes help\n\n# Installe votre projet (vous devez d\u00e9finir ce que `install` fait)\n.\/agnes install\n\n# cr\u00e9\u00e9 un dump de la base de donn\u00e9es\n.\/agnes dump\n<\/code><\/pre><p>Ce qui donne le fichier README.md suivant dans notre projet cookiecutter-trognon (<strong>APR\u00c8S<\/strong> modifications)\u00a0:<\/p>\n<pre tabindex=\"0\"><code># {{ cookiecutter.project_name }}\n\nUn lanceur de commandes personnalis\u00e9es pour votre projet de d\u00e9veloppement.\n\nExemple : \n\n# Affiche l&#39;aide\n.\/{{ cookiecutter.__command_slug }} help\n\n# Installe votre projet (vous devez d\u00e9finir ce que `install` fait)\n.\/{{ cookiecutter.__command_slug }} install\n\n# cr\u00e9\u00e9 un dump de la base de donn\u00e9es\n.\/{{ cookiecutter.__command_slug }} dump\n<\/code><\/pre><p>Nous avons ainsi remplac\u00e9 respectivement le titre du fichier et les commandes d&rsquo;exemple en <code>{{ cookiecutter.project_name }}<\/code> et <code>{{ cookiecutter.__command_slug }}<\/code>.<\/p>\n<p>Vous pouvez en savoir plus sur <a href=\"https:\/\/cookiecutter.readthedocs.io\/en\/stable\/index.html\">la documentation officielle de cookiecutter<\/a>.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>J&rsquo;esp\u00e8re qu&rsquo;\u00e0 travers cet article vous aurez compris l&rsquo;<strong>avantage d&rsquo;utiliser cookiecutter<\/strong> et que vous <strong>envisagerez \u00e0 l&rsquo;avenir de l&rsquo;int\u00e9grer dans votre processus d&rsquo;automatisation<\/strong> de la g\u00e9n\u00e9ration initiale de vos projets et de vos scripts.<\/p>\n<p>L&rsquo;informatique \u00e9nonce souvent d&rsquo;\u00e9viter de r\u00e9inventer la roue ; je pense que <strong>cookiecutter est l&rsquo;outil indispensable<\/strong> pour favoriser ce concept.<\/p>\n<p>Une fois ce processus de cr\u00e9ation automatis\u00e9, vous vous rendrez compte que vous g\u00e9n\u00e9rerez plus souvent des scripts\/projets, que vous vous concentrerez uniquement sur le contenu du projet et pas sur tout l&rsquo;environnement autour\u00a0: c&rsquo;est un gain de temps et de qualit\u00e9\u00a0! Une <strong>belle valeur ajout\u00e9e<\/strong> en somme.<\/p>\n"},{"title":"Vers quelle console se tourner en 2024 ?","link":"https:\/\/olivier.dossmann.net\/2024\/04\/vers-quelle-console-se-tourner-en-2024\/","pubDate":"Wed, 10 Apr 2024 16:24:18 +0200","guid":"https:\/\/olivier.dossmann.net\/2024\/04\/vers-quelle-console-se-tourner-en-2024\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Apr\u00e8s plusieurs \u00e9changes similaires avec des amis, il \u00e9tait temps de lister ce que je trouve int\u00e9ressant et\/ou probl\u00e9matique dans chacune des 3 consoles suivantes\u00a0:<\/p>\n<ul>\n<li><strong>Nintendo Switch<\/strong> (Switch)<\/li>\n<li><strong>Microsoft Xbox Serie X<\/strong> (Xbox)<\/li>\n<li><strong>Sony Playstation 5 Standard FAT<\/strong> (PS5)<\/li>\n<\/ul>\n<p>Ceci est mon avis, cela repr\u00e9sente <strong>ce que je pense<\/strong> sur ces derni\u00e8res. Il reste <strong>\u00e0 chacun de juger selon lui et suivant ses infos<\/strong>.<\/p>\n<p>Je regarde essentiellement le co\u00fbt de ces consoles et ce que nous avons \u00e0 disposition. Si vous d\u00e9testez voir des chiffres d\u00e9filer, arr\u00eatez la lecture de cet article &#x1f609;. Par ailleurs chaque console repr\u00e9sente un \u00e9cosyst\u00e8me entier, il est difficile d&rsquo;\u00eatre r\u00e9ellement objectif dans leur comparaison, tant ils sont diff\u00e9rents.<\/p>\n<p>Alerte spoiler : Cet article sera long.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/objets\/rouages_lumineux.jpg\" alt=\"Rouages lumineux\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/flickr.com\/photos\/adouea\/43889619445\/\">profil de Aur\u00e9lien Adoue sur Flickr<\/a><\/em> sous licence CC BY 2.0.<\/p>\n<h1 id=\"avant-de-commencer\">Avant de commencer<\/h1>\n<p>Dans cet article je comparerais plusieurs \u00e9l\u00e9ments comme :<\/p>\n<ul>\n<li>le <strong>prix d&rsquo;achat<\/strong> de la console<\/li>\n<li>l&rsquo;<strong>abonnement<\/strong> pour b\u00e9n\u00e9ficier de sauvegardes de ses parties (et parfois l&rsquo;acc\u00e8s \u00e0 un catalogue de jeux)<\/li>\n<li>la <strong>quantit\u00e9 de jeux disponibles<\/strong> (la vari\u00e9t\u00e9 du catalogue de jeux vid\u00e9os par exemple)<\/li>\n<li>le <strong>prix des jeux d&rsquo;occasion<\/strong>, ce qui permet d&rsquo;estimer l&rsquo;acc\u00e8s aux jeux pour les petits budgets<\/li>\n<li>les <strong>probl\u00e8mes mat\u00e9riels<\/strong> connus qui peuvent engendrer un rachat de la console ou bien des accessoires<\/li>\n<li>le <strong>prix d&rsquo;une manette<\/strong><\/li>\n<\/ul>\n<p>Je cherche plus ou moins \u00e0 savoir ce qui pourrait - <strong>\u00e9conomiquement parlant<\/strong> - \u00eatre plus accessible. Autrement dit sur quelle console pourrais-je estimer pouvoir <strong>d\u00e9penser le moins d&rsquo;argent par an pour jouer<\/strong> et renouveler mon catalogue de jeux jou\u00e9s ? \u00c9videmment <strong>les prix fluctuent beaucoup<\/strong>, notamment pour les consoles PS5 et Xbox qui varient parfois de 50\u20ac \u00e0 100\u20ac. \u00c0 vos calculatrices !<\/p>\n<p>Nous commencerons avec la Nintendo Switch, puis la Microsoft Xbox Serie X et enfin la Sony Playstation 5 Standard FAT. <strong>Ce sont 3 consoles que j&rsquo;ai eu en main, que j&rsquo;ai pu tester<\/strong> et o\u00f9 j&rsquo;ai pu exp\u00e9rimenter quelques \u00e9l\u00e9ments <strong>entre 2 et 5 ans<\/strong> (suivant les consoles).<\/p>\n<p>J&rsquo;insiste sur les mod\u00e8les disposant d&rsquo;un compartiment pour accueillir les jeux vid\u00e9os (lecteur DVD, slot pour des cartouches de jeu, etc.) car <strong>cela permet d&rsquo;avoir acc\u00e8s \u00e0 des jeux d&rsquo;occasion<\/strong> (donc co\u00fbter moins que les jeux neufs).<\/p>\n<p>Les 3 mod\u00e8les vis\u00e9s\/test\u00e9s <strong>se branchent au minimum sur une t\u00e9l\u00e9vision<\/strong>.<\/p>\n<p>On part du principe qu&rsquo;<strong>on joue environ 500 heures par an<\/strong>, c&rsquo;est \u00e0 dire <strong>environ 1h30 par jour<\/strong>. Si les jeux prennent une cinquantaine d&rsquo;heures \u00e0 finir, on consommerait <strong>10 jeux par an<\/strong>. Tout ceci est relatif et doit bien \u00eatre pris en compte dans <strong>vos calculs<\/strong>. C&rsquo;est int\u00e9ressant de faire le calcul pour soi, histoire de voir si le jeu vid\u00e9o est un loisir tr\u00e8s d\u00e9pensier dans VOTRE budget. Mine de rien, \u00e7a consomme de l&rsquo;argent tout cela, comme nous le verrons ci-apr\u00e8s !<\/p>\n<h1 id=\"nintendo-switch\">Nintendo Switch<\/h1>\n<h2 id=\"les-chiffres\">Les chiffres<\/h2>\n<p>Commen\u00e7ons par la plus ancienne console sur le march\u00e9 : la Nintendo Switch.<\/p>\n<ul>\n<li>Prix d&rsquo;<strong>achat<\/strong> (sur Amazon) : <strong>~ 250\u20ac<\/strong><\/li>\n<li>Prix <strong>abonnement annuel<\/strong> minimum : <strong>~ 20\u20ac<\/strong><\/li>\n<li>Avantage abonnement : <strong>jeux NES, SNES et Gameboy (color)<\/strong> (~ 30 jeux)<\/li>\n<li>Abonnement familial : <strong>~ 35\u20ac<\/strong><\/li>\n<li>Manettes disponibles \u00e0 l&rsquo;achat : <strong>2<\/strong> (gauche et droite, m\u00eame si petites)<\/li>\n<li>Particularit\u00e9 : <strong>utilisable en console portable, prend peu de place<\/strong> (~ 3h30 d&rsquo;autonomie)<\/li>\n<li>Prix d&rsquo;un jeu neuf : <strong>~ 50\u20ac<\/strong><\/li>\n<li>Prix du m\u00eame jeu en occasion : <strong>~30<\/strong> (varie en fonction des r\u00e9gions et de l&rsquo;interlocuteur + capacit\u00e9 \u00e0 discuter le prix)<\/li>\n<li>Prix d&rsquo;une manette (simple) : <strong>~ 45\u20ac<\/strong> \u00e0 l&rsquo;unit\u00e9, ou <strong>~ 65\u20ac<\/strong> les deux<\/li>\n<\/ul>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/materiel\/nintendo_switch_detail.jpg\" alt=\"Console Nintento Switch\u2122\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/www.flickr.com\/photos\/linsinchen\/39571683174\/\">profil de Sinchen.Lin sur Flickr<\/a><\/em> sous licence CC BY 2.0.<\/p>\n<h2 id=\"avantages\">Avantage(s)<\/h2>\n<p>L&rsquo;avantage principal de cette console : elle est aussi <strong>portable<\/strong>. C&rsquo;est \u00e0 dire qu&rsquo;elle s&rsquo;enl\u00e8ve du socle de la t\u00e9l\u00e9vision pour \u00eatre utilisable partout dans la maison (ou ailleurs). Sa <strong>batterie tient au moins 3h30<\/strong>.<\/p>\n<p>Elle est <strong>relativement petite<\/strong> pour une console de salon, ce qui fait un gain de place.<\/p>\n<p>Le <strong>catalogue des jeux est un des plus riches<\/strong> et des plus complets. Avec des <strong>jeux vari\u00e9s<\/strong>\u00a0: il y en a pour tous les go\u00fbts ! Certaines exclusivit\u00e9s d&rsquo;autres consoles sont port\u00e9s sur la Nintendo Switch, ce qui est tr\u00e8s plaisant.<\/p>\n<h2 id=\"inconv\u00e9nients\">Inconv\u00e9nient(s)<\/h2>\n<p>Du fait de la \u00ab petitesse \u00bb de cette console, il y a une <strong>limitation sur le mat\u00e9riel embarqu\u00e9<\/strong>, notamment pour ce qui est du refroidissement de la carte graphique. Ceci implique des graphismes moins proches de la r\u00e9alit\u00e9 (compar\u00e9 \u00e0 d&rsquo;autres consoles).<\/p>\n<p>Cependant sachez qu&rsquo;au lieu de se concentrer sur la parfaite illusion de r\u00e9alit\u00e9 dans les jeux vid\u00e9os, les concepteurs de jeu peuvent <strong>s&rsquo;atteler \u00e0 la jouabilit\u00e9<\/strong>.<\/p>\n<p>Autre point n\u00e9gatif : le probl\u00e8me connu dit du \u00ab <strong>Joy-Con Drift<\/strong> \u00bb. Apr\u00e8s un certain temps d&rsquo;utilisation, <strong>il arrive que la manette gauche pose probl\u00e8me<\/strong> : le personnage que vous dirigez dans le jeu bouge tout seul. Bien que ce soit probl\u00e9matique, au lieu de racheter une manette <strong>il existe une solution \u00ab maison \u00bb<\/strong> tel que vous pouvez le <a href=\"https:\/\/www.tomsguide.fr\/joy-con-drift-un-bout-de-carton-suffit-a-reparer-la-manette-de-la-nintendo-switch\/\">lire dans l&rsquo;article expliquant la solution avec\u2026 un bout de carton<\/a> ! <strong>Cette solution ne fonctionnera pas avec une Nintendo Switch Lite<\/strong> dont les manettes ne sont pas d\u00e9tachables.<\/p>\n<h2 id=\"au-minimum-sans-jeux\">Au minimum (sans jeux)<\/h2>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>270\u20ac<\/strong> (250\u20ac HA + 20\u20ac abonnement simple)<\/li>\n<li>ann\u00e9es suivantes : <strong>20\u20ac<\/strong> (20\u20ac abonnement simple)<\/li>\n<\/ul>\n<h2 id=\"au-minimum-avec-ha-de-jeux\">Au minimum (avec HA de jeux)<\/h2>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>770\u20ac<\/strong> (250\u20ac HA + 20\u20ac abonnement simple + 10 jeux neufs \u00d7 50\u20ac)<\/li>\n<li>ann\u00e9es suivantes : <strong>520\u20ac<\/strong> (20\u20ac abonnement simple + 10 jeux neufs \u00d7 50\u20ac)<\/li>\n<\/ul>\n<h2 id=\"minimum-familial\">Minimum familial<\/h2>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>785\u20ac<\/strong> (250\u20ac HA + 35\u20ac abonnement familial + 10 jeux neufs \u00d7 50\u20ac)<\/li>\n<li>ann\u00e9es suivantes : <strong>535\u20ac<\/strong> (35\u20ac abonnement familial + 10 jeux neufs \u00d7 50\u20ac)<\/li>\n<\/ul>\n<h2 id=\"au-maximum-familial--pack-additionnel-\u00e0-70an\">Au maximum (familial + pack additionnel \u00e0 70\u20ac\/an)<\/h2>\n<p>Avec le pack additionnel vous avez, en plus des jeux NES, SNES et GameBoy (color), un acc\u00e8s \u00e0 :<\/p>\n<ul>\n<li>des jeux GameBoy Advance<\/li>\n<li>des jeux Nintendo 64<\/li>\n<li>des jeux Mega Drive<\/li>\n<\/ul>\n<p>Ainsi :<\/p>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>320\u20ac<\/strong> (250\u20acHA + 70\u20ac abonnement familial + pack additionnel)<\/li>\n<li>ann\u00e9es suivantes : <strong>70\u20ac<\/strong> (70\u20ac abonnement familial + pack additionnel)<\/li>\n<\/ul>\n<h2 id=\"conclusion-interm\u00e9diaire---switch\">Conclusion interm\u00e9diaire - Switch<\/h2>\n<p>On notera :<\/p>\n<ul>\n<li>la diff\u00e9rence <strong>n\u00e9gligeable<\/strong> entre abonnement simple et familial : tr\u00e8s rentable quand nous sommes plusieurs \u00e0 la maison (m\u00eame avec chacun une console)<\/li>\n<li>le <strong>pack aditionnel<\/strong> (abonnement suppl\u00e9mentaire) permet de jouer \u00e0 des jeux r\u00e9tros <strong>mais pas avec des jeux modernes<\/strong><\/li>\n<li>le catalogue (Nintendo eShop) est <strong>large, vari\u00e9, avec des r\u00e9ductions fr\u00e9quentes de 80%<\/strong> sur de bons jeux<\/li>\n<li>vous jouez sur la TV et quelqu&rsquo;un de la famille voudrait regarder la TV ? Pas de souci : vous enlevez la console du socle et pouvez continuer votre partie ailleurs. Le c\u00f4t\u00e9 portable de cette console est tr\u00e8s pratique !<\/li>\n<\/ul>\n<p>La Nintendo Switch est donc une console bas prix avec beaucoup de possibilit\u00e9s !<\/p>\n<h1 id=\"microsoft-xbox-s\u00e9rie-x\">Microsoft Xbox S\u00e9rie X<\/h1>\n<h2 id=\"les-chiffres-1\">Les chiffres<\/h2>\n<ul>\n<li>Prix d&rsquo;<strong>achat<\/strong> (sur Amazon) : <strong>~ 550\u20ac<\/strong><\/li>\n<li>Prix <strong>abonnement annuel<\/strong> minimum : <strong>~ 84\u20ac<\/strong> (7\u20ac \u00d7 12 mois)<\/li>\n<li>Avantage abonnement : <strong>petit catalogue de jeux<\/strong> (~ 25 jeux)<\/li>\n<li>Abonnement familial : <strong>aucun<\/strong><\/li>\n<li>Manettes disponibles \u00e0 l&rsquo;achat : <strong>1<\/strong><\/li>\n<li>Particularit\u00e9 : <strong>prend peu de place, m\u00eame debout<\/strong><\/li>\n<li>Prix d&rsquo;un jeu neuf : <strong>~80\u20ac<\/strong><\/li>\n<li>Prix du m\u00eame jeu en occasion : <strong>~40<\/strong> (varie en fonction des r\u00e9gions et de l&rsquo;interlocuteur + capacit\u00e9 \u00e0 discuter le prix)<\/li>\n<li>Prix d&rsquo;une manette : <strong>~ 60\u20ac<\/strong><\/li>\n<\/ul>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/materiel\/xbox_serie_x.jpg\" alt=\"Console Xbox S\u00e9rie X avec une manette\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/flickr.com\/photos\/144333438@N07\/49626743158\/\">profil de ru3ch initiative sur Flickr<\/a><\/em> sous licence CC BY 2.0.<\/p>\n<h2 id=\"avantages-1\">Avantage(s)<\/h2>\n<ul>\n<li>console <strong>puissante<\/strong>, ce qui permet d&rsquo;acc\u00e9der \u00e0 des <strong>images plus fines<\/strong> et proches de la r\u00e9alit\u00e9<\/li>\n<li><strong>esth\u00e9tique<\/strong> de la console, tout en prenant <strong>peu de place<\/strong> ET <strong>passe inaper\u00e7ue dans le salon<\/strong> (vous ne passerez pas pour un gosse en soir\u00e9e &#x1f923;).<\/li>\n<li><strong>tout repose sur l&rsquo;abonnement au Game Pass<\/strong> : acc\u00e8s illimit\u00e9 pour <strong>11\u20ac\/mois<\/strong> \u00e0 une quantit\u00e9 incroyable de jeux. Vous trouverez toujours votre bonheur.<\/li>\n<li>acc\u00e8s d\u00e8s le premier jour \u00e0 des <strong>jeux d\u00e8s leur sortie<\/strong> (Halo, Starfield, Sea of thieves, etc.)<\/li>\n<\/ul>\n<h2 id=\"inconv\u00e9nients-1\">Inconv\u00e9nient(s)<\/h2>\n<ul>\n<li>le <strong>budget d&rsquo;achat<\/strong> initial est <strong>cons\u00e9quent<\/strong> (550\u20ac)<\/li>\n<li>jeux <strong>neufs tr\u00e8s chers<\/strong><\/li>\n<li>pour jouer <strong>en ligne : abonnement n\u00e9cessaire<\/strong> (7\u20ac\/mois minimum)<\/li>\n<\/ul>\n<h2 id=\"au-minimum-sans-jeux-1\">Au minimum (sans jeux)<\/h2>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>634\u20ac<\/strong> (550\u20ac HA + 84\u20ac abonnement)<\/li>\n<li>ann\u00e9es suivantes : <strong>84\u20ac<\/strong> (84\u20ac abonnement)<\/li>\n<\/ul>\n<h2 id=\"au-minimum-avec-ha-de-jeux-1\">Au minimum (avec HA de jeux)<\/h2>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>1434\u20ac<\/strong> (550\u20ac HA + 84\u20ac abonnement + 10 jeux neufs \u00d7 80\u20ac)<\/li>\n<li>ann\u00e9es suivantes : <strong>884\u20ac<\/strong> (84\u20ac abonnement + 10 jeux neufs \u00d7 80\u20ac)<\/li>\n<\/ul>\n<h2 id=\"au-maximum-abonnement-game-pass-console-\u00e0-11mois\">Au maximum (abonnement Game Pass Console \u00e0 11\u20ac\/mois)<\/h2>\n<p>Avec l&rsquo;achat d&rsquo;un abonnement - complet - donnant acc\u00e8s \u00e0 un catalogue de plus de 100 jeux vid\u00e9os, soit <strong>11\u20ac \u00d7 12 mois = 132\u20ac<\/strong>. Ceci enl\u00e8ve le besoin d&rsquo;avoir 10 jeux neufs par an\u2026<\/p>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>682\u20ac<\/strong> (550\u20ac HA + 132\u20ac abonnement Game Pass Console)<\/li>\n<li>ann\u00e9es suivantes : <strong>132\u20ac<\/strong> (132\u20ac abonnement Game Pass Console)<\/li>\n<\/ul>\n<h2 id=\"conclusion-interm\u00e9diaire---xbox-x\">Conclusion interm\u00e9diaire - Xbox X<\/h2>\n<p>Le catalogue de jeux vid\u00e9os du <strong>Game Pass Console (\u00e0 11\u20ac\/mois)<\/strong> donne non seulement acc\u00e8s \u00e0 beaucoup de jeux vid\u00e9os, mais il permet en plus d&rsquo;<strong>acc\u00e9der \u00e0 la plupart des sorties de jeux vid\u00e9os d\u00e8s le premier jour<\/strong> !<\/p>\n<p>C&rsquo;est <strong>ce qui fait toute la force de cette console\u00a0:<\/strong> tant que <strong>le Game Pass Console<\/strong> existe, l&rsquo;acc\u00e8s \u00e0 des jeux vid\u00e9os co\u00fbte 132\u20ac par an - except\u00e9 toute modification du prix \u00e9videmment.<\/p>\n<p>Si vous consommez <strong>plus de 2 jeux par an, la Xbox Serie X sera tr\u00e8s rentable d\u00e8s les premi\u00e8res ann\u00e9es<\/strong>.<\/p>\n<p>En revanche, je trouve que <strong>sans l&rsquo;abonnement Game Pass Console<\/strong> (11\u20ac\/mois), <strong>la console est co\u00fbteuse<\/strong>. Cela revient \u00e0 acheter une brique de 550\u20ac \u00e0 poser dans le salon sans jamais y toucher, avec des jeux horriblement chers.<\/p>\n<h1 id=\"sony-playstation-5-fat\">Sony Playstation 5 FAT<\/h1>\n<h2 id=\"les-chiffres-2\">Les chiffres<\/h2>\n<ul>\n<li>Prix d&rsquo;<strong>achat<\/strong> (sur Amazon) : <strong>~ 525\u20ac<\/strong><\/li>\n<li>Prix <strong>abonnement annuel<\/strong> minimum : <strong>~ 72\u20ac<\/strong><\/li>\n<li>Avantage abonnement : <strong>3 jeux par mois<\/strong> (~ 36 jeux sur l&rsquo;ann\u00e9e)<\/li>\n<li>Abonnement familial : <strong>aucun<\/strong><\/li>\n<li>Manettes disponibles \u00e0 l&rsquo;achat : <strong>1<\/strong><\/li>\n<li>Particularit\u00e9 : <strong>tr\u00e8s puissante pour les graphismes<\/strong><\/li>\n<li>Prix d&rsquo;un jeu neuf : <strong>~80\u20ac<\/strong><\/li>\n<li>Prix du m\u00eame jeu en occasion : <strong>~40<\/strong> (varie en fonction des r\u00e9gions et de l&rsquo;interlocuteur + capacit\u00e9 \u00e0 discuter le prix)<\/li>\n<li>Prix d&rsquo;une manette : <strong>~ 70\u20ac<\/strong><\/li>\n<\/ul>\n<p>\u00c0 noter : Le prix d&rsquo;une Playstation 5 SLIM est quasi similaire (525\u20ac).<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/materiel\/sony_ps5.jpg\" alt=\"Console Playstation 5 Standard FAT\"><\/p>\n<h2 id=\"avantages-2\">Avantage(s)<\/h2>\n<ul>\n<li>console <strong>puissante<\/strong>, ce qui permet d\u2019acc\u00e9der \u00e0 des <strong>images plus fines<\/strong> et proches de la r\u00e9alit\u00e9<\/li>\n<li>la plupart des <strong>jeux PS4 fonctionnent<\/strong> sur cette console avec une am\u00e9lioration graphique<\/li>\n<li><strong>rapidit\u00e9 de chargement<\/strong> des niveaux, gr\u00e2ce \u00e0 un disque dur SSD embarqu\u00e9<\/li>\n<\/ul>\n<h2 id=\"inconv\u00e9nients-2\">Inconv\u00e9nient(s)<\/h2>\n<ul>\n<li>le <strong>budget d&rsquo;achat<\/strong> initial est <strong>cons\u00e9quent<\/strong> (525\u20ac)<\/li>\n<li>jeux <strong>neufs tr\u00e8s chers<\/strong><\/li>\n<li>pour jouer <strong>en ligne : abonnement n\u00e9cessaire<\/strong> (6\u20ac\/mois minimum)<\/li>\n<li>le <a href=\"https:\/\/www.lesnumeriques.com\/joypad\/pourquoi-le-drift-de-la-manette-ps5-dualsense-n-est-pas-si-surprenant-n160739.html\">DualSense Drift de la manette de PS5<\/a> qui <strong>r\u00e9duit la dur\u00e9e de vie d&rsquo;une manette \u00e0 417h<\/strong>, soit environ <strong>7 mois d&rsquo;utilisation<\/strong> (\u00e0 raison de 2h par jour) - sans possibilit\u00e9 de r\u00e9paration pour le moment<\/li>\n<li>un <a href=\"https:\/\/www.numerama.com\/pop-culture\/1232326-utiliser-sa-ps5-a-la-verticale-est-il-dangereux-pas-si-vite.html\">potentiel probl\u00e8me de liquide de refroidissment sur la PS5 ?<\/a> sur une petite partie des consoles<\/li>\n<li><strong>prend une place monstrueuse<\/strong> au salon<\/li>\n<\/ul>\n<h2 id=\"au-minimum-sans-jeux-2\">Au minimum (sans jeux)<\/h2>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>597\u20ac<\/strong> (525\u20ac HA + 72\u20ac abonnement)<\/li>\n<li>ann\u00e9es suivantes : <strong>72\u20ac<\/strong> (72\u20ac abonnement)<\/li>\n<\/ul>\n<h2 id=\"au-minimum-avec-ha-de-jeux-2\">Au minimum (avec HA de jeux)<\/h2>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>1397\u20ac<\/strong> (525\u20ac HA + 72\u20ac abonnement + 10 jeux neufs \u00d7 80\u20ac)<\/li>\n<li>ann\u00e9es suivantes : <strong>872\u20ac<\/strong> (72\u20ac abonnement + 10 jeux neufs \u00d7 80\u20ac)<\/li>\n<\/ul>\n<h2 id=\"au-maximum-abonnement-premium-\u00e0-152an\">Au maximum (abonnement Premium \u00e0 ~152\u20ac\/an)<\/h2>\n<p>L&rsquo;abonnement Premium donne acc\u00e8s \u00e0 un bon catalogue de jeux vid\u00e9os. Cependant vous n&rsquo;aurez pas acc\u00e8s aux sorties des jeux vid\u00e9os : il faudra patienter quelques ann\u00e9es \u00e0 chaque fois.<\/p>\n<ul>\n<li>1<sup>\u00e8re<\/sup> ann\u00e9e : <strong>677\u20ac<\/strong> (525\u20ac HA + 152\u20ac abonnement Premium)<\/li>\n<li>ann\u00e9es suivantes : <strong>152\u20ac<\/strong> (152\u20ac abonnement Premium)<\/li>\n<\/ul>\n<h2 id=\"conclusion-interm\u00e9diaire---ps5\">Conclusion interm\u00e9diaire - PS5<\/h2>\n<p>Se tourner vers cette console para\u00eet curieux, \u00e0 moins que vous n&rsquo;<strong>\u00eates un grand fan de Playstation<\/strong> - comme je l&rsquo;ai \u00e9t\u00e9 de la PS1 \u00e0 la PS4 - et que vous appr\u00e9ciez \u00e0 la fois la qualit\u00e9 d&rsquo;image et des <strong>jeux en exclusivit\u00e9<\/strong> sur cette console.<\/p>\n<p>En effet, malgr\u00e9 la qualit\u00e9 des jeux vid\u00e9os (souvent \u00e0 1 seul joueur), ces jeux ont un <strong>prix \u00e9lev\u00e9<\/strong>. On se tournerait alors vers l&rsquo;abonnement premium (152\u20ac\/an) qui est \u00ab rentable \u00bb si vous consommez plus de 2 jeux par an.<\/p>\n<p>Cependant <strong>l&rsquo;abonnement Premium ne permet pas d&rsquo;acc\u00e9der r\u00e9guli\u00e8rement \u00e0 des nouveaut\u00e9s<\/strong> (comprendre des jeux qui viennent de sortir), il y a une forme de patience \u00e0 avoir.<\/p>\n<p>Si vous achetez 1 jeu neuf et l&rsquo;abonnement pour jouer en ligne, cela vous revient au prix de l&rsquo;abonnement Premium.<\/p>\n<p>Au final il <strong>bien trop d&rsquo;inconv\u00e9nients \u00e0 cette console<\/strong> pour si peu d&rsquo;avantages compar\u00e9 \u00e0 la concurrence : que se passe-t-il chez Sony ? &#x1f914;<\/p>\n<h1 id=\"r\u00e9capitulatif-des-chiffres\">R\u00e9capitulatif des chiffres<\/h1>\n<table>\n  <thead>\n      <tr>\n          <th><\/th>\n          <th>Switch<\/th>\n          <th>Switch OLED<\/th>\n          <th>Switch Lite<\/th>\n          <th>Xbox S<\/th>\n          <th>Xbox X<\/th>\n          <th>PS5 FAT<\/th>\n          <th>PS5 Slim<\/th>\n          <th>PS5 Digital Slim<\/th>\n      <\/tr>\n  <\/thead>\n  <tbody>\n      <tr>\n          <td>Achat<\/td>\n          <td>250 \u20ac<\/td>\n          <td>300 \u20ac<\/td>\n          <td>200 \u20ac<\/td>\n          <td>300 \u20ac<\/td>\n          <td>550 \u20ac<\/td>\n          <td>525 \u20ac<\/td>\n          <td>550 \u20ac<\/td>\n          <td>450 \u20ac<\/td>\n      <\/tr>\n      <tr>\n          <td>Abonnement<\/td>\n          <td>20 \u20ac<\/td>\n          <td>20 \u20ac<\/td>\n          <td>20 \u20ac<\/td>\n          <td>84 \u20ac<\/td>\n          <td>84 \u20ac<\/td>\n          <td>72 \u20ac<\/td>\n          <td>72 \u20ac<\/td>\n          <td>72 \u20ac<\/td>\n      <\/tr>\n      <tr>\n          <td>Abo. Familial (switch)<\/td>\n          <td>35 \u20ac<\/td>\n          <td>35 \u20ac<\/td>\n          <td>35 \u20ac<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n      <\/tr>\n      <tr>\n          <td>Game Pass Console (xbox)<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>132 \u20ac<\/td>\n          <td>132 \u20ac<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n      <\/tr>\n      <tr>\n          <td>Abo. Premium (PS5)<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>Aucun<\/td>\n          <td>152 \u20ac<\/td>\n          <td>152 \u20ac<\/td>\n          <td>152 \u20ac<\/td>\n      <\/tr>\n      <tr>\n          <td>Jeu neuf<\/td>\n          <td>50 \u20ac<\/td>\n          <td>50 \u20ac<\/td>\n          <td>50 \u20ac<\/td>\n          <td>80 \u20ac<\/td>\n          <td>80 \u20ac<\/td>\n          <td>80 \u20ac<\/td>\n          <td>80 \u20ac<\/td>\n          <td>80 \u20ac<\/td>\n      <\/tr>\n      <tr>\n          <td>Jeu occasion<\/td>\n          <td>30 \u20ac<\/td>\n          <td>30 \u20ac<\/td>\n          <td>30 \u20ac<\/td>\n          <td>40 \u20ac<\/td>\n          <td>40 \u20ac<\/td>\n          <td>40 \u20ac<\/td>\n          <td>40 \u20ac<\/td>\n          <td>40 \u20ac<\/td>\n      <\/tr>\n      <tr>\n          <td>Manette<\/td>\n          <td>45 \u20ac<\/td>\n          <td>45 \u20ac<\/td>\n          <td>45 \u20ac<\/td>\n          <td>60 \u20ac<\/td>\n          <td>60 \u20ac<\/td>\n          <td>70 \u20ac<\/td>\n          <td>70 \u20ac<\/td>\n          <td>70 \u20ac<\/td>\n      <\/tr>\n      <tr>\n          <td><strong>Totaux\u00a0:<\/strong><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n      <\/tr>\n      <tr>\n          <td>1<sup>\u00e8re<\/sup> ann\u00e9e (<strong>HA + abo<\/strong>)<\/td>\n          <td><strong>270 \u20ac<\/strong><\/td>\n          <td><strong>320 \u20ac<\/strong><\/td>\n          <td><strong>220 \u20ac<\/strong><\/td>\n          <td><strong>384 \u20ac<\/strong><\/td>\n          <td><strong>634 \u20ac<\/strong><\/td>\n          <td><strong>597 \u20ac<\/strong><\/td>\n          <td><strong>622 \u20ac<\/strong><\/td>\n          <td><strong>522 \u20ac<\/strong><\/td>\n      <\/tr>\n      <tr>\n          <td>Nbre jeux rentabilit\u00e9 (<strong>Abo<\/strong>)<\/td>\n          <td><strong>0,4<\/strong><\/td>\n          <td><strong>0,4<\/strong><\/td>\n          <td><strong>0,4<\/strong><\/td>\n          <td><strong>1,05<\/strong><\/td>\n          <td><strong>1,05<\/strong><\/td>\n          <td><strong>0,9<\/strong><\/td>\n          <td><strong>0,9<\/strong><\/td>\n          <td><strong>0,9<\/strong><\/td>\n      <\/tr>\n      <tr>\n          <td>1<sup>\u00e8re<\/sup> ann\u00e9e (<strong>HA + abo. Prem\/Fam\/Console<\/strong>)<\/td>\n          <td><strong>285 \u20ac<\/strong><\/td>\n          <td><strong>335 \u20ac<\/strong><\/td>\n          <td><strong>235 \u20ac<\/strong><\/td>\n          <td><strong>432 \u20ac<\/strong><\/td>\n          <td><strong>682 \u20ac<\/strong><\/td>\n          <td><strong>677 \u20ac<\/strong><\/td>\n          <td><strong>702 \u20ac<\/strong><\/td>\n          <td><strong>602 \u20ac<\/strong><\/td>\n      <\/tr>\n      <tr>\n          <td>Nbre jeux rentabilit\u00e9 (<strong>HA + abo. Prem\/Fam\/Console<\/strong>)<\/td>\n          <td><strong>0,7<\/strong><\/td>\n          <td><strong>0,7<\/strong><\/td>\n          <td><strong>0,7<\/strong><\/td>\n          <td><strong>1,65<\/strong><\/td>\n          <td><strong>1,65<\/strong><\/td>\n          <td><strong>1,9<\/strong><\/td>\n          <td><strong>1,9<\/strong><\/td>\n          <td><strong>1,9<\/strong><\/td>\n      <\/tr>\n      <tr>\n          <td><strong>Si budget de 700\u20ac\u00a0:<\/strong><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n          <td><\/td>\n      <\/tr>\n      <tr>\n          <td>Nbre jeux neufs rentabilit\u00e9 (<strong>Abo<\/strong>)<\/td>\n          <td><strong>8,60<\/strong><\/td>\n          <td><strong>7,60<\/strong><\/td>\n          <td><strong>9,60<\/strong><\/td>\n          <td><strong>3,95<\/strong><\/td>\n          <td><strong>0,83<\/strong><\/td>\n          <td><strong>1,29<\/strong><\/td>\n          <td><strong>0,98<\/strong><\/td>\n          <td><strong>2,23<\/strong><\/td>\n      <\/tr>\n      <tr>\n          <td>Nbre jeux neufs rentabilit\u00e9 (<strong>Abo. Prem\/Fam\/Console<\/strong>)<\/td>\n          <td><strong>8,30<\/strong><\/td>\n          <td><strong>7,30<\/strong><\/td>\n          <td><strong>9,30<\/strong><\/td>\n          <td><strong>3,35<\/strong><\/td>\n          <td><strong>0,23<\/strong><\/td>\n          <td><strong>0,29<\/strong><\/td>\n          <td><strong>-0,03<\/strong><\/td>\n          <td><strong>1,23<\/strong><\/td>\n      <\/tr>\n  <\/tbody>\n<\/table>\n<p>2 possibilit\u00e9s la premi\u00e8re ann\u00e9e\u00a0:<\/p>\n<ul>\n<li>achat de la console et abonnement simple (<strong>HA + abo<\/strong>),<\/li>\n<li>ou achat de la console avec l&rsquo;abonnement \u00ab int\u00e9ressant \u00bb\u00a0: Premium pour PS5, Familial pour Switch et Game Pass Console pour Xbox (<strong>HA + abo. Prem\/Fam\/Console<\/strong>).<\/li>\n<\/ul>\n<p>Ensuite, pour chacune des possibilit\u00e9s nous calculons le <strong>nombre de jeux gratuits \u00e0 consommer sur l&rsquo;abonnement pour arriver \u00e0 rentabilit\u00e9<\/strong> ; en comparaison au prix de jeux neufs donc. Le calcul est : prix de l&rsquo;abonnement \u00e0 l&rsquo;ann\u00e9e divis\u00e9 par le prix d&rsquo;un jeu neuf.<\/p>\n<p>Finalement les deux derni\u00e8res lignes calculent le nombre de jeux neufs qu&rsquo;on peut acheter ((700 - prix HA - abonnement choisi) \u00f7 prix jeu neuf) si on avait un budget initial de 700\u20ac. Et cela dans deux situations :<\/p>\n<ol>\n<li>avec un abonnement simple,<\/li>\n<li>avec un abonnement premium\/familial\/console tel qu&rsquo;\u00e9voqu\u00e9 ci-avant.<\/li>\n<\/ol>\n<p>Notez que si le chiffre est n\u00e9gatif alors il faudra ajouter un peu d&rsquo;argent pour acheter un jeu.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Apr\u00e8s avoir trait\u00e9 chaque console en \u00e9tudiant son prix, les abonnements possibles, ce \u00e0 quoi nous avons acc\u00e8s, les probl\u00e8mes mat\u00e9riels \u00e9ventuels et le prix des jeux neufs et d&rsquo;occasion, je pense qu&rsquo;on peut imaginer quelques types de public pour chacune des consoles.<\/p>\n<p>\u00c0 mon sens les <strong>petits budgets<\/strong> se tourneront vers la <strong>Nintendo Switch<\/strong> pour 270\u20ac (HA console + abonnement simple), ce qui permettra d&rsquo;acc\u00e9der \u00e0 des jeux NES, SNES et GameBoyColor, le temps de regarder sur l&rsquo;eShop (magasin virtuel) pour acheter des jeux \u00e0 bas prix (soit \u00e0 plus de 80% de r\u00e9duction, soit pour petits budgets), il y a m\u00eame des jeux gratuits comme des courses de voiture, des combats de Pok\u00e9mon, etc. C&rsquo;est une <strong>bonne entr\u00e9e en mati\u00e8re<\/strong> avec une souplesse \u00ab console de salon \u00bb vs. \u00ab portable \u00bb.<\/p>\n<p>Si, pour vous, <strong>la qualit\u00e9 d&rsquo;image doit se rapprocher de la r\u00e9alit\u00e9 dans les jeux vid\u00e9os<\/strong>, sachez qu&rsquo;<strong>on passe du simple au double avec la Xbox Serie X et la PS5 FAT<\/strong> aux alentours de 500\u20ac. \u00c0 ce prix l\u00e0, il faut tout de m\u00eame prendre un abonnement pour les jeux en ligne, pour une copie des sauvegardes des jeux, etc. Et l\u00e0 on douille : c&rsquo;est plus de 70\u20ac par an (soit quasiment le prix d&rsquo;un jeu). Vu le prix des jeux sous ces consoles, il est clair que pour 132\u20ac\/mois avec le Game Pass Console, c&rsquo;est rentable en moins de 2 jeux par an ET avec un catalogue de plus de 100 jeux ET des nouveaut\u00e9s. C&rsquo;est donc la <strong>Xbox Serie X avec abonnement Game Pass Console que vous devriez vous tourner<\/strong>.<\/p>\n<p><strong>Malheureusement la PS5<\/strong>, avec ses probl\u00e8mes de manettes, un abonnement qui ne donne acc\u00e8s qu&rsquo;\u00e0 peu de jeux, <strong>va \u00eatre plus co\u00fbteuse sur le long terme<\/strong>. Deux raisons pourraient justifier, selon moi, l&rsquo;achat et l&rsquo;utilisation de la console\u00a0:<\/p>\n<ol>\n<li>acc\u00e9der \u00e0 des jeux en exclusivit\u00e9 sous PS5 (comme God of War, Horizon Forbidden West, Kena\u00a0: bridge of spirit, Ratchet &amp; Clank, etc.). Mais l\u00e0 encore <strong>les jeux deviennent disponibles sur PC<\/strong> g\u00e9n\u00e9ralement (plateforme Steam par exemple)<\/li>\n<li><strong>am\u00e9liorer les performances de nos jeux PS4<\/strong> et passer \u00e0 une console plus moderne<\/li>\n<\/ol>\n<p>Au final <strong>je ne conseillerais que 2 consoles<\/strong> (suivant les budgets) - alors que je suis pourtant un grand fan des consoles Sony\u00a0:<\/p>\n<ul>\n<li>la <strong>Nintendo Switch pour les petits budgets<\/strong> et une <strong>utilisation plut\u00f4t familiale<\/strong> (jeux \u00e0 plusieurs, jeux \u00ab good vibes \u00bb simples, etc.), elle permet l&rsquo;acc\u00e8s \u00e0 des <strong>jeux r\u00e9tros Nintendo<\/strong> (NES, SNES, GameCube, Mega Drive, Nintendo 64, etc.),<\/li>\n<li>la <strong>Xbox S\u00e9rie X pour des budgets plus cons\u00e9quents avec l&rsquo;abonnement Game Pass Console<\/strong> (11\u20ac\/mois) pour acc\u00e9der r\u00e9guli\u00e8rement \u00e0 des nouveaut\u00e9s, des <strong>jeux vari\u00e9s et r\u00e9cents<\/strong> et le plaisir des yeux niveau <strong>qualit\u00e9 d&rsquo;image<\/strong>.<\/li>\n<\/ul>\n<p>Ma pr\u00e9f\u00e9rence, quitte \u00e0 payer un abonnement mensuel, est d&rsquo;utiliser le Game Pass Console sur la Xbox S\u00e9rie X pour pouvoir tester plusieurs jeux d\u00e8s leur sortie. Et d&rsquo;\u00e9ventuellement acheter lesdits jeux en occasion d\u00e8s que le prix me convient (pour les finir ou les garder \u00e0 la maison).<\/p>\n<p>Sachez que, quoiqu&rsquo;il arrive, d\u00e8s que vous souhaitez jouer en ligne, <strong>la plupart des consoles vous am\u00e8neront \u00e0 prendre un abonnement<\/strong> minimal pour cela\u2026 Une console de jeu est finalement un budget \u00e0 part enti\u00e8re de nos jours !<\/p>\n"},{"title":"Ordinateur portable Xiaomi Book Air 13 (version 2022)","link":"https:\/\/olivier.dossmann.net\/2023\/12\/xiaomi-book-air-13-de-2022\/","pubDate":"Fri, 01 Dec 2023 21:17:01 +0200","guid":"https:\/\/olivier.dossmann.net\/2023\/12\/xiaomi-book-air-13-de-2022\/","description":"<p><strong>Edit<\/strong> : article r\u00e9dig\u00e9 et publi\u00e9 ult\u00e9rieurement (courant 2024). La date du 1er d\u00e9cembre repr\u00e9sente approximativement l&rsquo;arriv\u00e9e de l&rsquo;appareil dans le foyer.<\/p>\n<h1 id=\"pr\u00e9sentation\">Pr\u00e9sentation<\/h1>\n<p>2024, <strong>\u00e9cologiquement<\/strong> parlant nous devrions <strong>faire durer un ordinateur un minimum de 5 \u00e0 7 ans<\/strong> ! Mon dernier achat d&rsquo;ordinateur portable \u00e9tait en d\u00e9cembre 2018, il y a exactement 5 ans ! C&rsquo;\u00e9tait un <a href=\"https:\/\/www.tuxboard.com\/plan-notebook-air-13-3-pc-portable-economique-puissant\/\">Xiaomi Mi Notebook Air 13.3 (version 2017)<\/a>.<\/p>\n<p>Techniquement il fonctionne encore tr\u00e8s bien. C&rsquo;est pourquoi je l&rsquo;ai donn\u00e9 \u00e0 quelqu&rsquo;un (en installant Windows 10 dessus, berk!). Je n&rsquo;avais pas \u00e0 en racheter un.<\/p>\n<p>Seulement voil\u00e0, les habitudes ont la vie rude ! Le confort de la mobilit\u00e9 (travail \u00e0 l&rsquo;ext\u00e9rieur, d\u00e9placements et utilisation dans diff\u00e9rentes pi\u00e8ces de la maison) me manquait \u00e9norm\u00e9ment.<\/p>\n<p>C&rsquo;est ainsi que mon c\u00f4t\u00e9 technophile a eu raison de moi\u00a0: j&rsquo;ai fais l&rsquo;acquisition d&rsquo;un <strong>Xiaomi Book Air 13 (version 2022)<\/strong>.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/materiel\/Xiaomi-book-air-13.png\" alt=\"Apparence de l&rsquo;ordinateur portable Xiaomi Book Air 13\"><\/p>\n<h1 id=\"description-du-produit\">Description du produit<\/h1>\n<p>Le Xiaomi Book Air 13 (version 2022) poss\u00e8de les caract\u00e9ristiques suivantes\u00a0:<\/p>\n<ul>\n<li>un processeur <strong>Intel Core i5-1230U<\/strong> (<strong>12e g\u00e9n\u00e9ration, 10 c\u0153urs, 12 threads, jusqu&rsquo;\u00e0 4.40Ghz<\/strong>)<\/li>\n<li>une m\u00e9moire vive de <strong>16 Go de RAM LPDDR5 cadenc\u00e9 \u00e0 5200 Mhz<\/strong><\/li>\n<li>un disque dur <strong>SSD de 512Go<\/strong><\/li>\n<li>une carte graphique int\u00e9gr\u00e9e <strong>Intel Iris Xe graphics<\/strong><\/li>\n<li>une batterie de <strong>12 heures d&rsquo;utilisation<\/strong> et 1 \u00e0 2h de charge<\/li>\n<li>un \u00e9cran <strong>13 pouces 360\u00b0 OLED tactile<\/strong> avec une r\u00e9solution 2.8K<\/li>\n<li>un <strong>lecteur d&rsquo;empreinte<\/strong><\/li>\n<li><strong>Bluetooth 5.2<\/strong><\/li>\n<li><strong>Wifi 6E<\/strong><\/li>\n<li><strong>2 prises USB 3 de Type C Thunderbolt<\/strong><\/li>\n<li>une <strong>prise jack<\/strong><\/li>\n<li>poids de <strong>1,3 kg<\/strong><\/li>\n<li>\u00e9paisseur de <strong>12 mm<\/strong><\/li>\n<\/ul>\n<p>Il n&rsquo;y a <strong>pas de ventilation ni d&rsquo;a\u00e9ration<\/strong>\u00a0: vous n&rsquo;entendrez jamais tourner cet ordinateur.<\/p>\n<h1 id=\"mes-impressions\">Mes impressions<\/h1>\n<p>Cet ordinateur, en plus d&rsquo;avoir de bonnes caract\u00e9ristiques pour l&rsquo;ann\u00e9e 2022, ressemble \u00e0 un <strong>v\u00e9ritable bijou<\/strong>\u00a0: il est brillant, pr\u00e9sent\u00e9 comme une parure qu&rsquo;on mettrait sur soi.<\/p>\n<p>Il est <strong>pratique\u00a0: tr\u00e8s l\u00e9ger, tr\u00e8s maniable<\/strong> (se plie m\u00eame en mode tablette, c&rsquo;est dire\u00a0!) et <strong>tient longtemps<\/strong> (entre 10h et 12h d&rsquo;autonomie). Il est tr\u00e8s impressionnant.<\/p>\n<p>Cependant sans le stylo sp\u00e9cifique (non fourni par Xiaomi) de cet ordinateur, <strong>l&rsquo;\u00e9cran tactile n&rsquo;est pas tr\u00e8s utile<\/strong>. Au contraire\u00a0: il me pose probl\u00e8me r\u00e9guli\u00e8rement. Sans doute ne suis-je pas habitu\u00e9 aux \u00e9crans taciles. Par contre plier l&rsquo;ordinateur et le <strong>poser comme un \u00e9cran de TV<\/strong> est pratique\u00a0!<\/p>\n<p>\u00c9tant donn\u00e9 qu&rsquo;il est <strong>silencieux<\/strong>, car aucune ventilation, on pourrait s&rsquo;attendre \u00e0 ce qu&rsquo;il chauffe \u00e9norm\u00e9ment. Mais ce n&rsquo;est pas le cas. Il chauffe, certes, mais avec l&rsquo;assemblage aluminium tout autour cela passe tr\u00e8s bien.<\/p>\n<p>Je regrette qu&rsquo;il soit si fin et donc <strong>quasiment pas r\u00e9parable<\/strong>\u00a0: si la moindre pi\u00e8ce venait \u00e0 tomber en panne, il faudrait quasiment changer l&rsquo;ordinateur portable entier. Cela m&rsquo;emb\u00eate un peu.<\/p>\n<p>On notera \u00e9galement un <strong>\u00e9cran avec une d\u00e9finition et des couleurs \u00e0 couper le souffle<\/strong>\u00a0! Et surtout un peu compliqu\u00e9 \u00e0 configurer pour moi sur ArchLinux car la r\u00e9solution \u00e9tait tellement grande qu&rsquo;il a fallu d\u00e9terminer un ratio d&rsquo;aggrandissement. J&rsquo;ai ri - de honte &#x1f609;.<\/p>\n<p>A contrario je d\u00e9plore le manque d&rsquo;informations pour savoir comment faire fonctionner la Webcam sur GNU\/Linux pour cette machine. Impossible pour le moment &#x1f937;.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Je dois dire que cet ordinateur est un <strong>objet de tr\u00e8s bonne facture<\/strong>. Avec du mat\u00e9riel \u00e0 la hauteur, de <strong>bonnes caract\u00e9ristiques techniques, l\u00e9ger, pratique et silencieux<\/strong>. Et pas cher pour ce que c&rsquo;est\u00a0!<\/p>\n<p>Pourtant le c\u00f4t\u00e9 <strong>peu r\u00e9parable<\/strong>, un \u00e9cran tactile - presque - inutile et une <strong>webcam ne fonctionnant pas sous GNU\/Linux<\/strong>, m&rsquo;ont l\u00e9g\u00e8rement irrit\u00e9.<\/p>\n<p>Je pense ne pas garder cet ordinateur portable longtemps et le revendre d\u00e8s que j&rsquo;aurais trouv\u00e9 un ordinateur qui me seille bien plus.<\/p>\n"},{"title":"La Gen\u00e8se d'un projet","link":"https:\/\/olivier.dossmann.net\/2023\/09\/la-gen%C3%A8se-dun-projet\/","pubDate":"Wed, 13 Sep 2023 12:13:23 +0200","guid":"https:\/\/olivier.dossmann.net\/2023\/09\/la-gen%C3%A8se-dun-projet\/","description":"<p>Il y a quelques temps je lan\u00e7ais un billet sur <a href=\"https:\/\/olivier.dossmann.net\/2023\/01\/la-gachette-qui-publie-mon-blog\/\">le projet Gachette<\/a> qui permet de publier mon blog. Dans la m\u00eame lanc\u00e9e <strong>je souhaite aujourd&rsquo;hui vous pr\u00e9senter un autre projet<\/strong> sur lequel je travaille de temps en temps : <strong><a href=\"https:\/\/github.com\/blankoworld\/genese\">Gen\u00e8se<\/a><\/strong>. Gen\u00e8se est assez difficile \u00e0 d\u00e9finir. Cependant je l&rsquo;imagine comme une <strong>commande unique pour permettre \u00e0 plusieurs d\u00e9veloppeurs de travailler sur un environnement commun<\/strong> et en lan\u00e7ant <strong>une instance par<\/strong> <em>ticket<\/em>\/probl\u00e8me relev\u00e9.<\/p>\n<p>Cet outil, bien qu&rsquo;il soit <strong>utile et facile \u00e0 utiliser<\/strong>, demande un minimum d&rsquo;explication pour comprendre \u00e0 la fois le concept et l&rsquo;utilit\u00e9 r\u00e9elle.<br \/>\nC&rsquo;est pourquoi je vais avant tout poser la probl\u00e9matique principale qui a initi\u00e9e le projet, puis montrer quelques usages int\u00e9ressants qui sont plus parlants et finalement tenter d&rsquo;expliquer son fonctionnement en interne.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/lieux\/genesis.jpg\" alt=\"Explosion d&rsquo;artifice au dessus de l&rsquo;eau\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/flickr.com\/photos\/23024164@N06\/\">profil de Damian Gadal sur Flickr<\/a><\/em> sous licence CC BY 2.0.<\/p>\n<h1 id=\"ce-qui-a-initi\u00e9-le-projet\">Ce qui a initi\u00e9 le projet<\/h1>\n<p>De <a href=\"https:\/\/olivier.dossmann.net\/cv\/\">nombreuses ann\u00e9es d&rsquo;exp\u00e9rience professionnelle dans le domaine informatique<\/a> m&rsquo;ont amen\u00e9 \u00e0 rencontrer plusieurs probl\u00e8mes et \u00e0 r\u00e9fl\u00e9chir sur \u00e9norm\u00e9ment de sujets.<\/p>\n<p>L&rsquo;un d&rsquo;eux se porte sur l&rsquo;environnement de travail : je parle de l&rsquo;environnement syst\u00e8me utilis\u00e9 pour travailler sur un projet. J&rsquo;ai relev\u00e9 quelques \u00e9l\u00e9ments int\u00e9ressants, similaires \u00e0 des postulats :<\/p>\n<ul>\n<li>on essaie souvent de se rapprocher de l&rsquo;environnement syst\u00e8me de la production : ceci pour rep\u00e9rer assez t\u00f4t d&rsquo;\u00e9ventuels probl\u00e8mes<\/li>\n<li>plus l&rsquo;environnement entre coll\u00e8gues est similaire, mieux nous travaillons : si un collaborateur rencontre un probl\u00e8me il peut se tourner vers le reste de l&rsquo;\u00e9quipe pour avoir de l&rsquo;aide<\/li>\n<li>nous en arrivons souvent \u00e0 faire un fichier docker-compose.yml pour cela : peut-\u00eatre est-ce initialement plus facile de partager un fichier l\u00e9ger (docker-compose.yml) qu&rsquo;une machine virtuelle enti\u00e8re ?<\/li>\n<li>quand on nouveau collaborateur arrive dans l&rsquo;\u00e9quipe, il a pas mal de choses \u00e0 apprendre sur le projet. Et il peine souvent - en plus de toutes les choses \u00e0 apprendre - \u00e0 devoir installer l&rsquo;environnement initial<\/li>\n<li>on utilise souvent un fichier docker-compose.yml tr\u00e8s proche entre plusieurs projets : ce qui est normal puisqu&rsquo;on utilise tr\u00e8s souvent des technologies similaires qu&rsquo;on ma\u00eetrise pour les vendre \u00e0 nos clients<\/li>\n<li>on travaille souvent sur plusieurs <em>tickets<\/em>, c&rsquo;est \u00e0 dire plusieurs probl\u00e8mes \u00e9mis par le client. Chaque fois que nous d\u00e9marrons un nouveau ticket, dans l&rsquo;id\u00e9al, nous devrions avoir un environnement sain et propre pour ne pas avoir de fioritures de nos travaux pr\u00e9c\u00e9dents<\/li>\n<\/ul>\n<p>En discutant avec <a href=\"https:\/\/www.richard-dern.fr\/\">Richard Dern<\/a> (qui a d&rsquo;ailleurs <a href=\"https:\/\/www.richard-dern.fr\/blog\/2023\/09\/03\/nouveau-site-en-ligne\/\">fait une refonte de son site web avec un nouveau moteur de blog<\/a>), nous avons r\u00e9fl\u00e9chi \u00e0 un outil capable de r\u00e9soudre plusieurs de ces probl\u00e9matiques. Le fil rouge \u00e9tait :<\/p>\n<ul>\n<li>les environnements doivent <strong>rester isol\u00e9s<\/strong> : utilisation d&rsquo;un fichier docker-compose.yml pour g\u00e9n\u00e9rer un environnement<\/li>\n<li>cr\u00e9ation d&rsquo;un <strong>r\u00e9pertoire unique pour chaque nouveau ticket\/projet\/autre<\/strong> afin de garder quelque chose de sain et propre<\/li>\n<li>\u00e9viter de devoir se r\u00e9p\u00e9ter entre plusieurs projets : <strong>mettre en commun<\/strong> les \u00e9l\u00e9ments qui le peuvent, comme par exemple le type de base de donn\u00e9es<\/li>\n<li>permettre \u00e0 <strong>plusieurs utilisateurs de travailler sur la m\u00eame machine<\/strong> : nommage des environnements d&rsquo;une mani\u00e8re particuli\u00e8re (avec le nom de l&rsquo;utilisateur par exemple)<\/li>\n<li>permettre \u00e0 chaque d\u00e9veloppeur de <strong>personnaliser son environnement<\/strong> : par exemple s&rsquo;il souhaite avoir une interface web pour la base de donn\u00e9es, qu&rsquo;il puisse rajouter l&rsquo;outil qu&rsquo;il souhaite<\/li>\n<li>si nous travaillons sur 2 tickets d&rsquo;un m\u00eame projet, sachant que l&rsquo;environnement est similaire pour un m\u00eame projet, ces deux environnements doivent pouvoir exister en m\u00eame temps : s&rsquo;ils utilisent les m\u00eames ports d&rsquo;adresses, cela va \u00e9chouer. Il faut donc des <strong>ports al\u00e9atoires<\/strong>.<\/li>\n<li>si les ports sont al\u00e9atoires, alors il faut un acc\u00e8s facile \u00e0 chacun des environnements, par exemple \u00e0 l&rsquo;aide d&rsquo;une <strong>interface Web commune<\/strong> \u00e0 tous ces environnements<\/li>\n<\/ul>\n<p>C&rsquo;est assez difficile \u00e0 se l&rsquo;imaginer sans des explications, une d\u00e9monstration et\/ou quelqu&rsquo;un qui nous guide pour nous montrer le fonctionnement, alors je vais vous pr\u00e9senter un cas d&rsquo;usage.<\/p>\n<h1 id=\"usage-et-exemple-de-gen\u00e8se\">Usage et exemple de Gen\u00e8se<\/h1>\n<p>Imaginons un cas d&rsquo;usage typique : un client d\u00e9tient une application web (faite avec un <a href=\"https:\/\/fr.m.wikipedia.org\/wiki\/Framework\">framework<\/a>) qui utilise une base de donn\u00e9es MySQL. Appelons cette application : memory_game.<\/p>\n<p>Il est tr\u00e8s probable que nous ayons d&rsquo;autres clients qui utilisent MySQL. Et il est tr\u00e8s probable que notre client fera des tickets de bugs que nous aurons \u00e0 r\u00e9soudre. Nous aurons probablement plusieurs collaborateurs (pour lesquels nous allons r\u00e9partir les tickets).<\/p>\n<p>Il utilise un fichier docker-compose.yml avec le contenu suivant :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">version<\/span>: <span style=\"color:#e6db74\">&#39;3&#39;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">services<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">php<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">toasterlint\/php-apache-mysql<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">ports<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">&#34;8888:80&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">volumes<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">.\/:\/var\/www\/html<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">db<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">mysql<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">command<\/span>: --<span style=\"color:#ae81ff\">default-authentication-plugin=mysql_native_password<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">restart<\/span>: <span style=\"color:#ae81ff\">always<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">environment<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">MYSQL_ROOT_PASSWORD<\/span>: <span style=\"color:#ae81ff\">mot2passe<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">MYSQL_DATABASE<\/span>: <span style=\"color:#ae81ff\">maBdd<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">volumes<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#ae81ff\">.\/base_de_donnees:\/docker-entrypoint-initdb.d<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>On comprend donc qu&rsquo;il y a :<\/p>\n<ul>\n<li>un service (nomm\u00e9 db) pour une base de donn\u00e9es MySQL,<\/li>\n<li>un service (nomm\u00e9 php) pour l&rsquo;application \u00e9crite en PHP sur le port 8888.<\/li>\n<\/ul>\n<p><strong>Avec Gen\u00e8se<\/strong>, g\u00e9n\u00e9rer un nouvel environnement de d\u00e9veloppement reviendrait \u00e0 faire :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># G\u00e9n\u00e8re un docker-compose.yml dans le dossier instances\/memory_game \u00e0 partir du profil nomm\u00e9 memory_game<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>.\/genese -p memory_game\n<\/span><\/span><\/code><\/pre><\/div><p><strong>instances\/memory_game est un r\u00e9pertoire de travail<\/strong> :<\/p>\n<ul>\n<li>avec le d\u00e9p\u00f4t de version du projet memory_game,<\/li>\n<li>un fichier docker-compose.yml pr\u00e9vu pour lancer un environnement complet,<\/li>\n<li>et un fichier <strong>.env<\/strong> contenant des variables permettant au projet d&rsquo;\u00eatre autonome.<\/li>\n<\/ul>\n<p>Si l&rsquo;ex\u00e9cutable <em>.\/genese<\/em> venait \u00e0 dispara\u00eetre, le dossier reste fonctionnel pour travailler (avec Docker Compose \u00e9videmment).<\/p>\n<p>Si on travaille sur plusieurs probl\u00e8mes en parall\u00e8le, on peut vouloir cr\u00e9er un dossier par probl\u00e8me. Imaginons le <strong>ticket 42<\/strong> et le <strong>ticket 16<\/strong> :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># G\u00e9n\u00e8re une instance du projet dans le dossier instances\/ticket42<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>.\/genese -p memory_game -n ticket42\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># M\u00eame chose avec le dossier instances\/ticket16<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>.\/genese -p memory_game -n ticket16\n<\/span><\/span><\/code><\/pre><\/div><p>Il faut r\u00e9cup\u00e9rer une branche sp\u00e9ciale du service memory_game ?<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># R\u00e9cup\u00e8re dans le d\u00e9p\u00f4t Git fourni dans le service memory_game la branche maBrancheSpeciale<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>BRANCHE_MEMORY_GAME<span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">&#34;maBrancheSpeciale&#34;<\/span> .\/genese -p memory_game -n essaiautrebranche\n<\/span><\/span><\/code><\/pre><\/div><p>Vous d\u00e9couvrirez dans un prochain article \u00e0 quel usage je r\u00e9serve cela &#x1f609;<\/p>\n<p>Quelles sont les instances dont je dispose ?<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Liste l&#39;ensemble des instances<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>.\/genese -i\n<\/span><\/span><\/code><\/pre><\/div><p>Et j&rsquo;obtiens une liste des dossiers ayant \u00e9t\u00e9 cr\u00e9\u00e9s.<\/p>\n<p>Si je veux supprimer une instance sp\u00e9cifique :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Supprimer l&#39;instance ticket16<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>.\/genese -s ticket16\n<\/span><\/span><\/code><\/pre><\/div><p>Et si on veut avoir une visualisation des instances lanc\u00e9es, d&rsquo;acc\u00e9der \u00e0 leur d\u00e9tail, \u00e9teindre un conteneur, le rallumer, regarder les fichiers de journalisation (logs) ou encore entrer dans un conteneur avec une invite de commande, on peut <strong>utiliser l&rsquo;interface Web Portainer<\/strong> fournie avec Gen\u00e8se. Cela ressemble \u00e0 quelque chose comme :<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/screenshots\/portainer.png\" alt=\"Impression \u00e9cran d&rsquo;une liste d&rsquo;instances lanc\u00e9es sur la machine locale\"><\/p>\n<p>Seriez-vous curieux de savoir comment cela fonctionne (sans entrer dans le technique \u00e9videmment\u00a0!) ?<\/p>\n<h1 id=\"comment-\u00e7a-marche-\">Comment \u00e7a marche ?<\/h1>\n<p>Nous allons voir que Gen\u00e8se s&rsquo;appuie sur quelques \u00e9l\u00e9ments cl\u00e9s comme :<\/p>\n<ul>\n<li>l&rsquo;instance : version autonome et isol\u00e9e, situ\u00e9e dans le dossier <strong>instance<\/strong>,<\/li>\n<li>le service : la description n\u00e9cessaire pour g\u00e9n\u00e9rer un service, par exemple MySQL,<\/li>\n<li>le profil : contient une liste de services permettant de d\u00e9marrer une instance.<\/li>\n<\/ul>\n<p>Prenons l&rsquo;exemple pr\u00e9c\u00e9dent, nous aurons :<\/p>\n<ul>\n<li>deux services :\n<ul>\n<li>l&rsquo;un nomm\u00e9 <code>mysql<\/code> pour la base de donn\u00e9es,<\/li>\n<li>l&rsquo;autre nomm\u00e9 <code>memory_game<\/code> pour notre application,<\/li>\n<\/ul>\n<\/li>\n<li>un profil nomm\u00e9 <code>memory_game<\/code> qui liste les services <code>mysql<\/code> et <code>memory_game<\/code>.<\/li>\n<\/ul>\n<p>Chaque fois que nous lancerons la commande <code>.\/genese<\/code> avec le profil <code>memory_game<\/code> nous aurons la possibilit\u00e9 de g\u00e9n\u00e9rer une instance avec un nom sp\u00e9cifique.<\/p>\n<p>Comment cela peut-il fonctionner ? Les services ont une structure permettant de d\u00e9crire :<\/p>\n<ul>\n<li>la <strong>partie du fichier docker-compose.yml<\/strong> qui va \u00eatre utilis\u00e9e,<\/li>\n<li>la possibilit\u00e9 de donner le <strong>d\u00e9p\u00f4t Git utilis\u00e9<\/strong> pour r\u00e9cup\u00e9rer les sources du projet et la <strong>branche par d\u00e9faut<\/strong> \u00e0 r\u00e9cup\u00e9rer,<\/li>\n<li>la possibilit\u00e9 de donner un dossier <code>extras<\/code> permettant de <strong>surcharger les sources du projet r\u00e9cup\u00e9r\u00e9<\/strong> (par exemple pour mettre des fichiers de configuration),<\/li>\n<li>la possibilit\u00e9 d&rsquo;<strong>agir \u00e0 des moments cl\u00e9s de la g\u00e9n\u00e9ration de l&rsquo;instance via des d\u00e9clencheurs<\/strong> qui sont des scripts pour :\n<ul>\n<li>proc\u00e9der \u00e0 des changements <strong>AVANT<\/strong> le <strong>lancement des conteneurs Docker<\/strong>, par exemple modifier des fichiers de configuration,<\/li>\n<li>agir <strong>APR\u00c8S le lancement des conteneurs<\/strong>, par exemple pour lancer des commandes dans les conteneurs Docker lanc\u00e9s,<\/li>\n<li>afficher des informations <strong>\u00e0 la fin du processus de cr\u00e9ation de l&rsquo;instance<\/strong>, par exemple pour afficher les ports utilis\u00e9s par les diff\u00e9rents conteneurs<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>\u00c0 cela s&rsquo;ajoute <strong>une interface Web<\/strong> (<a href=\"https:\/\/www.portainer.io\/\" title=\"Interface Web de contr\u00f4le des instances Docker lanc\u00e9es\">Portainer<\/a>) permettant de contr\u00f4ler les instances lanc\u00e9es et acc\u00e9der \u00e0 des fonctionnalit\u00e9s particuli\u00e8res de Docker.<\/p>\n<p><a href=\"https:\/\/github.com\/blankoworld\/genese#readme\">La documentation de Gen\u00e8se<\/a> devrait se charger bien mieux que moi pour les d\u00e9tails techniques et les possibilit\u00e9s de cet outil.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Malgr\u00e9 l&rsquo;exemple d&rsquo;utilisation et quelques d\u00e9tails sur son fonctionnement, Gen\u00e8se m\u00e9rite qu&rsquo;on s&rsquo;y attarde : nous n&rsquo;avons gratt\u00e9 que la surface.<\/p>\n<p>Cela ressemble \u00e0 un framework pour des profils de personnes de type <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Devops\">devops<\/a> afin de leur fournir un outil unique ; \u00e0 la fois d\u00e9ployer des instances pour les d\u00e9veloppeurs ; et des instances de type test pour les personnes fonctionnelles.<\/p>\n<p>Je ne sais pas comment sugg\u00e9rer de passer un peu de temps sur l&rsquo;outil pour comprendre que c&rsquo;est un bon investissement de temps. Surtout pour des personnes d&rsquo;un profil DevOps et qui connaissent le scripting Bash. J&rsquo;esp\u00e8re que la curiosit\u00e9 vous piquera au vif\u00a0!<\/p>\n<p>Nous nous retrouverons tr\u00e8s probablement pour en reparler dans un cadre diff\u00e9rent encore.<\/p>\n"},{"title":"Bye Bye Gandi Mail, Bonjour Mailo","link":"https:\/\/olivier.dossmann.net\/2023\/08\/bye-bye-gandi-mail-bonjour-mailo\/","pubDate":"Fri, 25 Aug 2023 21:26:05 +0200","guid":"https:\/\/olivier.dossmann.net\/2023\/08\/bye-bye-gandi-mail-bonjour-mailo\/","description":"<p>\u00c7a fait <a href=\"https:\/\/sebsauvage.net\/links\/?o9qvaA\">quelques mois qu&rsquo;on nous rabache que Gandi Mail va devenir payant<\/a>. <a href=\"https:\/\/www.nextinpact.com\/article\/71947\/gandi-passe-mails-en-payant-utilisateurs-cherchent-solutions-remplacement\">NextInpact affirme aussi que Gandi passe les mails en payant<\/a>. Quel probl\u00e8me cela pose ? O\u00f9 en suis-je ? Y a-t-il d&rsquo;autres alternatives \u00e0 cela ?<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/objets\/boites_aux_lettres.jpg\" alt=\"Trois bo\u00eetes aux lettres en forme de maison\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/flickr.com\/photos\/irio\/\">profil d&rsquo;irio sur Flickr<\/a><\/em> sous licence BY 2.0.<\/p>\n<h1 id=\"probl\u00e8mes-pos\u00e9s\">Probl\u00e8mes pos\u00e9s<\/h1>\n<p>Gandi annonce la mise du service Gandi Mail payant \u00e0 partir de fin septembre\/d\u00e9but octobre. Le prix des bo\u00eetes mail passe <strong>de gratuit \u00e0 4.79\u20ac par mois<\/strong>. C&rsquo;est \u00e0 dire <strong>57.48\u20ac\/an<\/strong>. Et cela <strong>pour chaque bo\u00eete courriel !<\/strong><\/p>\n<p>Le souci ? Quand on utilise plusieurs bo\u00eetes courriel, \u00e7a douille tout de suite plus ! Exemple : j&rsquo;ai 4 bo\u00eetes courriel utilis\u00e9es chez Gandi, <strong>ce qui me ferait passer de 0\u20ac\/an \u00e0\u2026 229.92\u20ac<\/strong> !!<\/p>\n<p>Avouez que <strong>la facture est plut\u00f4t cors\u00e9e<\/strong> quand on a \u00e9t\u00e9 habitu\u00e9 plus de 10 ans \u00e0 ne rien d\u00e9bourser pour cela. Et encore, j&rsquo;abuse un peu car il faut savoir que les domaines sous Gandi sont g\u00e9n\u00e9ralement plus chers que d&rsquo;autres fournisseurs de domaines. Les adresses courriel gratuites et de bonne qualit\u00e9 amenait des clients chez Gandi.<\/p>\n<p>J&rsquo;utilise Gandi pour l&rsquo;ensemble de mes domaines car j&rsquo;appr\u00e9cie le service, les possibilit\u00e9s (comme le \u00ab Live DNS \u00bb) et - ant\u00e9rieurement - la possibilit\u00e9 de cr\u00e9er des adresses courriels avec 1 000 alias de redirection courriels (vous choisissez des adresses courriels qui vont rediriger vers 1 \u00e0 plusieurs adresses courriels, m\u00eame si les adresses cibles ne vous appartiennent pas).<\/p>\n<p>Et dans tout cela, o\u00f9 me situe-je ?<\/p>\n<h1 id=\"o\u00f9-jen-suis\">O\u00f9 j&rsquo;en suis<\/h1>\n<p>Si j&rsquo;\u00e9cris cet article, c&rsquo;est parce que <strong>j&rsquo;aimerais partager \u00e0 la fois le fait qu&rsquo;il y a du changement chez Gandi Mail, mais aussi mes p\u00e9r\u00e9grinations en mati\u00e8re de recherche d&rsquo;un fournisseur de mail<\/strong> prenant en compte les noms de domaine sans forc\u00e9ment les g\u00e9rer.<\/p>\n<p>On le dit souvent, en informatique il est int\u00e9ressant de pouvoir changer un service sans avoir d&rsquo;impact sur un autre : choisir un fournisseur d&rsquo;adresses courriels sans changer de fournisseur de domaine par exemple. Et c&rsquo;est ce que je visais.<\/p>\n<h1 id=\"quelles-alternatives-\">Quelles alternatives ?<\/h1>\n<p><a href=\"https:\/\/sebsauvage.net\/links\/?o9qvaA\">Si SebSauvage propose Infomaniak<\/a> en migrant ses domaines l\u00e0 bas, d&rsquo;<a href=\"https:\/\/ludovic.hirlimann.net\/2023\/06\/gandi-je-suis-enerve-la-partie-email.html\">autres proposent diff\u00e9rents services comme FastMail, Ikoula<\/a>.<\/p>\n<p>La plupart du temps, il faut migrer ses domaines avec : ce que je ne recherche pas du tout. C&rsquo;est le cas pour <a href=\"https:\/\/www.ovhcloud.com\/fr\/\">OVH<\/a> qui propose des offres quand on g\u00e8re les domaines chez eux.<\/p>\n<p>Je suis alors tomb\u00e9 sur <a href=\"https:\/\/jtremesay.org\/migration-de-gandi-mail-vers-mailo.html\">un article int\u00e9ressant sur le passage \u00e0 Mailo.com<\/a>, service que je ne connaissais pas. <a href=\"https:\/\/www.mailo.com\/\">Mailo.com<\/a> est Fran\u00e7ais, garantit aux utilisateurs d&rsquo;\u00eatre respectueux de leurs donn\u00e9es, ne pas les vendre \u00e0 des tiers et visant une utilisation \u00e9thique et responsable du Web. Le service <strong>Mailo est gratuit, mais une option \u00e0 2\u20ac\/mois existe<\/strong>, ce qui permet de g\u00e9rer 5 adresses mails. \u00c7a tombe bien, j&rsquo;ai 4 adresses \u00e0 cr\u00e9er !<\/p>\n<h1 id=\"quelques-d\u00e9tails-sur-ma-migration\">Quelques d\u00e9tails sur ma migration<\/h1>\n<p>Ce qui est int\u00e9ressant dans ce dernier article sur Mailo.com, c&rsquo;est que l&rsquo;auteur sugg\u00e8re <strong>l&rsquo;utilisation d&rsquo;imapsync<\/strong> comme outil pour synchroniser son ancienne bo\u00eete aux lettres avec la nouvelle. Et il donne les d\u00e9tails sur comment configurer tout cela.<\/p>\n<p>\u00c0 noter une petite subtilit\u00e9 que je n&rsquo;avais pas prise en compte : si sous Gandi Mail vous avez droit \u00e0 1 000 redirections courriel, sous Mailo c&rsquo;est limit\u00e9 \u00e0 100 redirection par bo\u00eete courriel. Quand on a 390+ redirections\u2026 il faut faire du tri ! Fort heuresement, Mailo propose un fonctionnement int\u00e9ressant : ajouter <code>++<\/code> dans l&rsquo;adresse courriel. Par exemple si vous faites une bo\u00eete <a href=\"mailto:prenom.nom@domaine.tld\">prenom.nom@domaine.tld<\/a>, vous pouvez cr\u00e9er dynamiquement <a href=\"mailto:prenom.nom++quelquechose@domaine.tld\">prenom.nom++quelquechose@domaine.tld<\/a>. Autant que vous voulez. Sans utiliser d&rsquo;ordinateur. \u00c0 la vol\u00e9e je vous disais !<\/p>\n<p>Autre chose \u00e0 viser aussi : le catch-all. C&rsquo;est le fait que n&rsquo;importe quelle adresse mal \u00e9crite, ou diff\u00e9rente de votre bo\u00eete courriel entra\u00eenera quand m\u00eame la redirection vers votre bo\u00eete courriel. Exemple : au lieu d&rsquo;\u00e9crire <a href=\"mailto:prenom.nom@domaine.tld\">prenom.nom@domaine.tld<\/a>, si je me trompe en \u00e9crivant <a href=\"mailto:prenmo.nmo@domaine.tld\">prenmo.nmo@domaine.tld<\/a>, je recevrais quand m\u00eame l&rsquo;adresse courriel.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Je joue les montagnes russes avec les prix : je suis pass\u00e9 de 4 bo\u00eetes Gandi Mail gratuites \u00e0 potentiellement 230\u20ac\/an pour finalement payer 24\u20ac\/an. Le co\u00fbt num\u00e9rique augmente l\u00e9g\u00e8rement. Je pense \u00eatre en droit de me demander jusqu&rsquo;o\u00f9 cela ira pour b\u00e9n\u00e9ficier d&rsquo;un pseudo contr\u00f4le de mes donn\u00e9es et le choix de mes fournisseurs sur Internet ?<\/p>\n<p>Quoiqu&rsquo;il en soit le passage de Gandi Mail \u00e0 Mailo, m\u00eame si rocambolesque et pas de tout repos, a \u00e9t\u00e9 plus serein avec l&rsquo;aide de l&rsquo;<a href=\"https:\/\/jtremesay.org\/migration-de-gandi-mail-vers-mailo.html\">article de Jonathan Tremesaygues sur le passage de Gandi Mail \u00e0 Mailo \u00e0 l&rsquo;aide d&rsquo;imapsync<\/a>.<\/p>\n<p>Je trie encore mes 390+ redirections Gandi Mail vers Mailo au fur et \u00e0 mesure, mais c&rsquo;\u00e9tait n\u00e9cessaire et je suis bien content de l&rsquo;avoir fait, malgr\u00e9 le c\u00f4t\u00e9 payant de la chose !<\/p>\n<h1 id=\"liens-utiles\">Liens utiles<\/h1>\n<ul>\n<li><a href=\"https:\/\/www.infomaniak.com\/fr\">Infomaniak<\/a><\/li>\n<li><a href=\"https:\/\/www.fastmail.com\/\">FastMail<\/a><\/li>\n<li><a href=\"https:\/\/www.ikoula.com\/fr\/\">Ikoula<\/a><\/li>\n<li><a href=\"https:\/\/www.ovhcloud.com\/fr\/\">OVH<\/a><\/li>\n<li><a href=\"https:\/\/www.mailo.com\/\">Mailo<\/a><\/li>\n<li><a href=\"https:\/\/jtremesay.org\/migration-de-gandi-mail-vers-mailo.html\">Article de Jonathan Tremesaygues sur le passage de Gandi Mail \u00e0 Mailo<\/a><\/li>\n<\/ul>\n"},{"title":"La Gachette qui publie mon blog","link":"https:\/\/olivier.dossmann.net\/2023\/01\/la-gachette-qui-publie-mon-blog\/","pubDate":"Fri, 20 Jan 2023 17:46:32 +0200","guid":"https:\/\/olivier.dossmann.net\/2023\/01\/la-gachette-qui-publie-mon-blog\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Peut-\u00eatre serez-vous surpris d&rsquo;apprendre que la publication d&rsquo;un article sur ce blog revient simplement \u00e0 \u00e9crire du texte dans un fichier et <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Commit\">envoyer un commit<\/a> sur un d\u00e9p\u00f4t Git. <strong>Ni plus, ni moins<\/strong>.<\/p>\n<p>C&rsquo;est l\u00e0 que commence toute une <strong>cha\u00eene de publication<\/strong> afin de mettre \u00e0 disposition le fameux article sur le Web ! Pour cela j&rsquo;ai utilis\u00e9 tr\u00e8s peu d&rsquo;outils et un peu d&rsquo;imagination.<\/p>\n<p>Le but du pr\u00e9sent article est de <strong>vous pr\u00e9senter l&rsquo;outil de la cha\u00eene que j&rsquo;ai d\u00fb d\u00e9velopper pour mener \u00e0 bien ce projet : <a href=\"https:\/\/github.com\/blankoworld\/gachette\">Gachette<\/a><\/strong>. Puis de vous d\u00e9crire comment le mettre en place.<\/p>\n<p>Mais avant tout commen\u00e7ons \u00e0 planter le d\u00e9cor !<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/nature\/goutte.jpg\" alt=\"Ph\u00e9nom\u00e8ne de goutte qui remonte apr\u00e8s \u00eatre tomb\u00e9 dans l&rsquo;eau\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/flickr.com\/photos\/nikkvalentine\/\">profil de Nikk sur Flickr<\/a><\/em> sous licence CC BY 2.0.<\/p>\n<h1 id=\"plantons-le-d\u00e9cor-avec-lhistoire-logicielle-de-ce-blog\">Plantons le d\u00e9cor avec l&rsquo;Histoire logicielle de ce blog<\/h1>\n<p>(je comprendrais que ce chapitre ne soit pas tr\u00e8s enthousiasmant pour vous, si c&rsquo;est le cas, passez au suivant &#x1f609;)<\/p>\n<p><a href=\"https:\/\/olivier.dossmann.net\/2016\/03\/hugo_le_moteur_de_blog_statique_rapide_et_moderne\/\">En 2016 je vous parlais d&rsquo;Hugo, le moteur de blog statique<\/a> que j&rsquo;utilise pour \u00ab confectionner \u00bb ce blog. Outil tr\u00e8s int\u00e9ressant qui <strong>utilise de simples fichiers pour g\u00e9n\u00e9rer un site web entier<\/strong>, statique ; c&rsquo;est \u00e0 dire en pur HTML et ne demandant aucune autre ressource finale qu&rsquo;un serveur Web tel qu&rsquo;Apache2, nginx, lighttpd, Caddy Server et j&rsquo;en passe\u2026 Dans ma situation, le serveur Web en question est Nginx depuis 2013 environ - mais qu&rsquo;importe.<\/p>\n<p>Afin de garder en m\u00e9moire les travaux effectu\u00e9s, <a href=\"https:\/\/olivier.dossmann.net\/2017\/04\/gitea-service-git-sans-prise-de-t%C3%AAte-issu-de-gogs\/\">je vous partageais en 2017 l&rsquo;outil Gitea, service Git sans prise de t\u00eate issu de Gogs<\/a>.<\/p>\n<p>\u00c0 cette \u00e9poque (entre 2016 et 2019\/2020), pour publier mon blog il me faut :<\/p>\n<ul>\n<li>(manuel) \u00e9crire l&rsquo;article dans <strong>un fichier texte<\/strong><\/li>\n<li>(manuel) (facultatif) l&rsquo;enregistrer sous Git avec <strong>un commit<\/strong><\/li>\n<li>(manuel) (facultatif) <strong>envoyer<\/strong> l&rsquo;article <strong>sur Gitea<\/strong><\/li>\n<li>(manuel) <strong>compiler le site<\/strong> \u00e0 l&rsquo;aide de la commande <code>hugo<\/code><\/li>\n<li>(manuel) <strong>synchroniser les fichiers r\u00e9sultants<\/strong> sur mon serveur dot\u00e9 de Nginx, par exemple avec une commande <code>rsync<\/code> simple<\/li>\n<\/ul>\n<p>\u00c0 force de <a href=\"https:\/\/olivier.dossmann.net\/2016\/02\/fosdem-2016\/\">tra\u00eener \u00e0 FOSDEM en 2016<\/a> et <a href=\"https:\/\/olivier.dossmann.net\/2017\/02\/fosdem-2017-rencontre-avec-des-aliens\/\">en 2017<\/a>, j&rsquo;ai non seulement appris que l&rsquo;informatique sert \u00e0 automatiser des t\u00e2ches mais qu&rsquo;il est aussi possible de le faire de mani\u00e8re \u00ab simple \u00bb.<\/p>\n<p>Je me renseigne et j&rsquo;apprends que sous Gitlab, Github et Gitea (plateformes que j&rsquo;utilise \u00e0 l&rsquo;\u00e9poque), il existe un <a href=\"https:\/\/blog.hubspot.fr\/website\/webhook\">syst\u00e8me de webhooks<\/a>. Qu&rsquo;est-ce \u00e0 dire ? Grossomodo je peux configurer ces plateformes pour qu&rsquo;\u00e0 <strong>chaque envoi de mon travail peut r\u00e9sulter un appel \u00e0 une adresse Web<\/strong>.<\/p>\n<p>Il suffit ensuite, \u00e0 l&rsquo;adresse appel\u00e9e, de <strong>pouvoir \u00e9tudier la requ\u00eate envoy\u00e9e et d&rsquo;agir en cons\u00e9quence<\/strong> (par exemple d\u00e9ployer un site).<\/p>\n<p>Sur le papier \u00e7a a l&rsquo;air fun :<\/p>\n<ul>\n<li>(manuel) j&rsquo;\u00e9cris mon article dans <strong>un fichier texte<\/strong><\/li>\n<li>(manuel) je <strong>commite et pousse sur Gitea\/Github\/Gitlab<\/strong><\/li>\n<li>(automatique) la plateforme de r\u00e9ception <strong>envoie un appel<\/strong> \u00e0 un de mes serveurs<\/li>\n<li>(automatique) un de mes serveurs re\u00e7evant l&rsquo;appel <strong>lance un script<\/strong> ou une action <strong>pour publier<\/strong> les derni\u00e8res nouveaut\u00e9s<\/li>\n<\/ul>\n<p>Parfait on se lance l\u00e0 dedans !<\/p>\n<h1 id=\"la-naissance-de-gachette\">La naissance de Gachette<\/h1>\n<p>En 2019 o\u00f9 j&rsquo;\u00e9tudiais la question, je ne trouvais pas de serveur simple et l\u00e9ger qui puisse supporter les appels envoy\u00e9s par Github, Gitlab et Gitea. <strong>Hors de question d&rsquo;avoir 3 serveurs de webhooks diff\u00e9rents sur mes machines<\/strong>.<\/p>\n<p>Comme j&rsquo;ai cru comprendre \u00eatre ing\u00e9nieur logiciel au travail &#x1f923;, je me suis dit pouvoir l&rsquo;\u00eatre aussi \u00e0 la maison sur mon temps personnel. \u00c0 l&rsquo;\u00e9poque je m&rsquo;amusais un peu avec le <a href=\"https:\/\/crystal-lang.org\/\">langage Crystal<\/a> avec des amis, c&rsquo;\u00e9tait l&rsquo;occasion.<\/p>\n<p>C&rsquo;est ainsi qu&rsquo;est n\u00e9 <a href=\"https:\/\/github.com\/blankoworld\/gachette\">Gachette<\/a>, un serveur de webhooks sous Licence MIT en <a href=\"https:\/\/crystal-lang.org\/\">Crystal<\/a> (une syntaxe comme Ruby, mais compil\u00e9) qui supporte les plateformes Github, Gitlab et Gitea.<\/p>\n<p>Sous ce langage, il existe quelques <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Framework\">frameworks<\/a>, dont <a href=\"https:\/\/kemalcr.com\/\">Kemal, un framework web minimaliste<\/a> qui permet d&rsquo;ouvrir quelques routes tr\u00e8s facilement. C&rsquo;est ce que j&rsquo;ai utilis\u00e9 pour cr\u00e9er <a href=\"https:\/\/github.com\/blankoworld\/gachette\/releases\/tag\/0.1\">la premi\u00e8re version 0.1 de Gachette en 2019<\/a>.<\/p>\n<p>\u00c0 tout casser on doit \u00eatre \u00e0 100 lignes de code. Bref. \u00c0 aujourd&rsquo;hui, plut\u00f4t 200+ lignes de code Crystal.<\/p>\n<p>Voyons les particularit\u00e9s de mise en place de Gachette.<\/p>\n<h1 id=\"mise-en-place-de-gachette-sur-un-serveur\">Mise en place de Gachette sur un serveur<\/h1>\n<p>Je ne vais pas reproduire <a href=\"https:\/\/github.com\/blankoworld\/gachette\/blob\/master\/README.md#installation\">ce que le fichier README de Gachette \u00e9nonce si bien pour l&rsquo;installation<\/a>, cependant je peux vous faire un r\u00e9sum\u00e9 rapide de l&rsquo;installation\/configuration :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># On r\u00e9cup\u00e8re le projet<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>git clone https:\/\/github.com\/blankoworld\/gachette.git\n<\/span><\/span><span style=\"display:flex;\"><span>cd gachette\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># Pr\u00e9-requis : Crystal, Shards et make doivent \u00eatre install\u00e9s<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>shards install\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># On compile le logiciel<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>make\n<\/span><\/span><\/code><\/pre><\/div><p>\u00c0 l&rsquo;issue de ces commandes devrait appara\u00eetre un fichier <strong>bin\/gachette<\/strong> qui est un ex\u00e9cutable utilisable tel quel.<\/p>\n<p>On le place o\u00f9 on veut, par exemple dans <strong>\/srv\/www\/gachette\/gachette<\/strong> puis on \u00e9dite le fichier <strong>\/etc\/systemd\/system\/gachette.service<\/strong> dont le contenu est le suivant :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-service\" data-lang=\"service\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">[Unit]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">Description<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">Gachette webhooks service<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">After<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">network.target<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">[Service]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">Type<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">simple<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">User<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">http<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">WorkingDirectory<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">\/srv\/www\/gachette<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">ExecStart<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">\/srv\/www\/gachette\/gachette -c \/etc\/gachette.ini -b 0.0.0.0 -p 3000<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">Environment<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">&#34;KEMAL_ENV=production&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">Restart<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">always<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">[Install]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">WantedBy<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">multi-user.target<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>\u00c0 noter qu&rsquo;il faut adapter <strong>User=http<\/strong> par l&rsquo;utilisateur de votre syst\u00e8me d&rsquo;exploitation, par exemple sous Debian c&rsquo;est <em>User=www-data<\/em>. Mais \u00e9galement le port utilis\u00e9, ici c&rsquo;est le port <strong>3000<\/strong>.<\/p>\n<p>Ensuite cr\u00e9ez le fichier <strong>\/etc\/gachette.ini<\/strong> avec un contenu qui va d\u00e9crire les d\u00e9p\u00f4ts\/projets qui vont contacter le serveur webhooks et d\u00e9clencher une action. Imaginons que le projet Gachette, sur Github, \u00e0 l&rsquo;adresse <a href=\"https:\/\/github.com\/blankoworld\/gachette\">https:\/\/github.com\/blankoworld\/gachette<\/a> veuille qu&rsquo;\u00e0 chaque commit nous ex\u00e9cutions le contenu du script <em>\/srv\/www\/gachette\/deploiement.sh<\/em>, nous allons \u00e9crire :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># contenu du fichier \/etc\/gachette.ini<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>Un_nom_au_hasard<span style=\"color:#f92672\">]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>service <span style=\"color:#f92672\">=<\/span> github\n<\/span><\/span><span style=\"display:flex;\"><span>namespace <span style=\"color:#f92672\">=<\/span> blankoworld\/gachette\n<\/span><\/span><span style=\"display:flex;\"><span>key <span style=\"color:#f92672\">=<\/span> monMot2Passe\n<\/span><\/span><span style=\"display:flex;\"><span>scriptfile <span style=\"color:#f92672\">=<\/span> \/srv\/www\/gachette\/deploiement.sh\n<\/span><\/span><\/code><\/pre><\/div><p>Une fois la configuration faite, le service install\u00e9, vous pouvez faire en tant qu&rsquo;utilisateur root :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># pour lancer le service<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>systemctl start gachette.service\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># pour voir l&#39;\u00e9tat du service<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>systemctl status gachette.service\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># pour lancer le service \u00e0 chaque red\u00e9marrage : <\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>systemctl enable gachette.service\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># pour suivre ce qu&#39;il se passe dans Gachette : <\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>journalctl -xe -f -u gachette.service\n<\/span><\/span><\/code><\/pre><\/div><p>Tout r\u00e9sidera ensuite dans <strong>votre capacit\u00e9 \u00e0 cr\u00e9er des scripts qui feront le travail de d\u00e9ploiement<\/strong>.<\/p>\n<p>Dans ma situation, pour le pr\u00e9sent blog, j&rsquo;ai un script qui :<\/p>\n<ul>\n<li>r\u00e9cup\u00e8re la derni\u00e8re version du d\u00e9p\u00f4t Git du blog<\/li>\n<li>compile le site pour g\u00e9n\u00e9rer les fichiers statiques<\/li>\n<li>synchronise le r\u00e9sultat - si r\u00e9ussi - vers un dossier particulier<\/li>\n<li>relance le serveur Web avec les nouveaux fichiers<\/li>\n<\/ul>\n<p>Suite \u00e0 <a href=\"https:\/\/olivier.dossmann.net\/2022\/12\/utilisation-de-tr%C3%A6fik-pour-publier-ses-conteneurs-docker\/\">mon dernier article sur Tr\u00e6fik pour publier ses conteneurs Docker<\/a>, je me suis m\u00eame essay\u00e9 \u00e0 l&rsquo;utilisation de conteneurs Docker pour isoler les environnements, voire m\u00eame \u00e0 avoir des images Docker communes pour l&rsquo;ensemble des sites Web statiques : tr\u00e8s pratique !<\/p>\n<p>On s&rsquo;amuse bien par ici en somme &#x1f609;.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Bien que certains jugerons ma cha\u00eene de publication assez alambiqu\u00e9e, utilisant des outils peu communs, voire pas du tout utilis\u00e9s (Gachette notamment), ce syst\u00e8me a d\u00e9j\u00e0 fait ses preuves et tourne bien : moins d&rsquo;une minute apr\u00e8s avoir enregistr\u00e9 un article, il est publi\u00e9 sur le web sans action humaine.<\/p>\n<p>Il n&rsquo;y a pour cela que 3 outils simples :<\/p>\n<ul>\n<li>Gitea comme d\u00e9p\u00f4t Git ; qui d\u00e9clenche une demande aupr\u00e8s d&rsquo;un serveur distant (ici Gachette)<\/li>\n<li>Gachette qui re\u00e7oit la demande et lance un script de compilation\/d\u00e9ploiement\/traitement (bref, une action est men\u00e9e)<\/li>\n<li>Nginx comme serveur Web pour diffuser les pages statiques ainsi g\u00e9n\u00e9r\u00e9es<\/li>\n<\/ul>\n<p>L&rsquo;utilisation de Docker en sus am\u00e9liore le c\u00f4t\u00e9 r\u00e9utilisable des outils : pour plusieurs sites statiques j&rsquo;utilise la m\u00eame image qui va compiler les fichiers puis les mettre \u00e0 disposition sur le net.<\/p>\n<p><strong>La difficult\u00e9 r\u00e9side cependant \u00e0 notre capacit\u00e9 \u00e0 cr\u00e9er des scripts<\/strong> pour lancer les diff\u00e9rentes actions sur le serveur. La ligne de commande et Bash sont de fid\u00e8les alli\u00e9s !<\/p>\n<h1 id=\"liens-utiles\">Liens utiles<\/h1>\n<ul>\n<li><a href=\"https:\/\/docs.github.com\/fr\/webhooks-and-events\/webhooks\/creating-webhooks\">Configurer un Webhook sur Github<\/a><\/li>\n<li><a href=\"https:\/\/docs.gitlab.com\/ee\/user\/project\/integrations\/webhooks.html#configure-a-webhook-in-gitlab\">Configure a webhook in Gitlab<\/a><\/li>\n<li><a href=\"https:\/\/docs.gitea.io\/en-us\/usage\/webhooks\/\">Gitea Webhooks<\/a><\/li>\n<\/ul>\n"},{"title":"Utilisation de tr\u00e6fik pour publier ses conteneurs Docker","link":"https:\/\/olivier.dossmann.net\/2022\/12\/utilisation-de-tr%C3%A6fik-pour-publier-ses-conteneurs-docker\/","pubDate":"Wed, 07 Dec 2022 07:16:11 +0100","guid":"https:\/\/olivier.dossmann.net\/2022\/12\/utilisation-de-tr%C3%A6fik-pour-publier-ses-conteneurs-docker\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Il y a 8 ans <a href=\"https:\/\/olivier.dossmann.net\/2014\/04\/voici-docker-plus-l%C3%A9ger-et-plus-simple-quune-machine-virtuelle\/\">je vous faisais d\u00e9couvrir Docker<\/a>. \u00c0 l&rsquo;\u00e9poque je ne savais pas encore \u00e0 quel point cet outil tr\u00e8s controvers\u00e9 serait utilis\u00e9 !<\/p>\n<p><a href=\"https:\/\/www.docker.com\/\">Docker<\/a> est quasiment devenu un standard. Qu&rsquo;on le veuille ou non il arrive souvent qu&rsquo;on ait un conteneur Docker \u00e0 lancer. On finit toujours par devoir lire la <a href=\"https:\/\/docs.docker.com\/get-started\/\">documentation de Docker<\/a>.<\/p>\n<p>Avant de continuer sur la probl\u00e9matique du pr\u00e9sent article, je tiens \u00e0 vous signaler qu&rsquo;il est important de savoir un minimum jouer avec les fichiers <strong>docker-compose.yml<\/strong>. Cas \u00e9ch\u00e9ant je crains que cet article ne vous soit d&rsquo;aucune autre utilit\u00e9 que nourrir votre curiosit\u00e9.<\/p>\n<p>Ainsi, un probl\u00e8me qui survient fr\u00e9quemment quand on a cr\u00e9e nos images, nos conteneurs puis que nous lan\u00e7ons ces derniers : comment publier nos conteneurs afin qu&rsquo;ils soient disponibles sur Internet ? En g\u00e9n\u00e9ral la solution est d&rsquo;utiliser un proxy (par exemple <a href=\"https:\/\/nginx.org\/en\/docs\/\">Nginx<\/a>) qui va \u00e9tudier les requ\u00eates HTTP demand\u00e9es sur le serveur puis rediriger vers le port choisi d&rsquo;un conteneur Docker qu&rsquo;on a lanc\u00e9.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/logos\/traefik.png\" alt=\"Image du logo de Traefik\"><\/p>\n<p>Dans cet article je vais rapidement expliquer pourquoi le choix de <a href=\"https:\/\/traefik.io\/\">Tr\u00e6fik<\/a> plut\u00f4t que Nginx que j&rsquo;utilisais avant \u00e7a. Ensuite j&rsquo;expliquerai les configurations utilis\u00e9es pour mettre \u00e0 disposition nos conteneurs. Nous aborderons rapidement quelques points pour aller plus loin et finirons par une conclusion.<\/p>\n<h1 id=\"choix-du-proxy\">Choix du proxy<\/h1>\n<p>Habituellement, pour mettre en place l&rsquo;acc\u00e8s \u00e0 des sites web que j&rsquo;h\u00e9berge, j&rsquo;utilisais <a href=\"https:\/\/httpd.apache.org\/\">Apache<\/a>, <a href=\"https:\/\/www.nginx.com\/\">Nginx<\/a> ou encore <a href=\"https:\/\/caddyserver.com\/\">Caddy<\/a>.<\/p>\n<p>Cependant il y avait pas mal de contraintes :<\/p>\n<ul>\n<li>chaque nouveau domaine demandait une configuration sp\u00e9cifique avec un fichier sp\u00e9cifique, des dossiers sp\u00e9cifiques,<\/li>\n<li>pour avoir un certificat SSL par <a href=\"https:\/\/letsencrypt.org\/\">Let&rsquo;s Encrypt<\/a>, il fallait parfois user d&rsquo;intelligence, m\u00eame une fois qu&rsquo;on utilise <a href=\"https:\/\/certbot.eff.org\/\">Certbot<\/a>, notamment pour red\u00e9marrer le serveur Web une fois les certificats renouvel\u00e9s,<\/li>\n<li>quand on lan\u00e7ait un conteneur Docker, il valait mieux choisir un port sp\u00e9cifique, par exemple 8081, puis recopier ce nombre <strong>en dur<\/strong> dans la configuration du serveur Web (Nginx dans mon cas) et je ne suis pas le plus imaginatif pour les num\u00e9ros de ports qui se cumulaient\u2026<\/li>\n<\/ul>\n<p>Ce qui est assez p\u00e9nible. Tr\u00e8s consommateur de temps.<\/p>\n<p>\u00c9tant donn\u00e9 que nous utilisons d\u00e9j\u00e0 les possibilit\u00e9s de Docker et Docker Compose, autant continuer en utilisant un outil ayant un backend avec Docker, non ?<\/p>\n<p>C&rsquo;est l\u00e0 que Tr\u00e6fik entre en jeu :<\/p>\n<ul>\n<li>il permet <strong>avec quelques lignes dans vos fichiers docker-compose.yml<\/strong> de <strong>d\u00e9finir le domaine \u00e0 utiliser<\/strong> pour tel ou tel conteneur Docker<\/li>\n<li>il s&rsquo;occupe de <strong>faire la demande de certificat Let&rsquo;s Encrypt et le renouvellement<\/strong> de ces derniers<\/li>\n<li><strong>le lancement d&rsquo;un conteneur Docker ne demande pas de relancer Tr\u00e6fik<\/strong>, il y a une d\u00e9tection automatique des configurations<\/li>\n<li><strong>on laisse les ports choisis par Docker pour nos conteneurs<\/strong>, ce qui nous \u00e9vite d&rsquo;en faire une gestion<\/li>\n<li>\u00e9tant cod\u00e9 en Go (tout comme Docker et Docker Compose), Tr\u00e6fik fait partie du m\u00eame monde, il est donc aussi rapide et compatible avec les outils utilis\u00e9s<\/li>\n<li><strong>pas besoin d&rsquo;installer Tr\u00e6fik<\/strong> : il sera un conteneur Docker comme le reste des services de la machine :-)<\/li>\n<\/ul>\n<p>Au final ce sont les contraintes des serveurs Web dont j&rsquo;ai eu l&rsquo;usage qui m&rsquo;ont convaincu d&rsquo;essayer Tr\u00e6fik. Voyons donc \u00e0 quoi cela ressemble !<\/p>\n<p><strong>ATTENTION<\/strong> : Tr\u00e6fik ne r\u00e9soud pas tous les probl\u00e8mes du monde. Il va falloir exp\u00e9rimenter, recommencer plusieurs fois, plier son cerveau pour saisir de quoi il retourne. Parfois demander de l&rsquo;aide. C&rsquo;est un processus d&rsquo;apprentissage qui en vaut la peine cependant !<\/p>\n<h1 id=\"configuration-de-tr\u00e6fik\">Configuration de tr\u00e6fik<\/h1>\n<p>Pour mettre en place Tr\u00e6fik nous allons proc\u00e9der comme la <a href=\"https:\/\/doc.traefik.io\/traefik\/\">documentation de Tr\u00e6fik Proxy<\/a> le propose :<\/p>\n<ul>\n<li>faire un <strong>docker-compose.yml<\/strong> pour d\u00e9crire notre service Tr\u00e6fik<\/li>\n<li>utiliser un fichier de configuration externe pour configurer le service : le fichier <strong>traefik.yml<\/strong><\/li>\n<li>demander \u00e0 exposer les ports 80 et 443<\/li>\n<\/ul>\n<p>Pour cela, voici le fichier <strong>docker-compose.yml<\/strong> pour lancer le service Tr\u00e6fik :<\/p>\n<pre tabindex=\"0\"><code class=\"language-docker-compose\" data-lang=\"docker-compose\">version: &#39;3&#39;\n\nservices:\n    proxy:\n      image: traefik:v2.8\n      restart: always\n      ports:\n      \t- 80:80\n        - 443:443\n      volumes:\n        # pour que Tr\u00e6fik \u00e9coute les \u00e9v\u00e9nements Docker : \u00e0 adapter chez vous\n        - \/var\/run\/docker.sock:\/var\/run\/docker.sock\n        # Utilisation d&#39;un fichier de configuration\n        - ${PWD}\/traefik.yml:\/etc\/traefik\/traefik.yml\n        # M\u00e9morisation des certificats TLS (par Let&#39;s Encrypt)\n        - ${PWD}\/acme.json:\/acme.json\n      labels:\n        # Activation du tls\n        - &#34;traefik.http.routers.api.tls&#34;\n        # nawak, pour que les logs la boucle sur service error: port is missing\n        - &#34;traefik.http.services.nawak.loadbalancer.server.port=8484&#34;\n<\/code><\/pre><p>Ce qui va monter le fichier local <strong>\/var\/run\/docker.sock<\/strong> pour discuter avec Docker : adaptez-le au fichier Socket de votre service Docker.<\/p>\n<p>Cela va utiliser le fichier <strong>traefik.yml<\/strong> local qui contient notre configuration pour notre service Tr\u00e6fik.<\/p>\n<p>On stocke les donn\u00e9es des certificats dans le fichier local <strong>acme.json<\/strong> : pensez \u00e0 faire <code>touch acme.json<\/code> pour cr\u00e9er le fichier avant de lancer.<\/p>\n<p>Et voici le contenu de notre fichier <strong>traefik.yml<\/strong> :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">api<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">dashboard<\/span>: <span style=\"color:#66d9ef\">true<\/span> <span style=\"color:#75715e\"># pour activer un tableau de bord<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">providers<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">docker<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">network<\/span>: <span style=\"color:#ae81ff\">traefik<\/span> <span style=\"color:#75715e\"># r\u00e9seau de discussion entre les conteneurs et traefik<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">exposedByDefault<\/span>: <span style=\"color:#66d9ef\">false<\/span> <span style=\"color:#75715e\"># n&#39;active pas les conteneurs par d\u00e9faut sur le web<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">entryPoints<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">web<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">address<\/span>: <span style=\"color:#e6db74\">&#34;:80&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">http<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">redirections<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#f92672\">entryPoint<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>          <span style=\"color:#f92672\">to<\/span>: <span style=\"color:#ae81ff\">websecure<\/span> <span style=\"color:#75715e\"># pointe sur websecure plus bas (port 443 en somme)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>          <span style=\"color:#f92672\">scheme<\/span>: <span style=\"color:#ae81ff\">https<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">websecure<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">address<\/span>: <span style=\"color:#e6db74\">&#34;:443&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">http<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">tls<\/span>: <span style=\"color:#75715e\"># configuration par d\u00e9faut<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#f92672\">certResolver<\/span>: <span style=\"color:#ae81ff\">letsencrypt<\/span> <span style=\"color:#75715e\"># pointe sur la config. letsencrypt plus bas<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">traefik<\/span>: <span style=\"color:#75715e\"># adresse pour le dashboard ;-)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">address<\/span>: <span style=\"color:#e6db74\">&#34;:8080&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">certificatesResolvers<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">letsencrypt<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">acme<\/span>:\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">email<\/span>: <span style=\"color:#e6db74\">&#34;mon-courriel@domaine.tld&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">storage<\/span>: <span style=\"color:#e6db74\">&#34;\/acme.json&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>      <span style=\"color:#f92672\">httpChallenge<\/span>: <span style=\"color:#75715e\"># le type utilis\u00e9 pour valider les certificats Let&#39;s Encrypt<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#f92672\">entryPoint<\/span>: <span style=\"color:#ae81ff\">web<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Je me suis permis de commenter un peu partout pour savoir de quoi il retourne.<\/p>\n<p>Sachez qu&rsquo;il existe aussi une m\u00e9thode pour cr\u00e9er un fichier sur lequel Traefik va lire r\u00e9guli\u00e8rement pour mettre \u00e0 jour sa configuration. Peut-\u00eatre ferais-je un autre article sur ce sujet un jour. Petite piste : c&rsquo;est le <a href=\"https:\/\/doc.traefik.io\/traefik\/providers\/file\/\"><em>Provider<\/em> nomm\u00e9 <strong>file<\/strong><\/a> qui permet de faire \u00e7a.<\/p>\n<p>Une fois configur\u00e9, les autres conteneurs Docker que nous lan\u00e7ons n&rsquo;ont plus besoin que des lignes suivantes dans <strong>leur fichier docker-compose.yml<\/strong> :<\/p>\n<pre tabindex=\"0\"><code class=\"language-docker-compose\" data-lang=\"docker-compose\">labels:\n  - &#34;traefik.enable=true&#34;\n  - &#34;traefik.http.routers.un-nom-de-service.rule=Host(`mon.domaine.com`)&#34;\n  - &#34;traefik.http.routers.un-nom-de-service.entrypoints=websecure&#34;\n<\/code><\/pre><p>Au lancement de vos conteneurs via un fichier docker-compose.yml, Tr\u00e6fik lira les informations contenues dans <strong>labels<\/strong> et tentera de les analyser puis utiliser \u00e0 bon essien.<\/p>\n<p>Dans l&rsquo;exemple donn\u00e9 ci-avant, on comprend que :<\/p>\n<ul>\n<li>trafik.enable : le service doit \u00eatre publi\u00e9 par Traefik (car dans la configuration nous avions mis <code>exposedByDefault: false<\/code>, donc pas de publication par d\u00e9faut)<\/li>\n<li>traefik.http.routers.un-nom-de-service.rule=Host(`mon.domaine.com`) : pour votre service remplacez un-nom-de-service par ce que vous voulez. Et <strong>mon.domaine.com<\/strong> par le domaine que vous d\u00e9tenez<\/li>\n<li>la derni\u00e8re ligne ne sert qu&rsquo;\u00e0 indiquer quel entr\u00e9e par d\u00e9faut accepter\/utiliser. En l&rsquo;occurence celle nomm\u00e9e <em>websecure<\/em> qui, dans notre configuration du fichier traefik.yml est configur\u00e9e pour \u00eatre disponible sur le port 443 avec un r\u00e9solveur de certificat Let&rsquo;s Encrypt<\/li>\n<\/ul>\n<p>Exemple de docker-compose.yml pour lancer un service :<\/p>\n<pre tabindex=\"0\"><code class=\"language-docker-compose\" data-lang=\"docker-compose\">version: &#39;3&#39;\n\nservices:\n    whoami:\n      image: traefik\/whoami\n      labels:\n        - &#34;traefik.enable=true&#34;\n        - &#34;traefik.http.routers.whoami.rule=Host(`whoami.domaine.com`)&#34;\n        - &#34;traefik.http.routers.whoami.entrypoints=websecure&#34;\n<\/code><\/pre><p>Y aurait-il encore d&rsquo;autres fonctionnalit\u00e9s sympas ?<\/p>\n<h1 id=\"pour-aller-plus-loin\">Pour aller plus loin<\/h1>\n<p>Dans ce que j&rsquo;ai pratiqu\u00e9, voici quelques points \u00e0 \u00e9tudier pour aller plus loin :<\/p>\n<ul>\n<li>utilisation de <a href=\"https:\/\/doc.traefik.io\/traefik\/https\/acme\/#dnschallenge\">Let&rsquo;s Encrypt par challenge DNS<\/a> qui permet de g\u00e9rer les DNS de son fournisseur de domaine en direct. Par exemple pour Gandi il suffit de fournir la variable d&rsquo;environnement GANDIV5_API_KEY<\/li>\n<li>utilisation d&rsquo;un fichier externe avec <code>watch: true<\/code> pour appliquer les changements d\u00e8s modification du fichier<\/li>\n<li>utilisation de middleware pour une authentification basique<\/li>\n<li>activer un port, par exemple 8080 pour l&rsquo;acc\u00e8s au tableau de bord<\/li>\n<li>dans certains cas o\u00f9 les services ne sont pas forc\u00e9ment dans des Docker, il peut \u00eatre int\u00e9ressant de fournir une IP et un port sp\u00e9cifique par un loadBalancer, Cf. <a href=\"https:\/\/doc.traefik.io\/traefik\/routing\/services\/#servers\">https:\/\/doc.traefik.io\/traefik\/routing\/services\/#servers<\/a><\/li>\n<\/ul>\n<p>Il y a beaucoup plus \u00e0 d\u00e9couvrir avec Tr\u00e6fik. Je vous invite \u00e0 <a href=\"https:\/\/doc.traefik.io\/traefik\/\">lire la documentation<\/a> qui est tr\u00e8s riche et bien expliqu\u00e9e.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Utiliser Tr\u00e6fik a permis de r\u00e9duire consid\u00e9rablement le temps de maintenance de mes services, que ce soit en terme de mises \u00e0 jour, de renouvellement de certificats et d&rsquo;organisation des dossiers\/fichiers.<\/p>\n<p>Cependant qu&rsquo;on se le dise, ce n&rsquo;est pas exempt de quelques difficult\u00e9s :<\/p>\n<ul>\n<li>il n&rsquo;y a pas toujours des images Docker de vos services, il faut donc parfois fabriquer soi-m\u00eame les conteneurs,<\/li>\n<li>la courbe d&rsquo;apprentissage de Tr\u00e6fik est assez difficile,<\/li>\n<li>on se casse les m\u00e9ninges jusqu&rsquo;\u00e0 comprendre comment fournir un service sur Tr\u00e6fik<\/li>\n<\/ul>\n<p>Si on fait l&rsquo;effort d&rsquo;apprendre Tr\u00e6fik et d&rsquo;investir du temps, c&rsquo;est surtout pour assurer la possibilit\u00e9 de connecter tout cela \u00e0 un <a href=\"https:\/\/doc.traefik.io\/traefik\/observability\/metrics\/overview\/\">syst\u00e8me de m\u00e9triques<\/a>, pouvoir faire de la journalisation, v\u00e9rifier l&rsquo;\u00e9tat de sant\u00e9 des serveurs, etc.<\/p>\n<p>Il y a de quoi faire !<\/p>\n"},{"title":"Jeu, Dreamlight Valley","link":"https:\/\/olivier.dossmann.net\/2022\/11\/jeu-dreamlight-valley\/","pubDate":"Fri, 04 Nov 2022 21:58:39 +0100","guid":"https:\/\/olivier.dossmann.net\/2022\/11\/jeu-dreamlight-valley\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Dans <a href=\"https:\/\/olivier.dossmann.net\/2022\/09\/%C3%A9tat-des-lieux-2022\/\">l&rsquo;\u00e9tat des lieux 2022 de mon espace num\u00e9rique<\/a> (un pr\u00e9c\u00e9dent article de ce blog) j&rsquo;\u00e9non\u00e7ais DreamLight Valley, un jeu vid\u00e9o Disney cr\u00e9\u00e9 par <a href=\"https:\/\/www.gameloft.com\/\">GameLoft<\/a>.<\/p>\n<p>Parlons un peu de <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Disney_Dreamlight_Valley\">DreamLight Valley<\/a>, d\u00e9crivons le un peu pour ensuite s&rsquo;int\u00e9resser \u00e0 quelques \u00e9l\u00e9ments de la composition du jeu. Puis nous donnerons notre avis avant de conclure sur cet article.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/jeux\/dreamlight-valley-DDV_001.jpg\" alt=\"Image de DreamLight Valley\"><\/p>\n<h1 id=\"description\">Description<\/h1>\n<p>Qu&rsquo;est DreamLight Valley ? C&rsquo;est un jeu vid\u00e9o sorti en acc\u00e8s anticip\u00e9 en France le 6 septembre 2022. Disponible sur de nombreuses consoles comme Microsoft Xbox, Nintendo Switch, PC et Playstation 4 et 5.<\/p>\n<p>C&rsquo;est un jeu \u00e0 la troisi\u00e8me personne o\u00f9 le joueur \u00e9volue dans un monde en 3 dimensions et doit effectuer des qu\u00eates pour avancer dans l&rsquo;histoire.<\/p>\n<p>Vous \u00eates le h\u00e9ros principal de cette histoire et arrivez dans un univers qui a \u00e9t\u00e9 frapp\u00e9 par l&rsquo;Oubli : tous les personnages ont perdu la m\u00e9moire et plusieurs mal\u00e9fices parcourent ce monde.<\/p>\n<p>Comme dans beaucoup de jeux vous allez devoir sauver ce monde, lever les mal\u00e9fices, nettoyer l&rsquo;endroit et faire recouvrir la m\u00e9moire aux personnages autour de vous.<\/p>\n<p>Quelques exemples de personnages :<\/p>\n<ul>\n<li>Mickey,<\/li>\n<li>Picsou,<\/li>\n<li>Merlin l&rsquo;enchanteur,<\/li>\n<li>Wall-E,<\/li>\n<li>Dingo,<\/li>\n<li>etc.<\/li>\n<\/ul>\n<p>Ce jeu est fait pour les enfants, il n&rsquo;y a donc pas de syst\u00e8me de combats. Et vous \u00e9voluez dans un monde o\u00f9 vous devez travailler sur l&rsquo;amour, l&rsquo;amiti\u00e9, etc. C&rsquo;est \u00e0 dire que vous allez devoir cultiver vos amiti\u00e9s avec les diff\u00e9rents protagonistes.<\/p>\n<p>Dans ce jeu, vous avez plusieurs &ldquo;monnaies&rdquo; :<\/p>\n<ul>\n<li>des pi\u00e8ces d&rsquo;or quand vous vendez\/achetez des graines\/fruits\/l\u00e9gumes\/autres aux stands de Dingo<\/li>\n<li>des points Dreamlight \u00e0 utiliser pour d\u00e9bloquer des parties de l&rsquo;univers ou bien ouvrir des portes<\/li>\n<\/ul>\n<p>D&rsquo;autres points font penser \u00e0 d&rsquo;autres jeux vid\u00e9os connus.<\/p>\n<h1 id=\"\u00e9l\u00e9ments-du-jeu\">\u00c9l\u00e9ments du jeu<\/h1>\n<p>Qu&rsquo;on se le dise, DreamLight Valley n&rsquo;invente rien en la mati\u00e8re :<\/p>\n<ul>\n<li>on retrouve le syst\u00e8me d&rsquo;outils de Stardew Valley comme la pioche, l&rsquo;arrosoir, la pelle pour interagir avec l&rsquo;univers dans lequel on se trouve<\/li>\n<li>on discute avec les personnes comme dans beaucoup de jeux de r\u00f4le, on pourrait m\u00eame penser au jeu \u00ab Les Sims \u00bb<\/li>\n<li>il y a un peu du Animal Crossing dans la fa\u00e7on de pr\u00e9senter les qu\u00eates journali\u00e8res pour obtenir des points<\/li>\n<li>le ch\u00e2teau et ses portes pour transiter vers d&rsquo;autres univers fait penser \u00e0 Mario 64<\/li>\n<li>le choix des outils sur un cercle au milieu de l&rsquo;\u00e9cran fait penser \u00e0 Fortnite<\/li>\n<li>le syst\u00e8me de diff\u00e9rentes monnaies fait penser \u00e0 Animal Crossing<\/li>\n<li>on cultive des l\u00e9gumes comme dans Stardew Valley<\/li>\n<li>on d\u00e9core la maison comme dans Animal Crossing<\/li>\n<\/ul>\n<p>Le jeu est donc assez commun dans ses comportements et les \u00e9l\u00e9ments du jeu sont assez vus partout ailleurs pour ne pas se sentir d\u00e9pays\u00e9 !<\/p>\n<p>C&rsquo;est dans ce syst\u00e8me que nous avons \u00e9volu\u00e9 et nous allons exprimer notre ressenti \u00e0 ce sujet.<\/p>\n<h1 id=\"avis\">Avis<\/h1>\n<p>Bon enfant, ce jeu est purement et simplement de la d\u00e9tente o\u00f9 on \u00e9volue dans un univers o\u00f9 il fait bon vivre, bien qu&rsquo;on commence la partie de nuit, dans un monde tomb\u00e9 dans l&rsquo;Oubli o\u00f9 m\u00eame Merlin l&rsquo;enchanteur ne se souvient plus de grand chose.<\/p>\n<p>La particularit\u00e9 de ce jeu ira surtout dans le fait de se lier d&rsquo;amiti\u00e9 avec les personnages, de leur faire de bons petits plats avec les ingr\u00e9dients r\u00e9colt\u00e9s ici ou l\u00e0, de p\u00eacher, de planter, arroser, cueillir, etc.<\/p>\n<p>C&rsquo;est agr\u00e9able, on appr\u00e9cie !<\/p>\n<p>Cependant le jeu \u00e9tant encore en acc\u00e8s anticip\u00e9, le contenu est encore limit\u00e9 : m\u00eame avec 18 personnages (depuis une mise \u00e0 jour r\u00e9cente) les qu\u00eates sont vites finies.<\/p>\n<p>Au d\u00e9part la difficult\u00e9 est assez grande pour obtenir des DreamLight et pouvoir d\u00e9bloquer les parties de la carte pour acc\u00e9der \u00e0 de nouveaux personnages et nouvelles denr\u00e9es. Mais avec quelques habitudes on arrive ensuite \u00e0 cumuler assez de DreamLight pour continuer sereinement.<\/p>\n<p>Dans la partie on r\u00e9cup\u00e8re des morceaux de souvenirs mais on ne sait pas \u00e0 quoi cela sert pour le moment.<\/p>\n<p>Il y a aussi des qu\u00eates \u00e9v\u00e9nementielles qui permettent de d\u00e9bloquer des objets sp\u00e9cifiques \u00e0 l&rsquo;\u00e9v\u00e9nement pour les joueurs. D&rsquo;un point de vue &ldquo;monnaie&rdquo;, ils ne sont pas du tout int\u00e9ressants, mais il reste facile de les d\u00e9bloquer sur la p\u00e9riode limit\u00e9e. \u00c0 condition de jouer un peu tous les jours \u00e9videmment.<\/p>\n<p>On regrette aussi beaucoup d&rsquo;\u00eatre interrompu une fois par jour (voire plus) pour des bugs du jeu qui se fige, s&rsquo;arr\u00eate carr\u00e9ment sous Nintendo Switch sans avoir sauvegard\u00e9 ! Tr\u00e8s p\u00e9nible. Mais le jeu \u00e9tant en acc\u00e8s anticip\u00e9, on se fait une raison.<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Bien que nous ayons un jeu simple et bon enfant, avec de quoi s&rsquo;occuper la journ\u00e9e, les nombreux bugs du d\u00e9but et la rapidit\u00e9 de finition des qu\u00eates nous laisse beaucoup sur notre faim !<\/p>\n<p>En revanche le monde de Disney nous rappelant beaucoup de souvenirs - et on imagine bien que Disney s&rsquo;appuie l\u00e0 dessus pour vendre des produits d\u00e9riv\u00e9s - on a h\u00e2te de savoir quels seront les prochains personnages et comment les d\u00e9bloquer au plus vite lors des prochaines mises \u00e0 jour !<\/p>\n"},{"title":"Cron sous Docker avec Alpine","link":"https:\/\/olivier.dossmann.net\/2022\/10\/cron-sous-docker-avec-alpine\/","pubDate":"Wed, 12 Oct 2022 21:41:39 +0200","guid":"https:\/\/olivier.dossmann.net\/2022\/10\/cron-sous-docker-avec-alpine\/","description":"<h1 id=\"introduction\">Introduction<\/h1>\n<p>Chose promie, chose due ! J&rsquo;\u00e9cris un article sur mes p\u00e9r\u00e9grinations en informatique.<\/p>\n<p>Cette fois, je fais suite \u00e0 <a href=\"https:\/\/olivier.dossmann.net\/2022\/09\/%C3%A9tat-des-lieux-2022\/\">l&rsquo;\u00e9tat des lieux 2022 de mon espace num\u00e9rique<\/a>. Certains de mes services\/sites n\u00e9cessitent r\u00e9guli\u00e8rement d&rsquo;\u00eatre mis \u00e0 jour. Pour cela je souhaite utiliser <strong>Cron, un service sous GNU\/Linux qui permet de planifier le lancement de script(s) \u00e0 des fr\u00e9quences d\u00e9finies<\/strong>.<\/p>\n<p>Comme je souhaite utiliser Docker, je me suis demand\u00e9 s&rsquo;il \u00e9tait possible de cr\u00e9er un service d\u00e9di\u00e9 avec Cron, Docker et Alpine.<\/p>\n<p>Cet article explique quelques r\u00e8gles que j&rsquo;ai d\u00fb suivre pour y parvenir. Il vous faudra a minima conna\u00eetre Docker, voire Docker Compose pour comprendre quelque chose. \u00c0 la rigueur vous connaissez d\u00e9j\u00e0 Cron et souhaitez l&rsquo;utiliser sous Docker.<\/p>\n<p>Nous allons d&rsquo;abord r\u00e9flechir sur le sujet. Puis nous utiliserons l&rsquo;exemple d&rsquo;un site pour expliquer la solution choisie avant de conclure sur cette histoire.<\/p>\n<p>Pour les press\u00e9s, j&rsquo;ai fais un chapitre \u00ab En bref \u00bb \u00e0 la fin de cet article.<\/p>\n<p><img src=\"https:\/\/olivier.dossmann.net\/images\/objets\/montre.jpg\" alt=\"Montre de poignet avec un stylo\"><\/p>\n<p><em>Photo trouv\u00e9e sur le <a href=\"https:\/\/flickr.com\/photos\/ducly\/\">profil de Duc Ly sur Flickr<\/a><\/em> sous licence CC BY-SA 2.0.<\/p>\n<h1 id=\"r\u00e9flexion-sur-le-sujet\">R\u00e9flexion sur le sujet<\/h1>\n<p>Ce que nous souhaitons faire, c&rsquo;est lancer un service, par exemple <strong>almanax<\/strong>, et le mettre \u00e0 jour r\u00e9guli\u00e8rement avec une t\u00e2che dans le service <strong>cron<\/strong>. Je vais expliquer un peu ma r\u00e9flexion, si vous voulez entrer plus rapidement dans le vif du sujet, rendez-vous au chapitre suivant.<\/p>\n<p>Tout d&rsquo;abord il faut savoir que <strong>l&rsquo;environnement choisi est Docker<\/strong>. Un syst\u00e8me qui permet, <strong>\u00e0 partir d&rsquo;une image<\/strong> de <strong>g\u00e9n\u00e9rer plusieurs conteneurs<\/strong> ayant des points de montage diff\u00e9rents pour changer les donn\u00e9es qui s&rsquo;y trouvent.<\/p>\n<p><a href=\"https:\/\/docs.docker.com\/develop\/dev-best-practices\/\">Docker propose plusieurs bonnes pratiques<\/a>, parmi lesquelles :<\/p>\n<ul>\n<li>ne lancer qu&rsquo;un seul processus\/service dans chaque conteneur<\/li>\n<li>r\u00e9utiliser les images ou les parties d&rsquo;images au maximum pour r\u00e9duire \u00e0 la fois la maintenance des images\/conteneurs, le travail effectu\u00e9 et la place syst\u00e8me utilis\u00e9e pour les images de base<\/li>\n<\/ul>\n<p>Sachant que nous ne lan\u00e7ons qu&rsquo;un seul processus par conteneur Docker, et que je souhaite utiliser Cron, je vais devoir lancer cron dans un autre service. Concr\u00e8tement, Docker propose Docker Compose qui est un outil permettant de lancer plusieurs services \u00e0 la fois, avec des d\u00e9pendances entre eux (si besoin), une description des points de montage, des commandes \u00e0 lancer sur chaque service, les ports ouverts, etc.<\/p>\n<p>On peut \u00e9galement choisir l&rsquo;image qu&rsquo;on souhaite utiliser pour chaque service.<\/p>\n<p>Certes nous allons lancer Cron dans un service \u00e0 part, pour lancer les scripts que notre autre service a besoin pour mettre \u00e0 jour ce dernier - je pense notamment \u00e0 un script qui recompile et reconstitue mes pages webs statiques -, mais nous allons devoir avoir acc\u00e8s \u00e0 notre service <strong>almanax<\/strong> qui contient la logique principale du site.<\/p>\n<p>Pour cela, l&rsquo;id\u00e9e est de modifier l&rsquo;image de <strong>almanax<\/strong> pour qu&rsquo;elle contienne elle aussi <strong>cron<\/strong>. Ainsi on pourra lancer la m\u00eame image sous Docker Compose, avec une commande de lancement diff\u00e9rente.<\/p>\n<p>On utilise des services Docker. Chacun ayant d\u00e9j\u00e0 leur propre image sous Alpine. Ainsi, chaque image poss\u00e8de d\u00e9j\u00e0 Cron inclut dans Alpine.<\/p>\n<p>J&rsquo;allais dire qu&rsquo;une image vaut mieux que 1 000 mots, mais l\u00e0 nous allons donner un exemple avec Docker Compose pour comprendre de quoi il s&rsquo;agit.<\/p>\n<h1 id=\"mise-en-place-exemple-avec-lalmanax\">Mise en place, exemple avec l&rsquo;Almanax<\/h1>\n<p>Peut-\u00eatre le savez-vous d\u00e9j\u00e0 : j&rsquo;ai cr\u00e9e une page statique qui liste une quinzaine de qu\u00eates de l&rsquo;Almanax dans un jeu nomm\u00e9 Dofus.<\/p>\n<p>Cet outil est un de mes services fourni aux utilisateurs, mais \u00e9galement <a href=\"https:\/\/github.com\/blankoworld\/dofus-almanax\">un projet Open Source, dofus-almanax que je fournis via Github<\/a>. C&rsquo;est dans ce d\u00e9p\u00f4t Github que vous trouverez la plupart des fichiers n\u00e9cessaires au bon lancement d&rsquo;un service Cron que nous allons d\u00e9crire.<\/p>\n<p>On va \u00e9tudier le fichier docker-compose.yml suivant :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span>version: <span style=\"color:#e6db74\">&#39;3&#39;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>services:\n<\/span><\/span><span style=\"display:flex;\"><span>  generator:\n<\/span><\/span><span style=\"display:flex;\"><span>    build: .\n<\/span><\/span><span style=\"display:flex;\"><span>    image: dofus-almanax:0.2\n<\/span><\/span><span style=\"display:flex;\"><span>    volumes:\n<\/span><\/span><span style=\"display:flex;\"><span>      - almanax_public:\/opt\/almanax\/public:rw\n<\/span><\/span><span style=\"display:flex;\"><span>      - almanax_data:\/opt\/almanax\/dl:rw\n<\/span><\/span><span style=\"display:flex;\"><span>  web:\n<\/span><\/span><span style=\"display:flex;\"><span>    image: caddy:2-alpine\n<\/span><\/span><span style=\"display:flex;\"><span>    restart: always\n<\/span><\/span><span style=\"display:flex;\"><span>    depends_on:\n<\/span><\/span><span style=\"display:flex;\"><span>      - generator\n<\/span><\/span><span style=\"display:flex;\"><span>    volumes:\n<\/span><\/span><span style=\"display:flex;\"><span>      - almanax_public:\/usr\/share\/caddy:ro\n<\/span><\/span><span style=\"display:flex;\"><span>      - <span style=\"color:#e6db74\">${<\/span>PWD<span style=\"color:#e6db74\">}<\/span>\/Caddyfile:\/etc\/caddy\/Caddyfile\n<\/span><\/span><span style=\"display:flex;\"><span>    ports:\n<\/span><\/span><span style=\"display:flex;\"><span>      - 8888:80\n<\/span><\/span><span style=\"display:flex;\"><span>  cron:\n<\/span><\/span><span style=\"display:flex;\"><span>    image: dofus-almanax:0.2\n<\/span><\/span><span style=\"display:flex;\"><span>    restart: always\n<\/span><\/span><span style=\"display:flex;\"><span>    entrypoint: \/usr\/sbin\/crond\n<\/span><\/span><span style=\"display:flex;\"><span>    command: <span style=\"color:#f92672\">[<\/span><span style=\"color:#e6db74\">&#34;-f&#34;<\/span>, <span style=\"color:#e6db74\">&#34;-l&#34;<\/span>, <span style=\"color:#e6db74\">&#34;2&#34;<\/span>, <span style=\"color:#e6db74\">&#34;-L&#34;<\/span>, <span style=\"color:#e6db74\">&#34;\/dev\/stdout&#34;<\/span><span style=\"color:#f92672\">]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    volumes:\n<\/span><\/span><span style=\"display:flex;\"><span>      - almanax_public:\/opt\/almanax\/public:rw\n<\/span><\/span><span style=\"display:flex;\"><span>      - almanax_data:\/opt\/almanax\/dl:rw\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>volumes:\n<\/span><\/span><span style=\"display:flex;\"><span>  almanax_public:\n<\/span><\/span><span style=\"display:flex;\"><span>  almanax_data:\n<\/span><\/span><\/code><\/pre><\/div><p>Nous avons 3 services :<\/p>\n<ul>\n<li>generator : qui g\u00e9n\u00e8re le site web au lancement, suite \u00e0 quoi il s&rsquo;\u00e9teint,<\/li>\n<li>web : un service pour mettre \u00e0 disposition les fichiers statiques g\u00e9n\u00e9r\u00e9s par <strong>generator<\/strong>,<\/li>\n<li>cron : un service pour mettre \u00e0 jour r\u00e9guli\u00e8rement les fichiers statiques.<\/li>\n<\/ul>\n<p>La particularit\u00e9 de la solution :<\/p>\n<ul>\n<li><strong>generator<\/strong> et <strong>cron<\/strong> utilisent la m\u00eame image : dofus-almanax:0.2<\/li>\n<li>ces deux services n&rsquo;ont cependant pas la m\u00eame commande au lancement : l&rsquo;un prend la commande par d\u00e9faut de l&rsquo;image dofus-almanax:0.2, l&rsquo;autre lance sp\u00e9cifiquement cron<\/li>\n<li>pour le service <strong>cron<\/strong>, il sp\u00e9cifie avec le mot cl\u00e9 <strong>entrypoint<\/strong> d&rsquo;utiliser <strong>\/usr\/sbin\/crond<\/strong> (chemin absolu du binaire sous Alpine) et donne les param\u00e8tres <code>-f -l 2 -L \/dev\/stdout<\/code><\/li>\n<\/ul>\n<p>Que font les param\u00e8tres ?<\/p>\n<ul>\n<li><code>-f<\/code> d\u00e9finit de lancer le service en <code>foreground<\/code>, n\u00e9cessaire sous Docker, sinon cela ne fonctionne pas<\/li>\n<li><code>-l 2<\/code> d\u00e9finit un niveau de journalisation, habituellement c&rsquo;est 8 (Cf. <a href=\"https:\/\/unix.stackexchange.com\/a\/496741\">une explication des niveaux crond sous Stackoverflow<\/a><\/li>\n<li><code>-L \/dev\/stdout<\/code> permet d&rsquo;avoir une sortie \u00e0 l&rsquo;\u00e9cran dans Docker Compose<\/li>\n<\/ul>\n<p>\u00c7a, c&rsquo;est les \u00e9l\u00e9ments de base. Cependant comment fonctionne la mise \u00e0 jour, la configuration de cette derni\u00e8re, etc.?<\/p>\n<h1 id=\"fonctionnement-de-cron-dans-limage-dofus-almanax02\">Fonctionnement de Cron dans l&rsquo;image dofus-almanax:0.2<\/h1>\n<p>Nous l&rsquo;avions dit, l&rsquo;id\u00e9e est de tout mettre dans l&rsquo;image <strong>dofus-almanax:0.2<\/strong> (\u00e0 la fois nos scripts, nos fichiers <strong>et<\/strong> crond). Mais comment configurer le service Cron pour lui indiquer la fr\u00e9quence \u00e0 laquelle mettre \u00e0 jour et comment mettre \u00e0 jour nos fichiers ?<\/p>\n<p>Regardons l&rsquo;image Docker, \u00e9crite sous le fichier Dockerfile suivant :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-Dockerfile\" data-lang=\"Dockerfile\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">FROM<\/span> <span style=\"color:#e6db74\">alpine:3.16<\/span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">RUN<\/span> apk update <span style=\"color:#f92672\">&amp;&amp;<\/span> <span style=\"color:#ae81ff\">\\\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>    apk add --no-cache <span style=\"color:#ae81ff\">\\\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>        py3-lxml <span style=\"color:#ae81ff\">\\\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>\tpy3-mechanize <span style=\"color:#ae81ff\">\\\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>\ttzdata <span style=\"color:#f92672\">&amp;&amp;<\/span> <span style=\"color:#ae81ff\">\\\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>    rm -rf \/var\/cache\/apk\/*<span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">ENV<\/span> TZ<span style=\"color:#f92672\">=<\/span>Europe\/Paris<span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">WORKDIR<\/span> <span style=\"color:#e6db74\">\/opt\/almanax<\/span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">VOLUME<\/span> <span style=\"color:#e6db74\">\/opt\/almanax\/public<\/span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">VOLUME<\/span> <span style=\"color:#e6db74\">\/opt\/almanax\/dl<\/span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">ENTRYPOINT<\/span> [<span style=\"color:#e6db74\">&#34;python3&#34;<\/span>, <span style=\"color:#e6db74\">&#34;almanax_next_week.py&#34;<\/span>]<span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">CMD<\/span> [<span style=\"color:#e6db74\">&#34;\/opt\/almanax\/public\/index.html&#34;<\/span>]<span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">COPY<\/span> .\/src \/opt\/almanax<span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">COPY<\/span> .\/crontabs \/etc\/crontabs\/root<span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">RUN<\/span> chown root:root \/etc\/crontabs\/root <span style=\"color:#f92672\">&amp;&amp;<\/span> <span style=\"color:#ae81ff\">\\\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>    chmod <span style=\"color:#ae81ff\">600<\/span> \/etc\/crontabs\/root<span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">COPY<\/span> .\/cron_scripts\/generate.sh \/opt\/generate<span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><\/code><\/pre><\/div><p>\u00c9tudions ce fichier dans les grandes lignes concernant le service Cron notamment.<\/p>\n<ol>\n<li>Nous partons d&rsquo;une <strong>image Alpine 3.16 qui contient d\u00e9j\u00e0 crond<\/strong> (le service Cron)<\/li>\n<li>Nous installons <strong>tzdata<\/strong> qui nous permettra de choisir correctement un fuseau horaire<\/li>\n<li><code>ENV TZ=Europe\/Paris<\/code> d\u00e9finit notre fuseau horaire, histoire de lancer le script au moment o\u00f9 nous nous y attendons<\/li>\n<li>Le fichier le plus important, <strong>cronbtabs<\/strong> est copi\u00e9 vers <strong>\/etc\/crontabs\/root<\/strong>, la ligne <code>COPY .\/crontabs \/etc\/crontabs\/root<\/code> est d\u00e9terminante pour remplacer le fichier du service crond d&rsquo;Alpine Linux<\/li>\n<li>On copie aussi un script <strong>generate.sh<\/strong> dont nous parlerons apr\u00e8s<\/li>\n<li>Ce m\u00eame fichier n\u00e9cessite des permissions sp\u00e9cifiques, notifi\u00e9e vers la fin du fichier Dockerfile :<\/li>\n<\/ol>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-Dockerfile\" data-lang=\"Dockerfile\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">RUN<\/span> chown root:root \/etc\/crontabs\/root <span style=\"color:#f92672\">&amp;&amp;<\/span> <span style=\"color:#ae81ff\">\\\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>    chmod <span style=\"color:#ae81ff\">600<\/span> \/etc\/crontabs\/root<span style=\"color:#960050;background-color:#1e0010\">\n<\/span><\/span><\/span><\/code><\/pre><\/div><p>Que contient le fichier crontabs et generate.sh ?<\/p>\n<h1 id=\"la-configuration-du-service-crond\">La configuration du service crond<\/h1>\n<p>Sous Alpine, le fichier  utilis\u00e9 pour configurer les \u00e9l\u00e9ments \u00e0 lancer par crond est <strong>\/etc\/crontabs\/root<\/strong>. Dans le chapitre pr\u00e9c\u00e9dent nous parlions de comment le remplacer, voici d\u00e9sormais son contenu :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#ae81ff\">5<\/span>       <span style=\"color:#ae81ff\">0<\/span>       *       *       *       \/bin\/sh \/opt\/generate\n<\/span><\/span><\/code><\/pre><\/div><ol>\n<li>Nous voyons que nous utilisons effectivement le script <strong>generate.sh<\/strong> - qui a d&rsquo;ailleurs \u00e9t\u00e9 renomm\u00e9 <strong>generate<\/strong>.<\/li>\n<li>Nous utilisons <code>\/bin\/sh<\/code> devant notre script :\n<ul>\n<li>c&rsquo;est un chemin absolu vers le binaire sh<\/li>\n<li>et nous utilisons sh, pas bash (car bash n&rsquo;est, \u00e0 d\u00e9faut, pas d\u00e9livr\u00e9 sous Alpine Linux)<\/li>\n<\/ul>\n<\/li>\n<li>Nous utilisons des caract\u00e8res sp\u00e9cifique, format\u00e9s sp\u00e9cifiquement pour dire que nous lan\u00e7ons le service \u00e0 00h05 du matin<\/li>\n<\/ol>\n<p>Pour g\u00e9n\u00e9rer un fichier compatible avec crond, je vous sugg\u00e8re <a href=\"https:\/\/crontab-generator.org\/\">Crontab Generator<\/a>.<\/p>\n<p>Il va falloir appliquer quelques r\u00e8gles pour \u00eatre s\u00fbr que tout cela fonctionne \u00e9videmment.<\/p>\n<h1 id=\"en-bref-les-r\u00e8gles-\u00e0-appliquer-pour-crond\">En bref, les r\u00e8gles \u00e0 appliquer pour crond<\/h1>\n<p>Voici les r\u00e8gles \u00e0 retenir pour r\u00e9diger le fichier que le service crond va \u00e9tudier :<\/p>\n<ul>\n<li>7 espaces entre chaque \u00e9l\u00e9ment d\u00e9fini dans le crontab (le fichier se nomme ainsi)<\/li>\n<li>utiliser \/bin\/sh sous Alpine pour lancer un script, puis le nom de votre script<\/li>\n<li>ne pas mettre de point dans le script qu&rsquo;on lance (mettre par exemple <code>generate<\/code> au lieu de <code>generate.sh<\/code>)<\/li>\n<li>le fichier <code>\/etc\/crontabs\/root<\/code> doit appartenir \u00e0 root (<code>root:root<\/code>) et avoir les permissions 600 (<code>chmod 600<\/code>)<\/li>\n<li>utilisez <code>-L \/dev\/stdout<\/code> sous votre Docker Compose pour avoir une sortie lisible et <code>-l 2<\/code> pour plus de logs, mais <code>-l 8<\/code> suffit<\/li>\n<\/ul>\n<p>Ce qui fait d\u00e9j\u00e0 pas mal de choses \u00e0 savoir !<\/p>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>Bien que la r\u00e9flexion ait \u00e9t\u00e9 longue (et croyez moi j&rsquo;ai pris plusieurs jours pour tester au fur et \u00e0 mesure ce que je souhaitais), nous avons r\u00e9uni les seules r\u00e8gles \u00e0 appliquer pour que cela fonctionne, tout en trouvant une solution sympathique qui a plusieurs avantages :<\/p>\n<ul>\n<li>le service Cron acc\u00e8de \u00e0 tous les fichiers communs de notre service initial<\/li>\n<li>le service Cron acc\u00e8de \u00e9galement \u00e0 tous les scripts utiles de notre service initial<\/li>\n<li>il suffit de changer le script d&rsquo;entr\u00e9e de l&rsquo;image initiale pour lancer Cron<\/li>\n<li>on a peu de changement \u00e0 faire pour utiliser Cron finalement !<\/li>\n<\/ul>\n<p>L&rsquo;inconv\u00e9nient reste cependant qu&rsquo;il faut pouvoir modifier l&rsquo;image initiale. Si nous n&rsquo;avions pas la possibilit\u00e9 de faire \u00e7a, j&rsquo;imagine qu&rsquo;on devrait cr\u00e9er un point de montage commun entre le service initial et le service cron pour acc\u00e9der \u00e0 la fois aux fichiers mais aussi aux binaires\u2026 ce qui est bien plus complexe\/casse-t\u00eate !<\/p>\n<h1 id=\"liens-utiles\">Liens utiles<\/h1>\n<p>Je me suis grandement inspir\u00e9, pour mes p\u00e9r\u00e9grinations sur Cron dans Alpine sous Docker par :<\/p>\n<ul>\n<li><a href=\"https:\/\/devopsheaven.com\/cron\/docker\/alpine\/linux\/2017\/10\/30\/run-cron-docker-alpine.html\">https:\/\/devopsheaven.com\/cron\/docker\/alpine\/linux\/2017\/10\/30\/run-cron-docker-alpine.html<\/a><\/li>\n<li><a href=\"https:\/\/mixu.wtf\/cron-in-docker-alpine-image\/\">https:\/\/mixu.wtf\/cron-in-docker-alpine-image\/<\/a><\/li>\n<li><a href=\"https:\/\/crontab-generator.org\/\">Crontab Generator<\/a><\/li>\n<\/ul>\n"}]}}