<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Kubernetes on Zwindler's Reflection</title><link>https://blog.zwindler.fr/tags/kubernetes/</link><description>Recent content in Kubernetes 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/kubernetes/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>GenAI et développement logiciel, épisode 2 : kubectl-debug-pvc, de l'idée à krew en 2x30 minutes</title><link>https://blog.zwindler.fr/2026/03/16/genai-llm-dev-kubectl-debug-pvc/</link><pubDate>Tue, 17 Mar 2026 18:00:00 +0100</pubDate><guid>https://blog.zwindler.fr/2026/03/16/genai-llm-dev-kubectl-debug-pvc/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/03/kubectl-debug-pvc.webp" alt="Featured image of post GenAI et développement logiciel, épisode 2 : kubectl-debug-pvc, de l'idée à krew en 2x30 minutes" /&gt;&lt;h2 id="précédemment-dans-genai-et-dev"&gt;Précédemment, dans &amp;ldquo;GenAI et dev&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;Dans &lt;a class="link" href="https://blog.zwindler.fr/2026/03/08/genai-llm-dev-podsweeper/" &gt;mon article précédent&lt;/a&gt;, je vous parlais de mon retour d&amp;rsquo;expérience avec PodSweeper, un projet développé avec OpenCode et Claude Opus. Le bilan était nuancé : vitesse brute impressionnante, mais race conditions, gestion d&amp;rsquo;erreur laxiste, et nécessité constante de supervision humaine (entre autres déconvenues).&lt;/p&gt;
&lt;p&gt;Aujourd&amp;rsquo;hui, je vous parle d&amp;rsquo;un deuxième projet, bien plus simple, né d&amp;rsquo;un vrai besoin en production. Et le constat est assez différent.&lt;/p&gt;
&lt;h2 id="lincident-qui-a-tout-déclenché"&gt;L&amp;rsquo;incident qui a tout déclenché
&lt;/h2&gt;&lt;p&gt;Vous connaissez peut-être cette situation : un pod en production, en état de fonctionnement, qui utilise un PVC en &lt;code&gt;ReadWriteOnce&lt;/code&gt;. Vous avez besoin d&amp;rsquo;aller regarder le contenu de ce volume. Bonne pratique de production : le pod en question ne dispose pas de shell (on réduit la surface d&amp;rsquo;attaque). Pas de &lt;code&gt;/bin/sh&lt;/code&gt;, pas de &lt;code&gt;/bin/bash&lt;/code&gt;, rien.&lt;/p&gt;
&lt;p&gt;Pas grave, on a &lt;code&gt;kubectl debug&lt;/code&gt; pour ça, me direz-vous !&lt;/p&gt;
&lt;p&gt;Ok, créons un conteneur éphémère dans le pod et&amp;hellip; ah non. &lt;code&gt;kubectl debug&lt;/code&gt; ne permet pas de monter les volumes du pod dans le conteneur éphémère. Il n&amp;rsquo;expose tout simplement pas l&amp;rsquo;option &lt;code&gt;volumeMounts&lt;/code&gt; pour les conteneurs éphémères.&lt;/p&gt;
&lt;p&gt;On pourrait couper le pod, ce qui permet de monter le PVC RWO dans un autre pod avec un shell, mais on n&amp;rsquo;a pas envie, c&amp;rsquo;est de la prod.&lt;/p&gt;
&lt;p&gt;Mon collègue Maxime (encore lui !) me propose une méthode de contournement. La procédure manuelle, c&amp;rsquo;est :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Créer un conteneur éphémère sur le pod avec &lt;code&gt;kubectl debug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Construire à la main un patch JSON pour ajouter des &lt;code&gt;volumeMounts&lt;/code&gt; au conteneur éphémère qu&amp;rsquo;on vient de créer&lt;/li&gt;
&lt;li&gt;Dans un autre terminal, se brancher &lt;del&gt;au cul du camion&lt;/del&gt; sur l&amp;rsquo;API de kube en direct avec &lt;code&gt;kubectl proxy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Appliquer le patch via un curl sur l&amp;rsquo;API Kubernetes&lt;/li&gt;
&lt;li&gt;Attendre que le conteneur soit prêt, s&amp;rsquo;attacher au conteneur&lt;/li&gt;
&lt;li&gt;Se dire qu&amp;rsquo;il y avait quand même plus simple comme métier que patcheur de container à coup de curl.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Pour ceux qui pensent que c&amp;rsquo;est jouable, &amp;ldquo;téma la gueule du patch&amp;rdquo; comme disent (disaient ?) les jeunes :&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;curl&lt;/span&gt; &lt;span class="err"&gt;http:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:8001/api/v1/namespaces/&amp;lt;namespace&amp;gt;/pods/&amp;lt;pod&amp;gt;/ephemeralcontainers \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;-X&lt;/span&gt; &lt;span class="err"&gt;PATCH&lt;/span&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="err"&gt;-H&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;Content-Type:&lt;/span&gt; &lt;span class="err"&gt;application/strategic-merge-patch+json&amp;#39;&lt;/span&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="err"&gt;-d&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;&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;spec&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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;ephemeralContainers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;debugger&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;image&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ubuntu&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&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/bin/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;targetContainerName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;target-container&amp;gt;&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;stdin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;tty&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;volumeMounts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;volume-name&amp;gt;&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;mountPath&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/debug/&amp;lt;volume-name&amp;gt;&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="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si vous pensez que c&amp;rsquo;est error prone, vous avez raison. Et si vous pensez que c&amp;rsquo;est pénible à faire sous pression pendant un incident de prod, vous avez encore plus raison.&lt;/p&gt;
&lt;h2 id="side-note"&gt;Side note
&lt;/h2&gt;&lt;p&gt;Les plus aguerri·es d&amp;rsquo;entre vous dans les versions récentes de Kubernetes ont peut être entendu parler du &lt;a class="link" href="https://kep.k8s.io/2590" target="_blank" rel="noopener"
&gt;KEP-2590&lt;/a&gt;, qui introduit un (relativement) nouveau flag &lt;code&gt;--subresource&lt;/code&gt; de &lt;code&gt;kubectl&lt;/code&gt;. En effet, les containers éphémères créés par la commande &lt;code&gt;kubectl debug&lt;/code&gt; ne sont pas des &lt;em&gt;resources&lt;/em&gt; (un pod, un deployment, &amp;hellip;) mais des &lt;em&gt;subresources&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Jusqu&amp;rsquo;à cette version 1.33, il était littéralement impossible de réaliser des opérations sur les subresources avec &lt;code&gt;kubectl&lt;/code&gt;, seulement les resources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kubernetes v1.33: Octarine&lt;/strong&gt; apporte le support du flag &amp;ndash;subresource, &lt;strong&gt;mais seulement&lt;/strong&gt; pour 3 subresources pour l&amp;rsquo;instant &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;scale&lt;/code&gt; and &lt;code&gt;resize&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/blog/2025/04/23/kubernetes-v1-33-release/#subresource-support-in-kubectl" target="_blank" rel="noopener"
&gt;https://kubernetes.io/blog/2025/04/23/kubernetes-v1-33-release/#subresource-support-in-kubectl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/docs/reference/kubectl/conventions/#subresources" target="_blank" rel="noopener"
&gt;https://kubernetes.io/docs/reference/kubectl/conventions/#subresources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pas de solution de ce côté-là non plus&amp;hellip;&lt;/p&gt;
&lt;h1 id="le-prompt-de-réunion"&gt;Le prompt de réunion
&lt;/h1&gt;&lt;p&gt;J&amp;rsquo;avais cette idée en tête depuis l&amp;rsquo;incident. Lundi matin, au début d&amp;rsquo;une réunion (pendant que les gens faisaient encore des blagues), j&amp;rsquo;ai lancé OpenCode avec Claude Opus et j&amp;rsquo;ai tapé un prompt qui décrivait :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le problème : &lt;code&gt;kubectl debug&lt;/code&gt; ne monte pas les volumes PVC s&amp;rsquo;ils sont en RWO&lt;/li&gt;
&lt;li&gt;La procédure manuelle que je faisais à la main (les étapes douloureuses décrites un peu plus haut)&lt;/li&gt;
&lt;li&gt;Ce que je voulais : un outil qui automatise tout ça&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OpenCode + Opus m&amp;rsquo;ont posé 2 questions (et j&amp;rsquo;ai ajouté une consigne) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Quel langage ?&amp;rdquo;&lt;/em&gt; → &lt;strong&gt;Go&lt;/strong&gt; (je persiste)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;CLI seule ou TUI ?&amp;rdquo;&lt;/em&gt; → &lt;strong&gt;Les deux&lt;/strong&gt; (mode non-interactif pour le scripting, TUI pour l&amp;rsquo;usage quotidien)&lt;/li&gt;
&lt;li&gt;Et je lui ai demandé de toujours vérifier à chaque fois qu&amp;rsquo;il estime avoir fini de lancer le linter &lt;code&gt;golangci-lint&lt;/code&gt; (trauma de mon précédent test avec opencode)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il est parti de lui-même sur l&amp;rsquo;idée d&amp;rsquo;une TUI avec &lt;a class="link" href="https://github.com/charmbracelet/bubbletea" target="_blank" rel="noopener"
&gt;Bubble Tea&lt;/a&gt;. Puis j&amp;rsquo;ai arrêté de regarder &amp;amp; j&amp;rsquo;ai suivi ma réunion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;~30 minutes plus tard, fin de la réunion&lt;/strong&gt;, j&amp;rsquo;ai regardé le résultat. Le POC était fonctionnel.&lt;/p&gt;
&lt;h2 id="ce-quopus-a-produit-en-30-minutes"&gt;Ce qu&amp;rsquo;Opus a produit en 30 minutes
&lt;/h2&gt;&lt;p&gt;Sans aucune intervention de ma part, le LLM a scaffoldé un projet Go complet :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Structure propre&lt;/strong&gt; : &lt;code&gt;cmd/&lt;/code&gt; pour le CLI Cobra, &lt;code&gt;pkg/k8s/&lt;/code&gt; pour toute l&amp;rsquo;interaction Kubernetes, &lt;code&gt;pkg/tui/&lt;/code&gt; pour la TUI Bubble Tea&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le coeur du sujet&lt;/strong&gt; : un appel direct à l&amp;rsquo;API Kubernetes pour patcher le subresource &lt;code&gt;ephemeralcontainers&lt;/code&gt; du pod avec un strategic merge patch incluant les &lt;code&gt;volumeMounts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mode non-interactif&lt;/strong&gt; : &lt;code&gt;-n namespace -p pod -v volume:/mount/path&lt;/code&gt;, prêt pour le scripting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TUI complète&lt;/strong&gt; : navigation vim-style (&lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt;), filtrage fuzzy (&lt;code&gt;/&lt;/code&gt;), multi-sélection de volumes, spinner de chargement&amp;hellip;&lt;/li&gt;
&lt;li&gt;Makefile avec tout un tas de targets (vet, lint, test, build, install)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce que je lui ai demandé d&amp;rsquo;ajouter :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Discovery intelligente&lt;/strong&gt; : au lieu de scanner tous les pods de tous les namespaces (lent sur un de mes gros cluster), l&amp;rsquo;outil liste d&amp;rsquo;abord les PVCs cluster-wide (un seul appel API) pour identifier les namespaces pertinents, PUIS les pods dans le namespace sélectionné. On réduit drastiquement le nombre d&amp;rsquo;appels à l&amp;rsquo;API server de kube et la TUI ne montre que les namespaces et pods qui ont des volumes PVC&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Héritage du &lt;code&gt;SecurityContext&lt;/code&gt;&lt;/strong&gt; : le conteneur éphémère copie le &lt;code&gt;securityContext&lt;/code&gt; du conteneur cible, ce qui lui permet de passer les PodSecurity policies (&lt;code&gt;restricted&lt;/code&gt;, &lt;code&gt;baseline&lt;/code&gt;&amp;hellip;). C&amp;rsquo;est un cas que je n&amp;rsquo;avais pas envisagé dans mon prompt initial et qui a planté immédiatement sur un cluster bien configuré.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sans ces petites améliorations le tool était déjà utilisable (sauf pour le cas où vous avez configuré des SecurityContext), mais c&amp;rsquo;est nettement plus confortable à l&amp;rsquo;usage (parce que oui, je l&amp;rsquo;utilise).&lt;/p&gt;
&lt;p&gt;Le code de la mécanique centrale (le patch de l&amp;rsquo;API) fait quelques centaines de lignes de Go propre. Il récupère le pod, construit le patch JSON avec le conteneur éphémère et ses &lt;code&gt;volumeMounts&lt;/code&gt;, l&amp;rsquo;applique via &lt;code&gt;Patch()&lt;/code&gt; sur le subresource, attend que le conteneur soit Running, puis lance &lt;code&gt;kubectl attach&lt;/code&gt;. Rien de sorcier, c&amp;rsquo;est juste ce qu&amp;rsquo;il faut, bien fait du premier coup.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-golang" data-lang="golang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ephemeralContainer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EphemeralContainer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;EphemeralContainerCommon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EphemeralContainerCommon&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/bin/sh&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stdin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TTY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;VolumeMounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SecurityContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targetSecurityContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TargetContainerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targetContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&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="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Clientset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CoreV1&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Pods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StrategicMergePatchType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;patchBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PatchOptions&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ephemeralcontainers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="les-itérations-suivantes-30-minutes-ish"&gt;Les itérations suivantes (30 minutes-ish)
&lt;/h2&gt;&lt;p&gt;Une fois le POC validé, j&amp;rsquo;ai enchaîné quelques prompts ciblés. Je lui ai demandé ce qu&amp;rsquo;on pouvait améliorer. Il a proposé (et implémenté) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;quelques corrections mineures de code qu&amp;rsquo;il avait mal codé (mais non bloquant)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Warnings&lt;/strong&gt; : si le SecurityContext hérité a &lt;code&gt;readOnlyRootFilesystem&lt;/code&gt;, &lt;code&gt;runAsNonRoot&lt;/code&gt;, ou autre mesure de sécurité, l&amp;rsquo;outil prévient l&amp;rsquo;utilisateur avant l&amp;rsquo;attach&lt;/li&gt;
&lt;li&gt;Ajouté une CI complète à base de &lt;code&gt;goreleaser&lt;/code&gt; et de github actions&lt;/li&gt;
&lt;li&gt;Ajouté de la documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="la-cerise-sur-le-macdo--publication-sur-krew"&gt;La cerise sur le Macdo : publication sur Krew
&lt;/h2&gt;&lt;p&gt;Une fois que j&amp;rsquo;avais une version propre, j&amp;rsquo;ai demandé à Opus comment faciliter l&amp;rsquo;installation du plugin. Il m&amp;rsquo;a proposé &lt;code&gt;brew&lt;/code&gt;, ou alors &lt;a class="link" href="https://krew.sigs.k8s.io/" target="_blank" rel="noopener"
&gt;Krew&lt;/a&gt;, le gestionnaire de plugins kubectl.&lt;/p&gt;
&lt;p&gt;Je lui ai demandé si le processus d&amp;rsquo;acceptation du plugin était complexe. Il a dit que non, je lui ai dit &amp;ldquo;chiche !&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Il a &lt;strong&gt;réalisé l&amp;rsquo;intégralité du processus&lt;/strong&gt; :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Création d&amp;rsquo;un fork de &lt;a class="link" href="https://github.com/kubernetes-sigs/krew-index" target="_blank" rel="noopener"
&gt;krew-index&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Rédaction du manifeste Krew (le fichier &lt;code&gt;.yaml&lt;/code&gt; qui décrit le plugin)&lt;/li&gt;
&lt;li&gt;Prise en compte de toutes les bonnes pratiques demandées par les maintainers de krew-index (format des URLs de téléchargement, descriptions courtes, checksums SHA256, etc.)&lt;/li&gt;
&lt;li&gt;Préparation de la PR, directement sur krew-index&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;J&amp;rsquo;ai juste regardé. La PR a été mergée 12 heures plus tard par les mainteneurs.&lt;/p&gt;
&lt;p&gt;Résultat : &lt;code&gt;kubectl krew install debug-pvc&lt;/code&gt; fonctionne.&lt;/p&gt;
&lt;h2 id="mini-échec---déléguer-aussi-la-démo"&gt;Mini échec - déléguer aussi la démo
&lt;/h2&gt;&lt;p&gt;Fort de ce succès avec la PR pour krew-index, je me suis demandé si on ne pourrait pas faire une démo visuelle (un GIF à ajouter dans le README.md du projet qui montre comment ça fonctionne) avec le LLM.&lt;/p&gt;
&lt;p&gt;Je lui ai demandé s&amp;rsquo;il pouvait le faire avec &lt;code&gt;asciinema&lt;/code&gt; et le LLM m&amp;rsquo;a répondu que &amp;ldquo;oui&amp;rdquo; (oui, je discute avec le LLM comme avec mes collègues). Banco, on a essayé.&lt;/p&gt;
&lt;p&gt;Le résultat était &lt;em&gt;presque&lt;/em&gt; bon, j&amp;rsquo;aurais pu m&amp;rsquo;en contenter si j&amp;rsquo;étais pas quelqu&amp;rsquo;un de pénible : c&amp;rsquo;était un poil lent. J&amp;rsquo;ai l&amp;rsquo;impression la réactivité du LLM dans ses actions était inégale, ce qui rendait le rendu un peu désagréable au visionnage. J&amp;rsquo;aurais pu insister jusqu&amp;rsquo;à avoir quelque chose de correct, mais j&amp;rsquo;ai finalement enregistré moi-même une vidéo, convertie en GIF avec &lt;code&gt;ffmpeg&lt;/code&gt;. C&amp;rsquo;était plus fluide et plus rapide que d&amp;rsquo;itérer.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/03/kubectl-debug-pvc-demo.gif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="le-diff-avec-podsweeper"&gt;Le diff avec PodSweeper
&lt;/h2&gt;&lt;p&gt;La différence de ressenti entre ce projet et PodSweeper est frappante. Là où PodSweeper était un combat constant (race conditions, amnésie du LLM, fonctionnalités hors-spécifications), &lt;code&gt;kubectl-debug-pvc&lt;/code&gt; s&amp;rsquo;est déroulé sans accroc notable.&lt;/p&gt;
&lt;p&gt;Pourquoi ? Je pense que ça tient à la nature du projet :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Une seule chose à faire&lt;/strong&gt;, clairement définie : patcher un subresource Kubernetes avec des volumeMounts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pas de logique concurrente&lt;/strong&gt; : on fait une requête API, on attend, on s&amp;rsquo;attache&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Un domaine bien documenté&lt;/strong&gt; : l&amp;rsquo;API Kubernetes, Cobra, Bubble Tea. Le LLM connaît tout ça par coeur&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pas d&amp;rsquo;état partagé complexe&lt;/strong&gt; : pas de goroutines qui se marchent dessus, pas de graceful shutdown à gérer, de microservices multiples&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scope limité&lt;/strong&gt; : le projet entier tient dans une quinzaine de fichiers, CI et documentation incluses&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est probablement le terrain de jeu idéal pour un LLM. Un problème bien cadré, un domaine technique bien balisé, une solution linéaire.&lt;/p&gt;
&lt;h2 id="quest-ce-que-jen-pense-"&gt;Qu&amp;rsquo;est-ce que j&amp;rsquo;en pense ?
&lt;/h2&gt;&lt;p&gt;Objectivement, ce genre de projet &amp;ldquo;simple&amp;rdquo; (une chose à faire, simple à comprendre) marche &lt;strong&gt;vraiment super bien&lt;/strong&gt; avec OpenCode + Opus. Je pense que c&amp;rsquo;est ce genre à cause (grâce) de ce genre de petits projets que la hype est tellement forte autour du développement agentique. On a une idée en tête qu&amp;rsquo;on avait la flemme de dev, on teste, ça marche. &amp;ldquo;WOW&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Mais je ne veux pas non plus minimiser le travail réalisé. Le fait qu&amp;rsquo;un outil fonctionnel, propre, publié sur Krew et utilisable par n&amp;rsquo;importe qui ait pu émerger d&amp;rsquo;un prompt lancé en début de réunion, c&amp;rsquo;est quand même assez dingue.&lt;/p&gt;
&lt;p&gt;Un peu flippant aussi, de se dire qu&amp;rsquo;un nombre hallucinant de micro tools vont apparaître dans les prochains mois, avec un niveau de qualité probablement inégal.&lt;/p&gt;
&lt;h2 id="le-projet"&gt;Le projet
&lt;/h2&gt;&lt;p&gt;Le code est disponible ici :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/zwindler/kubectl-debug-pvc" target="_blank" rel="noopener"
&gt;https://github.com/zwindler/kubectl-debug-pvc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Installation :&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="c1"&gt;# Via Krew (recommandé)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl krew install debug-pvc
&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;# Utilisation interactive (TUI)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl debug-pvc
&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;# Utilisation non-interactive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl debug-pvc -n my-namespace -p my-pod-0 -v data:/debug/data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si vous aussi vous avez déjà galéré à inspecter un PVC sur un pod sans shell, essayez. Et si vous trouvez des bugs, vous pourrez blâmer le LLM 😄.&lt;/p&gt;</description></item><item><title>Flannel et NetworkPolicies : comment ajouter le support avec Cilium en CNI chaining</title><link>https://blog.zwindler.fr/2026/03/15/flannel-networkpolicies-cilium-cni-chaining/</link><pubDate>Sun, 15 Mar 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/03/15/flannel-networkpolicies-cilium-cni-chaining/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/03/flannel-cilium-chaining.webp" alt="Featured image of post Flannel et NetworkPolicies : comment ajouter le support avec Cilium en CNI chaining" /&gt;&lt;h2 id="flannel-flannel-flannel"&gt;Flannel, flannel, flannel&amp;hellip;
&lt;/h2&gt;&lt;p&gt;Flannel est un CNI simple et populaire 😢.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est le CNI par défaut de k3s, celui que la moitié des tutos kubeadm utilisent, et on le retrouve aussi dans pas mal d&amp;rsquo;offres managées.&lt;/p&gt;
&lt;p&gt;OK, il est simple, il route les paquets entre les pods, il supporte VXLAN et WireGuard, il se configure en 2 minutes. Que demander de plus ?&lt;/p&gt;
&lt;p&gt;Ben justement. Il y a un truc que flannel ne fait &lt;strong&gt;pas&lt;/strong&gt; : les NetworkPolicies. (Et c&amp;rsquo;est très grave).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Flannel is focused on networking. For network policy, other projects such as Calico can be used.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;[Edit]&lt;/strong&gt; Contrairement à ce que j&amp;rsquo;écris ici, flannel supporte en fait les NetworkPolicies depuis au moins 2 ans, via l&amp;rsquo;implémentation de référence &lt;a class="link" href="https://github.com/kubernetes-sigs/kube-network-policies" target="_blank" rel="noopener"
&gt;kube-network-policies&lt;/a&gt; du projet Kubernetes. L&amp;rsquo;option n&amp;rsquo;est pas mise en avant dans le README et ce n&amp;rsquo;est probablement pas plus recommandé en production, mais elle existe. Voir la &lt;a class="link" href="https://github.com/flannel-io/flannel/blob/master/Documentation/netpol.md" target="_blank" rel="noopener"
&gt;documentation officielle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Et le piège, c&amp;rsquo;est que si vous n&amp;rsquo;avez pas lu cette petite ligne dans le &lt;strong&gt;README.md&lt;/strong&gt;, rien ne vous le dit explicitement.&lt;/p&gt;
&lt;p&gt;Vous pouvez parfaitement créer des objets &lt;code&gt;NetworkPolicy&lt;/code&gt; dans votre cluster, &lt;code&gt;kubectl apply&lt;/code&gt; ne bronchera pas, &lt;code&gt;kubectl get netpol&lt;/code&gt; vous les listera gentiment. Sauf que&amp;hellip; elles ne sont pas enforced. Le trafic passe quand même. Votre deny-all ne deny rien du tout.&lt;/p&gt;
&lt;h2 id="on-vérifie-pour-être-bien-sûr"&gt;On vérifie pour être bien sûr
&lt;/h2&gt;&lt;p&gt;Avant de résoudre quoi que ce soit, vérifions que le problème existe. En partant du prérequis qu&amp;rsquo;on a un cluster Kubernetes qui a flannel comme CNI et que tout est fonctionnel, on va déployer deux pods dans deux namespaces différents : un client (curl) et un serveur (nginx).&lt;/p&gt;
&lt;p&gt;On vérifie que le client peut joindre le serveur :&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;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n netpol-test-a client -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -s --max-time &lt;span class="m"&gt;5&lt;/span&gt; http://server.netpol-test-b.svc.cluster.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On obtient la page d&amp;rsquo;accueil nginx. Jusque-là, tout est normal. Maintenant, on applique une NetworkPolicy &lt;em&gt;deny-all&lt;/em&gt; sur le namespace du serveur :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 01-deny-all-ingress.yaml&lt;/span&gt;&lt;span class="w"&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;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;networking.k8s.io/v1&lt;/span&gt;&lt;span class="w"&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;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;NetworkPolicy&lt;/span&gt;&lt;span class="w"&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;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;deny-all-ingress&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;netpol-test-b&lt;/span&gt;&lt;span class="w"&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;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;podSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;policyTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Ingress&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;kubectl apply -f 01-deny-all-ingress.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et on re-teste :&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;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n netpol-test-a client -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -s --max-time &lt;span class="m"&gt;5&lt;/span&gt; http://server.netpol-test-b.svc.cluster.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Résultat : la page nginx s&amp;rsquo;affiche toujours.&lt;/strong&gt; La NetworkPolicy est bien créée (&lt;code&gt;kubectl get netpol -n netpol-test-b&lt;/code&gt; la montre), mais elle n&amp;rsquo;est pas enforced. Le trafic passe comme si de rien n&amp;rsquo;était.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est le comportement attendu avec flannel (par défaut). Flannel ne fait que du routage L3 (overlay VXLAN ou WireGuard). Il n&amp;rsquo;implémente pas de contrôleur NetworkPolicy. Les objets existent dans etcd, mais personne ne les traduit en règles de filtrage.&lt;/p&gt;
&lt;p&gt;On nettoie la policy avant de passer à la suite :&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;kubectl delete -f 01-deny-all-ingress.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="les-alternatives-pour-ajouter-le-support"&gt;Les alternatives pour ajouter le support
&lt;/h2&gt;&lt;p&gt;Pendant longtemps j&amp;rsquo;ai pensé que c&amp;rsquo;était une fatalité. Je pense toujours que n&amp;rsquo;est pas un bon choix pour n&amp;rsquo;importe quelle production.&lt;/p&gt;
&lt;p&gt;MAIS récemment, j&amp;rsquo;ai découvert qu&amp;rsquo;il était possible de chaîner les CNI au sein d&amp;rsquo;un même cluster, et ainsi d&amp;rsquo;avoir un CNI qui gère la majorité des tâches (ici Flannel) et un autre qui se charge d&amp;rsquo;autres tâches, comme par exemple enforcer des Netpols.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est d&amp;rsquo;ailleurs le principe de Canal, que je connaissais de nom mais que je n&amp;rsquo;avais jamais exploré. En fait, c&amp;rsquo;est ni plus ni moins qu&amp;rsquo;un manifeste qui déploie Flannel comme CNI principal avec Calico pour l&amp;rsquo;enforcing des NetOK fine, it&amp;rsquo;s truepols !&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Canal was the name of Tigera and CoreOS’s project to integrate Calico and flannel.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note : &lt;a class="link" href="https://github.com/projectcalico/canal?tab=readme-ov-file" target="_blank" rel="noopener"
&gt;le projet GitHub a été archivé en octobre 2025&lt;/a&gt; mais en théorie ça devrait encore fonctionner, si on trouve la doc correcte (pas trouvé, les liens sont KO, mais j&amp;rsquo;ai pas vraiment cherché non plus).&lt;/p&gt;
&lt;p&gt;Vous avez donc compris le principe, on va ajouter un composant qui va &lt;strong&gt;watch&lt;/strong&gt; les objets NetworkPolicy et les traduire en règles de filtrage effectives (iptables, eBPF, nftables&amp;hellip;), &lt;strong&gt;sans toucher au flannel existant&lt;/strong&gt;. C&amp;rsquo;est ce qu&amp;rsquo;on appelle le &amp;ldquo;CNI chaining&amp;rdquo; ou le mode &amp;ldquo;policy-only&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Il existe plusieurs solutions :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Calico (Canal)&lt;/strong&gt;, Historiquement, la combinaison flannel + Calico s&amp;rsquo;appelle &amp;ldquo;Canal&amp;rdquo;, dont je viens de parler. &lt;strong&gt;Mais&lt;/strong&gt; le manifeste Canal officiel embarque son propre flannel dans le même DaemonSet que calico-node. Si votre flannel est déjà installé et géré (par vous, par un opérateur, par un provider&amp;hellip;), vous ne voulez probablement pas le remplacer. Et l&amp;rsquo;opérateur Tigera (la méthode Helm &amp;ldquo;officielle&amp;rdquo;) ne supporte pas non plus le déploiement en mode policy-only sur un flannel existant. Bref, c&amp;rsquo;est faisable mais ça nécessite un peu d&amp;rsquo;effort. Flemme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;kube-router&lt;/strong&gt;, kube-router peut fonctionner en mode firewall-only (&lt;code&gt;--run-firewall=true&lt;/code&gt;) et n&amp;rsquo;a besoin que d&amp;rsquo;iptables/ipset. C&amp;rsquo;est d&amp;rsquo;ailleurs ce que k3s utilise par défaut pour les NetworkPolicies. C&amp;rsquo;est la solution la plus légère (a priori ~50 Mo de RAM par node). Vérifiez que votre kernel dispose du module &lt;code&gt;ip_set&lt;/code&gt;, sinon ça ne fonctionnera pas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cilium en mode CNI chaining&lt;/strong&gt;, C&amp;rsquo;est la solution que j&amp;rsquo;ai retenue et qu&amp;rsquo;on va détailler. Cilium s&amp;rsquo;attache aux interfaces veth créées par flannel et ajoute ses programmes eBPF pour le policy enforcement. Pas de dépendance à iptables ou ipset, et en bonus on récupère Hubble pour l&amp;rsquo;observabilité réseau.&lt;/p&gt;
&lt;h2 id="installer-cilium-en-mode-cni-chaining"&gt;Installer Cilium en mode CNI chaining
&lt;/h2&gt;&lt;h3 id="prérequis"&gt;Prérequis
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Un cluster Kubernetes fonctionnel avec flannel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;helm&lt;/code&gt; v3+&lt;/li&gt;
&lt;li&gt;Un kernel &amp;gt;= 4.19 (idéalement &amp;gt;= 5.10 pour toutes les features eBPF)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="récupérer-la-configuration-cni-de-flannel"&gt;Récupérer la configuration CNI de flannel
&lt;/h3&gt;&lt;p&gt;Cilium en mode chaining doit connaître la configuration CNI existante. On va la récupérer depuis un node :&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;kubectl debug node/&lt;span class="k"&gt;$(&lt;/span&gt;kubectl get nodes -o &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{.items[0].metadata.name}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -it --image&lt;span class="o"&gt;=&lt;/span&gt;busybox -- cat /host/etc/cni/net.d/10-flannel.conflist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sur mon cluster, ça donne :&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="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;cbr0&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;cniVersion&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.3.1&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;plugins&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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;flannel&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;delegate&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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;hairpinMode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;isDefaultGateway&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="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;portmap&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;capabilities&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&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;portMappings&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Notez le champ &lt;code&gt;name&lt;/code&gt;&lt;/strong&gt; (ici &lt;code&gt;cbr0&lt;/code&gt;). On en aura besoin.&lt;/p&gt;
&lt;h3 id="créer-le-configmap-de-chaining"&gt;Créer le ConfigMap de chaining
&lt;/h3&gt;&lt;p&gt;On va créer un ConfigMap qui reprend la config flannel et y ajoute le plugin &lt;code&gt;cilium-cni&lt;/code&gt; en chaining :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&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;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ConfigMap&lt;/span&gt;&lt;span class="w"&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;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;cni-configuration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kube-system&lt;/span&gt;&lt;span class="w"&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cni-config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|-&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;name&amp;#34;: &amp;#34;cbr0&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;cniVersion&amp;#34;: &amp;#34;0.3.1&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;plugins&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;type&amp;#34;: &amp;#34;flannel&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;delegate&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;hairpinMode&amp;#34;: true,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;isDefaultGateway&amp;#34;: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;type&amp;#34;: &amp;#34;portmap&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;capabilities&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;portMappings&amp;#34;: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;type&amp;#34;: &amp;#34;cilium-cni&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;chaining-mode&amp;#34;: &amp;#34;generic-veth&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; }&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Attention&lt;/strong&gt; : le champ &lt;code&gt;name&lt;/code&gt; doit correspondre à celui de votre &lt;em&gt;conflist&lt;/em&gt; flannel. Si le vôtre s&amp;rsquo;appelle &lt;code&gt;cni0&lt;/code&gt; ou autre chose, adaptez.&lt;/p&gt;
&lt;p&gt;Avant d&amp;rsquo;appliquer, vérifiez aussi que votre CNI utilise bien des interfaces &lt;strong&gt;veth&lt;/strong&gt; (c&amp;rsquo;est le cas par défaut avec flannel, mais mieux vaut s&amp;rsquo;en assurer). Depuis un node :&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;ip -d link &lt;span class="p"&gt;|&lt;/span&gt; grep veth
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Vous devriez voir des interfaces de type &lt;code&gt;veth&lt;/code&gt; correspondant à vos pods, par exemple :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;103: lxcb3901b7f9c02@if102: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; ...
veth addrgenmode eui64 numtxqueues 1 numrxqueues 1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Si c&amp;rsquo;est bien le cas, le mode &lt;code&gt;generic-veth&lt;/code&gt; de Cilium fonctionnera. On applique :&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;kubectl apply -f cilium-cni-configmap.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="installer-cilium-via-helm"&gt;Installer Cilium via Helm
&lt;/h3&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;helm repo add cilium https://helm.cilium.io/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm repo update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Voici les values pour le mode chaining :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# cilium-values.yaml&lt;/span&gt;&lt;span class="w"&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;cni&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;chainingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;generic-veth&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;customConf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;configMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;cni-configuration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&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;routingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;native&lt;/span&gt;&lt;span class="w"&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;enableIPv4Masquerade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&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;enableIPv6Masquerade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&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;hubble&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Les points importants :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cni.chainingMode: generic-veth&lt;/code&gt;, c&amp;rsquo;est le mode chaining, Cilium s&amp;rsquo;attache aux interfaces veth existantes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cni.customConf: true&lt;/code&gt; + &lt;code&gt;cni.configMap&lt;/code&gt;, on fournit notre propre config CNI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;routingMode: native&lt;/code&gt;, flannel gère le routage, pas Cilium&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enableIPv4Masquerade: false&lt;/code&gt;, flannel gère le masquerading&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hubble.enabled: true&lt;/code&gt;, l&amp;rsquo;observabilité réseau, c&amp;rsquo;est le gros bonus de Cilium&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;helm install cilium cilium/cilium --version 1.19.1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --namespace kube-system &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -f cilium-values.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On attend que tout soit prêt :&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;kubectl rollout status daemonset/cilium -n kube-system --timeout&lt;span class="o"&gt;=&lt;/span&gt;120s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="vérification"&gt;Vérification
&lt;/h3&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;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n kube-system ds/cilium -- cilium status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ce qui nous intéresse :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Kubernetes: Ok 1.35 (v1.35.0) [linux/amd64]
CNI Chaining: generic-veth
Cilium: Ok 1.19.1
Hubble: Ok
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;La ligne &lt;code&gt;CNI Chaining: generic-veth&lt;/code&gt; confirme que Cilium fonctionne en mode &lt;em&gt;chaining&lt;/em&gt; et ne remplace pas flannel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; : les pods qui existaient avant l&amp;rsquo;installation de Cilium ne sont pas automatiquement gérés par Cilium. Il faut les redémarrer pour que Cilium attache ses programmes eBPF. Pensez à faire un &lt;code&gt;kubectl rollout restart&lt;/code&gt; de vos workloads de test (ou à les recréer).&lt;/p&gt;
&lt;h2 id="tester-les-networkpolicies"&gt;Tester les NetworkPolicies
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est le moment de vérité. On re-applique notre deny-all :&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;kubectl apply -f 01-deny-all-ingress.yaml
&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;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n netpol-test-a client -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -s --max-time &lt;span class="m"&gt;5&lt;/span&gt; http://server.netpol-test-b.svc.cluster.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Résultat : timeout&lt;/strong&gt; ! Cette fois, la NetworkPolicy est bien enforced. Le trafic est bloqué.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai enchaîné avec les autres scénarios classiques de NetworkPolicy, et tout fonctionne :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Allow ingress sélectif par namespace&lt;/strong&gt;, en ajoutant une policy qui autorise le trafic depuis &lt;code&gt;netpol-test-a&lt;/code&gt; uniquement, le curl passe depuis ce namespace mais reste bloqué depuis &lt;code&gt;default&lt;/code&gt;. L&amp;rsquo;isolation par namespace fonctionne.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deny-all egress&lt;/strong&gt;, en bloquant tout le trafic sortant du client, même la résolution DNS est bloquée (timeout immédiat).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Allow egress sélectif&lt;/strong&gt;, en autorisant uniquement le DNS (port 53) et le serveur (port 80 dans le namespace &lt;code&gt;netpol-test-b&lt;/code&gt;), le curl vers le serveur passe mais &lt;code&gt;curl http://example.com&lt;/code&gt; reste bloqué.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bref, ingress, egress, sélectif par namespace, tout marche comme attendu.&lt;/p&gt;
&lt;h2 id="bonus--hubble-lobservabilité-réseau"&gt;Bonus : Hubble, l&amp;rsquo;observabilité réseau
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est pour moi le vrai atout de Cilium par rapport aux alternatives. Hubble permet de voir en temps réel les flux réseau et les verdicts de policy :&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;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n kube-system ds/cilium -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; hubble observe --namespace netpol-test-b --last &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;Mar 15 13:20:42.287: netpol-test-a/client:40066 (ID:9745) -&amp;gt;
netpol-test-b/server:80 (ID:22271)
policy-verdict:none ALLOWED (TCP Flags: SYN)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On voit le pod source, le pod destination, le port, l&amp;rsquo;identité Cilium, et le verdict de policy. Quand vous debuggez une NetworkPolicy qui ne se comporte pas comme prévu, c&amp;rsquo;est vraiment pratique.&lt;/p&gt;
&lt;h2 id="combien-ça-coûte-en-ressources-"&gt;Combien ça coûte en ressources ?
&lt;/h2&gt;&lt;p&gt;Sur mon cluster (3 nodes), voici ce que Cilium consomme juste après installation :&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Composant&lt;/th&gt;
&lt;th&gt;Par node&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;cilium agent&lt;/td&gt;
&lt;td&gt;oui (DaemonSet)&lt;/td&gt;
&lt;td&gt;~160 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cilium-envoy&lt;/td&gt;
&lt;td&gt;oui (DaemonSet)&lt;/td&gt;
&lt;td&gt;~22 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cilium-operator&lt;/td&gt;
&lt;td&gt;non (2 replicas)&lt;/td&gt;
&lt;td&gt;~42 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hubble-relay&lt;/td&gt;
&lt;td&gt;non (1 replica)&lt;/td&gt;
&lt;td&gt;~16 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hubble-ui&lt;/td&gt;
&lt;td&gt;non (1 replica)&lt;/td&gt;
&lt;td&gt;~21 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Soit environ &lt;strong&gt;180 Mo par node&lt;/strong&gt; pour l&amp;rsquo;agent + envoy. J&amp;rsquo;ai pas de point de comparaison par rapport à Calico ou kube-router, mais ça me semble acceptable, et le fait de pouvoir avoir une vision complète sur la totalité des flux avec Hubble justifie largement le surcoût (à mon avis).&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Si jamais vous n&amp;rsquo;avez pas le choix et que vous devez composer avec flannel, et que vous voulez boucher le trou béant de sécurité que représente l&amp;rsquo;absence d&amp;rsquo;enforcement des Netpols, sachez donc qu&amp;rsquo;il est maintenant possible :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;d&amp;rsquo;activer l&amp;rsquo;ajout de l&amp;rsquo;implémentation officielle (même si je ne l&amp;rsquo;ai pas testée, ça doit marcher)&lt;/li&gt;
&lt;li&gt;de chaîner un autre CNI pour le faire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;À défaut de l&amp;rsquo;avoir en CNI pour tout (tout bon cluster Kubernetes a Cilium comme CNI), Cilium en mode CNI chaining (generic-veth) est une solution plutôt sympa pour combler ce manque. Il ne touche pas au flannel existant, il s&amp;rsquo;y greffe. Et en bonus, vous récupérez Hubble pour l&amp;rsquo;observabilité réseau, ce qui est franchement appréciable.&lt;/p&gt;
&lt;p&gt;Have fun :)&lt;/p&gt;</description></item><item><title>KubeSolo sur Raspberry Pi Zero : mon article dans Sysops Pratique</title><link>https://blog.zwindler.fr/2026/03/09/kubesolo-raspberry-pi-zero-sysops-pratique/</link><pubDate>Mon, 09 Mar 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/03/09/kubesolo-raspberry-pi-zero-sysops-pratique/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/03/sysops-pratique.webp" alt="Featured image of post KubeSolo sur Raspberry Pi Zero : mon article dans Sysops Pratique" /&gt;&lt;h2 id="kubernetes-sur-512-mo-de-ram-cest-possible"&gt;Kubernetes sur 512 Mo de RAM, c&amp;rsquo;est possible
&lt;/h2&gt;&lt;p&gt;Après avoir teasé sur Bluesky, je peux enfin vous annoncer (fièrement) la publication de mon article dans le numéro de mars/avril de &lt;strong&gt;Sysops Pratique&lt;/strong&gt; (le successeur de Linux Pratique, magazine bien connu des administrateurs système francophones).&lt;/p&gt;
&lt;p&gt;Le sujet : faire tourner &lt;strong&gt;&lt;a class="link" href="https://github.com/portainer/kubesolo" target="_blank" rel="noopener"
&gt;KubeSolo&lt;/a&gt;&lt;/strong&gt;, une distribution extra-légère de Kubernetes développée par Portainer, sur un &lt;strong&gt;Raspberry Pi Zero 2 W&lt;/strong&gt; et ses maigres 512 Mo de RAM.&lt;/p&gt;
&lt;h2 id="kubesolo-cest-quoi-"&gt;KubeSolo, c&amp;rsquo;est quoi ?
&lt;/h2&gt;&lt;p&gt;KubeSolo est une distribution Kubernetes minimaliste, pensée pour les environnements très contraints (IoT, edge). Contrairement à K3s ou MicroK8s qui sont économes, mais tout de même pas assez pour un Pi Zero, KubeSolo vise le strict minimum pour faire tourner un kubelet et un runtime de conteneurs.&lt;/p&gt;
&lt;p&gt;À vrai dire, même KubeSolo seul ne suffisait pas pour mon Raspberry Pi Zero. Il a fallu pas mal d&amp;rsquo;optimisations supplémentaires pour que tout rentre dans 512 Mo. Réduction de la mémoire allouée au GPU, configuration de zswap, ajustements du kernel&amp;hellip; bref, de la bonne bidouille d&amp;rsquo;admin sys comme on les aime.&lt;/p&gt;
&lt;p&gt;Si vous avez lu mon article de décembre sur l&amp;rsquo;&lt;a class="link" href="https://blog.zwindler.fr/2025/12/23/alpine-linux-raspberry-pi-headless/" &gt;installation d&amp;rsquo;Alpine Linux en mode headless sur Raspberry Pi&lt;/a&gt;, c&amp;rsquo;était en quelque sorte le préambule de cette expérimentation. J&amp;rsquo;y avais déjà exploré les optimisations mémoire possibles sur le Pi Zero.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2025/12/rpizero.webp"
loading="lazy"
alt="Le Raspberry Pi Zero 2 W, dans toute sa splendeur"
&gt;&lt;/p&gt;
&lt;h2 id="doom-dans-kubernetes-parce-que-pourquoi-pas-"&gt;Doom dans Kubernetes, parce que pourquoi pas ?
&lt;/h2&gt;&lt;p&gt;Et parce que faire tourner Kubernetes sur un nano-ordinateur c&amp;rsquo;est bien, mais que ce n&amp;rsquo;est pas suffisamment absurde à mon goût, j&amp;rsquo;ai aussi réussi à y faire tourner &lt;strong&gt;kubedoom&lt;/strong&gt; : une version de Doom packagée pour Kubernetes avec un serveur VNC intégré.&lt;/p&gt;
&lt;p&gt;Oui, c&amp;rsquo;est complètement inutile. Mais avouez que c&amp;rsquo;est beau.&lt;/p&gt;
&lt;h2 id="où-trouver-le-magazine-"&gt;Où trouver le magazine ?
&lt;/h2&gt;&lt;p&gt;Sysops Pratique est disponible en kiosque et &lt;a class="link" href="https://connect.ed-diamond.com/linux-pratique/lp-154/un-cluster-kubernetes-sur-raspberry-pi-zero-2w-vraiment" target="_blank" rel="noopener"
&gt;en version numérique&lt;/a&gt;. Si le sujet vous intéresse (Kubernetes, Raspberry Pi, optimisation système, ou juste l&amp;rsquo;envie de voir jusqu&amp;rsquo;où on peut pousser le bouchon), je vous invite à aller y jeter un œil.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est aussi une bonne manière de soutenir la presse technique francophone, qui n&amp;rsquo;a pas la vie facile.&lt;/p&gt;
&lt;h2 id="à-propos-de-la-rémunération"&gt;À propos de la rémunération
&lt;/h2&gt;&lt;p&gt;Je tiens à être transparent sur ce sujet. Écrire dans un magazine, c&amp;rsquo;est rémunéré. J&amp;rsquo;ai beaucoup de chance, je n&amp;rsquo;ai pas besoin de cet argent.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/03/revenu.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc reversé la totalité des revenus nets de cet article (445€, donc) au &lt;a class="link" href="https://www.planning-familial.org/fr/le-planning-familial-de-gironde-33" target="_blank" rel="noopener"
&gt;Planning Familial de la Gironde&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pourquoi ? Parce que nous vivons dans un monde de violences sexistes et sexuelles, et que nous assistons au recul des droits des femmes et des communautés LGBT (notamment des personnes transgenres et non binaires), attaqués par les mouvements d&amp;rsquo;extrême droite, masculinistes, identitaires et autres réactionnaires de tout poil.&lt;/p&gt;
&lt;p&gt;Le Planning Familial fait un travail essentiel face à ces menaces (droit à l&amp;rsquo;information, éducation sexuelle, défense du droit à l&amp;rsquo;IVG, combat des stéréotypes de genre), et c&amp;rsquo;est une des meilleures façons que j&amp;rsquo;ai trouvées de contribuer à mon échelle.&lt;/p&gt;</description></item><item><title>GenAI et développement logiciel : retour d'expérience avec PodSweeper</title><link>https://blog.zwindler.fr/2026/03/08/genai-llm-dev-podsweeper/</link><pubDate>Sun, 08 Mar 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/03/08/genai-llm-dev-podsweeper/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/02/opencode.webp" alt="Featured image of post GenAI et développement logiciel : retour d'expérience avec PodSweeper" /&gt;&lt;h2 id="la-genai-cest-fantastique-"&gt;La GenAI, c&amp;rsquo;est fantastique ?
&lt;/h2&gt;&lt;p&gt;Pas mal de gens ont sorti leur avis sur la Gen AI pour dev en très peu de temps, alors je me rends compte qu&amp;rsquo;il est plus que temps que je poste ce brouillon commencé il y a plus de deux semaines 🙃.&lt;/p&gt;
&lt;p&gt;Pour le travail, j&amp;rsquo;utilise de plus en plus d&amp;rsquo;assistants IA pour m&amp;rsquo;épauler dans mes tâches du quotidien. En 2024, c&amp;rsquo;était surtout pour automatiser des tâches pénibles (scripter des trucs, faire une liste pénible de tâches répétitives sans coder ça proprement). Courant 2025, j&amp;rsquo;ai testé plusieurs fois de lui faire faire de l&amp;rsquo;Ops et, à chaque fois, les résultats étaient moyens, tant pour concevoir une infra cohérente, fiable et efficiente que pour de l&amp;rsquo;assistance à la résolution d&amp;rsquo;incident (cf. &lt;a class="link" href="https://blog.zwindler.fr/2025/08/15/ops-disparition-suite/#et-dans-linfra-" &gt;mon article sur le sujet&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;En 2026, sur ce dernier point, je trouve qu&amp;rsquo;on n&amp;rsquo;a toujours pas avancé, même si je reconnais qu&amp;rsquo;il existe des outils spécialisés que je n&amp;rsquo;ai pas encore suffisamment testés, open source ou non. Je peux citer &lt;a class="link" href="https://k8sgpt.ai/" target="_blank" rel="noopener"
&gt;k8sgpt&lt;/a&gt; et &lt;a class="link" href="https://github.com/HolmesGPT/holmesgpt" target="_blank" rel="noopener"
&gt;HolmesGPT&lt;/a&gt; en open source, et &lt;a class="link" href="https://www.anyshift.io/" target="_blank" rel="noopener"
&gt;Anyshift&lt;/a&gt; avec son agent SRE pour la détection de root cause et la résolution d&amp;rsquo;incident.&lt;/p&gt;
&lt;p&gt;Note : fun fact, dans son post &amp;ldquo;&lt;a class="link" href="https://alex.balmes.co/fr/blog/mon-positionnement-sur-l-intelligence-artificielle-generative#pour-les-ops" target="_blank" rel="noopener"
&gt;Mon positionnement sur l&amp;rsquo;Intelligence Artificielle Générative&lt;/a&gt;&amp;rdquo; posté juste avant moi, Alex cite une longue liste de métiers qui vont profiter de l&amp;rsquo;IA Gen, et les ops ont le droit à rien d&amp;rsquo;autre qu&amp;rsquo;un argument de plus pour passer sur Kube 😭.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai en revanche commencé à utiliser la GenAI pour réaliser plusieurs projets de sites web à 100% (&lt;em&gt;vibe coding&lt;/em&gt; ?), dont je vous ai d&amp;rsquo;ailleurs déjà parlé :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="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/" &gt;Convertir un blog de Bloggrify à Hugo&lt;/a&gt;&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;Créer un site &amp;ldquo;joli&amp;rdquo;&lt;/a&gt; (largement au-delà de mes compétences) pour héberger les recherches que j&amp;rsquo;ai faites sur les différentes façons de déployer Kubernetes&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2026/02/20/securite-headers-http-observatory-hugo/" &gt;Améliorer la sécurité de mon site web&lt;/a&gt; en automatisant la modification du thème Hugo que j&amp;rsquo;utilise, de manière à avoir la meilleure note sur Mozilla HTTP Observatory&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans les 3 cas, le résultat est là. Ou plutôt, il &lt;em&gt;semble&lt;/em&gt; l&amp;rsquo;être pour le béotien que je suis.&lt;/p&gt;
&lt;p&gt;Je n&amp;rsquo;ai jamais caché que je n&amp;rsquo;ai aucune compétence en front, et peut-être que pour un expert du domaine, ce que j&amp;rsquo;ai fait avec l&amp;rsquo;IA est horrible (ou non ?). Dans tous les cas, ça ne peut pas être pire que les UIs que j&amp;rsquo;ai faites sans. Petit exemple 😄 :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/02/groroti.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="ou-alors--la-gêneai-"&gt;ou alors : la GêneAI ?
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est le genre de retours qu&amp;rsquo;on voit fleurir de la part de toutes les personnes qui sont &lt;em&gt;vraiment&lt;/em&gt; expertes d&amp;rsquo;un domaine quand elles voient des novices s&amp;rsquo;extasier. On a de bons exemples avec les vidéos générées par IA : si on ne fait pas attention, on a l&amp;rsquo;impression que c&amp;rsquo;est incroyable. Mais n&amp;rsquo;importe qui avec un œil un peu critique voit tout de suite de &lt;strong&gt;GROS&lt;/strong&gt; problèmes de cohérence. Pareil pour la génération d&amp;rsquo;image, ou de musique.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est aussi vrai pour le code, et on a de très bons exemples de projets &lt;em&gt;vibe codés&lt;/em&gt; qui sont d&amp;rsquo;immondes plats de spaghetti, et des passoires en termes de cybersécurité. Bref, vous l&amp;rsquo;aurez compris, j&amp;rsquo;ai beau être utilisateur (quotidien), je ne suis pas &amp;ldquo;incroyablé&amp;rdquo; (comme disent mes enfants) par les LLMs, même les meilleurs.&lt;/p&gt;
&lt;p&gt;Pourtant, j&amp;rsquo;ai plusieurs copains qui ne jurent que par ça, notamment des gens bien plus brillants / intelligents (appelez ça comme vous voulez) que moi. Donc je me dis &lt;em&gt;&amp;ldquo;OK, peut-être que je m&amp;rsquo;y prends mal. Essayons avec ce qui se fait de mieux&lt;/em&gt;&amp;rdquo; : un IDE type &amp;ldquo;Claude Code&amp;rdquo; et un modèle Opus.&lt;/p&gt;
&lt;h2 id="léditeur"&gt;L&amp;rsquo;éditeur
&lt;/h2&gt;&lt;p&gt;Après avoir fait une petite étude de marché rapide, on se rend compte que les options sont pléthoriques : Claude Code, OpenCode, Amp Code, &amp;hellip;&lt;/p&gt;
&lt;p&gt;Le problème de Claude Code, c&amp;rsquo;est le ticket d&amp;rsquo;entrée. Je ne suis pas encore prêt à lâcher 100 ou 200 € par mois pour l&amp;rsquo;usage que j&amp;rsquo;en ai aujourd&amp;rsquo;hui. Je ne sais pas si l&amp;rsquo;offre à 20€ me suffirait ou non. Au-delà de rares side projects perso que j&amp;rsquo;ai cités plus haut, je code peu. Le gros de mes geekeries, c&amp;rsquo;est de l&amp;rsquo;infra : installer des Kube et des OS de virtualisation. Des trucs difficiles à automatiser avec un LLM.&lt;/p&gt;
&lt;p&gt;On (Pierre surtout) m&amp;rsquo;a conseillé &lt;a class="link" href="https://ampcode.com/" target="_blank" rel="noopener"
&gt;Amp Code&lt;/a&gt; pour du perso, notamment parce que de base, il y a un tier gratuit avec un certain nombre de tokens et que si on n&amp;rsquo;est pas trop gourmand, c&amp;rsquo;est assez efficace. J&amp;rsquo;ai préféré n&amp;rsquo;en faire qu&amp;rsquo;à ma tête et tester plutôt sur &lt;strong&gt;OpenCode&lt;/strong&gt;, qui a l&amp;rsquo;avantage d&amp;rsquo;être plus flexible sur les providers de LLM et la consommation de tokens. Il est d&amp;rsquo;ailleurs possible de profiter de modèles locaux (gratuits, donc) ou &lt;a class="link" href="https://opencode.ai/docs/fr/zen/" target="_blank" rel="noopener"
&gt;des modèles inclus gratuitement (en tout cas pour l&amp;rsquo;instant) tels que GLM 5 Free&lt;/a&gt;, qui est plutôt bien placé dans les modèles de dev (surtout, c&amp;rsquo;est gratuit&amp;hellip;).&lt;/p&gt;
&lt;p&gt;OK, on a l&amp;rsquo;éditeur. Maintenant, le code ?&lt;/p&gt;
&lt;p&gt;Pour me faire une idée de la pertinence du code généré par mon LLM favori (Sonnet et Opus, depuis un moment), il me faut donc un langage &lt;strong&gt;et&lt;/strong&gt; un cas d&amp;rsquo;usage que je maîtrise, pour être capable de lui dire &lt;em&gt;&amp;ldquo;non mais là, c&amp;rsquo;est n&amp;rsquo;importe quoi !?&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Cas d&amp;rsquo;usage que je maîtrise&amp;hellip; vous vous en doutez, il y aura forcément du &lt;strong&gt;Kubernetes&lt;/strong&gt; dedans.&lt;/p&gt;
&lt;p&gt;Langage que je maîtrise : j&amp;rsquo;ai appris Go en 2022 grâce à un collègue de chez Deezer (encore merci Martial !) et j&amp;rsquo;ai publié quelques outils et fait des contributions mineures. J&amp;rsquo;ai écrit une grande partie de l&amp;rsquo;outil &lt;a class="link" href="https://github.com/deezer/GroROTI" target="_blank" rel="noopener"
&gt;GroROTI&lt;/a&gt; et aussi commencé (mais jamais terminé) le développement d&amp;rsquo;un RPG en Go, fortement inspiré de Castle of the Winds, un jeu de mon enfance : &lt;a class="link" href="https://github.com/zwindler/gocastle" target="_blank" rel="noopener"
&gt;gocastle&lt;/a&gt;. Et j&amp;rsquo;ai une capacité professionnelle (même si ce n&amp;rsquo;est pas le coeur de mon métier) dans mon travail de tous les jours.&lt;/p&gt;
&lt;h2 id="après-le-contexte-le-projet"&gt;Après le contexte, le projet
&lt;/h2&gt;&lt;p&gt;OK, on a le contexte technique : Kube + Go. Reste donc l&amp;rsquo;idée à implémenter. Il faut un projet suffisamment volumineux pour que le test soit pertinent, suffisamment rigolo pour que j&amp;rsquo;aie envie d&amp;rsquo;y passer du temps perso, mais quand même un peu utile pour que j&amp;rsquo;aie envie d&amp;rsquo;en parler et de montrer l&amp;rsquo;avancement. Et surtout, un projet dont la logique métier reste à ma portée : pour évaluer honnêtement la qualité du code produit par l&amp;rsquo;IA, il faut que je sois capable de le relire et de le critiquer.&lt;/p&gt;
&lt;p&gt;Et là, dans mes tiroirs à idées débiles qui débordent de mon cerveau, je me suis souvenu de ce &amp;ldquo;pitch&amp;rdquo; de 2023-2024, que j&amp;rsquo;avais laissé moisir faute de temps :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;PodSweeper&lt;/strong&gt; : la manière la plus complexe, over-engineerée et chaotique de jouer au démineur.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/02/Wat8.webp"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="partez-pas-vous-allez-voir-cest-rigolo-si-"&gt;Partez pas, vous allez voir c&amp;rsquo;est rigolo. Si !
&lt;/h2&gt;&lt;p&gt;Au lieu d&amp;rsquo;une grille visuelle où on clique sur des cases en espérant ne pas tomber sur une &amp;ldquo;mine&amp;rdquo;, on a une &amp;ldquo;grille&amp;rdquo; virtuelle où chaque case est un Pod et le clic est un &lt;code&gt;kubectl delete&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Oui, c&amp;rsquo;est débile. Ça me plaît donc beaucoup :).&lt;/p&gt;
&lt;p&gt;Au-delà du troll assumé (over-engineering des enfers) de ce jeu, il y a quand même un objectif pédagogique derrière.&lt;/p&gt;
&lt;p&gt;Si, si, vraiment.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai pensé le jeu comme une &lt;strong&gt;introduction à la sécurité dans Kubernetes&lt;/strong&gt;, avec des niveaux de difficulté à débloquer, façon CTF de cybersécurité.&lt;/p&gt;
&lt;p&gt;Au début, vous pouvez &lt;strong&gt;tout&lt;/strong&gt; faire. Vous pouvez bien sûr jouer normalement (delete un Pod, voir s&amp;rsquo;il y a des mines à côté) pour vous faire la main. Vous pouvez aussi automatiser les actions (un script qui &amp;ldquo;clique sur une case&amp;rdquo; au hasard, récupère les hints de proximité de mines, clique à un endroit safe, &amp;hellip;).&lt;/p&gt;
&lt;p&gt;Mais vous pouvez aussi &lt;strong&gt;tricher&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Et c&amp;rsquo;est tout l&amp;rsquo;intérêt du jeu. Dans les premiers niveaux de difficulté, les manifests Kubernetes du jeu sont volontairement vulnérables. On peut donc gagner sans effort, si on sait où chercher.&lt;/p&gt;
&lt;p&gt;Cependant, très vite, les niveaux s&amp;rsquo;enchaînent et les restrictions avec. Assez rapidement, on arrive dans des bonnes pratiques de production qui empêchent toute &amp;ldquo;triche&amp;rdquo;. Et si vous trouvez des vulnérabilités non prévues dans le code du jeu lui-même&amp;hellip; eh bien, c&amp;rsquo;est du bonus 😈.&lt;/p&gt;
&lt;h2 id="le-processus-initial"&gt;Le processus initial
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est probablement une erreur, mais j&amp;rsquo;ai fait toute la phase d&amp;rsquo;&lt;strong&gt;idéation avec Gemini&lt;/strong&gt; avant de lancer OpenCode. L&amp;rsquo;expérience aurait probablement été meilleure (a minima plus représentative) si j&amp;rsquo;avais commencé directement avec OpenCode.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai pondu tout ce que j&amp;rsquo;avais dans la tête, ainsi qu&amp;rsquo;un gros pavé informe de dizaines de lignes provenant de mes notes de quand j&amp;rsquo;ai eu l&amp;rsquo;idée en 2023, et je lui ai demandé de me sortir un fichier &lt;a class="link" href="https://github.com/zwindler/PodSweeper/blob/main/SPECIFICATION.md" target="_blank" rel="noopener"
&gt;SPECIFICATIONS.md&lt;/a&gt; avec toutes les infos importantes, remises dans l&amp;rsquo;ordre.&lt;/p&gt;
&lt;p&gt;Une fois les specs écrites, je lui ai demandé de détailler les différents niveaux et les difficultés que nous allions progressivement ajouter, dans &lt;a class="link" href="https://github.com/zwindler/PodSweeper/blob/main/GAMEPLAY.md" target="_blank" rel="noopener"
&gt;GAMEPLAY.md&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Enfin, j&amp;rsquo;ai demandé de faire un découpage du projet par &amp;ldquo;issues&amp;rdquo; et par priorité, de manière à avoir un MVP rapidement : &lt;a class="link" href="https://github.com/zwindler/PodSweeper/blob/main/ISSUES_PRIORITY.md" target="_blank" rel="noopener"
&gt;ISSUES_PRIORITY.md&lt;/a&gt;. Mon idée était de donner le plan de bataille très détaillé et découpé finement à OpenCode et de lui laisser dérouler.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Première déconvenue&lt;/strong&gt; : Gemini 3 Pro est assez mauvais pour ça. Les tâches étaient crédibles(-ish) mais ordonnées n&amp;rsquo;importe comment (dépendances entre tâches non respectées). J&amp;rsquo;ai donc lancé pour la première fois OpenCode dans mon repo et je lui ai dit &lt;em&gt;&amp;ldquo;voilà le contexte, voilà ce qu&amp;rsquo;on a imaginé comme tâches. Qu&amp;rsquo;est-ce que tu en penses ?&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="le-bon-opencode"&gt;Le bon (open)code&amp;hellip;
&lt;/h2&gt;&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/02/opencode.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Dans les trucs assez chouettes : OpenCode + Claude Opus a tout de suite vu que le plan imaginé par Gemini était bancal, et m&amp;rsquo;a posé des questions et proposé un découpage encore imparfait mais déjà plus cohérent. C&amp;rsquo;est probablement là le gros de l&amp;rsquo;intérêt de ces logiciels. Ils embarquent des connaissances pour guider le développement logiciel et surtout (SURTOUT) apportent du cadre.&lt;/p&gt;
&lt;p&gt;Assez vite, on finit par se mettre d&amp;rsquo;accord sur un plan qui me plaît. OpenCode met à jour les documents et se met à développer (scaffolding, création des pipelines, premiers binaires et images Docker vides). On a un projet prêt à développer en un claquement de doigt, c&amp;rsquo;est assez enthousiasmant, de prime abord.&lt;/p&gt;
&lt;p&gt;Les assistants LLMs ont très envie de se jeter dans le code, ça reste vrai avec OpenCode+Opus. Alors même qu&amp;rsquo;on n&amp;rsquo;avait pas fini de discuter du plan et des options possibles, il me proposait de lui-même de passer au code. Cela dit, c&amp;rsquo;était pareil (voire pire) pour Gemini (à qui j&amp;rsquo;avais dit spécifiquement qu&amp;rsquo;on n&amp;rsquo;allait pas faire de code du tout).&lt;/p&gt;
&lt;p&gt;Très vite, les créations de fichiers s&amp;rsquo;enchaînent. J&amp;rsquo;ai du mal à suivre et à chaque pause (quand le LLM a fini une tâche et qu&amp;rsquo;il me demande s&amp;rsquo;il peut passer à la suivante), je prends de longues minutes à lire ce que le LLM a produit. C&amp;rsquo;est à la fois grisant et épuisant (relire tout ça, c&amp;rsquo;est dur pour mon cerveau rouillé).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La vitesse brute est impressionnante.&lt;/strong&gt; En 3 sessions de 1 à 2h, j&amp;rsquo;ai un MVP fonctionnel. Je comprends qu&amp;rsquo;on parle régulièrement de 10x engineering quand on parle de génération de code avec les LLMs récents. Si on ne parle pas de génération de code brute (ce qui n&amp;rsquo;a pas vraiment de sens), à la louche, le code fonctionnel est généré entre 3x et 10x plus rapidement que ce que j&amp;rsquo;aurais pu faire seul. Des features qui mises bout à bout m&amp;rsquo;auraient pris des heures à implémenter (génération de la grille, logique de reveal, gestion des états de jeu) tombent en quelques minutes. Vous l&amp;rsquo;avez déjà lu ailleurs, je dis pareil - le goulot d&amp;rsquo;étranglement, ce n&amp;rsquo;est plus le code que je tape : c&amp;rsquo;est le code que je dois relire.&lt;/p&gt;
&lt;p&gt;De ce que j&amp;rsquo;ai lu, le code est correct, en particulier lors des premières itérations, un peu moins au bout d&amp;rsquo;un moment. Mais quand je dis &amp;ldquo;correct&amp;rdquo;, je sous-vends peut-être le truc : le code produit est du &lt;strong&gt;Go idiomatique&lt;/strong&gt;. Bonne structure en packages, conventions de nommage respectées, gestion d&amp;rsquo;erreur dans le style Go (quand elle est là&amp;hellip;), utilisation appropriée des interfaces. Ce n&amp;rsquo;est pas juste du code qui compile : c&amp;rsquo;est du code qu&amp;rsquo;un reviewer Go ne rejetterait pas d&amp;rsquo;emblée. On a pour objectif un MVP donc ça reste de la logique &amp;ldquo;métier&amp;rdquo; très simple, mais le socle est propre.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tout est testé&lt;/strong&gt;, très vite le projet contient une couverture parfaite et 100+ tests alors qu&amp;rsquo;on ne fait encore rien. Si c&amp;rsquo;était du code généré par un humain, je ne saurais pas dire si c&amp;rsquo;est une bonne chose. On est pas du tout dans du TDD, beaucoup de code teste des choses inutiles. Dans le cas du code généré par le LLM, c&amp;rsquo;est probablement pour le mieux, car le LLM a tendance à ajouter très régulièrement des régressions (on en reparlera). C&amp;rsquo;est donc intéressant ici, car :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ça ne coûte quasiment rien en temps de dev (ça va tellement vite à générer)&lt;/li&gt;
&lt;li&gt;cela permet au LLM de se rendre compte que son code introduit une régression, et de fixer de lui-même, sans attendre.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="-et-le-mauvais-opencode"&gt;&amp;hellip; et le mauvais (open)code
&lt;/h2&gt;&lt;p&gt;Côté outil et modèle d&amp;rsquo;abord, quelques &lt;strong&gt;irritants&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Par défaut, OpenCode n&amp;rsquo;incite pas le LLM à lancer &lt;code&gt;golangci-lint&lt;/code&gt; à chaque commit. On corrige ça avec un pre-commit hook et/ou les fameux AGENTS.md / skills, mais ça aurait DÛ faire partie du kit de base d&amp;rsquo;un projet golang (il y a bien les tests&amp;hellip;).&lt;/li&gt;
&lt;li&gt;Le LLM (Claude Opus sur OpenCode, mais c&amp;rsquo;est un biais habituel même en dehors) &lt;strong&gt;adore&lt;/strong&gt; les versions de logiciels qui existaient au moment de son entraînement. Il faut continuellement lui répéter que les versions qu&amp;rsquo;il suggère sont obsolètes. C&amp;rsquo;est vrai pour tout : le code Go, les dépendances, les versions des actions GitHub.&lt;/li&gt;
&lt;li&gt;On tombe très souvent dans la limite des 100k tokens. On perd du temps à &amp;ldquo;compacter&amp;rdquo; et on perd de la précision. Typiquement : le LLM me demande s&amp;rsquo;il peut &lt;code&gt;git commit&lt;/code&gt; des modifs, le contexte se compacte avant que je réponde, et il commit sans mon autorisation en redéroulant le contexte. Sur du code aussi peu sensible, ça va. Mais sur de la prod, ça serait un vrai problème.&lt;/li&gt;
&lt;li&gt;Le LLM est souvent amnésique : il oublie qu&amp;rsquo;il a accès à un cluster kind pour faire des tests réels (alors qu&amp;rsquo;il l&amp;rsquo;a fait juste avant la précédente compaction, par exemple).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Côté &lt;strong&gt;qualité du code&lt;/strong&gt; produit, c&amp;rsquo;est plus préoccupant :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Gestion d&amp;rsquo;erreur laxiste.&lt;/strong&gt; Certaines erreurs qui auraient dû retourner un FATAL (controller qui n&amp;rsquo;arrive pas à s&amp;rsquo;initialiser correctement !) sont simplement logguées comme ERROR. On se retrouve à ship des versions qui échouent silencieusement. Là encore, ça doit pouvoir se fine tuner avec un AGENTS.md.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Race conditions.&lt;/strong&gt; Je suis tombé très vite sur des race conditions assez bêtes. Dans mon cas, des pods bloqués en terminating impactaient le jeu suivant (graceful shutdown mal géré). Opus est parti en boucle sans comprendre la source du problème. J&amp;rsquo;ai dû le stopper et lui spécifier qu&amp;rsquo;il ne fallait jamais lancer une nouvelle partie sans s&amp;rsquo;être assuré que la précédente était terminée.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Génération de code en dehors des clous.&lt;/strong&gt; Parfois, sans crier gare, le LLM ajoute une fonctionnalité qui n&amp;rsquo;est pas demandée, inutile, voire qui va à l&amp;rsquo;encontre de la SPECIFICATION écrite précédemment. On est obligé de lui remettre un taquet derrière la tête.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Une fois remis sur les rails, le LLM déroule à nouveau, mais ces épisodes montrent bien que sur du code concurrent ou en dehors de user stories hyper strictes (ce qui sort du workflow de base d&amp;rsquo;opencode), la supervision humaine reste indispensable.&lt;/p&gt;
&lt;p&gt;On me répondra que je débute avec ces outils, et que j&amp;rsquo;aurais pu éviter certains écueils en configurant mieux mon environnement (AGENTS.md, pre-commit hooks, etc.). C&amp;rsquo;est vrai.&lt;/p&gt;
&lt;p&gt;Mais c&amp;rsquo;est justement là que le bât blesse : une des promesses vantées de la GenAI appliquée au dev, c&amp;rsquo;est de permettre à des non-développeurs (ou des débutants) de produire du logiciel de qualité à grande vitesse, de manière à pouvoir ship des logiciels complets. Si pour en tirer le meilleur, il faut déjà être un développeur expérimenté qui sait configurer un environnement complexe (avec les spécificités de ces outils IA), anticiper les race conditions et relire du code concurrent&amp;hellip; on n&amp;rsquo;a pas démocratisé le développement, on a juste donné un outil de plus aux gens qui savaient déjà coder. Mon profil (ops qui code, pas dev pur jus) était précisément le public cible de cette promesse. Et aujourd&amp;rsquo;hui, &amp;ldquo;les calculs sont pas bons, Kévin&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="où-jen-suis-"&gt;Où j&amp;rsquo;en suis ?
&lt;/h2&gt;&lt;p&gt;Depuis tout à l&amp;rsquo;heure, je vous parle de PodSweeper. Mais il en est où, ce projet ?&lt;/p&gt;
&lt;p&gt;Après 3 sessions de 1 à 2h max, sans compter l&amp;rsquo;idéation, j&amp;rsquo;ai un MVP fonctionnel. Comme je l&amp;rsquo;ai dit plus haut, le gain de productivité est indéniable, pour quelqu&amp;rsquo;un qui n&amp;rsquo;est pas &amp;ldquo;dev&amp;rdquo; de métier.&lt;/p&gt;
&lt;p&gt;Le code est probablement &amp;ldquo;globalement&amp;rdquo; de meilleure qualité que le code golang que j&amp;rsquo;aurais pu écrire, &lt;strong&gt;mais&lt;/strong&gt; le LLM laisse passer des trous béants dans la logique métier (tout simplement parce qu&amp;rsquo;il ne réfléchit pas, alors que moi, oui. Enfin, normalement). Dur pour la confiance&amp;hellip; Il faut vraiment rester très méfiant de tout ce qui est produit.&lt;/p&gt;
&lt;p&gt;Le code est disponible à l&amp;rsquo;adresse suivante :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/zwindler/PodSweeper/tree/main" target="_blank" rel="noopener"
&gt;https://github.com/zwindler/PodSweeper/tree/main&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/03/podsweeper.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Vous pouvez aller voir le code pour vous faire une idée. Si vous n&amp;rsquo;avez pas peur de vous spoiler, vous pouvez lire la &lt;a class="link" href="https://github.com/zwindler/PodSweeper/blob/main/SPECIFICATION.md" target="_blank" rel="noopener"
&gt;SPECIFICATION.md&lt;/a&gt; (spoilers), voire &lt;a class="link" href="https://github.com/zwindler/PodSweeper/blob/main/GAMEPLAY.md" target="_blank" rel="noopener"
&gt;GAMEPLAY.md&lt;/a&gt; (j&amp;rsquo;y détaille tous les niveaux donc c&amp;rsquo;est giga spoilers).&lt;/p&gt;
&lt;p&gt;Vous pouvez (normalement) jouer aux premiers niveaux de difficulté. Ça reste assez basique, si vous avez déjà manipulé &lt;code&gt;kubectl&lt;/code&gt; vous devriez trouver la solution très rapidement. Mon but est de rajouter des niveaux au fur et à mesure, j&amp;rsquo;en ai déjà imaginé 10, certains bien retors 😈 et d&amp;rsquo;autres viendront peut-être ensuite.&lt;/p&gt;
&lt;p&gt;Le code est en MPL v2.0 parce que c&amp;rsquo;est une licence copyleft que j&amp;rsquo;aime bien, &lt;a class="link" href="https://www.tldrlegal.com/license/mozilla-public-license-2-0-mpl-2" target="_blank" rel="noopener"
&gt;car elle est à la fois assez peu restrictive, OSI compliant et oblige quand même à reverser les modifs si on en fait dans son coin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Voilà :) si vous aussi vous trouvez ça rigolo, n&amp;rsquo;hésitez pas à tester et/ou à me faire des retours.&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;$ kubectl apply -k https://github.com/zwindler/podsweeper//deploy/base?ref&lt;span class="o"&gt;=&lt;/span&gt;v0.1.4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;namespace/podsweeper-game created
&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; --for&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ready pod -l app.kubernetes.io/name&lt;span class="o"&gt;=&lt;/span&gt;podsweeper &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -n podsweeper-game --timeout&lt;span class="o"&gt;=&lt;/span&gt;60s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pod/gamemaster-54f4dddcc4-6m88z condition met
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Have fun !&lt;/p&gt;</description></item><item><title>Kyverno a tué mon API Server. Encore.</title><link>https://blog.zwindler.fr/2026/02/26/kyverno-a-tue-mon-api-server-encore/</link><pubDate>Thu, 26 Feb 2026 08:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/02/26/kyverno-a-tue-mon-api-server-encore/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/02/0_days_without_kyverno.webp" alt="Featured image of post Kyverno a tué mon API Server. Encore." /&gt;&lt;h2 id="le-retour-de-la-vengeance"&gt;Le retour de la vengeance
&lt;/h2&gt;&lt;p&gt;Vous avez peut-être lu &lt;a class="link" href="https://blog.zwindler.fr/2023/11/30/kubernetes-error-etcdserver-mvcc-database-space-exceeded/" &gt;mon article précédent sur etcd il y a 3 ans&lt;/a&gt;. Si vous vous en souvenez bien, le crash, c&amp;rsquo;était etcd, mais le vrai coupable, c&amp;rsquo;était Kyverno. J&amp;rsquo;adore Kyverno. C&amp;rsquo;est vraiment un logiciel que j&amp;rsquo;aime beaucoup. Notamment parce que c&amp;rsquo;est puissant(issime). J&amp;rsquo;ai d&amp;rsquo;ailleurs écrit &lt;a class="link" href="https://blog.zwindler.fr/2022/08/01/vos-politiques-de-conformite-sur-kubernetes-avec-kyverno/" &gt;un article d&amp;rsquo;intro&lt;/a&gt; et &lt;a class="link" href="https://blog.zwindler.fr/2022/09/05/vos-politiques-de-conformite-sur-kubernetes-avec-kyverno-part2/" &gt;un second qui va plus loin&lt;/a&gt; sur le sujet.&lt;/p&gt;
&lt;p&gt;Mais le nombre d&amp;rsquo;incidents et de side effects chelou que ça provoque. Mamamia&amp;hellip; C&amp;rsquo;est pas le premier incident de l&amp;rsquo;année que j&amp;rsquo;ai avec Kyverno (oui, on est en février) mais comme celui-là est rigolo, je vous le partage.&lt;/p&gt;
&lt;p&gt;Lors d&amp;rsquo;une opération de maintenance de routine pour mettre à jour un cluster Kubernetes vers la version &lt;strong&gt;1.34&lt;/strong&gt; (depuis la 1.32), on s&amp;rsquo;est retrouvé face au scénario redouté par tout admin kube : un API Server totalement injoignable au redémarrage des nœuds du Control Plane.&lt;/p&gt;
&lt;p&gt;Ce qui ressemblait initialement à une erreur réseau classique s&amp;rsquo;est avéré être un &lt;strong&gt;deadlock&lt;/strong&gt; subtil entre les nouvelles fonctionnalités réseau natives de Kubernetes et notre cher Kyverno 😘.&lt;/p&gt;
&lt;p&gt;Spoiler : c&amp;rsquo;était pas un problème réseau. C&amp;rsquo;est jamais un problème réseau. Enfin si, des fois. Mais pas cette fois.&lt;/p&gt;
&lt;h2 id="lupgrade-qui-commence-bien"&gt;L&amp;rsquo;upgrade qui commence bien
&lt;/h2&gt;&lt;p&gt;Bon, un upgrade de Kubernetes, c&amp;rsquo;est devenu assez banal à ce stade. On fait ça régulièrement, on a nos procédures, on est des pros (si si). On passe de la 1.32 à la 1.34 en un seul commit, sans faire le petit saut par la 1.33 comme recommandé.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TKT FRR.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Dans le contexte technique dont je parle, tout est géré as code. De la création des machines jusqu&amp;rsquo;au déploiement de Talos, en passant par les MachineConfig (les CustomResources pour modifier&amp;hellip; et bien, la machine).&lt;/p&gt;
&lt;p&gt;Pour plus d&amp;rsquo;infos, voir &lt;a class="link" href="https://docs.siderolabs.com/talos/v1.12/reference/configuration/v1alpha1/config#machineconfig" target="_blank" rel="noopener"
&gt;la documentation Talos sur les Machine Configs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Le premier cluster qu&amp;rsquo;on teste n&amp;rsquo;a qu&amp;rsquo;un seul control plane (me demandez pas pourquoi, ça n&amp;rsquo;aurait probablement rien changé). Talos relance l&amp;rsquo;API Server dans la nouvelle version et là&amp;hellip; rien.&lt;/p&gt;
&lt;p&gt;Les logs de l&amp;rsquo;API Server &amp;ldquo;chelous&amp;rdquo; (terme technique) parlent d&amp;rsquo;eux-mêmes :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;I0224 15:32:50.979280 1 default_servicecidr_controller.go:166] Creating default ServiceCIDR with CIDRs: [10.1.0.0/20]
W0224 15:32:50.984784 1 dispatcher.go:225] rejected by webhook &amp;#34;validate.kyverno.svc-fail&amp;#34;:
admission webhook &amp;#34;validate.kyverno.svc-fail&amp;#34; denied the request:
Get &amp;#34;https://10.1.0.1:443/api&amp;#34;: dial tcp 10.1.0.1:443: connect: operation not permitted
I0224 15:32:50.985342 1 event.go:389] &amp;#34;Event occurred&amp;#34; kind=&amp;#34;ServiceCIDR&amp;#34;
apiVersion=&amp;#34;networking.k8s.io/v1&amp;#34; type=&amp;#34;Warning&amp;#34;
reason=&amp;#34;KubernetesDefaultServiceCIDRError&amp;#34;
message=&amp;#34;The default ServiceCIDR can not be created&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;😬😬😬&lt;/p&gt;
&lt;h2 id="la-root-cause--un-magnifique-cercle-vicieux"&gt;La root cause : un magnifique cercle vicieux
&lt;/h2&gt;&lt;p&gt;Après investigation, on a découvert que l&amp;rsquo;incident était le résultat d&amp;rsquo;une collision entre une évolution du core de Kubernetes et notre configuration Kyverno. Un beau cas d&amp;rsquo;école de deadlock.&lt;/p&gt;
&lt;p&gt;Décomposons le mécanisme :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Le nouveau Kind &lt;code&gt;ServiceCIDR&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Dans les versions récentes (v1.33+), Kubernetes migre la gestion des plages d&amp;rsquo;IP de services vers des objets dédiés nommés &lt;code&gt;ServiceCIDR&lt;/code&gt;. Au premier démarrage après l&amp;rsquo;upgrade, l&amp;rsquo;API Server tente de créer automatiquement l&amp;rsquo;objet par défaut (par exemple &lt;code&gt;10.1.0.0/20&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Pour les curieux, &lt;a class="link" href="https://github.com/kubernetes/enhancements/issues/1880" target="_blank" rel="noopener"
&gt;la KEP-1880&lt;/a&gt; et &lt;a class="link" href="https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/service-cidr-v1/" target="_blank" rel="noopener"
&gt;la documentation officielle du ServiceCIDR&lt;/a&gt; détaillent cette évolution.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est nouveau, c&amp;rsquo;est propre, c&amp;rsquo;est bien pensé. Sauf que&amp;hellip;&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Interception par le Webhook Kyverno&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Kyverno, configuré avec une &lt;code&gt;failurePolicy: Fail&lt;/code&gt; (parce qu&amp;rsquo;on est des gens sérieux qui ne laissent pas passer n&amp;rsquo;importe quoi en prod), est programmé pour intercepter les créations de ressources afin de les valider, et &lt;strong&gt;faire échouer le call si Kyverno ne répond pas&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Y compris le &lt;code&gt;ServiceCIDR&lt;/code&gt; fraîchement créé par l&amp;rsquo;API Server lui-même.&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Deadlock&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Et c&amp;rsquo;est là que ça devient magnifique (&amp;ldquo;la douleur peut être une source de plaisir&amp;rdquo; - Mozinor) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;L&amp;rsquo;API Server suspend la création du &lt;code&gt;ServiceCIDR&lt;/code&gt; en attendant le &amp;ldquo;OK&amp;rdquo; de Kyverno&lt;/li&gt;
&lt;li&gt;Pour contacter le service Kyverno, l&amp;rsquo;API Server doit router la requête via l&amp;rsquo;IP du service Kubernetes (en général &lt;code&gt;10.1.0.1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sauf que&lt;/strong&gt; la couche réseau (routage vers les services) ne peut pas s&amp;rsquo;initialiser tant que l&amp;rsquo;objet &lt;code&gt;ServiceCIDR&lt;/code&gt; n&amp;rsquo;est pas validé et créé&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est l&amp;rsquo;œuf et la poule, version &amp;ldquo;j&amp;rsquo;ai les clés de la voiture dans la voiture fermée à clé&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;PTSD. Oui ça m&amp;rsquo;est arrivé. Dans le désert. Sans réseau.&lt;/p&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Profit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;L&amp;rsquo;API Server tombe en timeout ou renvoie une erreur &lt;code&gt;connect: operation not permitted&lt;/code&gt; en tentant de joindre le webhook, bloquant ainsi sa propre initialisation. CrashLoopBackOff sur l&amp;rsquo;API Server. :D&lt;/p&gt;
&lt;h2 id="sortir-du-deadlock"&gt;Sortir du deadlock
&lt;/h2&gt;&lt;p&gt;Pour sortir de ce deadlock, il faut bypasser temporairement la couche d&amp;rsquo;admission. Facile, non ?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le workaround &amp;ldquo;habituel&amp;rdquo; : inutile&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Les deadlocks avec Kyverno, on a l&amp;rsquo;habitude à ce stade. Normalement, comme &lt;code&gt;kube-system&lt;/code&gt; est ignoré, on peut simplement se connecter avec un kube-config de secours (breaking glass, on est en OIDC normalement) qui a le cluster role cluster-admin et supprimer les validating webhooks de Kyverno :&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;kubectl delete validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Sauf que là&lt;/strong&gt;, c&amp;rsquo;est l&amp;rsquo;API Server qui ne démarre même pas. Mon &lt;code&gt;kubectl&lt;/code&gt; ne risque pas de fonctionner !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le vrai workaround : désactiver les webhooks au boot&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;La solution qu&amp;rsquo;on a choisie a été de modifier la configuration de l&amp;rsquo;API Server pour désactiver temporairement les webhooks de validation au démarrage. Mon éminent collègue Maxime a édité à chaud le machine config (avec un accès &lt;code&gt;talosctl&lt;/code&gt; breaking glass) &lt;a class="link" href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#how-do-i-turn-off-an-admission-controller" target="_blank" rel="noopener"
&gt;pour ajouter le flag suivant&lt;/a&gt; directement dans les &lt;code&gt;extraArgs&lt;/code&gt; de l&amp;rsquo;API server :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;--disable-admission-plugins=ValidatingAdmissionWebhook
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pour ceux qui ne savent pas trop comment ça fonctionne l&amp;rsquo;admission control dans Kubernetes, sachez juste qu&amp;rsquo;il y a une liste de plugins &amp;ldquo;par défaut&amp;rdquo; mais que tout est débrayable. Je ferai peut-être un jour un deep dive sur l&amp;rsquo;admission control dans Kubernetes, c&amp;rsquo;est fascinant ;).&lt;/p&gt;
&lt;p&gt;Avec ce flag, l&amp;rsquo;API Server peut enfin créer ses objets &lt;code&gt;ServiceCIDR&lt;/code&gt; sans demander la permission à personne (on bypass complètement tous les mécanismes de validation que Kyverno ou assimilé &lt;em&gt;enforce&lt;/em&gt;), le réseau s&amp;rsquo;initialise, Kyverno démarre, et on peut ensuite retirer le flag et relancer proprement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&amp;rsquo;option &amp;ldquo;golri&amp;rdquo; qu&amp;rsquo;on n&amp;rsquo;a pas testée&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Personnellement, je trouvais très rigolo l&amp;rsquo;idée d&amp;rsquo;aller modifier directement la base etcd pour supprimer la clé du webhook qui posait problème (là aussi en passant par &lt;code&gt;talosctl&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="c1"&gt;# Exemple via etcdctl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;etcdctl del /registry/admissionregistration.k8s.io/validatingwebhookconfigurations/kyverno-resource-validating-webhook-cfg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mes collègues étaient moins chauds : &amp;ldquo;Ouais mais tu comprends, si on casse etcd ça va être pénible&amp;rdquo;. On a joué la sécurité avec le flag. Je suis super déçu, on n&amp;rsquo;a pas testé 😂.&lt;/p&gt;
&lt;h2 id="la-solution-permanente--les-matchconditions"&gt;La solution permanente : les MatchConditions
&lt;/h2&gt;&lt;p&gt;OK, maintenant que le cluster est reparti, comment on s&amp;rsquo;assure que ça ne se reproduise pas au prochain upgrade ?&lt;/p&gt;
&lt;p&gt;La solution propre consiste à utiliser les &lt;code&gt;matchConditions&lt;/code&gt; (introduites en Kubernetes 1.27) au niveau de la &lt;code&gt;ValidatingWebhookConfiguration&lt;/code&gt;. Ça permet d&amp;rsquo;exclure les ressources critiques de bootstrap réseau &lt;strong&gt;avant même&lt;/strong&gt; que la requête ne tente de sortir de l&amp;rsquo;API Server vers le pod Kyverno.&lt;/p&gt;
&lt;p&gt;Voir &lt;a class="link" href="https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchconditions" target="_blank" rel="noopener"
&gt;la doc officielle sur les matchConditions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On utilisait déjà cette option pour limiter le trafic parfois abusif de Kyverno (si vous administrez du Kyverno, vous savez de quoi je parle) sur un certain nombre d&amp;rsquo;événements (on écroule l&amp;rsquo;API server et/ou Kyverno en CPU ou RAM, selon les cas). Il a suffi d&amp;rsquo;ajouter les exclusions pour les nouveaux types :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Exclusion des ressources de bootstrap réseau pour éviter le deadlock&lt;/span&gt;&lt;span class="w"&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;matchConditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;exclude-ServiceCIDR&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;!(request.kind.kind == &amp;#34;ServiceCIDR&amp;#34;)&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;exclude-IPAddress&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;!(request.kind.kind == &amp;#34;IPAddress&amp;#34;)&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Avec ça, quand l&amp;rsquo;API Server crée un &lt;code&gt;ServiceCIDR&lt;/code&gt; au boot, la requête ne passe même plus par le webhook Kyverno. Pas de dépendance circulaire, pas de deadlock, tout le monde est content.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Comme dirait notre cher (non) président :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;qui aurait pu prévoir ?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Bon ok, il suffisait de lire les release notes de Kubernetes 1.33. Cela étant dit, on a un cluster de staging, il sert à ça. On a cassé staging, no big deal&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/02/bigdeal.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Peut-être qu&amp;rsquo;on lira quand même, la prochaine fois ?&lt;/p&gt;</description></item><item><title>Touraine Tech 2026 - Récap du jour 1</title><link>https://blog.zwindler.fr/2026/02/12/recap-touraine-tech-2026-jour1/</link><pubDate>Thu, 12 Feb 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/02/12/recap-touraine-tech-2026-jour1/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/02/tnt26-entree.webp" alt="Featured image of post Touraine Tech 2026 - Récap du jour 1" /&gt;&lt;h2 id="de-retour-à-touraine-tech-"&gt;De retour à Touraine Tech !
&lt;/h2&gt;&lt;p&gt;Pour la 2ème fois, j&amp;rsquo;ai participé à la conférence Touraine Tech (la première fois c&amp;rsquo;était &lt;a class="link" href="https://blog.zwindler.fr/2022/01/12/touraine-tech-22-dis-papa-cest-quoi-un-sre/" &gt;en 2022&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Contrairement à 2022, la conférence a maintenant lieu à l&amp;rsquo;Université de Sciences de Tours (et non plus à Polytech) et se déroule sur 2 jours. L&amp;rsquo;occasion de revenir profiter des tourangelles et des tourangeux qui nous reçoivent toujours si bien.&lt;/p&gt;
&lt;h2 id="keynote-douverture---la-guerre-informationnelle"&gt;Keynote d&amp;rsquo;ouverture - La guerre informationnelle
&lt;/h2&gt;&lt;p&gt;Clément Hammel-Cazenave d&amp;rsquo;Agoratlas nous a proposé une keynote percutante sur les ingérences numériques et la guerre informationnelle en cours.&lt;/p&gt;
&lt;img src="https://blog.zwindler.fr/2026/02/tnt26_agoratlas.avif"
alt="Keynote Agoratlas"
loading="lazy"
decoding="async"&gt;
&lt;p&gt;Son point de départ : la crise agricole autour de l&amp;rsquo;abattage des troupeaux bovins à cause de la dermatose nodulaire. En analysant 500k tweets fournis par Visibrain, son équipe a reconstruit le graphe des interactions sur les réseaux sociaux. En utilisant &lt;a class="link" href="https://fr.wikipedia.org/wiki/M%C3%A9thode_de_Louvain" target="_blank" rel="noopener"
&gt;la méthode de Louvain&lt;/a&gt; (maximiser les liens intra-communauté et minimiser les liens inter-communautés), puis en labellisant manuellement les communautés, ils ont pu mettre en évidence un phénomène inquiétant : l&amp;rsquo;exclusion de la parole experte, cantonnée à sa petite bulle, pendant que le débat se polarise entre extrêmes (droites).&lt;/p&gt;
&lt;p&gt;Clément a aussi présenté &lt;a class="link" href="https://github.com/VIGINUM-FR/D3lta" target="_blank" rel="noopener"
&gt;&lt;strong&gt;D3lta&lt;/strong&gt;, un outil open source créé par Viginum&lt;/a&gt;, permettant de détecter les contenus dupliqués dans l&amp;rsquo;objectif de repérer les tentatives d&amp;rsquo;ingérences numériques.&lt;/p&gt;
&lt;p&gt;Point important soulevé : les réseaux sociaux impactent Internet de manière générale, y compris les LLMs qui sont entraînés en partie sur ces contenus. C&amp;rsquo;est une stratégie délibérée pour transformer les LLMs en &amp;ldquo;machines à narratif (russe)&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Mais ils ont aussi des impacts parfois importants sur &amp;ldquo;la vraie vie&amp;rdquo;. L&amp;rsquo;exemple des élections en Roumanie est frappant : un &amp;ldquo;inconnu&amp;rdquo; est arrivé au second tour grâce à des ingérences russes sur TikTok. L&amp;rsquo;élection a été annulée par la Cour constitutionnelle.&lt;/p&gt;
&lt;p&gt;Comment riposter ?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Viginum&lt;/strong&gt; : réseau de coordination et de protection des élections, créé après l&amp;rsquo;assassinat de Samuel Paty. La France est très en avance sur ce sujet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;L&amp;rsquo;Arcom&lt;/strong&gt; : manque malheureusement de moyens et de soutien politique&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DMA / DSA&lt;/strong&gt; : les régulations européennes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nous&lt;/strong&gt; : quitter X (&lt;a class="link" href="https://openportability.org/fr/auth/signin" target="_blank" rel="noopener"
&gt;openportability.org&lt;/a&gt;, non mais sérieux, vous attendez quoi ?), faire de l&amp;rsquo;analyse soi-même (NodeXL, Python, Gephi), éducation, contribuer aux outils comme ATProto ou Gephi&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Une keynote qui fait réfléchir sur notre responsabilité collective face à ces enjeux.&lt;/p&gt;
&lt;h2 id="mes-trains-jouef-passent-au-numérique-avec-des-raspberry-et-tinygo-"&gt;Mes trains Jouef passent au numérique, avec des Raspberry et TinyGo !
&lt;/h2&gt;&lt;p&gt;Un talk bonbon, qui parle d&amp;rsquo;une belle histoire de side project tech intergénérationnel ! Les speakers nous ont raconté comment ils ont modernisés les trains analogiques du grand-père.&lt;/p&gt;
&lt;img src="https://blog.zwindler.fr/2026/02/tnt26_trains_tinygo.avif"
alt="Talk Trains TinyGo"
loading="lazy"
decoding="async"&gt;
&lt;p&gt;Le problème de base : les trains analogiques fonctionnent en 14V/-14V (pour avant/arrière), mais il est difficile d&amp;rsquo;en faire circuler plus de 3 simultanément sans collisions. La solution professionnelle, le DCC, permet d&amp;rsquo;envoyer de la data dans le courant pour gérer plusieurs trains.&lt;/p&gt;
&lt;p&gt;La solution DIY en 2024 : TinyGo sur Raspberry Pi Pico W (version Wireless).&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;implémentation a été semée d&amp;rsquo;embûches techniques :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pont abaisseur de tension (14V → 5V) + contrôleur PWM pour la vitesse&lt;/li&gt;
&lt;li&gt;Problèmes de stabilité du réseau électrique&lt;/li&gt;
&lt;li&gt;Stack réseau WiFi absente de TinyGo, donc à recoder !&lt;/li&gt;
&lt;li&gt;Watchdog pour reboot automatique en cas de deadlock&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour réduire encore la taille et rentrer dans certains trains, il a dû changer de carte. Exit le WiFi/TCP-IP, bonjour le Bluetooth ! Résultat : latence passée de 300ms à 1ms. L&amp;rsquo;inconvénient : cette carte n&amp;rsquo;a qu&amp;rsquo;un seul core, donc il a fallu ruser avec des channels pour avoir des boucles non bloquantes. Fini le REST, place aux WebSockets.&lt;/p&gt;
&lt;p&gt;Un side project comme on les aime, avec ses galères et ses victoires !&lt;/p&gt;
&lt;h2 id="notre-talk--limits-requests-qos-priorityclasses"&gt;Notre talk : Limits, Requests, QoS, PriorityClasses
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;était notre tour avec Quentin Joly ! Nous avions amélioré le talk par rapport au &lt;a class="link" href="https://blog.zwindler.fr/2025/10/17/recap-devfest-nantes-2025/" &gt;DevFest Nantes&lt;/a&gt;, j&amp;rsquo;avais rajouté une démo et changé une autre qui devait être un peu stable. Enfin ça c&amp;rsquo;était sur le papier&lt;/p&gt;
&lt;img src="https://blog.zwindler.fr/2026/02/tnt26_cestpasschedule.avif"
alt="Talk Kubernetes scheduling"
loading="lazy"
decoding="async"&gt;
&lt;p&gt;&lt;strong&gt;Spoiler&lt;/strong&gt; : les deux nouvelles démos ont échoué&amp;hellip; et nous n&amp;rsquo;avions pas de backup 🙈.&lt;/p&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas grave en soi, les 6 autres démos ont parfaitement fonctionné et le public a été indulgent. Nous avons eu de très bons retours sur OpenFeedback, avec des commentaires très cools mettant en avant que le talk était accessible même aux &amp;ldquo;non sachants&amp;rdquo; Kube :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je rejoins les autres commentaires, côté développement je n&amp;rsquo;avais qu&amp;rsquo;une brève connaissance de kubernetes et j&amp;rsquo;ai pu suivre la conf&amp;rsquo; et apprendre plein de choses ! Beau challenge pour les démos c&amp;rsquo;était au top ! Bravo et merci !&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/02/tnt26_openfeedbacks.avif"
loading="lazy"
alt="Retours positifs du public sur OpenFeedback pour notre talk Kubernetes"
&gt;&lt;/p&gt;
&lt;p&gt;La leçon à retenir : même quand on est sûr de la démo et qu&amp;rsquo;on la lancée 10 fois sans incident, toujours avoir un plan B !&lt;/p&gt;
&lt;h2 id="pause-déjeuner"&gt;Pause déjeuner
&lt;/h2&gt;&lt;p&gt;On souffle, on profite du créneau du midi pour déguster un super potage ainsi qu&amp;rsquo;un très bon burger au pulled pork. Le traiteur était excellent, et malgré le nombre de personnes à nourrir les plats sortaient très vite. Chapeau !&lt;/p&gt;
&lt;h2 id="metal-as-a-service--gérer-votre-bare-metal-comme-une-machine-virtuelle-"&gt;Metal-As-A-Service : Gérer votre bare-metal comme une machine virtuelle !
&lt;/h2&gt;&lt;p&gt;Julien Briault continue de nous émerveiller avec la prouesse qu&amp;rsquo;il a lancé (au début seul, maintenant avec la petite 20aine de bénévoles) en réinternalisant l&amp;rsquo;informatique des 113 associations, en mutualisant sur une plateforme commune &amp;ldquo;le cloud du coeur&amp;rdquo;, entièrement construite sur des dons et des produits open source.&lt;/p&gt;
&lt;img src="https://blog.zwindler.fr/2026/02/tnt26_maas.avif"
alt="Talk MAAS"
loading="lazy"
decoding="async"&gt;
&lt;p&gt;La philosophie : &lt;strong&gt;&amp;ldquo;Tout doit être API&amp;rdquo;&lt;/strong&gt;. Ça me rappelle un peu le &lt;a class="link" href="https://nordicapis.com/the-bezos-api-mandate-amazons-manifesto-for-externalization/" target="_blank" rel="noopener"
&gt;fameux Bezos API Mandate&lt;/a&gt;, mais je ne suis pas sûr que Julien apprécierait la comparaison 😉.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;infrastructure est répartie sur 3 régions (Paris, Chartres, Marseille), avec pour chaque région 3 datacenters proches pour la haute disponibilité. Le choix du bare-metal permet d&amp;rsquo;éviter les dépendances centralisées (on se souvient tous de la panne US-EAST-1 qui a fait tomber les 3/4 d&amp;rsquo;Internet&amp;hellip;).&lt;/p&gt;
&lt;p&gt;Pourquoi &lt;strong&gt;MAAS&lt;/strong&gt; (Metal-As-A-Service de Canonical) ?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ironic&lt;/strong&gt; (OpenStack) : mauvaise UX, nécessite un OpenStack complet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tinkerbell&lt;/strong&gt; : a besoin de Kubernetes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MAAS&lt;/strong&gt; : existe depuis 2012, AGPL, supporte tous les vieux serveurs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le cycle de vie : boot PXE, récupération des paramètres BMC, remontée dans l&amp;rsquo;inventaire, configuration BMC pour le power control managé par MAAS. Tout est API : les calls sont envoyés à Netbox et Slack.&lt;/p&gt;
&lt;p&gt;Point FinOps intéressant : grâce à &lt;strong&gt;Open Baremetal&lt;/strong&gt; (écrit en Rust) qui utilise RAPL (Running Average Power Limit) via Scaphandre et VictoriaMetrics, ils allument et éteignent les machines en fonction de seuils. Résultat : &lt;strong&gt;division par 3 de la consommation électrique&lt;/strong&gt; et &lt;strong&gt;300 000€ économisés par an&lt;/strong&gt; (soit 300 000 repas !).&lt;/p&gt;
&lt;h2 id="pause-et-discussions-avec-les-sponsors"&gt;Pause et discussions avec les sponsors
&lt;/h2&gt;&lt;p&gt;J&amp;rsquo;ai longuement discuté avec Horacio et Yannick (akanoa) de Clever Cloud. Au menu : développement à l&amp;rsquo;ère de la GenAI, ordonnancement d&amp;rsquo;agents IA, et&amp;hellip; Kubernetes managé chez Clever Cloud. Je n&amp;rsquo;en dis pas plus 😏.&lt;/p&gt;
&lt;h2 id="les-agents--lordre-dans-le-chaos-des-ia"&gt;Les Agents : l&amp;rsquo;ordre dans le chaos des IA
&lt;/h2&gt;&lt;p&gt;Thierry Chantier nous a fait un état des lieux du monde des agents IA.&lt;/p&gt;
&lt;img src="https://blog.zwindler.fr/2026/02/tnt26_agentsia.avif"
alt="Talk Agents IA"
loading="lazy"
decoding="async"&gt;
&lt;p&gt;Rappel sur les LLMs : ce sont des modèles statistiques entraînés sur des montagnes de texte. On est passé du &amp;ldquo;texte only&amp;rdquo; à des systèmes &amp;ldquo;agentic&amp;rdquo; qui bougent très vite. Un agent, c&amp;rsquo;est une entité autonome avec la capacité de décomposer des tâches et de s&amp;rsquo;auto-évaluer.&lt;/p&gt;
&lt;p&gt;Conseil important : &lt;strong&gt;spécialiser les agents&lt;/strong&gt; pour optimiser les outils, réduire le domaine et être plus performant. On peut même utiliser des SLM voire des TinyLM pour des cas d&amp;rsquo;usage spécifiques.&lt;/p&gt;
&lt;p&gt;Les différents types de workflows :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Séquentiel&lt;/strong&gt; : les agents s&amp;rsquo;enchaînent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Routeur&lt;/strong&gt; : route vers le bon agent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hand-offs&lt;/strong&gt; : comme le routeur, mais les agents peuvent déléguer s&amp;rsquo;ils s&amp;rsquo;estiment incapables de répondre&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Superviseur&lt;/strong&gt; : comme le routeur, mais vérifie que l&amp;rsquo;objectif est atteint&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Côté frameworks, Thierry a mentionné :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LangGraph&lt;/strong&gt; (de LangChain) : un peu trop couplé&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ADK&lt;/strong&gt; (Agent Development Kit)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strands Agent&lt;/strong&gt; : beaucoup plus agnostique&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Crew.ai&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;ai aimé la mention de l&amp;rsquo;option &lt;code&gt;Tracing=true&lt;/code&gt; dans crew.ai. Si je comprend bien, ce sont des traces OpenTelemetry et pour pouvoir debug un workflow, ça me parait hyper intéressant.&lt;/p&gt;
&lt;h2 id="ko-technique"&gt;KO technique
&lt;/h2&gt;&lt;p&gt;J&amp;rsquo;avais prévu de voir le talk de Stéphane Philippart sur les CLI boostées à l&amp;rsquo;IA avec Picocli, ou celui de Laurent Grangeau sur la protection de la mémoire.&lt;/p&gt;
&lt;p&gt;Mais la fatigue de la soirée &amp;ldquo;before&amp;rdquo; passant par là, j&amp;rsquo;ai décidé de rentrer un peu plus tôt à mon hôtel, en attendant la soirée speaker. Celle-ci a eu lieu dans un bar à jeux privatisé, l&amp;rsquo;occasion de rencontrer d&amp;rsquo;autres speakers autour de tapas et d&amp;rsquo;une bière bien fraîche.&lt;/p&gt;
&lt;h2 id="à-suivre"&gt;À suivre&amp;hellip;
&lt;/h2&gt;&lt;p&gt;La suite au prochain épisode avec le récap du jour 2 ! Stay tuned 🎉&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></channel></rss>