<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GitHub on Zwindler's Reflection</title><link>https://blog.zwindler.fr/tags/github/</link><description>Recent content in GitHub on Zwindler's Reflection</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>Licensed under CC BY-SA 4.0</copyright><lastBuildDate>Sun, 29 Mar 2026 18:00:00 +0200</lastBuildDate><atom:link href="https://blog.zwindler.fr/tags/github/index.xml" rel="self" type="application/rss+xml"/><item><title>101 façons de déployer Kubernetes : 125 solutions, des PRs communautaires et une UI qui s'améliore</title><link>https://blog.zwindler.fr/2026/03/29/101-facons-de-deployer-kubernetes-v3/</link><pubDate>Sun, 29 Mar 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/03/29/101-facons-de-deployer-kubernetes-v3/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/03/125-kube.webp" alt="Featured image of post 101 façons de déployer Kubernetes : 125 solutions, des PRs communautaires et une UI qui s'améliore" /&gt;&lt;h2 id="il-bouge-encore"&gt;Il bouge encore
&lt;/h2&gt;&lt;p&gt;Depuis la sortie de &lt;a class="link" href="https://blog.zwindler.fr/2026/02/09/101-facons-de-deployer-kubernetes-nouvelle-ui/" &gt;l&amp;rsquo;interface web du projet&lt;/a&gt; début février, le projet a continué à évoluer. Ajout de solutions manquantes, petites améliorations de l&amp;rsquo;UI, contributions externes, optimisations de performances&amp;hellip; Bref, un bon petit mois bien rempli pour ce gros side project que j&amp;rsquo;aimerais faire grossir.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://zwindler.github.io/101-ways-to-deploy-kubernetes/" target="_blank" rel="noopener"
&gt;101-ways-to-deploy-kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note : si vous n&amp;rsquo;avez pas suivi les épisodes précédents :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2025/11/02/93-facons-de-deployer-kubernetes/" &gt;93 façons de déployer Kubernetes&lt;/a&gt; — le Google Sheet original&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2026/01/04/101-facons-de-deployer-kubernetes-v2/" &gt;101 façons de déployer Kubernetes (v2)&lt;/a&gt; — passage sur GitHub&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2026/02/09/101-facons-de-deployer-kubernetes-nouvelle-ui/" &gt;Une nouvelle UI pour explorer les 118+ solutions&lt;/a&gt; — l&amp;rsquo;appli Astro + Tailwind&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="7-nouvelles-solutions"&gt;7 nouvelles solutions
&lt;/h2&gt;&lt;p&gt;Le catalogue est passé de 118 à &lt;strong&gt;125 solutions&lt;/strong&gt;. Voici les ajouts depuis le dernier article :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Xen Orchestra&lt;/strong&gt; - ajouté pendant un stream de Cuistops =&amp;gt; Déploiement de clusters Kubernetes (MicroK8s) via les &amp;ldquo;recettes&amp;rdquo; de Xen Orchestra, la plateforme de management de Vates. Catégorie ManagementPlatform.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Charmed Kubernetes&lt;/strong&gt; - suite à un commentaire sur LinkedIn, j&amp;rsquo;ai remplacé l&amp;rsquo;entrée &amp;ldquo;Juju&amp;rdquo; par Charmed Kubernetes, qui est le vrai nom du produit Canonical pour déployer Kubernetes via Juju.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;kubeaver&lt;/strong&gt; - découvert en scrollant bluesky sans but =&amp;gt; un installateur Kubernetes offline/online avec une GUI, basé sur Kubespray et Ansible. Certifié CNCF conformance (pour ce que ça vaut&amp;hellip;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;talos-bootstrap&lt;/strong&gt; - script interactif de Cozystack pour bootstrapper des clusters Kubernetes sur Talos OS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;boot-to-talos&lt;/strong&gt; - Outil de Cozystack (encore) qui convertit n&amp;rsquo;importe quel OS existant en Talos Linux, complètement depuis le userspace. Partiellement utilisé par Quentin dans un contexte que je ne peux révéler ;-)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TOPF&lt;/strong&gt; - Talos Orchestrator by PostFinance, un CLI pour gérer des clusters Kubernetes basés sur Talos. Découvert sur le discord de Cuistops&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Talhelper&lt;/strong&gt; - Outil CLI pour créer des clusters Talos en mode GitOps, à partir d&amp;rsquo;un seul fichier YAML déclaratif. Je l&amp;rsquo;avais initialement mis de côté mais à y réfléchir, pourquoi pas le mettre.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On notera qu&amp;rsquo;au même titre que kubeadm et k3s, il y a beaucoup de solutions qui s&amp;rsquo;appuient sur &lt;strong&gt;Talos Linux&lt;/strong&gt;. De mon point de vue, ça valide l&amp;rsquo;OS et ça confirme l&amp;rsquo;adoption croissante de cet OS immutable pour Kubernetes.&lt;/p&gt;
&lt;h2 id="refonte-complète-des-tags"&gt;Refonte complète des tags
&lt;/h2&gt;&lt;p&gt;Un gros chantier a été la &lt;strong&gt;refonte de tous les tags&lt;/strong&gt; du catalogue. Ils étaient incohérents : des noms d&amp;rsquo;outils utilisés comme tags (openshift, rancher&amp;hellip;), des tags redondants (on-premise + self-hosted), des tags manquants ou parfois faux (par ex. &amp;ldquo;lightweight&amp;rdquo; sur des outils qui lancent des VMs).&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai nettoyé les tags de &lt;strong&gt;toutes les catégories&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Desktop&lt;/strong&gt; : suppression des noms d&amp;rsquo;outils comme tags, ajout de &lt;code&gt;gui&lt;/code&gt; pour Docker Desktop, Orbstack, Podman Desktop, Rancher Desktop&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Selfhosted&lt;/strong&gt; : suppression du tag redondant &lt;code&gt;on-premise&lt;/code&gt; (29 solutions), ajout de tags pertinents (&lt;code&gt;automation&lt;/code&gt;, &lt;code&gt;edge&lt;/code&gt;, &lt;code&gt;lightweight&lt;/code&gt;, &lt;code&gt;bare-metal&lt;/code&gt;&amp;hellip;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ManagementPlatform&lt;/strong&gt; : toutes les solutions ont maintenant 3 tags (&lt;code&gt;multi-cloud&lt;/code&gt;, &lt;code&gt;managed&lt;/code&gt;, &lt;code&gt;gui&lt;/code&gt;, &lt;code&gt;automation&lt;/code&gt;&amp;hellip;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KubernetesInKubernetes&lt;/strong&gt; : correction de vcluster (&lt;code&gt;virtualization&lt;/code&gt; → &lt;code&gt;lightweight&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="de-nouveaux-filtres-dans-lui"&gt;De nouveaux filtres dans l&amp;rsquo;UI
&lt;/h2&gt;&lt;p&gt;J&amp;rsquo;ai ajouté un &lt;strong&gt;filtre par nombre d&amp;rsquo;étoiles GitHub&lt;/strong&gt; qui n&amp;rsquo;apparaît que quand le filtre &amp;ldquo;Open Source only&amp;rdquo; est activé. Plutôt pratique pour identifier rapidement les projets les plus populaires (&amp;gt; 100, &amp;gt; 1k, &amp;gt; 10k stars).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/03/stars-filter.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;occasion aussi de réorganiser la barre de filtres : le sélecteur de statut (Active/Abandoned) est passé en dropdown compact, et le layout général a été retravaillé pour rester lisible même avec plus de filtres.&lt;/p&gt;
&lt;p&gt;Dernier point, lorsque vous faites une recherche dans la barre principale, une variable vient d&amp;rsquo;ajouter à l&amp;rsquo;URL : pratique pour partager des solutions directement avec le lien.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/03/search.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="les-contributions-dantoine-caron-slashgear"&gt;Les contributions d&amp;rsquo;Antoine Caron (Slashgear)
&lt;/h2&gt;&lt;p&gt;Un grand merci à &lt;a class="link" href="https://bsky.app/profile/slashgear.dev" target="_blank" rel="noopener"
&gt;Antoine Caron (@slashgear)&lt;/a&gt; qui, au-delà de m&amp;rsquo;avoir aidé sur &lt;a class="link" href="https://blog.zwindler.fr/2026/02/19/optimisation-webperf-avif-precompression/" &gt;les performances&lt;/a&gt; et &lt;a class="link" href="https://blog.zwindler.fr/2026/02/20/securite-headers-http-observatory-hugo/" &gt;la sécurité&lt;/a&gt; de ce blog, a aussi contribué directement au projet 101 ways avec plusieurs PRs !&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hiérarchie des headings&lt;/strong&gt; - les cartes utilisaient des &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; sans &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; dans la page. Antoine a corrigé ça pour une hiérarchie correcte (&lt;code&gt;h1&lt;/code&gt; → &lt;code&gt;h2&lt;/code&gt;), ce qui améliore la navigation pour les lecteurs d&amp;rsquo;écran.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accessibilité du champ de recherche&lt;/strong&gt; - ajout d&amp;rsquo;un vrai &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; (en &lt;code&gt;sr-only&lt;/code&gt;) et passage du &lt;code&gt;type=&amp;quot;text&amp;quot;&lt;/code&gt; à &lt;code&gt;type=&amp;quot;search&amp;quot;&lt;/code&gt; pour une meilleure sémantique.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skip links&lt;/strong&gt; - ajout de liens &amp;ldquo;skip to content&amp;rdquo; pour la navigation au clavier (WCAG 2.1, critère 2.4.1). Trois liens : vers la recherche, les filtres, et le contenu principal.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="amélioration-des-performances"&gt;Amélioration des performances
&lt;/h2&gt;&lt;p&gt;En parlant de performances, le site s&amp;rsquo;est aussi amélioré côté PageSpeed. Voici un avant/après sur Lighthouse mobile :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/03/101-avant.avif"
loading="lazy"
alt="Score PageSpeed avant optimisation : 82"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/03/101-apr%c3%a8s.avif"
loading="lazy"
alt="Score PageSpeed après optimisation : 99"
&gt;&lt;/p&gt;
&lt;p&gt;De &lt;strong&gt;82 à 99&lt;/strong&gt; en performances mobiles, avec du 100/100 en bonnes pratiques et SEO.&lt;/p&gt;
&lt;p&gt;Ces gains viennent d&amp;rsquo;un ensemble de corrections, notamment la résolution du &lt;strong&gt;clipping des cartes au hover&lt;/strong&gt;, un bug CSS assez vicieux lié à &lt;code&gt;content-visibility: auto&lt;/code&gt; qui impliquait &lt;code&gt;contain: paint&lt;/code&gt; et coupait le haut des cartes quand elles se soulevaient au survol. Antoine avait identifié le problème avec l&amp;rsquo;&lt;code&gt;overflow-hidden&lt;/code&gt;, et Copilot a trouvé la cause racine dans le CSS containment.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/03/clipping.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="merci-"&gt;Merci !
&lt;/h2&gt;&lt;p&gt;Un grand merci à tous ceux qui contribuent, que ce soit en me mentionnant les outils manquants, en soumettant des PRs, ou simplement en partageant le projet.&lt;/p&gt;
&lt;p&gt;Merci spécial à &lt;strong&gt;Antoine Caron&lt;/strong&gt; pour ses PRs sur l&amp;rsquo;accessibilité et la CI - c&amp;rsquo;est vraiment chouette de voir quelqu&amp;rsquo;un prendre le temps d&amp;rsquo;améliorer un projet open source qui n&amp;rsquo;est pas le sien. Et c&amp;rsquo;est d&amp;rsquo;autant plus appréciable qu&amp;rsquo;Antoine m&amp;rsquo;avait déjà aidé sur ce blog avec &lt;a class="link" href="https://blog.zwindler.fr/2026/02/19/optimisation-webperf-avif-precompression/" &gt;l&amp;rsquo;optimisation des images en AVIF&lt;/a&gt; et &lt;a class="link" href="https://blog.zwindler.fr/2026/02/20/securite-headers-http-observatory-hugo/" &gt;les security headers HTTP&lt;/a&gt;. 🙏&lt;/p&gt;
&lt;p&gt;Le projet compte maintenant &lt;strong&gt;125 solutions&lt;/strong&gt;. Si vous en connaissez d&amp;rsquo;autres, n&amp;rsquo;hésitez pas à &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes/issues" target="_blank" rel="noopener"
&gt;ouvrir une issue&lt;/a&gt; ou une PR (ou juste à me ping, en cas de flemme) !&lt;/p&gt;
&lt;p&gt;👉 &lt;a class="link" href="https://zwindler.github.io/101-ways-to-deploy-kubernetes/" target="_blank" rel="noopener"
&gt;zwindler.github.io/101-ways-to-deploy-kubernetes&lt;/a&gt;&lt;/p&gt;</description></item><item><title>101 façons de déployer Kubernetes : une nouvelle UI pour explorer les 118+ solutions</title><link>https://blog.zwindler.fr/2026/02/09/101-facons-de-deployer-kubernetes-nouvelle-ui/</link><pubDate>Mon, 09 Feb 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/02/09/101-facons-de-deployer-kubernetes-nouvelle-ui/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/02/101-kubernetes-ui-screenshot.webp" alt="Featured image of post 101 façons de déployer Kubernetes : une nouvelle UI pour explorer les 118+ solutions" /&gt;&lt;h2 id="de-google-sheet-à-une-vraie-application-web"&gt;De Google Sheet à une vraie application web
&lt;/h2&gt;&lt;p&gt;Vous vous souvenez peut-être de mes précédents articles sur ce projet : d&amp;rsquo;abord un &lt;a class="link" href="https://blog.zwindler.fr/2025/11/02/93-facons-de-deployer-kubernetes/" &gt;simple Google Sheet avec 93 méthodes&lt;/a&gt;, puis un &lt;a class="link" href="https://blog.zwindler.fr/2026/01/04/101-facons-de-deployer-kubernetes-v2/" &gt;dépôt GitHub avec plus de 100 entrées&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Aujourd&amp;rsquo;hui, je peux présenter la dernière itération de ce projet : &lt;strong&gt;une vraie interface web&lt;/strong&gt; pour explorer toutes ces solutions !&lt;/p&gt;
&lt;p&gt;👉 &lt;a class="link" href="https://zwindler.github.io/101-ways-to-deploy-kubernetes/" target="_blank" rel="noopener"
&gt;101-ways-to-deploy-kubernetes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/02/101-kubernetes-ui-screenshot.avif"
loading="lazy"
alt="Interface web du projet 101 ways to deploy Kubernetes"
&gt;&lt;/p&gt;
&lt;h2 id="pourquoi-une-ui-"&gt;Pourquoi une UI ?
&lt;/h2&gt;&lt;p&gt;Le tableau Markdown sur GitHub, c&amp;rsquo;était déjà mieux que le Google Sheet pour la collaboration, mais à peine. Difficile à parser, difficile de rajouter des colonnes sans que ça devienne trop le bazar (c&amp;rsquo;était déjà le cas, ahah), et surtout, ultra MOCHE.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/101-kubernetes-v2-screenshot.avif"
loading="lazy"
alt="Ancien tableau Markdown sur GitHub, difficile à lire"
&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc décidé de transformer tout ça en une interface moderne et intuitive, avec l&amp;rsquo;aide d&amp;rsquo;un LLM.&lt;/p&gt;
&lt;h2 id="stack-technique--astro--tailwind"&gt;Stack technique : Astro + Tailwind
&lt;/h2&gt;&lt;p&gt;Pour ce projet, j&amp;rsquo;ai choisi une stack simple mais efficace :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="https://astro.build/" target="_blank" rel="noopener"
&gt;Astro&lt;/a&gt;&lt;/strong&gt; : un framework moderne qui génère des sites statiques ultra-rapides&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="https://tailwindcss.com/" target="_blank" rel="noopener"
&gt;Tailwind CSS&lt;/a&gt;&lt;/strong&gt; : pour un design responsive sans prise de tête&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le résultat ? Un site léger, plutôt rapide, et qui fonctionne aussi bien sur &lt;strong&gt;desktop&lt;/strong&gt; que sur &lt;strong&gt;mobile&lt;/strong&gt; (même si l&amp;rsquo;expérience desktop reste plus confortable vu la quantité de données).&lt;/p&gt;
&lt;h2 id="les-fonctionnalités"&gt;Les fonctionnalités
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Des cartes pour chaque solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Fini le tableau digne d&amp;rsquo;un dev back (non, pire, un ingé kube&amp;hellip;) ! Chaque outil dispose maintenant de sa propre &amp;ldquo;carte&amp;rdquo; animée avec :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le logo du projet&lt;/li&gt;
&lt;li&gt;Le type de licence (OSS ou propriétaire)&lt;/li&gt;
&lt;li&gt;Le nombre d&amp;rsquo;étoiles GitHub&lt;/li&gt;
&lt;li&gt;Des liens directs vers le projet et des ressources tierces (blogs indépendants, REX, tutos&amp;hellip;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/02/101-kubernetes-zoom.avif"
loading="lazy"
alt="Vue détaillée d’une carte de solution Kubernetes avec logo, licence et étoiles GitHub"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Des filtres puissants&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Vous cherchez uniquement des solutions open source ? Des outils pour le développement local ? Des plateformes de management ?&lt;/p&gt;
&lt;p&gt;Les filtres par &lt;strong&gt;catégories&lt;/strong&gt; permettent de naviguer facilement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Desktop (développement local)&lt;/li&gt;
&lt;li&gt;Managed (offres cloud)&lt;/li&gt;
&lt;li&gt;Self-hosted (automatisation on-premise)&lt;/li&gt;
&lt;li&gt;Infra As Code&lt;/li&gt;
&lt;li&gt;Kubernetes OS (systèmes spécialisés)&lt;/li&gt;
&lt;li&gt;Management Platform&lt;/li&gt;
&lt;li&gt;Kubernetes in Kubernetes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et vous pouvez aussi filtrer par &lt;strong&gt;statut&lt;/strong&gt; (actif, abandonné) ou afficher uniquement les solutions &lt;strong&gt;open source&lt;/strong&gt; ou &lt;strong&gt;production ready&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Une barre de recherche&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Vous savez ce que vous cherchez ? Tapez directement le nom dans la barre de recherche pour trouver la solution en un instant.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Des tags pour affiner&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Au-delà des catégories, les tags permettent d&amp;rsquo;identifier rapidement les technologies sous-jacentes (kubeadm, k3s, k0s&amp;hellip;).&lt;/p&gt;
&lt;h2 id="le-saviez-vous--au-moins-18-outils-utilisent-kubeadm-"&gt;Le saviez-vous ? Au moins 18 outils utilisent kubeadm !
&lt;/h2&gt;&lt;p&gt;En compilant toutes ces données, j&amp;rsquo;ai découvert quelque chose de fascinant : &lt;strong&gt;au moins 18 outils&lt;/strong&gt; utilisent &lt;code&gt;kubeadm&lt;/code&gt; comme backend pour déployer Kubernetes ! 🤯&lt;/p&gt;
&lt;p&gt;Et ce n&amp;rsquo;est même pas en comptant les offres managées des cloud providers !&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est typiquement le genre d&amp;rsquo;information qu&amp;rsquo;on peut maintenant visualiser instantanément grâce à cette nouvelle interface.&lt;/p&gt;
&lt;h2 id="un-projet-collaboratif"&gt;Un projet collaboratif
&lt;/h2&gt;&lt;p&gt;Le projet reste &lt;strong&gt;100% open source&lt;/strong&gt; et collaboratif. Les données sont toujours stockées dans le dépôt GitHub, et l&amp;rsquo;UI est générée automatiquement à partir de ces données (j&amp;rsquo;ai même rajouté des previews pour les PRs).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Il manque un outil ou un provider ?&lt;/strong&gt; Vous avez repéré un bug ? N&amp;rsquo;hésitez pas à &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes/issues" target="_blank" rel="noopener"
&gt;ouvrir une issue&lt;/a&gt; ou à soumettre une Pull Request !&lt;/p&gt;
&lt;p&gt;Le projet compte maintenant &lt;strong&gt;118 solutions&lt;/strong&gt; (et je sais qu&amp;rsquo;il en manque certainement), avec pour chacune :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Des liens à jour&lt;/li&gt;
&lt;li&gt;Le statut du projet&lt;/li&gt;
&lt;li&gt;Des références externes (tutoriels, retours d&amp;rsquo;expérience&amp;hellip;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="essayez-commentez-partagez"&gt;Essayez, commentez, partagez
&lt;/h2&gt;&lt;p&gt;Rendez-vous sur &lt;a class="link" href="https://zwindler.github.io/101-ways-to-deploy-kubernetes/" target="_blank" rel="noopener"
&gt;zwindler.github.io/101-ways-to-deploy-kubernetes&lt;/a&gt; pour explorer toutes ces solutions !&lt;/p&gt;
&lt;p&gt;OK, c&amp;rsquo;est un infâme &amp;ldquo;call to action&amp;rdquo; comme on en voit sur tous les réseaux sociaux. Soit.&lt;/p&gt;
&lt;p&gt;Cependant, je ne peux pas savoir si c&amp;rsquo;est utile (ou non) si vous ne le faites pas. Je peux le laisser en l&amp;rsquo;état (ça n&amp;rsquo;est pas génant en soit, j&amp;rsquo;ai plein d&amp;rsquo;autres side projects qui n&amp;rsquo;attendent que mon temps libre) ou le faire vivre, si ça vous plait / sert.&lt;/p&gt;
&lt;p&gt;Et si vous trouvez ce projet utile, n&amp;rsquo;hésitez pas à :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Star&lt;/strong&gt; le projet sur &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes" target="_blank" rel="noopener"
&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partager&lt;/strong&gt; avec vos collègues de la communauté Cloud Native&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contribuer&lt;/strong&gt; en ajoutant des outils manquants ou en corrigeant des erreurs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Merci d&amp;rsquo;avance ! 🙏&lt;/p&gt;</description></item><item><title>101 façons de déployer Kubernetes (v2) : maintenant sur Github avec plus de méthodes, plus de détails, plus de tout</title><link>https://blog.zwindler.fr/2026/01/04/101-facons-de-deployer-kubernetes-v2/</link><pubDate>Sun, 04 Jan 2026 10:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/01/04/101-facons-de-deployer-kubernetes-v2/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/01/101-kubernetes-v2-screenshot.webp" alt="Featured image of post 101 façons de déployer Kubernetes (v2) : maintenant sur Github avec plus de méthodes, plus de détails, plus de tout" /&gt;&lt;h2 id="une-nouvelle-version-une-nouvelle-maison"&gt;Une nouvelle version, une nouvelle maison
&lt;/h2&gt;&lt;p&gt;Il y a quelques mois, je vous présentais mon &lt;a class="link" href="https://blog.zwindler.fr/2025/11/02/93-facons-de-deployer-kubernetes/" &gt;tableau recensant 93 façons différentes de déployer Kubernetes&lt;/a&gt;, sur Google Sheets.&lt;/p&gt;
&lt;p&gt;À la suite de la remarque de Ludovic Piot qui aurait aimé contribuer un ou deux outils manquants, j&amp;rsquo;ai pris un peu (beaucoup, en vrai) de temps pendant ces vacances pour &lt;strong&gt;déménager tout ça sur Github&lt;/strong&gt; : &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes" target="_blank" rel="noopener"
&gt;101-ways-to-deploy-kubernetes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/101-kubernetes-v2-screenshot2.avif"
loading="lazy"
alt="Capture d’écran du dépôt GitHub 101 ways to deploy Kubernetes v2"
&gt;&lt;/p&gt;
&lt;p&gt;Pourquoi sur un dépôt &lt;code&gt;git&lt;/code&gt; plutôt que sur un Google Sheet comme au début ? Parce que &lt;strong&gt;ça me permet facilement de rendre ce projet vraiment collaboratif&lt;/strong&gt;, notamment via des PRs (et surtout leur validation ou non).&lt;/p&gt;
&lt;p&gt;Google Sheets, c&amp;rsquo;était bien pour commencer, quand c&amp;rsquo;était un outil perso pour m&amp;rsquo;aider à écrire mon livre, mais pour la collaboration, c&amp;rsquo;était pas viable.&lt;/p&gt;
&lt;h2 id="quoi-de-neuf-dans-cette-v2-"&gt;Quoi de neuf dans cette v2 ?
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Contributions facilitées&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le projet dispose maintenant d&amp;rsquo;un &lt;strong&gt;&lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener"
&gt;CONTRIBUTING.md&lt;/a&gt;&lt;/strong&gt; qui explique comment participer.&lt;/p&gt;
&lt;p&gt;Vous connaissez une solution qui n&amp;rsquo;est pas listée ? Vous avez repéré une erreur ? Un lien cassé ? Il vous suffit de faire une Pull Request !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;De nombreuses nouvelles méthodes (j&amp;rsquo;ai pas compté mais probablement une douzaine)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le projet est passé de 93 à &lt;strong&gt;plus de 100 méthodes&lt;/strong&gt; ! J&amp;rsquo;ai continué mes explorations et ajouté une douzaine de nouvelles solutions, dont certaines que vous m&amp;rsquo;aviez suggérées.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Informations sur les licences&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai enrichi le tableau avec &lt;strong&gt;des détails sur les licences&lt;/strong&gt; de chaque projet. C&amp;rsquo;est particulièrement important pour identifier d&amp;rsquo;un coup d&amp;rsquo;oeil si un produit est réellement open source ou si c&amp;rsquo;est une solution à licence propriétaire ou commerciale.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/101-kubernetes-v2-screenshot.avif"
loading="lazy"
alt="Tableau des méthodes de déploiement Kubernetes avec informations de licence"
&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai eu le débat avec Ludovic justement, qui voulait comprendre pourquoi je n&amp;rsquo;avais pas listé &amp;ldquo;MKE (Mirantis Kubernetes Engine)&amp;rdquo;. Je lui avais répondu à l&amp;rsquo;époque que c&amp;rsquo;était parce que je ne pouvais pas avoir accès à une version d&amp;rsquo;essai en tant que &amp;ldquo;random des Internets&amp;rdquo; sans passer par un commercial, mais l&amp;rsquo;argument n&amp;rsquo;était pas hyper solide.&lt;/p&gt;
&lt;p&gt;Pour trouver une règle &amp;ldquo;unique&amp;rdquo; à indiquer dans le &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener"
&gt;CONTRIBUTING.md&lt;/a&gt;, j&amp;rsquo;ai donc réintégré une partie des solutions que j&amp;rsquo;avais &amp;ldquo;censurées&amp;rdquo;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;While the heart of the cloud native ecosystem is open source, both open source and closed source (proprietary) tools are accepted in this list. What matters is whether the tool helps deploy Kubernetes clusters, not its licensing model.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nombreuses références externes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai fait un &lt;strong&gt;important travail&lt;/strong&gt; sur la partie &amp;ldquo;références externes&amp;rdquo;. L&amp;rsquo;idée du répo, au delà de juste fournir une liste à la prévert, est de référencer des articles de tiers indiquant &lt;strong&gt;comment&lt;/strong&gt; mettre en place ces solutions et bénéficier des retours d&amp;rsquo;expérience de la communauté.&lt;/p&gt;
&lt;p&gt;Dans l&amp;rsquo;idée de rendre le projet plus international (ça sera tout aussi utile aux anglophones qu&amp;rsquo;aux petits frenchies de ma communauté), j&amp;rsquo;ai récupéré en priorité des liens en anglais, pour le plus de solutions possibles de cette liste. Il en reste encore qui n&amp;rsquo;en ont pas, mais ça progresse.&lt;/p&gt;
&lt;p&gt;Note importante : au-delà de la documentation officielle (qui existe très souvent pour la grande majorité des projets), j&amp;rsquo;ai en priorité voulu recenser :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Des tutoriels de qualité de la communauté&lt;/li&gt;
&lt;li&gt;Des articles de blog détaillés&lt;/li&gt;
&lt;li&gt;Des retours d&amp;rsquo;expérience concrets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="un-projet-toujours-sous-cc-by-sa-40"&gt;Un projet toujours sous CC BY-SA 4.0
&lt;/h2&gt;&lt;p&gt;Le contenu reste sous licence &lt;a class="link" href="https://creativecommons.org/licenses/by-sa/4.0/deed.fr" target="_blank" rel="noopener"
&gt;CC BY-SA 4.0 (Attribution - Share Alike)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Cela signifie que vous êtes libre de :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Partager&lt;/strong&gt; : copier et redistribuer le contenu&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adapter&lt;/strong&gt; : remixer, transformer et créer à partir de ce matériel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sous les conditions suivantes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Attribution&lt;/strong&gt; : vous devez créditer le projet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partage dans les mêmes conditions&lt;/strong&gt; : si vous adaptez le contenu, vous devez le distribuer sous la même licence&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="comment-contribuer-"&gt;Comment contribuer ?
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est simple :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Forkez le dépôt &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes" target="_blank" rel="noopener"
&gt;101-ways-to-deploy-kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ajoutez ou modifiez le contenu (en suivant le format du tableau)&lt;/li&gt;
&lt;li&gt;Soumettez une Pull Request&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lisez le &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener"
&gt;CONTRIBUTING.md&lt;/a&gt; pour plus de détails sur le processus et les bonnes pratiques.&lt;/p&gt;
&lt;p&gt;Toutes les contributions sont les bienvenues : nouvelles entrées, corrections, enrichissements, suggestions&amp;hellip;&lt;/p&gt;
&lt;h2 id="et-la-suite-"&gt;Et la suite ?
&lt;/h2&gt;&lt;p&gt;Le projet va continuer à évoluer au fil des découvertes et de vos contributions.&lt;/p&gt;
&lt;p&gt;Je continue aussi à explorer de nouvelles solutions et à enrichir les informations existantes. Et qui sait, peut-être qu&amp;rsquo;on dépassera les 150 méthodes d&amp;rsquo;ici la fin de l&amp;rsquo;année ? 😄&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est le moment où je fais mon influenceur et que je vous demande de ne pas hésiter à :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Star&lt;/strong&gt; le projet sur Github si vous le trouvez utile ⭐⭐⭐&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contribuer&lt;/strong&gt; en ajoutant vos découvertes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partager&lt;/strong&gt; autour de vous si vous le trouvez utile&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rendez-vous sur &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes" target="_blank" rel="noopener"
&gt;github.com/zwindler/101-ways-to-deploy-kubernetes&lt;/a&gt; !&lt;/p&gt;</description></item><item><title>J'ai donné 1 heure à des agents Copilot pour migrer un site de Bloggrify à Hugo</title><link>https://blog.zwindler.fr/2025/12/02/jai-donn%C3%A9-1-heure-%C3%A0-des-agents-copilot-pour-migrer-un-site-de-bloggrify-%C3%A0-hugo/</link><pubDate>Tue, 02 Dec 2025 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2025/12/02/jai-donn%C3%A9-1-heure-%C3%A0-des-agents-copilot-pour-migrer-un-site-de-bloggrify-%C3%A0-hugo/</guid><description>&lt;img src="https://blog.zwindler.fr/2025/12/playwright1.webp" alt="Featured image of post J'ai donné 1 heure à des agents Copilot pour migrer un site de Bloggrify à Hugo" /&gt;&lt;h2 id="tldr-pour-les-pressés"&gt;TL;DR pour les pressés
&lt;/h2&gt;&lt;p&gt;J&amp;rsquo;ai fait migrer un site statique de Bloggrify vers Hugo par GitHub Copilot Agent.&lt;/p&gt;
&lt;p&gt;Les règles ? Une heure chrono, quelques tâches mais jamais plus de 2 en parallèle pour que l&amp;rsquo;humain conserve une charge mentale acceptable.&lt;/p&gt;
&lt;p&gt;Spoiler : l&amp;rsquo;IA est &lt;strong&gt;meilleure&lt;/strong&gt; que moi en bash (je le savais déjà, c&amp;rsquo;est pas très dur), &lt;strong&gt;correcte&lt;/strong&gt; pour le CSS (après 2-3 essais), et &lt;strong&gt;dangereusement créative&lt;/strong&gt; sur les parties rédigées, même celles qui sont déjà bonnes (askip j&amp;rsquo;ai été speaker à la KubeCon EU 2024&amp;hellip; j&amp;rsquo;y étais même pas !).&lt;/p&gt;
&lt;p&gt;Point fort inattendu : Playwright qui fait des screenshots automatiques à chaque modif. &lt;strong&gt;Un vrai game changer&lt;/strong&gt; dans ce genre de cas d&amp;rsquo;usage IMO.&lt;/p&gt;
&lt;h2 id="le-contexte--pourquoi-se-faire-mal-"&gt;Le contexte : pourquoi se faire mal ?
&lt;/h2&gt;&lt;p&gt;Si vous suivez le blog, vous savez que j&amp;rsquo;ai publié un livre (Kubernetes : 50 solutions pour les postes de développement et les clusters de production), et vous savez aussi qu&amp;rsquo;il y a un site séparé pour la promotion du livre : &lt;a class="link" href="https://50ndk.zwindler.fr/" target="_blank" rel="noopener"
&gt;50ndk.zwindler.fr&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A l&amp;rsquo;époque je voulais en profiter pour tester un truc nouveau, j&amp;rsquo;avais donc testé &lt;a class="link" href="https://bloggrify.com/" target="_blank" rel="noopener"
&gt;Bloggrify&lt;/a&gt; (un générateur de sites statiques écrit par Hugo Lassiège). Sauf qu&amp;rsquo;à l&amp;rsquo;usage, je suis perdu dans l&amp;rsquo;écosystème JS, et je me suis dit que j&amp;rsquo;allais le migrer vers Hugo avec le thème &lt;a class="link" href="https://github.com/CaiJimmy/hugo-theme-stack" target="_blank" rel="noopener"
&gt;Stack&lt;/a&gt; (exactement le même que ce blog).&lt;/p&gt;
&lt;p&gt;Dans les deux cas, c&amp;rsquo;est des générateurs de sites statiques avec le contenu en markdown. Théoriquement, c&amp;rsquo;est pas compliqué à faire.&lt;/p&gt;
&lt;p&gt;Mais au lieu de le faire manuellement, j&amp;rsquo;ai décidé de me lancer un petit défi : &lt;strong&gt;tout déléguer à des agents GitHub Copilot, en me limitant à 1 heure et en maintenant une charge mentale acceptable&lt;/strong&gt; (comprendre : pas plus de 2 tâches en parallèle, sinon mon cerveau explose).&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc ouvert Copilot, choisi Claude Sonnet 4.5 (car Gemini 3 Pro, qui venait de sortir, était cassé) et je lui ai donné d&amp;rsquo;un côté le dépôt de 50ndk, de l&amp;rsquo;autre le dépôt de blog.zwindler.fr et je lui ai donné un court prompt du type :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ce site a été créé à partir d&amp;rsquo;un outil appelé bloggrify.
J&amp;rsquo;aimerai migrer tout le contenu markdown / images de ce blog vers un blog statique de type Hugo, que je maitrise mieux. Bases toi sur @zwindler/blog.zwindler.fr pour la structure et le thème à utiliser&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="phase-1--la-migration-technique-15-minutes"&gt;Phase 1 : La migration technique (15 minutes)
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Ce qui a marché tout seul&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Première bonne impression : la migration de base a pris 15 minutes. Copilot Agent a :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Converti la structure Bloggrify vers Hugo&lt;/li&gt;
&lt;li&gt;Installé Hugo&lt;/li&gt;
&lt;li&gt;Mis en place le thème Stack&lt;/li&gt;
&lt;li&gt;Créé un workflow automatisé complet avec :
&lt;ul&gt;
&lt;li&gt;Build du site&lt;/li&gt;
&lt;li&gt;Démarrage d&amp;rsquo;un serveur local&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Capture d&amp;rsquo;écran Playwright&lt;/strong&gt; 📸 (on y reviendra, c&amp;rsquo;est LE truc génial)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Nettoyer tout ce qui avait un rapport avec Bloggrify / Javascript&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Résultat : un site fonctionnel du premier coup. Mais pas parfait !&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2025/12/playwright1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les premiers soucis (détectés par l&amp;rsquo;humain&amp;hellip; aka moi)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;En regardant le screenshot généré, j&amp;rsquo;ai repéré deux trucs bofs :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Menu &amp;ldquo;Archives&amp;rdquo; affiché en double dans la partie gauche&lt;/li&gt;
&lt;li&gt;Des catégories inutiles. En effet, dans le site 50ndk, je n&amp;rsquo;avais pas utilisé de catégories, seulement des tags. Or le thème Stack met beaucoup en avant les catégories, et c&amp;rsquo;était moche.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;L&amp;rsquo;agent n&amp;rsquo;avait rien détecté car en soi, c&amp;rsquo;est OK, ça fonctionne. Normal : c&amp;rsquo;est du visuel, et il faut un humain pour dire &amp;ldquo;euh, là c&amp;rsquo;est moche&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les corrections (7 min + 4 min)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Une fois que je lui ai signalé les problèmes, l&amp;rsquo;agent a :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;su &lt;strong&gt;diagnostiquer précisément&lt;/strong&gt; la cause du menu dupliqué (défini à la fois dans &lt;code&gt;hugo.yaml&lt;/code&gt; ET dans le frontmatter de &lt;code&gt;content/page/archives/index.md&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;corrigé en ~7 minutes&lt;/li&gt;
&lt;li&gt;réglé le problème des catégories en ~4 minutes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L&amp;rsquo;agent est bon pour débugger&amp;hellip; une fois que l&amp;rsquo;humain a détecté le bug :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Agent génère&lt;/li&gt;
&lt;li&gt;Screenshot Playwright&lt;/li&gt;
&lt;li&gt;Humain vérifie puis formule un feedback&lt;/li&gt;
&lt;li&gt;Agent corrige&lt;/li&gt;
&lt;li&gt;et on itère comme ça&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sans Playwright, j&amp;rsquo;aurais dû manuellement builder, lancer le serveur, rafraîchir le navigateur à chaque itération. &lt;strong&gt;L&amp;rsquo;automatisation de cette boucle, c&amp;rsquo;est un game changer absolu&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="phase-2--la-page-à-propos-20-25-min-et-des-hallucinations"&gt;Phase 2 : La page &amp;ldquo;À propos&amp;rdquo; (20-25 min, et des &amp;ldquo;&amp;ldquo;&amp;ldquo;hallucinations&amp;rdquo;&amp;rdquo;&amp;rdquo;)
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Ce qui a bien commencé&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Deuxième tâche : améliorer la page &amp;ldquo;À propos&amp;rdquo;. L&amp;rsquo;agent a :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Analysé le contexte (mes articles, ma page whoami sur blog.zwindler.fr)&lt;/li&gt;
&lt;li&gt;Pondu une première version correcte, bien structurée&lt;/li&gt;
&lt;li&gt;Su ajouter des images quand je lui ai demandé&lt;/li&gt;
&lt;li&gt;Été capable de faire des ajustements rapides sur demande (&amp;ldquo;double la longueur&amp;rdquo;) ou seul, &amp;ldquo;j&amp;rsquo;au corrigé la faute de frappe &amp;lsquo;20240&amp;rsquo; → &amp;lsquo;2024&amp;rsquo;&amp;rdquo;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jusque-là, RAS. Puis&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quand l&amp;rsquo;IA devient romancier&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Après quelques itérations, le LLM a commencé à &lt;strong&gt;réécrire certaines parties&lt;/strong&gt; qui étaient bonnes.&lt;/p&gt;
&lt;p&gt;Genre, carrément :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Participation à la KubeCon EU 2024 en tant qu&amp;rsquo;organisateur/speaker&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Changé le nom du livre et l&amp;rsquo;éditeur&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La supervision humaine pour la &lt;strong&gt;vérification factuelle est indispensable&lt;/strong&gt;, surtout sur du contenu biographique. J&amp;rsquo;ai dû corriger plusieurs fois en mode &amp;ldquo;non, cette partie est fausse&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="phase-3--les-tâches-techniques-et-là-ça-roule"&gt;Phase 3 : Les tâches techniques (et là, ça roule)
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Ajout de scripts de tracking&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Tâche : intégrer &lt;a class="link" href="https://hakanai.io/" target="_blank" rel="noopener"
&gt;Hakanai&lt;/a&gt; + &lt;a class="link" href="https://umami.is/" target="_blank" rel="noopener"
&gt;Umami&lt;/a&gt; sur toutes les pages.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;agent a :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Créé un partial template dans &lt;code&gt;layouts/partials/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Inclus automatiquement dans le layout principal&lt;/li&gt;
&lt;li&gt;Placé les scripts au bon endroit (avant &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est ce que j&amp;rsquo;aurais fait aussi. Nickel, RAS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Optimisation d&amp;rsquo;image (succès&amp;hellip; mais&amp;hellip; pourquoi ???)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Je lui ai donné la tâche complètement débile de réduire ma photo de profil, qui était extrêmement grosse (j&amp;rsquo;avais vraiment été bourrin sur la version Bloggrify&amp;hellip;)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tâche : réduire &lt;code&gt;avatar.jpg&lt;/code&gt; de 1.3 Mo à ~250 Ko.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;L&amp;rsquo;agent l&amp;rsquo;a fait. Techniquement, ✅. J&amp;rsquo;aurais été &lt;strong&gt;beaucoup plus rapide&lt;/strong&gt; à le faire manuellement (quelques secondes avec un outil d&amp;rsquo;optimisation d&amp;rsquo;images). Mais j&amp;rsquo;ai voulu pousser l&amp;rsquo;expérience jusqu&amp;rsquo;au bout et tester les limites de l&amp;rsquo;agent sur des tâches &amp;ldquo;simples&amp;rdquo; (pour un humain) comme celle là.&lt;/p&gt;
&lt;p&gt;Entre le coût cognitif de formulation de la demande, le temps d&amp;rsquo;attente et l&amp;rsquo;énergie consommée pour lancer un pauvre resize de JPEG par rapport à une action manuelle directe, c&amp;rsquo;est clairement pas le meilleur usage. Mais ça fonctionne.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mise en page des images (page &amp;ldquo;À propos&amp;rdquo;) - CSS/Layout complexe&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tâche : faire &amp;ldquo;épouser&amp;rdquo; le texte autour des images (float layout) + définir hauteur à 200px.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2025/12/playwright2.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;agent a proposé plusieurs itérations avant d&amp;rsquo;arriver à une version qui fonctionnait. Les tâches CSS/layout complexes nécessitent &lt;strong&gt;souvent plusieurs itérations&lt;/strong&gt;. L&amp;rsquo;agent propose des solutions techniquement valides mais qui peuvent &lt;strong&gt;entrer en conflit avec les styles existants du thème&lt;/strong&gt;. La validation visuelle via screenshot est indispensable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Widget &amp;ldquo;Commander le livre&amp;rdquo; dans la barre de droite&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tâche : ajouter un widget dans la sidebar avec liens vers vendeurs (Eyrolles, Cultura, Amazon, Fnac).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Là encore, il a fallu plusieurs tentatives pour avoir un truc qui fonctionne sur Hugo / mon thème. La première itération était KO. Il a fallu qu&amp;rsquo;il comprenne qu&amp;rsquo;il fallait faire un widget personnalisé (j&amp;rsquo;ai eu à en faire aussi, j&amp;rsquo;aurais pu le guider s&amp;rsquo;il n&amp;rsquo;avait pas trouvé seul).&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;agent a créé :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;layouts/partials/widget/book-links.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;HTML + CSS inline cohérent avec le thème Stack&lt;/li&gt;
&lt;li&gt;Modification de &lt;code&gt;hugo.yaml&lt;/code&gt; pour référencer &lt;code&gt;book-links&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Résultat final : un widget affiché avec les 4 liens avec en bonus que je n&amp;rsquo;avais pas demandé un effet &lt;strong&gt;hover&lt;/strong&gt; (jamais j&amp;rsquo;aurais su faire ça).&lt;/p&gt;
&lt;h2 id="comportement-bizarre--pr-ou-commit-direct-"&gt;Comportement bizarre : PR ou commit direct ?
&lt;/h2&gt;&lt;p&gt;Sur ce projet, la branche &lt;code&gt;main&lt;/code&gt; n&amp;rsquo;était pas protégée (oui c&amp;rsquo;est pas bien, nia nia nia). Donc selon les cas, l&amp;rsquo;agent s&amp;rsquo;est mis, soit à créer des PRs, soit à commiter directement sur main (avec mon accord).&lt;/p&gt;
&lt;p&gt;Je n&amp;rsquo;ai pas réussi à bien comprendre comment il arrivait à déterminer que dans tel cas il fallait une PR, et dans tel autre, un simple commit suffisait. Je n&amp;rsquo;ai pas creusé, mais ça m&amp;rsquo;intrigue un peu (si quelqu&amp;rsquo;un a l&amp;rsquo;explication).&lt;/p&gt;
&lt;p&gt;Dans tous les cas, cette question est probablement un peu bêbette, le mieux, c&amp;rsquo;est de protéger &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="un-paas-ça-aide"&gt;Un PaaS, ça aide
&lt;/h2&gt;&lt;p&gt;Petit point &lt;strong&gt;bonus&lt;/strong&gt; qui a bien aidé pour cette migration, le fait que le site ait été hébergé sur Clever Cloud (sur un PaaS plus généralement, car ça aurait probablement été vrai sur d&amp;rsquo;autres PaaS).&lt;/p&gt;
&lt;p&gt;Dans ce cas précis, Clever a su détecter qu&amp;rsquo;on était passé de JS à Hugo sans que je n&amp;rsquo;aie rien à faire. Ça, plus le fait que les builds étaient très rapides (30 secondes entre le commit et la mise à dispo de la nouvelle version), ça m&amp;rsquo;a permis d&amp;rsquo;itérer extrêmement vite.&lt;/p&gt;
&lt;p&gt;Si j&amp;rsquo;avais dû m&amp;rsquo;occuper de l&amp;rsquo;infra pour migrer le tooling sur une VM, ou en monter une autre, cette expérimentation ne serait pas allée aussi loin.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;La découverte majeure de l&amp;rsquo;expérimentation, c&amp;rsquo;est sans aucun doute possible le fait que Copilot utilise &lt;strong&gt;Playwright&lt;/strong&gt; pour tester, et quand il est content de son résultat, pouvoir afficher à l&amp;rsquo;humain une capture d&amp;rsquo;écran de ce qu&amp;rsquo;il pense être valide.&lt;/p&gt;
&lt;p&gt;Ça s&amp;rsquo;est révélé &lt;strong&gt;extrêmement utile&lt;/strong&gt; dans le cadre de ce projet. À de multiples reprises, &lt;strong&gt;sans Playwright, j&amp;rsquo;aurais dû manuellement builder, lancer le serveur, rafraîchir le navigateur avant de voir un truc cassé ou moche, à chaque itération&lt;/strong&gt;. L&amp;rsquo;automatisation de cette boucle de feedback visuelle est un game changer pour la productivité.&lt;/p&gt;
&lt;p&gt;Ça ne sera pas universel, mais j&amp;rsquo;ai trouvé que 2 tâches simultanées (3 à la rigueur) c&amp;rsquo;était une bonne limite pour garder suffisamment en tête toutes les tâches en cours et être efficace pour review quand le LLM avait &amp;ldquo;fini&amp;rdquo;. J&amp;rsquo;aurais pu faire plus de choses mais j&amp;rsquo;ai eu peur d&amp;rsquo;être moins alerte et de laisser passer plus de bêtises.&lt;/p&gt;
&lt;p&gt;Cette expérimentation d&amp;rsquo;environ &lt;strong&gt;1 heure&lt;/strong&gt; a démontré que GitHub Copilot Agent est particulièrement efficace pour :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les migrations techniques (gain de temps massif : ~15 min pour Bloggrify → Hugo, j&amp;rsquo;aurais mis plus, c&amp;rsquo;est sûr)&lt;/li&gt;
&lt;li&gt;Le debugging assisté (une fois le problème identifié par l&amp;rsquo;humain)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cependant, il nécessite toujours une supervision humaine attentive, voire très attentive pour :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les faits (ça sortait tellement de nulle part, cette réécriture du contenu alors que je lui demandais de rajouter des images ?!?)&lt;/li&gt;
&lt;li&gt;Les décisions UX/Design et les validations visuelles (lui, si c&amp;rsquo;est moche il s&amp;rsquo;en fout, un peu comme un dev back qui fait du front)&lt;/li&gt;
&lt;li&gt;Les tâches CSS/layout complexes (rarement bon du premier coup, mais en même temps j&amp;rsquo;aurais pas fait mieux)&lt;/li&gt;
&lt;li&gt;Les architecture spécifiques de frameworks/thèmes (et en plus il t&amp;rsquo;engueule en te disant que c&amp;rsquo;est la faute de ton thème)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;insiste une dernière fois, mais l&amp;rsquo;intégration de Playwright pour les screenshots automatiques crée une boucle de feedback visuel qui transforme radicalement l&amp;rsquo;expérience de développement avec les agents. Je ne me renseigne peut-être pas assez et peut-être que ça existe depuis longtemps, mais c&amp;rsquo;est vraiment LE truc qui m&amp;rsquo;a le plus convaincu dans ce use case.&lt;/p&gt;
&lt;p&gt;Globalement satisfait, ça fait réfléchir. &lt;a class="link" href="https://50ndk.zwindler.fr/" target="_blank" rel="noopener"
&gt;Je vous laisse admirer le résultat&lt;/a&gt; :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2025/12/resultat.avif"
loading="lazy"
&gt;&lt;/p&gt;</description></item><item><title>Une issue ouverte sur un projet, 7 ans après ?</title><link>https://blog.zwindler.fr/2023/11/24/issue-ouverte-apres-7-ans/</link><pubDate>Fri, 24 Nov 2023 14:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2023/11/24/issue-ouverte-apres-7-ans/</guid><description>&lt;img src="https://blog.zwindler.fr/2016/09/article_docker_xwiki.webp" alt="Featured image of post Une issue ouverte sur un projet, 7 ans après ?" /&gt;&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;Il y a 7 ans, en août 2016 (!!), je créais ma première &amp;ldquo;vraie&amp;rdquo; image docker.&lt;/p&gt;
&lt;p&gt;Il s&amp;rsquo;agissait d&amp;rsquo;une image custom pour un outil que j&amp;rsquo;utilisais énormément &lt;a class="link" href="https://blog.zwindler.fr/recherche/?keyword=xwiki" target="_blank" rel="noopener"
&gt;XWiki&lt;/a&gt;, un outil de Knowledge management open source (et français) que j&amp;rsquo;apprécie particulièrement et qui remplace avantageusement un Confluence (si vous voulez mon avis).&lt;/p&gt;
&lt;p&gt;A l&amp;rsquo;époque, mon image fournissait un XWiki prêt à l&amp;rsquo;emploi avec Tomcat 8 et le driver JDBC pour postgresql.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/zwindler/docker-xwiki" target="_blank" rel="noopener"
&gt;github.com/zwindler/docker-xwiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;avais fait ce projet car les images officielles du projet étaient encore balbutiantes (ça date !) et ne proposaient pas cette alternative.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://blog.zwindler.fr/2016/09/15/installer-xwiki-8-2-1-avec-docker-compose-en-2-lignes-de-commandes/" &gt;J&amp;rsquo;avais d&amp;rsquo;ailleurs écrit un article pour l&amp;rsquo;occasion (Installer XWiki 8.2.1 avec Docker (compose) en 2 lignes de commandes)&lt;/a&gt; et fourni au fil des mois suivants des manifests pour Kubernetes.&lt;/p&gt;
&lt;h2 id="et-aujourdhui-"&gt;Et aujourd&amp;rsquo;hui ?
&lt;/h2&gt;&lt;p&gt;Ca fait bien longtemps que ce projet n&amp;rsquo;a plus aucun intérêt, même si mes images ont été téléchargées environ 30000 fois (quand même !), avec un dernier téléchargement l&amp;rsquo;an dernier, malgré une version vielle de plus de 6 ans (mais pourquoiiiiii ?).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://hub.docker.com/repository/docker/zwindler/xwiki-tomcat8/general" target="_blank" rel="noopener"
&gt;hub.docker.com - zwindler/xwiki-tomcat8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les images officielles proposent désormais (depuis bien longtemps) toutes les options les plus courantes pour toutes les versions supportées&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/11/dockerhub_xwiki.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="pourtant-"&gt;Pourtant ?!?
&lt;/h2&gt;&lt;p&gt;Pourtant, début novembre, j&amp;rsquo;ai reçu une issue, d&amp;rsquo;un utilisateur qui m&amp;rsquo;informait qu&amp;rsquo;il ne pouvait pas rebuild mon image car l&amp;rsquo;URL permettant de télécharger les artefacts WAR ne répondait plus (effectivement, elle a changé).&lt;/p&gt;
&lt;p&gt;Même si au début j&amp;rsquo;étais un peu interloqué, je me suis dis que ça pouvait être fun de remettre les mains dedans et de tenter de mettre à jour mon image sur toute la stack, pour voir si ça fonctionne encore.&lt;/p&gt;
&lt;h2 id="voyons-voir-ce-qui-a-changé-depuis-2016"&gt;Voyons voir ce qui a changé depuis 2016&amp;hellip;
&lt;/h2&gt;&lt;p&gt;Évidemment, beaucoup de choses !&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord, les versions de XWiki (de 10 à 14), qui ont forcément évoluées, ainsi que cette fameuse URL de téléchargement des artefacts qui avait changé.&lt;/p&gt;
&lt;p&gt;La version de postgres est passée de 9.5 à 16, les versions du driver JDBC ont évoluées de plusieurs mineures.&lt;/p&gt;
&lt;p&gt;Plus impactant, tomcat est passé de la version 8 à la version 10, avec un gros breaking change entre la 9 et la 10, notamment à cause de JEE, renommé en JakartaEE !&lt;/p&gt;
&lt;p&gt;Ca parlera aux Javaistes ;-)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/11/javax.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Plus anecdotique, le MAINTAINER a disparu depuis bien longtemps des Dockerfile (remplacé par un LABEL opencontainers), le MaxPermSize n&amp;rsquo;existe plus en Java et le format des fichiers docker-compose est passé en version 3.&lt;/p&gt;
&lt;h2 id="les-modifs"&gt;Les modifs
&lt;/h2&gt;&lt;p&gt;Pour le fun, j&amp;rsquo;ai donc réparé mon image pour qu&amp;rsquo;elle marche (et pas plus), puis j&amp;rsquo;ai passé le repo en readonly, pour la postérité, en prenant soin d&amp;rsquo;informer l&amp;rsquo;auteur de l&amp;rsquo;issue qu&amp;rsquo;il valait mieux regarder du côté des images officielles ;-).&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/zwindler/docker-xwiki/commit/c5955f1c35f809cdf3b4f56b20cc204a37a27f17" target="_blank" rel="noopener"
&gt;Le code de fix est disponible ici, pour les curieux/curieuses&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Mais&amp;hellip; ça fonctionne :-p&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/11/xwiki-14.avif"
loading="lazy"
&gt;&lt;/p&gt;</description></item><item><title>Automatiser son site Hugo sans Github Action ou Vercel</title><link>https://blog.zwindler.fr/2023/08/01/automatiser-hugo-sans-github-action/</link><pubDate>Tue, 01 Aug 2023 06:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2023/08/01/automatiser-hugo-sans-github-action/</guid><description>&lt;img src="https://blog.zwindler.fr/2021/11/wordpress-to-hugo.webp" alt="Featured image of post Automatiser son site Hugo sans Github Action ou Vercel" /&gt;&lt;h2 id="contexte"&gt;Contexte
&lt;/h2&gt;&lt;p&gt;Fin 2021, après plus de 10 ans à écrire des articles de blog tech sur Wordpress, je prenais la décision radicale d&amp;rsquo;arrêter de maintenir cette bouse infâme (je mâche mes mots) et de partir sur des articles rédigés en markdown et un site statique avec Hugo.&lt;/p&gt;
&lt;p&gt;Au tout début, je gérais le blog moi-même mais assez vite j&amp;rsquo;en ai eu assez de devoir aller rebuild le blog à chaque modification. J&amp;rsquo;ai rapidement mis un cron qui faisait des &lt;code&gt;git pull&lt;/code&gt; régulièrement, mais on ne va pas se mentir, c&amp;rsquo;est assez crado&amp;hellip;&lt;/p&gt;
&lt;p&gt;Finalement, on m&amp;rsquo;a rapidement pointé que je devrais arrêter de m&amp;rsquo;embêter et utiliser &lt;a class="link" href="https://www.netlify.com/" target="_blank" rel="noopener"
&gt;Netlify&lt;/a&gt; ou &lt;a class="link" href="https://vercel.com/" target="_blank" rel="noopener"
&gt;Vercel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;étais pas super chaud, car ça allait à l&amp;rsquo;encontre de ma volonté d&amp;rsquo;auto héberger mon contenu et de limiter l&amp;rsquo;impact sur la vie privée de mes lecteurs, mais finalement, l&amp;rsquo;expérience utilisateur sur Vercel était tellement bonne que j&amp;rsquo;avais craqué. Je ferai peut-être un article là-dessus d&amp;rsquo;ailleurs, c&amp;rsquo;est la première fois que j&amp;rsquo;ai compris (ressenti) l&amp;rsquo;intérêt d&amp;rsquo;un PaaS.&lt;/p&gt;
&lt;p&gt;Mais aujourd&amp;rsquo;hui, je reviens sur cette décision et j&amp;rsquo;essaye de voir ce qu&amp;rsquo;il est nécessaire de mettre en place pour disposer soi-même d&amp;rsquo;un site hugo qui se refresh tout seul dès qu&amp;rsquo;un commit arrive sur le dépôt git, sans utiliser de plateforme type Vercel ni d&amp;rsquo;outils types Github action+pages.&lt;/p&gt;
&lt;h2 id="prérequis"&gt;Prérequis
&lt;/h2&gt;&lt;p&gt;Je pars du principe que vous avez déjà un site statique généré avec Hugo. Si ce n&amp;rsquo;est pas le cas je vous invite à aller lire mes articles précédents sur le sujet (&lt;a class="link" href="https://blog.zwindler.fr/2019/06/10/comment-migrer-de-wordpress-a-hugo/" &gt;premiers pas&lt;/a&gt;, &lt;a class="link" href="https://blog.zwindler.fr/2019/06/18/mes-stats-de-visites-hugo-sans-google-analytics-avec-matomo/" &gt;stats avec matomo&lt;/a&gt;, &lt;a class="link" href="https://blog.zwindler.fr/2021/11/22/jai-enfin-migre-de-wordpress-a-hugo-partie-1/" &gt;migration wordpress vers hugo&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Vous avez donc un serveur Linux sur lequel vous avez la main pour installer des choses et un dépôt git (sous Github dans mon cas, mais on peut probablement adapter l&amp;rsquo;article pour autre chose).&lt;/p&gt;
&lt;p&gt;Hugo étant d&amp;rsquo;abord un générateur de site statique, je vais utiliser nginx en frontal pour servir les fichiers qu&amp;rsquo;on va générer. Jusqu&amp;rsquo;à il y a peu, l&amp;rsquo;usage du mode &amp;ldquo;server&amp;rdquo; de hugo n&amp;rsquo;était d&amp;rsquo;ailleurs pas conseillé en production (mais ça ne semble plus être le cas, je n&amp;rsquo;ai pas vu de warning dernièrement ?).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt install nginx git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="déposer-les-sources-sur-le-serveur"&gt;Déposer les sources sur le serveur
&lt;/h2&gt;&lt;p&gt;Dans mon cas, les sources de mon blog ne sont pas disponibles publiquement (parce que j&amp;rsquo;ai pas envie, c&amp;rsquo;est comme ça ¯\_(ツ)_/¯).&lt;/p&gt;
&lt;p&gt;Il faut donc préalablement se loguer en SSH avec git avant de pouvoir &lt;em&gt;pull&lt;/em&gt; le code. Et comme je n&amp;rsquo;ai pas envie de laisser ma clé privée sur le serveur qui va héberger mon blog, il faut que j&amp;rsquo;utilise une autre clé.&lt;/p&gt;
&lt;p&gt;Dans mon cas, Github, qui héberge le code markdown de mon blog, a une notion de &amp;ldquo;deploy keys&amp;rdquo;, qui servent justement pour ça :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys" target="_blank" rel="noopener"
&gt;docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je vais donc créer une clé sur le serveur qui héberge le blog :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh-keygen -t ed25519 -C &lt;span class="s2"&gt;&amp;#34;xxx@xxx.tld&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Generating public/private ed25519 key pair.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Enter file in which to save the key &lt;span class="o"&gt;(&lt;/span&gt;/home/toto/.ssh/id_ed25519&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et je la copie dans le dossier de &lt;em&gt;www-data&lt;/em&gt; (l&amp;rsquo;utilisateur nginx sous Ubuntu)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod &lt;span class="m"&gt;600&lt;/span&gt; /root/.ssh/id_ed25519
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mkdir /var/www/.ssh/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cp ~/.ssh/id_ed25519* /var/www/.ssh/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chown www-data: /var/www/.ssh/*
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Une fois la clé créée, on doit l&amp;rsquo;ajouter dans Github.com. Je ne lui donne que les droits &amp;ldquo;read only&amp;rdquo; puisque le but c&amp;rsquo;est seulement de récupérer le code et générer le site statique, pas qu&amp;rsquo;on puisse éditer le code (&lt;em&gt;cé pour la sécuritay&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/07/github-deploy-keys2.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Je retourne ensuite sur mon serveur et j&amp;rsquo;essaye de télécharger les sources et le thème:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /usr/share/nginx/html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;GIT_SSH_COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ssh -i /var/www/.ssh/id_ed25519 -o IdentitiesOnly=yes&amp;#39;&lt;/span&gt; git clone git@github.com:zwindler/blog.example.org.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; blog.example.org/themes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git submodule add -f https://github.com/CaiJimmy/hugo-theme-stack.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chown -R www-data: /usr/share/nginx/html/blog.example.org
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="webhook"&gt;Webhook
&lt;/h2&gt;&lt;p&gt;Pour réagir à l&amp;rsquo;arrivée d&amp;rsquo;un nouveau commit sur notre dépôt source, on va utiliser 2 choses :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un projet qui s&amp;rsquo;appelle sobrement &amp;ldquo;Webhook&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;configurer un webhook dans github, qui va envoyer l&amp;rsquo;info que quelque chose est arrivé sur le dépôt (mais on verra ça plus tard)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Revenons à &amp;ldquo;Webhook&amp;rdquo; dans un premier temps :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;webhook is a lightweight configurable tool written in Go, that allows you to easily create HTTP endpoints (hooks) on your server, which you can use to execute configured commands. You can also pass data from the HTTP request (such as headers, payload or query variables) to your commands. webhook also allows you to specify rules which have to be satisfied in order for the hook to be triggered.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On récupère la dernière release et déposer le binaire sur notre serveur :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/adnanh/webhook/releases/tag/2.8.1" target="_blank" rel="noopener"
&gt;github.com/adnanh/webhook/releases/tag/2.8.1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2.8.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wget https://github.com/adnanh/webhook/releases/download/&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/webhook-linux-amd64.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tar -xvf webhook*.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mv webhook-linux-amd64/webhook /usr/local/bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf webhook-linux-amd64*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;webhook --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Une fois que c&amp;rsquo;est fait, on va générer une chaîne aléatoire qui va nous servir de &amp;ldquo;secret&amp;rdquo; qui sera partagé entre le binaire &lt;em&gt;webhook&lt;/em&gt; qui tourne sur notre serveur et un &lt;em&gt;webhook&lt;/em&gt; sur Github.com :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;WHSECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;uuidgen &lt;span class="p"&gt;|&lt;/span&gt; base64&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On crée un fichier de configuration &lt;code&gt;hook.json&lt;/code&gt; pour déclarer nos webhooks&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;mkdir&lt;/span&gt; &lt;span class="err"&gt;/etc/webhook&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;cat&lt;/span&gt; &lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;/etc/webhook/hook.json&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="err"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;redeploy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;execute-command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/usr/share/nginx/html/blog.example.org/blog_refresh.sh&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;command-working-directory&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/usr/share/nginx/html/blog.example.org&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;trigger-rule&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;payload-hmac-sha1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;secret&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;$WHSECRET&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;parameter&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;header&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;X-Hub-Signature&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ici, quand le serveur va recevoir l&amp;rsquo;url /hooks/redeploy, on va lancer le script &lt;code&gt;/usr/share/nginx/html/blog.example.org/blog_refresh.sh&lt;/code&gt; depuis le répertoire /usr/share/nginx/html/blog.example.org.&lt;/p&gt;
&lt;p&gt;Charge à nous de créer un script qui va soit :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;récupérer les sources (git pull)&lt;/li&gt;
&lt;li&gt;relancer un build&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voilà à quoi pourrait ressembler le script &lt;code&gt;blog_refresh.sh&lt;/code&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /usr/share/nginx/html/blog.example.org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#pull latest version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;GIT_SSH_COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ssh -i /var/www/.ssh/id_ed25519 -o IdentitiesOnly=yes&amp;#39;&lt;/span&gt; git clone git@github.com:zwindler/blog.example.org.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#refresh theme submodule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git submodule init
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git submodule update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#rebuild (fire and forget)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo --minify --gc &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On va ensuite créer un service systemd qui va lancer le binaire &lt;em&gt;webhook&lt;/em&gt; en tâche de fond&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat &amp;gt; /etc/systemd/system/webhook.service &lt;span class="s"&gt;&amp;lt;&amp;lt; EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;[Unit]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Description=Simple Golang webhook server
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;ConditionPathExists=/usr/local/bin/webhook
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;After=network.target
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;[Service]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;User=www-data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Group=www-data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Type=simple
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;WorkingDirectory=/usr/share/nginx/html/blog.example.org
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;ExecStart=/usr/local/bin/webhook -ip 127.0.0.1 -hooks /etc/webhook/hook.json -verbose
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Restart=on-failure
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;[Install]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;WantedBy=default.target
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; webhook
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl start webhook
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Côté Github.com, on crée le webhook qui va contacter le serveur webhook de la façon suivante :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/07/github-webhook.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;On a maintenant un serveur web qui est en attente de certains événements webhooks et qui va lancer un script si jamais l&amp;rsquo;URL de webhook est appelée par Github.com.&lt;/p&gt;
&lt;h2 id="configuration-nginx"&gt;Configuration nginx
&lt;/h2&gt;&lt;p&gt;Il reste maintenant toute la configuration de notre nginx en frontal à faire. En partant du principe qu&amp;rsquo;on vient juste de faire l&amp;rsquo;installation du package, on commence par supprimer le fichier &lt;code&gt;/etc/nginx/sites-enabled/default&lt;/code&gt;, et on en crée un nouveau :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm /etc/nginx/sites-enabled/default
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat &amp;gt; /etc/nginx/sites-available/blog.example.org.conf &lt;span class="s"&gt;&amp;lt;&amp;lt; EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;# Root configuration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;server {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; listen 80;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; server_name blog.example.org;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; location /hooks/ {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; proxy_pass http://127.0.0.1:9000/hooks/;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; error_page 404 /404.html;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; location / {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; root /usr/share/nginx/html/blog.example.org/public;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; index index.html;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ln -s /etc/nginx/sites-&lt;span class="o"&gt;{&lt;/span&gt;available,enabled&lt;span class="o"&gt;}&lt;/span&gt;/blog.example.org.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On vérifie que la configuration est valide et si oui on redémarre nginx&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nginx -t
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl reload nginx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A partir de maintenant, toutes les URLs qui arriveront jusqu&amp;rsquo;à votre serveur en /hooks seront redirigées sur le logiciel &amp;ldquo;webhook&amp;rdquo; et le reste ira sur votre site statique (/usr/share/nginx/html/blog.example.org/public).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;curl https://blog.example.org/hooks/redeploy -L
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Hook rules were not satisfied.%
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ici le hook renvoie une erreur, car on a pas envoyé le token secret mais c&amp;rsquo;est probable que ça fonctionne. Toutes les autres pages renvoient bien une page du blog :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;curl https://blog.example.org/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;lt;html lang=&amp;#34;fr-fr&amp;#34; dir=&amp;#34;ltr&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; &amp;lt;head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; &amp;lt;meta name=&amp;#34;generator&amp;#34; content=&amp;#34;Hugo 0.115.1&amp;#34;&amp;gt;&amp;lt;meta charset=&amp;#39;utf-8&amp;#39;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;[...]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On peut maintenant envoyer des commits et voir si ça déclenche des rebuilds de notre blog. Ou alors, on peut récupérer un push précédent et cliquer sur &amp;ldquo;Redeliver&amp;rdquo; si on veut trigger un rebuild du blog.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/07/github-redelivery.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;root@hugo:~# systemctl status webhook
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;* webhook.service - Simple Golang webhook server
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Loaded: loaded (/etc/systemd/system/webhook.service; enabled; vendor preset: enabled)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Active: active (running) since Mon 2023-07-31 07:34:35 UTC; 33s ago
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Main PID: 900109 (webhook)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Tasks: 15 (limit: 4574)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Memory: 240.4M
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; CPU: 45.504s
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; CGroup: /system.slice/webhook.service
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; |-900109 /usr/local/bin/webhook -ip 127.0.0.1 -hooks /etc/webhook/hook.json -verbose
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; `-900161 hugo --minify --gc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:35 hugo webhook[900109]: [webhook] 2023/07/31 07:34:35 attempting to load hooks from /etc/webhook/hook.json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:35 hugo webhook[900109]: [webhook] 2023/07/31 07:34:35 found 1 hook(s) in file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:35 hugo webhook[900109]: [webhook] 2023/07/31 07:34:35 loaded: redeploy
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:35 hugo webhook[900109]: [webhook] 2023/07/31 07:34:35 serving hooks on http://127.0.0.1:9000/hooks/{id}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:35 hugo webhook[900109]: [webhook] 2023/07/31 07:34:35 os signal watcher ready
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:41 hugo webhook[900109]: [webhook] 2023/07/31 07:34:41 [f089a0] incoming HTTP POST request from 127.0.0.1:43258
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:41 hugo webhook[900109]: [webhook] 2023/07/31 07:34:41 [f089a0] redeploy got matched
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:41 hugo webhook[900109]: [webhook] 2023/07/31 07:34:41 [f089a0] redeploy hook triggered successfully
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:41 hugo webhook[900109]: [webhook] 2023/07/31 07:34:41 [f089a0] 200 | 0 B | 1.151688ms | 127.0.0.1:9000 | POST /hooks/redeploy
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:34:41 hugo webhook[900109]: [webhook] 2023/07/31 07:34:41 [f089a0] executing /usr/share/nginx/html/blog.example.org/blog_refresh.sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;[...]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: Pages | 3572
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: Paginator pages | 516
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: Non-page files | 14
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: Static files | 2282
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: Processed images | 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: Aliases | 1563
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: Sitemaps | 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: Cleaned | 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: Total in 166131 ms
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Jul 31 07:37:28 hugo webhook[900109]: [webhook] 2023/07/31 07:37:28 [f089a0] finished handling redeploy
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Si vous êtes à l&amp;rsquo;aise avec Linux et nginx, ces quelques manipulations sont assez rapides à faire, il n&amp;rsquo;y a rien de vraiment complexe.&lt;/p&gt;
&lt;p&gt;Cependant, ça demande à l&amp;rsquo;usage un peu d&amp;rsquo;administration et de vouloir mettre les mains dans le cambouis (avoir une VM à gérer). Je ne peux reprocher à personne de préférer l&amp;rsquo;utilisation de Github/Gitlab pages, couplé à de Github actions/Gitlab runners, voire de tout déléguer à Vercel/Netlify&amp;hellip;&lt;/p&gt;
&lt;p&gt;Mais j&amp;rsquo;avais envie de reprendre la main sur le hosting de mon blog (avec tous les inconvénients que ça va avoir en termes de maintien en condition opérationnelle), notamment pour disposer à nouveau de l&amp;rsquo;IPv6 (perdue lors du passage à Vercel) et une navigation sur mon blog plus respectueuse de votre vie privée (pas que j&amp;rsquo;aie pas confiance en Vercel, mais bon&amp;hellip;).&lt;/p&gt;
&lt;p&gt;Et c&amp;rsquo;est en revanche beaucoup plus simple que d&amp;rsquo;héberger son propre serveur Gitlab + Gitlab runners + Gitlab pages ;-).&lt;/p&gt;
&lt;p&gt;Donc, pour mon usage, ça fait le taf :)&lt;/p&gt;
&lt;h2 id="ressources-additionnelles"&gt;Ressources additionnelles
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://ansonvandoren.com/posts/deploy-hugo-from-github/" target="_blank" rel="noopener"
&gt;ansonvandoren.com/posts/deploy-hugo-from-github/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://magefan.com/blog/configure-webhooks-in-github" target="_blank" rel="noopener"
&gt;magefan.com/blog/configure-webhooks-in-github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Déployer en 5 minutes un cluster Kubernetes sur ARM avec k3s et Ansible</title><link>https://blog.zwindler.fr/2019/03/21/deployer-en-5-minutes-un-cluster-kubernetes-sur-arm-avec-k3s-et-ansible/</link><pubDate>Thu, 21 Mar 2019 13:00:14 +0000</pubDate><guid>https://blog.zwindler.fr/2019/03/21/deployer-en-5-minutes-un-cluster-kubernetes-sur-arm-avec-k3s-et-ansible/</guid><description>&lt;img src="https://blog.zwindler.fr/2019/03/k3s_scaleway.webp" alt="Featured image of post Déployer en 5 minutes un cluster Kubernetes sur ARM avec k3s et Ansible" /&gt;&lt;h2 id="cest-quoi-k3s-"&gt;C’est quoi k3s ?
&lt;/h2&gt;&lt;p&gt;Il y a quelques jours, vous avez peut être vu passer dans vos fil d’actus &lt;strong&gt;k3s&lt;/strong&gt;, ce nouveau projet &lt;a class="link" href="https://k3s.io/" target="_blank" rel="noopener"
&gt;open sourcé par Rancher&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lightweight Kubernetes. 5 less than k8s.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Il s’agit d’une version réduite de Kubernetes, sans pour autant être minimaliste, qui nous vante la possibilité de monter des clusters Kubernetes avec très peu de ressources nécessaires. On parle de moins de 512 Mo de RAM pour un master, encore moins pour un worker, tout dans un binaire de 40 Mo, support de &lt;strong&gt;armhf&lt;/strong&gt;, et &lt;strong&gt;arm64&lt;/strong&gt;, &amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Great for Edge, IoT, CI, ARM, and situations where a PhD in k8s clusterology is infeasible&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Cerise sur le gâteau, comme tout est regroupé dans un seul et même binaire, l’installation est ultra simple et se résume en :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Récupérer un binaire&lt;/li&gt;
&lt;li&gt;Lancer le binaire sur le master&lt;/li&gt;
&lt;li&gt;Lancer le binaire sur le worker avec l’URL du master et un token&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mais-cest-génial-"&gt;Mais c’est génial !
&lt;/h2&gt;&lt;p&gt;Nécessairement, pour arriver à ça, il a fallut faire quelques concessions mais pour l’instant je ne les trouves pas très gênantes. Parmi les modifications notables, on retrouve :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Suppression des features alpha, legacy et non standard&lt;/li&gt;
&lt;li&gt;Suppression de tous les add-on des cloud providers&lt;/li&gt;
&lt;li&gt;Remplacement de etcd3 par sqlite3 (même si etcd3 peut être toujours être utilisé)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="jen-veux-un-"&gt;J’en veux un !
&lt;/h2&gt;&lt;p&gt;Autant dire que pas mal de bidouilleurs du dimanche se sont jetés dessus.&lt;/p&gt;
&lt;p&gt;Le premier exemple qui vient à l’esprit est de monter un cluster Kubernetes sur Raspberry pi. Nombre de personnes ont déjà installé Docker sur un raspberry qui traînait dans un tiroir (moi compris), et le nombre d’articles avec Docker Swarm sur plusieurs Raspberry pullule sur le web.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2016/09/05/geekerie-du-week-end-installer-docker-sur-un-raspberry-pi/" &gt;Geekerie du week end : installer Docker sur un Raspberry Pi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tout ça c’est très bien mais jusqu’à présent, il était difficile d’installer Kubernetes sur ce genre de machines, assez limités en CPU/RAM. Du coup, vous vous en doutez, je ne suis pas le premier à parler de ça.&lt;/p&gt;
&lt;p&gt;Vincent RABAT aka &lt;strong&gt;itwars&lt;/strong&gt; (que vous connaissez sûrement si vous écumez les meetups sur Bordeaux) m’a coiffé au poteau en &lt;a class="link" href="https://github.com/itwars/k3s-ansible" target="_blank" rel="noopener"
&gt;releasant un playbook Ansible pour installer k3s&lt;/a&gt;, et notamment sur un Raspberry Pi (mais pas que).&lt;/p&gt;
&lt;p&gt;A charge de revanche, Vincent ;-).&lt;/p&gt;
&lt;h2 id="un-peu-différent"&gt;Un peu différent
&lt;/h2&gt;&lt;p&gt;Du coup, pour me démarquer, je vous propose aujourd’hui quelque chose d’un peu différent. N’ayant pas suffisament de raspberry sous la main, j’ai voulu faire un PoC de k3s en me basant sur des machines ARM créées chez un cloud provider.&lt;/p&gt;
&lt;p&gt;L’idée étant que si ça marche sur des machines de faible puissance en ARM, ça marchera partout (x64, raspberry, etc).&lt;/p&gt;
&lt;p&gt;Ça fait plusieurs fois que j’utilise &lt;a class="link" href="https://www.scaleway.com/" target="_blank" rel="noopener"
&gt;Scaleway&lt;/a&gt; comme hébergeur pour des petits projets, et en particulier pour déployer rapidement des machines car leur API est pas trop mal fichues et surtout ils disposent de modules Ansible très bien fait, notamment avec la feature « inventaire dynamique », ce que beaucoup ne font pas.&lt;/p&gt;
&lt;p&gt;Pour ceux que ça intéresse, mon talk sur BDX.IO était basé sur le même principe :&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=WPRE1_f0pyg" target="_blank" rel="noopener"
&gt;https://www.youtube.com/watch?v=WPRE1_f0pyg&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dans ce tuto, je vais donc vous montrer comment en quelque commande, monter un cluster k3s sur des machines ARM créées à la volée chez Scaleway, le tout avec Ansible (vous l’aurez compris).&lt;/p&gt;
&lt;h2 id="quelques-prérequis"&gt;Quelques prérequis
&lt;/h2&gt;&lt;p&gt;La première chose à faire est de cloner sur votre machine les playbooks Ansible que j’ai mis à disposition sur Github à l’adresse suivante : &lt;a class="link" href="https://github.com/zwindler/ansible-scaleway-k3s" target="_blank" rel="noopener"
&gt;github.com/zwindler/ansible-scaleway-k3s&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pour réaliser ce tuto, je pars du principe que vous avec déjà un compte sur Scaleway, que vous avez un clé SSH qu’on déposera à la racine du projet, appelée &lt;strong&gt;admin.pub&lt;/strong&gt; (c’est original).&lt;/p&gt;
&lt;p&gt;Il faudra également installer les package &lt;strong&gt;pipy&lt;/strong&gt; suivant sur la machine locale :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;pip install jinja2 PyYAML paramiko cryptography packaging
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ensuite, vous devrez installer Ansible depuis les sources (&amp;gt;= 2.8devel) et éventuellement le binaire &lt;strong&gt;jq&lt;/strong&gt; pour requêter dans les output JSON (ça c’est juste pour se faciliter la vie)&lt;/p&gt;
&lt;p&gt;Dans la console Scaleway, vous devrez créer un token sur le site de Scaleway pour les accès distants et le stocker dans un fichier &lt;strong&gt;scaleway_token&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;export SCW_API_KEY=&amp;#39;aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et enfin sourcer le fichier pour avoir la variable dans votre environnement&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;source scaleway_token
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="ok-on-est-prêt"&gt;OK, on est prêt
&lt;/h2&gt;&lt;p&gt;La première étape de cette procédure va être de générer des instances ARM pour héberger le master et le worker Kubernetes. Pour ça, le playbook Ansible &lt;strong&gt;create_arm_vms.yaml&lt;/strong&gt; va :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;récupérer l’ID d’organisation du compte Scaleway&lt;/li&gt;
&lt;li&gt;récupérer un ID d’image compatible debian Stretch&lt;/li&gt;
&lt;li&gt;ajouter si nécessaire la clé SSH de l’admin&lt;/li&gt;
&lt;li&gt;créer autant de machines que nécessaire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On lance la commande suivante :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook create_arm_vms_scaleway.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ET C’EST TOUT !&lt;/p&gt;
&lt;p&gt;Normalement en 2 minutes, vous devriez avoir 2 instances ARM sur le datacenter de Paris (par1).&lt;/p&gt;
&lt;p&gt;A noter, il se peut qu’elles ne soient pas accessibles tout de suite en SSH. J’adapterai peut être le playbook pour qu’il ne rende pas la main tant que les machines ne sont pas accessibles, et qu’on enchaîne automatiquement sur l’étape suivante (TODO).&lt;/p&gt;
&lt;h2 id="inventaire-automatique"&gt;Inventaire automatique
&lt;/h2&gt;&lt;p&gt;Je le disais plus haut, la grande force de Scaleway avec Ansible est le fait qu’ils ont fait l’effort de coder &lt;strong&gt;l’inventaire dynamique&lt;/strong&gt;. Vous n’avez pas besoin de renseigner à la main les IPs des instances que vous venez de créer dans un fichier ansible/hosts. Grâce à l’API, la découverte se fait automatiquement. On va dont pouvoir enchaîner sur la suite directement.&lt;/p&gt;
&lt;p&gt;De base, voici le contenu de mon fichier d’inventaire (inventory.yml) :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;plugin: scaleway
regions:
- par1
tags:
- k3smaster
- k3sworker
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Maintenant que les machines sont UP, on peut vérifier ce que nous renvoie Scaleway avec la commande &lt;strong&gt;ansible-inventory&lt;/strong&gt;. Ça devrait ressembler à ça :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-inventory --list -i inventory.yml
{
&amp;#34;_meta&amp;#34;: {
&amp;#34;hostvars&amp;#34;: {
&amp;#34;x.x.x.x&amp;#34;: {
&amp;#34;arch&amp;#34;: &amp;#34;arm64&amp;#34;,
&amp;#34;commercial_type&amp;#34;: &amp;#34;ARM64-2GB&amp;#34;,
&amp;#34;hostname&amp;#34;: &amp;#34;k3smaster1&amp;#34;,
[...]
&amp;#34;k3sworker&amp;#34;: {
&amp;#34;hosts&amp;#34;: [
&amp;#34;x.x.x.x&amp;#34;,
&amp;#34;y.y.y.y&amp;#34;,
&amp;#34;z.z.z.z&amp;#34;
]
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="ssh-fingerprints"&gt;SSH fingerprints
&lt;/h2&gt;&lt;p&gt;Une étape qui est souvent fastidieuse, surtout quand on ajoute beaucoup de serveur, est l’étape de vérification de l&amp;rsquo;empreinte SSH des nouveaux serveurs lors de la première connexion. Cette authentification est très importante et il &lt;b&gt;n’est pas du tout conseillé&lt;/b&gt; (comme je le vois parfois) d’ajouter l’option &lt;strong&gt;ANSIBLE_HOST_KEY_CHECKING=False&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ici, je vous propose de scanner automatiquement &lt;em&gt;la première fois&lt;/em&gt; l&amp;rsquo;empreinte, et l’ajouter dans votre known_hosts. Ainsi, si l&amp;rsquo;empreinte change en cours de route, vous serez prévenus. Cependant, ce n’est à utiliser que dans le cas de notre bidouille, pas en production.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-inventory --list -i inventory.yml | jq -r &amp;#39;.k3smaster.hosts | .[]&amp;#39; | xargs ssh-keyscan &amp;gt;&amp;gt; ~/.ssh/known_hosts
ansible-inventory --list -i inventory.yml | jq -r &amp;#39;.k3sworker.hosts | .[]&amp;#39; | xargs ssh-keyscan &amp;gt;&amp;gt; ~/.ssh/known_hosts
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Vous pouvez maintenant ajouter votre clé SSH dans le ssh-agent, et vérifier qu’on vous pouvez vous connecter à tous les serveurs via Ansible :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;eval `ssh-agent`
ssh-add myprivate.key
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="préparation-du-serveur"&gt;Préparation du serveur
&lt;/h2&gt;&lt;p&gt;C’était facile, non ?&lt;/p&gt;
&lt;p&gt;Et bien la suite l’est encore plus, à l’exception de cette petite subtilité/piège =&amp;gt; Il se trouve que l’image ARM par défaut proposée par Scaleway ne dispose ni de python ni de sudo. Pour faire du Ansible, c’est très très handicapant.&lt;/p&gt;
&lt;p&gt;J’ai donc écris un petit playbook, à n’exécuter la première fois, qui installe les prérequis non présents sur l’image de base (&lt;strong&gt;python&lt;/strong&gt; et &lt;strong&gt;sudo&lt;/strong&gt;)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i inventory.yml -u root install_prerequisites_scaleway.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Si vous n’êtes pas sur ce genre de machines, vous n’aurez pas à faire cette étapes.&lt;/p&gt;
&lt;p&gt;Maintenant qu’on a des machines avec python et sudo, on peut installer k3s normalement avec Ansible :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i inventory.yml -u root install_k3s_scaleway.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Une fois de plus : ET C’EST TOUT !&lt;/p&gt;
&lt;p&gt;Le cluster est maintenant opérationnel. Les serveurs ont Kubernetes installé et fonctionnel. Le ou les workers ont rejoint le master et font parti d’un même cluster. Un Ingress controller Treafik est créé, on peut jouer dessus directement :-)&lt;/p&gt;
&lt;h2 id="bonus--accéder-au-cluster"&gt;Bonus : accéder au cluster
&lt;/h2&gt;&lt;p&gt;Bon je vous ai un peu feinté.&lt;/p&gt;
&lt;p&gt;Certes, on peut se connecter en SSH sur le master et le piloter avec les commandes &lt;strong&gt;kubectl&lt;/strong&gt; classique (mais il faut rajouter k3s devant car le binaire kubectl est intégré à k3s). Mais c’est quand même plus simple si on peut y accéder à distance, depuis votre machine locale.&lt;/p&gt;
&lt;p&gt;Là encore, j’ai donc fais un petit playbook qui va aspirer la configuration &lt;strong&gt;kubectl&lt;/strong&gt; et la coller dans le fichier &lt;strong&gt;~/.kube/config.k3smaster&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i inventory.yml -u root configure_kubeconfig.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A partir de là, vous devriez pouvoir tester votre nouveau cluster depuis votre PC :-)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cd
kubectl --kubeconfig=.kube/config.k3smaster get nodes
NAME STATUS ROLES AGE VERSION
k3smaster1 Ready &amp;lt;none&amp;gt; 2d v1.13.3-k3s.6
k3sworker1 Ready &amp;lt;none&amp;gt; 2d v1.13.3-k3s.6
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Enjoy !!!&lt;/p&gt;</description></item><item><title>Sortie d’Ansible 2.7 et ma première contribution</title><link>https://blog.zwindler.fr/2018/10/10/sortie-dansible-2-7-et-ma-premiere-contribution/</link><pubDate>Wed, 10 Oct 2018 12:15:12 +0000</pubDate><guid>https://blog.zwindler.fr/2018/10/10/sortie-dansible-2-7-et-ma-premiere-contribution/</guid><description>&lt;img src="https://blog.zwindler.fr/2018/10/ansible_logo.webp" alt="Featured image of post Sortie d’Ansible 2.7 et ma première contribution" /&gt;&lt;h2 id="sortie-dansible-27"&gt;Sortie d’Ansible 2.7
&lt;/h2&gt;&lt;p&gt;Hier soir, la dernière version d’Ansible (2.7) est sortie. Cette release s’est concentrée sur une amélioration de la performance et de la stabilité, même si de très nombreux modules ont fait leur apparition, comme à chaque nouvelle version.&lt;/p&gt;
&lt;h3 id="python"&gt;Python
&lt;/h3&gt;&lt;p&gt;Le support de Python 2.6 est abandonné, au profit de Python 2.7. Cela signifie que l’hôte qui exécutera le playbook devra disposer de Python 2.7 au minimum (même si Python 2.6 est toujours supporté sur la machine cible).&lt;/p&gt;
&lt;h3 id="écritures-concurrentes"&gt;Écritures concurrentes
&lt;/h3&gt;&lt;p&gt;Un problème auquel j’ai déjà eu affaire est la gestion des écritures concurrentes sur un même fichier (par exemple si plusieurs hôtes écrivent sur un même fichier distant ou alors un delegate_to). A priori il existe maintenant un « lock » pour éviter d’écraser les modifications. Il faudra que je teste pour voir si ça marche bien.&lt;/p&gt;
&lt;h3 id="performances-accrues-généralisation-du-ssh-pipelining-"&gt;Performances accrues, généralisation du SSH pipelining ?
&lt;/h3&gt;&lt;p&gt;La plus grosse modification, qui va nécessiter des tests approfondis, est la modification de la manière dont sont déclenchées les actions/modules sur les serveurs distants. Précédemment, les tasks nécessitaient 2 commandes SSH au lieu d’une maintenant. Effectivement, le temps d’exécution de playbooks contenant des actions simple mais très nombreuses pouvait être très long.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ansible-2.7 changes the Ansiballz strategy for running modules remotely so that invoking a module only needs to invoke python once per module on the remote machine instead of twice.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Un gain de performance dans ce genre de cas où on passe plus de temps à lancer des commandes que les exécuter est forcément une bonne nouvelle. Je me demande si ce n’est pas simplement une activation « par défaut » de la variable d’environnement ANSIBLE_SSH_PIPELINING=1 (que j’utilise déjà dans ce genre de cas).&lt;/p&gt;
&lt;p&gt;A priori non &lt;a class="link" href="https://github.com/ansible/ansible/issues/36275" target="_blank" rel="noopener"
&gt;si j’en crois cette issue&lt;/a&gt;, mais pour l’instant je n’ai pas remarqué de différence significative sur mes playbooks les plus gourmands en temps d’exécution.&lt;/p&gt;
&lt;h3 id="module-reboot"&gt;Module reboot
&lt;/h3&gt;&lt;p&gt;A noter, la plupart des gens remontent l’arrivée d’un module « reboot » (et son équivalent « win_reboot ») qui redémarre une machine et ré-initie une connexion une fois que la machine a refait surface. Personnellement je n’utilise pas ce genre de fonctionnalité mais j’imagine que c’est une feature « nice to have » plutôt que d’utiliser le module « pause » ou la fonctionnalité « retry » en attendant que l’hôte refasse surface.&lt;/p&gt;
&lt;h2 id="les-nouveaux-modules"&gt;Les nouveaux modules
&lt;/h2&gt;&lt;p&gt;Je ne vais pas en faire la liste complète (disponible &lt;a class="link" href="https://github.com/ansible/ansible/tree/stable-2.7/changelogs" target="_blank" rel="noopener"
&gt;dans le changelog&lt;/a&gt;), mais cette version a visiblement été l’occasion pour les équipes de plusieurs cloud providers de se mettre en avant.&lt;/p&gt;
&lt;p&gt;On peut noter les équipes de Google Cloud comme grands vainqueurs avec 39 nouveaux modules. Ils sont suivi par Azure (+21, avec l’apport des bases de données et des webapps), Vultr (13), et 8 pour Scaleway.&lt;/p&gt;
&lt;p&gt;Tout ça m’intéresse beaucoup car, notamment pour Scaleway, de nombreux objets étaient encore indisponibles et il a fallut que j’écrive des scripts à la main pour curler l’API, ne serait ce que pour &lt;a class="link" href="https://docs.ansible.com/ansible/2.7/modules/scaleway_image_facts_module.html#scaleway-image-facts-module" target="_blank" rel="noopener"
&gt;récupérer les IDs des templates de VMs&lt;/a&gt; que je voulais utiliser (impossible à lister autrement).&lt;/p&gt;
&lt;p&gt;Avant :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;curl -X GET -H &amp;#34;X-Auth-Token: $SCW_API_KEY&amp;#34; -H &amp;#39;Content-Type: application/json&amp;#39; https://cp-par1.scaleway.com/images | jq -r &amp;#39;.images[] | select(.name|contains(&amp;#34;CentOS&amp;#34;)) | .id,.name,.arch,.modification_date&amp;#39;
37832f54-c18f-4338-a552-113e4302a236
CentOS 7.4
x86_64
2018-09-06T10:40:08.145552+00:00
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Maintenant, je peux sois l’exécuter à la main directement depuis Ansible, soit requêter le serveur Scaleway pour avoir une liste des images disponible directement dans mes playbooks :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible localhost -m scaleway_image_facts -a region=par1
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match &amp;#39;all&amp;#39;
localhost | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;scaleway_image_facts&amp;#34;: [
{
&amp;#34;arch&amp;#34;: &amp;#34;x86_64&amp;#34;,
&amp;#34;creation_date&amp;#34;: &amp;#34;2018-09-14T10:01:24.268850+00:00&amp;#34;,
&amp;#34;default_bootscript&amp;#34;: {
&amp;#34;architecture&amp;#34;: &amp;#34;x86_64&amp;#34;,
&amp;#34;bootcmdargs&amp;#34;: &amp;#34;LINUX_COMMON scaleway boot=local nbd.max_part=16&amp;#34;,
&amp;#34;default&amp;#34;: false,
&amp;#34;dtb&amp;#34;: &amp;#34;&amp;#34;,
&amp;#34;id&amp;#34;: &amp;#34;15fbd2f7-a0f9-412b-8502-6a44da8d98b8&amp;#34;,
[...]
- name: Gather Scaleway images facts 
scaleway_image_facts: 
region: par1
register: images_facts
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et tant qu’on est dans le genre module qui vient remplacer des trucs que je faisais à la mimine, on peut aussi citer &lt;a class="link" href="https://docs.ansible.com/ansible/2.7/modules/docker_swarm_module.html#docker-swarm-module" target="_blank" rel="noopener"
&gt;docker_swarm&lt;/a&gt; + &lt;a class="link" href="https://docs.ansible.com/ansible/2.7/modules/docker_swarm_service_module.html#docker-swarm-service-module" target="_blank" rel="noopener"
&gt;docker_swarm_service&lt;/a&gt; pour gérer un cluster docker Swarm (comme son nom l’indique), ou &lt;a class="link" href="https://docs.ansible.com/ansible/2.7/modules/k8s_facts_module.html#k8s-facts-module" target="_blank" rel="noopener"
&gt;k8s_facts&lt;/a&gt; pour récupérer des objets dans Kubernetes.&lt;/p&gt;
&lt;h2 id="et-ma-première-contribution-"&gt;Et ma première contribution !
&lt;/h2&gt;&lt;p&gt;Même si j’ai déjà contribué sur plusieurs projets open source (traductions du site de Shinken, petites contributions à plusieurs plugins Nagios et à XWiki, ouverture d’issues dans de nombreux projects open source), j’ai également résolu ma première issue sur un gros projet grâce à Ansible et l’équipe d’Azure.&lt;/p&gt;
&lt;p&gt;Il y a peu, Microsoft a mis à disposition sur Azure de machine virtuelle disposant gratuitement d’interface physique dédiées (non virtualisées donc) à 30 Gbps et avec une latence moindre (plus d’info sur &lt;a class="link" href="https://docs.microsoft.com/fr-fr/azure/virtual-network/create-vm-accelerated-networking-cli" target="_blank" rel="noopener"
&gt;la documentation officielle&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Comme il s’agissait d’un simple flag à ajouter dans l’API et que &lt;a class="link" href="https://github.com/ansible/ansible/issues/41218" target="_blank" rel="noopener"
&gt;l’issue avait été taguée en « easy fix » par les maintainer&lt;/a&gt;, j’ai sauté sur l’occasion et j’ai pris l’issue à mon compte.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2018/10/accelerated_network_ansible1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Au final, sur les 70 lignes de mon commit, il n’y a que 10 lignes de codes (le reste étant des tests et de la documentation, obligatoires pour toute soumission), mais je suis quand même content car ça marche bien ;)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2018/10/accelerated_network_ansible2.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Champomy !!&lt;/p&gt;
&lt;/blockquote&gt;</description></item></channel></rss>