<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>cluster on Zwindler's Reflection</title><link>https://blog.zwindler.fr/categories/cluster/</link><description>Recent content in cluster on Zwindler's Reflection</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>Licensed under CC BY-SA 4.0</copyright><lastBuildDate>Tue, 12 May 2026 12:00:00 +0200</lastBuildDate><atom:link href="https://blog.zwindler.fr/categories/cluster/index.xml" rel="self" type="application/rss+xml"/><item><title>Test de CKE, le Kubernetes managé chez Clever Cloud (partie 1)</title><link>https://blog.zwindler.fr/2026/05/12/test-cke-kubernetes-manage-clever-cloud-partie-1/</link><pubDate>Tue, 12 May 2026 12:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/05/12/test-cke-kubernetes-manage-clever-cloud-partie-1/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/05/logo-cke2026.webp" alt="Featured image of post Test de CKE, le Kubernetes managé chez Clever Cloud (partie 1)" /&gt;&lt;h2 id="la-suite-de-ma-saga-avec-clever-cloud"&gt;La suite de ma saga avec Clever Cloud
&lt;/h2&gt;&lt;p&gt;Ça fait maintenant quelques années que je teste les produits de Clever Cloud sur ce blog. En 2022, j&amp;rsquo;avais testé le &lt;a class="link" href="https://blog.zwindler.fr/2022/05/17/et-si-on-testait-clever-kubernetes-operator/" &gt;Clever Kubernetes Operator&lt;/a&gt;, qui permet de provisionner des services managés (MySQL, Redis, Pulsar&amp;hellip;) directement depuis un cluster Kubernetes via des CRDs. En 2025, j&amp;rsquo;ai réessayé leur offre &lt;a class="link" href="https://blog.zwindler.fr/2025/07/07/je-reessaye-les-sites-statiques-chez-clever/" &gt;d&amp;rsquo;hébergement de sites statiques&lt;/a&gt;, et j&amp;rsquo;ai même poussé le délire jusqu&amp;rsquo;à &lt;a class="link" href="https://blog.zwindler.fr/2025/07/20/kubernetes-sur-clever-cloud-linux/" &gt;déployer un control plane Kubernetes sur leurs apps Linux&lt;/a&gt;, pour rigoler en attendant leur offre managée.&lt;/p&gt;
&lt;p&gt;Bref, à la fin de cet article, je concluais avec un &lt;em&gt;trollface&lt;/em&gt; et un &amp;ldquo;j&amp;rsquo;attends mon accès au k8s managé de Clever Cloud&amp;rdquo;. Et bien&amp;hellip; c&amp;rsquo;est maintenant !&lt;/p&gt;
&lt;p&gt;Clever Cloud a lancé &lt;strong&gt;CKE&lt;/strong&gt; (Clever Kubernetes Engine) en bêta publique. J&amp;rsquo;ai eu accès en avant-première pour tester et je leur ai fait quelques retours. Maintenant que le produit est public, j&amp;rsquo;ai &lt;strong&gt;enfin&lt;/strong&gt; le droit de vous partager mes impressions.&lt;/p&gt;
&lt;h2 id="cke-cest-quoi-exactement-"&gt;CKE c&amp;rsquo;est quoi exactement ?
&lt;/h2&gt;&lt;p&gt;CKE est l&amp;rsquo;offre Kubernetes managée de Clever Cloud. Le positionnement est clair : Vanilla Kubernetes, pas de lock-in, le plus standard possible dans l&amp;rsquo;usage du cluster.&lt;/p&gt;
&lt;p&gt;Ce qui distingue CKE des offres plus conventionnelles, c&amp;rsquo;est ça :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Materia etcd&lt;/strong&gt; : leur implémentation maison de l&amp;rsquo;API etcd, construite sur FoundationDB (pas d&amp;rsquo;etcd VM à provisionner ou dimensionner, c&amp;rsquo;est serverless). C&amp;rsquo;était pas gagné, cf cet article de Pierre Zemb &lt;a class="link" href="https://pierrezemb.fr/posts/diving-into-kubernetes-watch-cache/" target="_blank" rel="noopener"
&gt;&amp;ldquo;Diving into Kubernetes&amp;rsquo; Watch Cache&amp;rdquo;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure souveraine et isolée des autres clients&lt;/strong&gt; : tout tourne sur des VMs dédiées, en France. Rien de mutualisé entre tenants.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cilium + eBPF&lt;/strong&gt; natif, avec WireGuard pour le chiffrement inter-nodes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="activation--cest-un-peu-caché"&gt;Activation : c&amp;rsquo;est un peu caché
&lt;/h2&gt;&lt;p&gt;CKE est en bêta publique, mais l&amp;rsquo;accès n&amp;rsquo;est pas actif par défaut. Il faut l&amp;rsquo;activer, soit via la CLI :&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;clever features &lt;span class="nb"&gt;enable&lt;/span&gt; k8s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note : les commandes &lt;code&gt;clever k8s&lt;/code&gt; sont disponibles dans la CLI à partir d&amp;rsquo;une version récente de leur CLI, idéalement version 4.9+.&lt;/p&gt;
&lt;p&gt;Soit depuis l&amp;rsquo;interface web, dans l&amp;rsquo;onglet &lt;strong&gt;Labs&lt;/strong&gt; de votre compte : &lt;a class="link" href="https://console.clever-cloud.com/users/me/feature-list" target="_blank" rel="noopener"
&gt;https://console.clever-cloud.com/users/me/feature-list&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;onglet Labs n&amp;rsquo;est pas particulièrement mis en avant dans la console (il faut savoir où chercher). Une fois le feature flag activé, un nouveau menu apparaît dans la console.&lt;/p&gt;
&lt;h2 id="création-dun-cluster"&gt;Création d&amp;rsquo;un cluster
&lt;/h2&gt;&lt;p&gt;Depuis la CLI, voici la commande minimale pour créer un cluster :&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;clever k8s create moncluster --watch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;--watch&lt;/code&gt; suit le déploiement jusqu&amp;rsquo;à ce que le cluster soit &lt;code&gt;ACTIVE&lt;/code&gt;. En pratique, ça ressemble à ça :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;⏳ Cluster status: DEPLOYING. Waiting for 10s before checking again...
✓ Cluster moncluster (kubernetes_01KRDDW6YNMDE7ZT0RYD3MY0GE) deployed successfully
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On peut passer tout un tas d&amp;rsquo;options : version K8s (&lt;code&gt;--cluster-version 1.36&lt;/code&gt;), topology, flavor du control plane, replication factor, autoscaling, CSI&amp;hellip; On y reviendra.&lt;/p&gt;
&lt;p&gt;Mais il est aussi possible, depuis peu, de créer un cluster via formulaire de création, avec un récapitulatif en temps réel.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/05/clever-create-cluster.avif"
loading="lazy"
alt="SCREENSHOT console création cluster"
&gt;&lt;/p&gt;
&lt;p&gt;On y retrouve tous les paramètres : version K8s, topology, flavor, replication factor, autoscaling, persistent storage. C&amp;rsquo;est bien fait, les topologies sont expliquées inline. Le nom de cluster est généré aléatoirement (style &lt;code&gt;cunning-arcane-runic-corsair&lt;/code&gt;, modifiable bien sûr).&lt;/p&gt;
&lt;h2 id="les-3-topologies--essential-business-enterprise"&gt;Les 3 topologies : Essential, Business, Enterprise
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est l&amp;rsquo;un des aspects les plus inhabituel de CKE. Trois topologies sont disponibles à la création (et &lt;strong&gt;immutables&lt;/strong&gt; après, ce qui oblige à bien choisir dès le départ).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topology&lt;/th&gt;
&lt;th&gt;Offre&lt;/th&gt;
&lt;th&gt;Ce que vous payez&lt;/th&gt;
&lt;th&gt;Usage typique&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALL_IN_ONE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Essential&lt;/td&gt;
&lt;td&gt;rf (replication factor) × VM bundle (CP + worker intégré)&lt;/td&gt;
&lt;td&gt;Dev, test, petits clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEDICATED_COMPUTE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Business&lt;/td&gt;
&lt;td&gt;rf × VM CP + chaque worker VM&lt;/td&gt;
&lt;td&gt;Prod avec isolation CP/workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DISTRIBUTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;5 composants × rf × VM + workers&lt;/td&gt;
&lt;td&gt;Prod HA fine par composant&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;ALL_IN_ONE&lt;/strong&gt; est la topologie par défaut. Le control plane et un worker node partagent la même VM (c&amp;rsquo;est le mode k3s-style, pour reprendre la description de la console). Ça a une conséquence concrète : &lt;code&gt;kubectl get nodes&lt;/code&gt; liste le node du control plane comme worker disponible pour les pods.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DEDICATED_COMPUTE&lt;/strong&gt; isole le CP sur ses propres VMs, les workers sont dans des node groups séparés. Avec un replication factor de 3, les VMs sont réparties sur les 3 datacenters parisiens de Clever Cloud (un datacenter peut tomber sans impacter le control plane).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DISTRIBUTED&lt;/strong&gt; pousse l&amp;rsquo;isolation plus loin : chaque composant du CP (apiserver, controller-manager, scheduler, cloud-controller-manager, node-group-operator) tourne sur sa propre VM, avec sa propre flavor et son propre replication factor. J&amp;rsquo;ai du mal à voir dans quel cas c&amp;rsquo;est utile, mais pourquoi pas.&lt;/p&gt;
&lt;h3 id="un-mot-sur-la-tarification"&gt;Un mot sur la tarification
&lt;/h3&gt;&lt;p&gt;Contrairement à certains cloud providers (de moins en moins cela dit&amp;hellip;) qui offrent parfois le control plane quand il est mutualisé et ne facturent que les workers, Clever Cloud facture une vraie VM dédiée par cluster (rien de mutualisé entre tenants).&lt;/p&gt;
&lt;p&gt;En ALL_IN_ONE, cette VM fait aussi office de worker, ce qui en fait paradoxalement l&amp;rsquo;option la moins chère. La flavor S (8 vCPU / 12 GB RAM) revient à &lt;strong&gt;64 €/mois&lt;/strong&gt;, CP et workers inclus sur la même machine. L&amp;rsquo;isolation se paye davantage : DEDICATED_COMPUTE commence à 96 €/mois pour le CP seul (+ workers séparés), DISTRIBUTED à 180 €/mois minimum pour 5 composants CP en 2XS.&lt;/p&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas donné pour du dev, mais le prix s&amp;rsquo;explique par le choix architectural : vous avez vraiment 8 vCPU / 12 GB de RAM rien que pour vous, pas un dixième de VM partagée entre dix clients.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note : pendant la bêta publique, le CSI (Ceph) et les LoadBalancers ne sont pas encore facturés. Seuls le CP et les workers le sont.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="récupérer-le-kubeconfig"&gt;Récupérer le kubeconfig
&lt;/h2&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;clever k8s get-kubeconfig moncluster &amp;gt; ~/.kube/clever.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl --kubeconfig ~/.kube/clever.yaml get nodes
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Un détail à noter : l&amp;rsquo;API server écoute sur un port non standard (&lt;strong&gt;1032&lt;/strong&gt; ou &lt;strong&gt;1022&lt;/strong&gt; selon les clusters). Ce n&amp;rsquo;est pas bloquant dans la plupart des environnements, mais j&amp;rsquo;ai eu la surprise de me retrouver bloqué depuis un réseau qui filtre les ports non-standards. À garder en tête si vous devez accéder au cluster depuis un réseau d&amp;rsquo;entreprise restrictif.&lt;/p&gt;
&lt;h2 id="anatomie-du-cluster"&gt;Anatomie du cluster
&lt;/h2&gt;&lt;p&gt;Une fois connecté, quelques observations :&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 get nodes -o wide
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME STATUS VERSION OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;control-plane-be4e3383-...-node Ready v1.36.0 Exherbo Linux 6.19.14-clevercloud-vm-dirty &lt;span class="o"&gt;(&lt;/span&gt;amd64&lt;span class="o"&gt;)&lt;/span&gt; containerd://2.3.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Exherbo Linux&lt;/strong&gt; : on retrouve bien la marque de fabrique de Clever Cloud. Pour celleux qui ne connaissent pas, Exherbo est une distro minimaliste et source-based, peu connue en dehors de cercles très spécialisés. Le kernel est un build custom Clever Cloud (&lt;code&gt;clevercloud-vm-dirty&lt;/code&gt;), et le container runtime est &lt;strong&gt;containerd 2.3.0&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Les composants pré-installés dans &lt;code&gt;kube-system&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;$ kubectl get pods -A
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAMESPACE NAME READY
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system cilium-tv9m7 1/1 &lt;span class="c1"&gt;# CNI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system cilium-operator-7db7f998d7-z5zq8 1/1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system coredns-5986bf5c44-wkzb9 1/1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system konnectivity-agent-l26kl 2/2 &lt;span class="c1"&gt;# proxy CP-&amp;gt;workers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system kube-state-metrics-75b86d74f6-6slx8 2/2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system kubelet-csr-approver-68d5bfcb6b-kgz5c 1/1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system metrics-server-559c9b85f9-wflvk 1/1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system cc-collector-wwpld 1/1 &lt;span class="c1"&gt;# collecteur OTel Clever Cloud&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Quelques absences notables par rapport à une install classique : &lt;strong&gt;pas de kube-proxy&lt;/strong&gt; (remplacé par Cilium en mode eBPF &lt;code&gt;kube-proxy replacement&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Pour la petite histoire, les premiers teasers de CKE avaient montré l&amp;rsquo;usage de flannel. Heureusement que les équipes de clever ont écouté la communauté et migré vers cilium.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Konnectivity : l&amp;rsquo;isolation réseau du control plane&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le pod &lt;code&gt;konnectivity-agent&lt;/code&gt; mérite une mention. C&amp;rsquo;est un composant qu&amp;rsquo;on retrouve assez souvent sur les Kubernetes managés et certaines distribution de Kubernetes. Il tourne sur chaque node (2 containers : &lt;code&gt;proxy-agent&lt;/code&gt; + &lt;code&gt;haproxy&lt;/code&gt;) et sert de proxy entre le control plane et les workers. Le CP ne parle pas directement aux pods : tout le trafic CP-&amp;gt;workers (logs, exec, port-forward, métriques) passe par ce tunnel.&lt;/p&gt;
&lt;p&gt;Pour un service managé, c&amp;rsquo;est la bonne pratique : le control plane n&amp;rsquo;a pas besoin d&amp;rsquo;accès réseau direct aux pods, et les workers n&amp;rsquo;ont pas besoin d&amp;rsquo;exposer kubelet directement au CP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Materia etcd&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pas de pod etcd dans le cluster : c&amp;rsquo;est normal. Clever Cloud utilise &lt;strong&gt;Materia etcd&lt;/strong&gt;, leur implémentation serverless de l&amp;rsquo;API etcd construite sur FoundationDB. C&amp;rsquo;est transparent pour vous.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cilium&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le CNI est &lt;strong&gt;Cilium v1.19.1&lt;/strong&gt;. Pour aller plus loin sur l&amp;rsquo;intégration Cilium/CKE, je vous recommande l&amp;rsquo;article de &lt;a class="link" href="https://blog.littlejo.link/cilium/clever/intro/" target="_blank" rel="noopener"
&gt;Joseph&lt;/a&gt; qui est le premier à en avoir parlé publiquement et qui s&amp;rsquo;y connaît bien mieux que moi sur le sujet.&lt;/p&gt;
&lt;h2 id="les-nodegroups"&gt;Les NodeGroups
&lt;/h2&gt;&lt;p&gt;Les workers sont gérés via une CRD custom &lt;code&gt;NodeGroup&lt;/code&gt; (groupe &lt;code&gt;api.clever-cloud.com/v1&lt;/code&gt;).&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;api.clever-cloud.com/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;NodeGroup&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;mes-workers&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;flavor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;S&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;nodeCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&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 nodegroup.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Les nodes apparaissent dans &lt;code&gt;kubectl get nodes&lt;/code&gt; environ 35-40 secondes après la création du NodeGroup. La CRD supporte le subresource &lt;code&gt;scale&lt;/code&gt;, ce qui permet de faire :&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 scale nodegroup mes-workers --replicas&lt;span class="o"&gt;=&lt;/span&gt;&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;p&gt;Les flavors disponibles en ALL_IN_ONE : S (8 vCPU / 12 GB), M (10 vCPU / 16 GB), L (12 vCPU / 24 GB), XL (16 vCPU / 32 GB). Un quota global s&amp;rsquo;applique au niveau de l&amp;rsquo;organisation (40 vCPU / 40 GB RAM par défaut, tous clusters confondus). Ca peut probablement être augmenté en faisant une demande au support, mais j&amp;rsquo;ai pas assez d&amp;rsquo;argent à crâmer pour justifier une telle demande 😅.&lt;/p&gt;
&lt;p&gt;En cas de dépassement de quota, le NodeGroup passe en phase &lt;code&gt;QuotaExceeded&lt;/code&gt; avec un message d&amp;rsquo;erreur très lisible :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;#39;Quota exceeded: RAM max: limit = 40.0 GB | actual = 48.0 GB | excess = 8.0 GB&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;C&amp;rsquo;est appréciable : on sait exactement ce qui bloque et de combien.&lt;/p&gt;
&lt;p&gt;La CLI propose aussi une gestion des node groups :&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;clever k8s nodegroups create moncluster workers M:3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;clever k8s nodegroups update moncluster workers --count &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;clever k8s nodegroups update moncluster workers --autoscaling --min &lt;span class="m"&gt;2&lt;/span&gt; --max &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="combien-de-temps-pour-booter-"&gt;Combien de temps pour booter ?
&lt;/h2&gt;&lt;p&gt;Un des points forts mis en avant par Clever Cloud. J&amp;rsquo;ai mesuré avec un script de benchmark (3 runs) et via la commande &lt;code&gt;clever k8s activity&lt;/code&gt; qui expose les timestamps précis de chaque étape :&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;clever k8s activity moncluster --format json
&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-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="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;operation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CREATE&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;stepName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Deploy Control Plane&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;STARTED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;createdAt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2026-05-12T06:27:11Z&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;operation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CREATE&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;stepName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Deploy Control Plane&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;COMPLETED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;createdAt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2026-05-12T06:27:28Z&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;operation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CREATE&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;stepName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Install Plugins&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;STARTED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;createdAt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2026-05-12T06:27:28Z&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;operation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CREATE&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;stepName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Install Plugins&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;COMPLETED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;createdAt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2026-05-12T06:28:01Z&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;activity&lt;/code&gt; détaille chaque étape du bootstrap : création du réseau, déploiement de Materia LogicalDB, déploiement du control plane, configuration du load balancer, installation des plugins. Elle couvre aussi les opérations sur les NodeGroups.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Résultats mesurés (K8s 1.35, 3 runs, 5 nodes S) :&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Étape&lt;/th&gt;
&lt;th&gt;Moyenne&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cluster DEPLOYING -&amp;gt; ACTIVE&lt;/td&gt;
&lt;td&gt;~57s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NodeGroup -&amp;gt; 1er node Ready&lt;/td&gt;
&lt;td&gt;~38s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NodeGroup -&amp;gt; tous les nodes Ready&lt;/td&gt;
&lt;td&gt;~46s (donc 8s après le premier node)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Suppression du cluster (API)&lt;/td&gt;
&lt;td&gt;~244ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Pour comparaison, SKS d&amp;rsquo;Exoscale bootstrappe le control plane en ~2 minutes (on me souffle dans l&amp;rsquo;oreille que ça a peut être baissé depuis). Et à la bonne époque où je faisais du AKS, c&amp;rsquo;était carrément 20+ minutes (j&amp;rsquo;espère qu&amp;rsquo;ils se sont améliorés depuis). Clever Cloud se place très haut sur ce critère.&lt;/p&gt;
&lt;h2 id="réseau-services-sécurité"&gt;Réseau, services, sécurité
&lt;/h2&gt;&lt;p&gt;LoadBalancer: chaque service de type LoadBalancer provisionne un L4 LB avec &lt;strong&gt;deux IPs publiques dédiées&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ingress : pas d&amp;rsquo;ingress controller préinstallé. Traefik s&amp;rsquo;installe sans problème via Helm.&lt;/p&gt;
&lt;p&gt;NetworkPolicies : Cilium étant le CNI natif, les NetworkPolicies sont &lt;strong&gt;enforced nativement&lt;/strong&gt;. Pas besoin d&amp;rsquo;installer quoi que ce soit en plus.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note pour ceux qui auraient lu mon article de mars sur le &lt;a class="link" href="https://blog.zwindler.fr/2026/03/15/flannel-networkpolicies-cilium-cni-chaining/" &gt;CNI chaining Flannel/Cilium&lt;/a&gt; : cet article était né des tests sur la bêta privée de CKE, qui utilisait Flannel à l&amp;rsquo;époque. Depuis le passage en bêta publique, CKE livre Cilium nativement (les NetworkPolicies fonctionnent out of the box).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Pod Security Admission : aucun namespace n&amp;rsquo;a de labels PSA par défaut (ni &lt;code&gt;kube-system&lt;/code&gt;, ni &lt;code&gt;default&lt;/code&gt;). Ça signifie que les pods sont en mode privileged implicite, sans restriction. On peut bien sûr configurer ça soi-même, mais sur un cluster managé on s&amp;rsquo;attendrait à avoir au minimum des profils &lt;code&gt;baseline&lt;/code&gt; ou &lt;code&gt;restricted&lt;/code&gt; préconfigurés sur les namespaces utilisateur.&lt;/p&gt;
&lt;p&gt;RBAC : pas de surprise, le kubeconfig fourni donne un accès &lt;code&gt;cluster-admin&lt;/code&gt;. A ne pas utiliser au delà de l&amp;rsquo;administration initiale ;-).&lt;/p&gt;
&lt;h2 id="stockage-csi"&gt;Stockage (CSI)
&lt;/h2&gt;&lt;p&gt;Le CSI n&amp;rsquo;est pas activé par défaut. Pour l&amp;rsquo;activer :&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;clever k8s add-persistent-storage moncluster
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Activation en ~1 minute. Une StorageClass &lt;code&gt;csi-rbd-sc&lt;/code&gt; (défaut) est créée, provisionnée par &lt;code&gt;rbd.csi.ceph.com&lt;/code&gt; (Ceph RBD), filesystem XFS, volume expansion activée.&lt;/p&gt;
&lt;p&gt;Les tests complets du cycle de vie (PVC, resize, snapshots) feront l&amp;rsquo;objet de la partie 2.&lt;/p&gt;
&lt;h2 id="un-vrai-déploiement--groroti"&gt;Un vrai déploiement : GroROTI
&lt;/h2&gt;&lt;p&gt;Histoire de valider que tout fonctionne de bout en bout, j&amp;rsquo;ai déployé &lt;a class="link" href="https://github.com/deezer/GroROTI" target="_blank" rel="noopener"
&gt;GroROTI&lt;/a&gt; (ma propre application web en golang de ROTI) via Helm, avec Traefik en ingress controller et un certificat TLS.&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;helm install traefik traefik/traefik -n traefik --create-namespace
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm install groroti oci://ghcr.io/zwindler/groroti-chart &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 values-clever.yaml &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 groroti --create-namespace
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Au bout de quelques minutes, l&amp;rsquo;application tourne sur &lt;code&gt;groroti.zwindler.fr&lt;/code&gt;, accessible publiquement, TLS inclus. Ça fonctionne.&lt;/p&gt;
&lt;h2 id="bugs-rencontrés-alpha--bêta-oblige"&gt;Bugs rencontrés (alpha / bêta oblige)
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;LoadBalancer&lt;/strong&gt; : lors de mes premiers tests (alpha / bêta privée), j&amp;rsquo;avais eu un cas où l&amp;rsquo;EXTERNAL-IP restait en &lt;code&gt;&amp;lt;pending&amp;gt;&lt;/code&gt; pendant&amp;hellip; 32 heures. Ce problème semble avoir disparu en bêta publique.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cluster FAILED sans diagnostic&lt;/strong&gt; : lors de mes tests, j&amp;rsquo;ai eu plusieurs clusters qui passaient en &lt;code&gt;FAILED&lt;/code&gt; ~25 secondes après la création, sans aucun message d&amp;rsquo;erreur exploitable. &lt;code&gt;clever k8s get &amp;lt;ID&amp;gt;&lt;/code&gt; retourne juste &lt;code&gt;Status: FAILED&lt;/code&gt;, même avec &lt;code&gt;--verbose&lt;/code&gt;. Aucune commande &lt;code&gt;logs&lt;/code&gt; ou &lt;code&gt;events&lt;/code&gt; pour un cluster n&amp;rsquo;existe dans la CLI.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est d&amp;rsquo;autant plus frustrant que le status &lt;code&gt;QuotaExceeded&lt;/code&gt; des NodeGroups est, lui, extrêmement bien documenté dans le message d&amp;rsquo;erreur. Il y a une belle asymétrie là.&lt;/p&gt;
&lt;p&gt;Le bug a été résolu côté Clever Cloud dans la nuit. Mais le manque de diagnostic reste un point à améliorer. L&amp;rsquo;UI est aussi encore clairement en bêta, avec un petit bug graphique que j&amp;rsquo;ai pu remonter.&lt;/p&gt;
&lt;h2 id="conclusion-partie-1"&gt;Conclusion (partie 1)
&lt;/h2&gt;&lt;p&gt;Première impression globalement positive. CKE est impressionnant pour une bêta :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Boot time excellent&lt;/strong&gt; : ~57s pour le cluster (CP à part ou All in one), ~38s pour le premier node quand on ajoute des NodeGroups (dans le haut du panier des Kubernetes managés que j&amp;rsquo;ai testé au fil des années)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cilium natif&lt;/strong&gt; avec WireGuard inter-nodes, NetworkPolicies out of the box&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Materia etcd sur FoundationDB&lt;/strong&gt; : une approche originale pour gérer les problématiques etcd (scalabilité, multi tenancy) d&amp;rsquo;un cloud provider&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design CRD propre&lt;/strong&gt; pour les NodeGroups, compatible &lt;code&gt;kubectl scale&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Konnectivity&lt;/strong&gt; : isolation réseau du CP comme chez les grands cloud providers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les points à améliorer : diagnostic du status FAILED, PSA non configurée par défaut, pas d&amp;rsquo;ingress controller inclus.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai aussi une petite feature que j&amp;rsquo;ai demandé à Antoine chez Clever, l&amp;rsquo;audit logging, c&amp;rsquo;est en cours. Wait and see ;-).&lt;/p&gt;
&lt;p&gt;La tarification du control plane me parait un poil élevée, typiquement face à des concurrents européens comme SKS ou Kapsule (mode mutualisé), mais elle s&amp;rsquo;explique par le choix architectural (VMs dédiées, rien de mutualisé). Pour une équipe déjà sur Clever Cloud, l&amp;rsquo;intégration avec le Clever Kubernetes Operator et les add-ons managés est un vrai argument.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dans la partie 2&lt;/strong&gt;, on creusera sûrement plus de fonctionnalités, comme le CSI (lifecycle PVC, resize, snapshots), les upgrades de cluster, l&amp;rsquo;intégration avec Clever Cloud operator (justement). Peut être aussi qu&amp;rsquo;on testera vCluster ou k3k ?&lt;/p&gt;</description></item><item><title>UserNamespaces dans Kubernetes : la feature géniale qui sert (presque) à rien</title><link>https://blog.zwindler.fr/2026/04/28/kubernetes-usernamespaces/</link><pubDate>Tue, 28 Apr 2026 10:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/04/28/kubernetes-usernamespaces/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/04/usernamespaces.webp" alt="Featured image of post UserNamespaces dans Kubernetes : la feature géniale qui sert (presque) à rien" /&gt;&lt;h2 id="linfographie-qui-ma-trigger"&gt;L&amp;rsquo;infographie qui m&amp;rsquo;a trigger
&lt;/h2&gt;&lt;p&gt;Depuis quelques jours, les infographies se suivent (et se ressemblent) sur Linkedin. Kubernetes 1.36 est sorti et une des features qui fait le plus parler, c&amp;rsquo;est la sortie en GA des &lt;strong&gt;UserNamespaces&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est un sujet que je suis depuis 2018 (talk &lt;a class="link" href="https://blog.zwindler.fr/2018/05/03/recap-du-premier-jour-de-kubecon-europe-2018/" target="_blank" rel="noopener"
&gt;The Route to rootless container&lt;/a&gt; à la kubecon EU de 2018) donc je peux dire que je suis content de voir l&amp;rsquo;aboutissement de ce long chemin. Cependant, je suis &amp;ldquo;profondément choqué&amp;rdquo; de voir la façon dont c&amp;rsquo;est présenté sur LinkedIn, visiblement par des gens qui n&amp;rsquo;ont aucune idée de comment ça fonctionne (et qui probablement, s&amp;rsquo;en fichent).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Kubernetes just made root safer. Just add &lt;code&gt;hostUsers: false&lt;/code&gt; to your Pod spec.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Le visuel : un roi tout-puissant &amp;ldquo;inside the container&amp;rdquo; et un mendiant impuissant &amp;ldquo;outside on the host&amp;rdquo;. La promesse : &amp;ldquo;No Host Access. No Privilege Escalation. No Lateral Movement. No Node Takeover.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est accrocheur.&lt;/p&gt;
&lt;p&gt;Mais c&amp;rsquo;est surtout hyper grave de le présenter comme ça, parce que ça occulte des pans entiers de sécurité applicative et opérationnelle. Vendre &lt;code&gt;hostUsers: false&lt;/code&gt; comme le remède universel au problème du &amp;ldquo;root dans les containers&amp;rdquo;, c&amp;rsquo;est une simplification dramatique qui va pousser des équipes à ignorer les vraies &lt;strong&gt;priorités&lt;/strong&gt; de sécurité.&lt;/p&gt;
&lt;h2 id="ce-que-les-usernamespaces-font-réellement-sans-bullshit"&gt;Ce que les UserNamespaces font réellement, sans bullshit
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Le threat model : le container escape&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Avant tout, de quoi parle-t-on exactement ? Un &lt;strong&gt;container escape&lt;/strong&gt; (évasion de container), c&amp;rsquo;est quand un attaquant réussit à sortir de son container et à accéder directement au kernel ou au système de fichiers de l&amp;rsquo;hôte, en bypassant complètement les mécanismes d&amp;rsquo;isolation habituels.&lt;/p&gt;
&lt;p&gt;Ce type de vulnérabilité est &lt;strong&gt;rare&lt;/strong&gt;, mais des exemples bien réels existent :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2019-5736" target="_blank" rel="noopener"
&gt;CVE-2019-5736&lt;/a&gt;&lt;/strong&gt; (runc) : écriture dans &lt;code&gt;/proc/self/exe&lt;/code&gt; du process hôte depuis le container&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2022-0492" target="_blank" rel="noopener"
&gt;CVE-2022-0492&lt;/a&gt;&lt;/strong&gt; (cgroups v1) : escape via &lt;code&gt;unshare&lt;/code&gt; dans certaines configurations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2024-21626" target="_blank" rel="noopener"
&gt;CVE-2024-21626&lt;/a&gt;&lt;/strong&gt; (runc, &amp;ldquo;Leaky Vessels&amp;rdquo;) : fuite de file descriptor vers le répertoire de travail de l&amp;rsquo;hôte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;S&amp;rsquo;il y a une faille de ce type sur votre Node ET qu&amp;rsquo;un process est compromis ET s&amp;rsquo;il tourne en root dans le container ET qu&amp;rsquo;il n&amp;rsquo;a pas les UserNamespaces, l&amp;rsquo;attaquant obtient &lt;strong&gt;root sur l&amp;rsquo;hôte&lt;/strong&gt;, c&amp;rsquo;est &lt;strong&gt;game over&lt;/strong&gt;. Accès à tous les fichiers du Node, à tous les secrets montés par les autres pods, possibilité d&amp;rsquo;installer un rootkit ou d&amp;rsquo;exfiltrer les données de tous les tenants présents sur le Node.&lt;/p&gt;
&lt;p&gt;Ca reste possible, mais ça fait beaucoup de &amp;ldquo;si&amp;rdquo;. Quoiqu&amp;rsquo;il en soit, c&amp;rsquo;est exactement ce scénario que les UserNamespaces adressent. Ils introduisent un &lt;strong&gt;mapping d&amp;rsquo;UID&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;L&amp;rsquo;UID 0 dans le container est mappé vers un UID non-privilégié sur l&amp;rsquo;hôte (ex: 100000, propre à chaque pod)&lt;/li&gt;
&lt;li&gt;Si un attaquant réussit à s&amp;rsquo;échapper du container via un exploit kernel, il se retrouve &lt;strong&gt;&lt;code&gt;nobody&lt;/code&gt;&lt;/strong&gt; sur le node, l&amp;rsquo;escape réussit, mais l&amp;rsquo;impact post-escape est dramatiquement réduit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est le scénario &amp;ldquo;Breakouts Lose Impact&amp;rdquo; de l&amp;rsquo;infographie, et là-dessus, &lt;strong&gt;l&amp;rsquo;infographie dit vrai&lt;/strong&gt;. C&amp;rsquo;est le vrai apport de la feature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cas particulier : le multi-tenant même en non-root&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Même sans container root, les UserNamespaces apportent aussi quelque chose dans un contexte &lt;strong&gt;vraiment multi-tenant&lt;/strong&gt; (plusieurs clients différents sur le même cluster). Sans UserNamespaces, si deux pods de clients différents tournent tous les deux avec &lt;code&gt;runAsUser: 1000&lt;/code&gt;, ils partagent le même UID 1000 sur le node. En cas d&amp;rsquo;escape sur l&amp;rsquo;un, l&amp;rsquo;attaquant peut accéder aux fichiers de l&amp;rsquo;autre pod qui ont le même propriétaire. Le mapping UID de UserNamespaces, en donnant un offset unique à chaque pod, isole les UID entre pods même à valeur identique dans le container.&lt;/p&gt;
&lt;p&gt;Pour les clusters internes où vous contrôlez tous les workloads, ce scénario est théorique. Pour une plateforme SaaS multi-tenant ou un service de build public, c&amp;rsquo;est une vraie ligne de défense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prérequis techniques&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pour pouvoir bénéficier de cette feature, il y a quelques prérequis, mais la majorité des clusters à jour devraient pouvoir se qualifier.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kernel Linux ≥ 5.19&lt;/li&gt;
&lt;li&gt;Runtime compatible (containerd ≥ 1.7, CRI-O ≥ 1.25)&lt;/li&gt;
&lt;li&gt;Support des &lt;em&gt;idmapped mounts&lt;/em&gt; pour les volumes persistants (XFS, ext4, mais pas NFS dans tous les cas par exemple)&lt;/li&gt;
&lt;li&gt;Kubernetes ≥ 1.33 (Beta), ≥ 1.36 (GA)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ce-que-linfographie-exagère-et-ce-quelle-oublie"&gt;Ce que l&amp;rsquo;infographie exagère (et ce qu&amp;rsquo;elle oublie)
&lt;/h2&gt;&lt;p&gt;L&amp;rsquo;infographie a donc raison sur un point précis : UserNamespaces réduit l&amp;rsquo;impact d&amp;rsquo;un container escape réussi. C&amp;rsquo;est réel. Le problème, c&amp;rsquo;est qu&amp;rsquo;elle vend la feature comme solution universelle au &amp;ldquo;root dans les containers&amp;rdquo;, et là c&amp;rsquo;est n&amp;rsquo;importe quoi.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. L&amp;rsquo;isolation de l&amp;rsquo;UID n&amp;rsquo;est pas une isolation des privilèges applicatifs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;infographie promet &amp;ldquo;No Lateral Movement&amp;rdquo;. C&amp;rsquo;est faux (et archi faux).&lt;/p&gt;
&lt;p&gt;Un container root avec &lt;code&gt;hostUsers: false&lt;/code&gt; peut toujours lire le &lt;strong&gt;ServiceAccount Token&lt;/strong&gt; monté dans &lt;code&gt;/var/run/secrets/kubernetes.io/serviceaccount/token&lt;/code&gt;. Si ce token a des permissions RBAC étendues (ce qui arrive, on en reparlera peut être dans un autre article prochainement), l&amp;rsquo;attaquant peut appeler l&amp;rsquo;API Server, énumérer les ressources du cluster, et se déplacer latéralement, le tout sans jamais toucher le Node hôte.&lt;/p&gt;
&lt;p&gt;Le mapping d&amp;rsquo;UID protège l&amp;rsquo;hôte. Il ne protège pas le cluster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Un container root reste root dans le container&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Install Anything ✅&amp;rdquo; — c&amp;rsquo;est littéralement écrit dans l&amp;rsquo;infographie, présenté comme un avantage 😖.&lt;/p&gt;
&lt;p&gt;Dans un container root (même avec UserNS), un attaquant qui prend la main peut :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installer &lt;code&gt;nmap&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;nc&lt;/code&gt; pour scanner le réseau interne&lt;/li&gt;
&lt;li&gt;Modifier les fichiers de l&amp;rsquo;application, les binaires, les configurations&lt;/li&gt;
&lt;li&gt;Lire tous les fichiers montés en volume&lt;/li&gt;
&lt;li&gt;Persister dans le container entre les redémarrages si le filesystem est writable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les UserNamespaces ne retirent aucun de ces vecteurs d&amp;rsquo;attaque. Pouvoir ajouter des logiciels, c&amp;rsquo;est la garantie d&amp;rsquo;un mouvement latéral rapide (là encore).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. C&amp;rsquo;est pas si facile, surtout pour le sto&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Activer &lt;code&gt;hostUsers: false&lt;/code&gt; casse le stockage existant dans la plupart des cas.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;UID 0 du container est mappé sur l&amp;rsquo;UID 100000+ sur l&amp;rsquo;hôte (chaque container a son propre &amp;ldquo;offset&amp;rdquo;). Si un volume persistant (NFS, EBS, Ceph RBD) appartient à l&amp;rsquo;UID 1000, le container root ne peut ni lire ni écrire dessus. Le résultat : des &lt;code&gt;Permission Denied&lt;/code&gt; contre intuitifs, potentiellement complexes à diagnostiquer car l&amp;rsquo;application n&amp;rsquo;a probablement pas été conçue, si elle est root, pour ne pas avoir accès à ses propres fichiers.&lt;/p&gt;
&lt;p&gt;La solution technique existe (&lt;em&gt;idmapped mounts&lt;/em&gt;), mais elle nécessite un kernel récent et un filesystem compatible. Voir la &lt;a class="link" href="https://www.kernel.org/doc/html/latest/filesystems/idmappings.html" target="_blank" rel="noopener"
&gt;documentation officielle des idmapped mounts&lt;/a&gt; pour les détails.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Idem, mais pour le réseau&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hostUsers: false&lt;/code&gt; est incompatible avec &lt;code&gt;hostNetwork: true&lt;/code&gt;. C&amp;rsquo;est un détail, mais il piège les workloads réseau (agents de monitoring, CNI plugins, etc.).&lt;/p&gt;
&lt;p&gt;Note : cela dit, avoir des containers en hostNetwork est &lt;strong&gt;un autre souci de sécurité&lt;/strong&gt;, donc bon&amp;hellip;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="comparaison-sans-bullshit--userns-vs-les-alternatives-existantes"&gt;Comparaison sans bullshit : UserNS vs les alternatives existantes
&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left"&gt;Vecteur d&amp;rsquo;attaque&lt;/th&gt;
&lt;th style="text-align: center"&gt;UserNS (root inside)&lt;/th&gt;
&lt;th style="text-align: center"&gt;Non-root (UID 1000)&lt;/th&gt;
&lt;th style="text-align: center"&gt;Distroless / Scratch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Impact post-escape si container escape réussi&lt;/td&gt;
&lt;td style="text-align: center"&gt;✅ Nobody sur l&amp;rsquo;hôte&lt;/td&gt;
&lt;td style="text-align: center"&gt;⚠️ UID 1000 sur l&amp;rsquo;hôte&lt;/td&gt;
&lt;td style="text-align: center"&gt;⚠️ UID 1000 sur l&amp;rsquo;hôte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Isolation UID entre pods (multi-tenant)&lt;/td&gt;
&lt;td style="text-align: center"&gt;✅ Offset unique par pod&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ UID partagé sur le node&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ UID partagé sur le node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Installation de malwares dans le container&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ Trivial&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ Possible&lt;/td&gt;
&lt;td style="text-align: center"&gt;✅ Quasi impossible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Périmètre d&amp;rsquo;écriture dans le FS éphémère du container&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ Tout le FS&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ Répertoire de l&amp;rsquo;app&lt;/td&gt;
&lt;td style="text-align: center"&gt;✅ Quasi impossible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Mouvement latéral via SA Token&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ Possible&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ Possible&lt;/td&gt;
&lt;td style="text-align: center"&gt;⚠️ Potentiellement difficile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Complexité opérationnelle&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ Parfois élevée&lt;/td&gt;
&lt;td style="text-align: center"&gt;✅ Souvent quasi nulle&lt;/td&gt;
&lt;td style="text-align: center"&gt;✅ Souvent faible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Compatibilité stockage existant&lt;/td&gt;
&lt;td style="text-align: center"&gt;❌ Parfois problématique&lt;/td&gt;
&lt;td style="text-align: center"&gt;✅ Standard&lt;/td&gt;
&lt;td style="text-align: center"&gt;✅ Standard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;La lecture de la table révèle la vraie nature d&amp;rsquo;UserNamespaces, il excelle sur &lt;strong&gt;exactement deux lignes&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l&amp;rsquo;impact post-escape&lt;/li&gt;
&lt;li&gt;l&amp;rsquo;isolation UID en multi-tenant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sur tout le reste, non-root + distroless fait mieux, ou aussi bien, sans la complexité opérationnelle. Et ce sont ces &amp;ldquo;tout le reste&amp;rdquo; (périmètre d&amp;rsquo;écriture dans le FS, installation de malwares, mouvement latéral via SA Token) qui représentent la grande majorité des vecteurs d&amp;rsquo;attaque réels, bien plus fréquents qu&amp;rsquo;un container escape. On en reparlera dans la section &lt;a class="link" href="#o%c3%b9-investir-son-budget-s%c3%a9curit%c3%a9" &gt;Où investir son budget sécurité&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="les-vrais-cas-dusage"&gt;Les vrais cas d&amp;rsquo;usage
&lt;/h2&gt;&lt;p&gt;Ce serait malhonnête de tout rejeter. Il existe trois scénarios où les UserNamespaces ne sont pas une option &amp;ldquo;fainéante&amp;rdquo; mais une &lt;em&gt;nécessité&lt;/em&gt; technique (et encore, ça se discute).&lt;/p&gt;
&lt;h3 id="1-build-as-a-service-buildah-rootless-podman"&gt;1. Build-as-a-Service (Buildah, rootless Podman)
&lt;/h3&gt;&lt;p&gt;Pour construire une image Docker, le moteur de build doit pouvoir faire des &lt;code&gt;chown&lt;/code&gt;, &lt;code&gt;chmod&lt;/code&gt; et &lt;code&gt;mknod&lt;/code&gt;. Ces opérations nécessitent &lt;code&gt;CAP_CHOWN&lt;/code&gt; et &lt;code&gt;CAP_FOWNER&lt;/code&gt;. Avant les UserNamespaces, la solution était de lancer le pod en &lt;code&gt;--privileged&lt;/code&gt;, ce qui est évidemment une porte ouverte sur l&amp;rsquo;hôte.&lt;/p&gt;
&lt;p&gt;Avec &lt;code&gt;hostUsers: false&lt;/code&gt;, le moteur de build a l&amp;rsquo;illusion d&amp;rsquo;être root pour manipuler ses fichiers, mais il est incapable d&amp;rsquo;impacter l&amp;rsquo;hôte. C&amp;rsquo;est le seul cas où &amp;ldquo;root inside&amp;rdquo; est une contrainte technique et non de la dette.&lt;/p&gt;
&lt;p&gt;Note : &lt;a class="link" href="https://github.com/GoogleContainerTools/kaniko" target="_blank" rel="noopener"
&gt;Kaniko&lt;/a&gt;, longtemps la référence pour le build in-cluster, est archivé depuis juin 2025 et ne reçoit plus de mises à jour de sécurité. Buildah ou rootless Podman sont les alternatives actives.&lt;/p&gt;
&lt;p&gt;Mon avis : ça peut être éventuellement utile pour les plateformes CI/CD mutualisées (GitLab Runners, Tekton) qui refusent les pods privilégiés. Mais si l&amp;rsquo;isolation est critique (plateforme publique, multi-tenant agressif), les microVMs (Kata Containers, Firecracker) offrent une garantie bien supérieure pour un overhead devenu raisonnable.&lt;/p&gt;
&lt;h3 id="2-multi-tenancy-hostile-plateformes-de-code-utilisateur"&gt;2. Multi-tenancy hostile (plateformes de code utilisateur)
&lt;/h3&gt;&lt;p&gt;Si votre métier est de faire tourner du code fourni par des inconnus (PaaS, éditeur de code en ligne, CI/CD publique), vous savez d&amp;rsquo;avance que l&amp;rsquo;utilisateur &lt;em&gt;va&lt;/em&gt; essayer d&amp;rsquo;escalader ses privilèges. Dans ce contexte, l&amp;rsquo;UserNS est une barrière supplémentaire contre les 0-day kernel.&lt;/p&gt;
&lt;p&gt;Mon avis : honnêtement, si l&amp;rsquo;environnement est vraiment &lt;strong&gt;hostile&lt;/strong&gt;, l&amp;rsquo;UserNS seul n&amp;rsquo;est pas suffisant. Les microVMs (Kata Containers, Firecracker) offrent une isolation matérielle réelle et sont le choix correct pour ce cas. L&amp;rsquo;UserNS peut être un complément, pas un substitut.&lt;/p&gt;
&lt;h3 id="3-legacy-hard-coded-postfix-dovecot-bind"&gt;3. Legacy &amp;ldquo;hard-coded&amp;rdquo; (Postfix, Dovecot, BIND)
&lt;/h3&gt;&lt;p&gt;Certains vieux démons UNIX démarrent en root pour ouvrir un port &amp;lt; 1024 ou lire des fichiers de config sensibles, puis &amp;ldquo;drop&amp;rdquo; leurs privilèges via &lt;code&gt;setuid()&lt;/code&gt;. Ce mécanisme échoue dans un container non-root classique.&lt;/p&gt;
&lt;p&gt;Les UserNamespaces permettent à ces processus de croire qu&amp;rsquo;ils peuvent faire leurs appels système de gestion d&amp;rsquo;identité, car ils sont root dans leur namespace.&lt;/p&gt;
&lt;p&gt;Voici un exemple concret écrit par un collègue (thanks Louis 😘) :&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;Pod&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;postfix&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;hostUsers: false # mapping UID &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;root dans le container → nobody sur l&amp;#39;hôte&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;securityContext&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;runAsNonRoot: false # autorisé en PSS Restricted *uniquement* grâce à hostUsers&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 class="nt"&gt;fsGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;103&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# GID postfix&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;containers&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;postfix&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;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;postfix:latest&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;securityContext&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;runAsNonRoot&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 class="c"&gt;# idem, cf. https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/#integration-with-pod-security-admission-checks&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;allowPrivilegeEscalation&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 class="nt"&gt;readOnlyRootFilesystem&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;seccompProfile&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;RuntimeDefault&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;capabilities&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;drop&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="s2"&gt;&amp;#34;ALL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# d&amp;#39;abord, on drop tout&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;add&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;SETUID &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# on ajoute SETUID mais le drop de privilèges via setuid() &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="c"&gt;# est fait par postfix lui-même au démarrage &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;SETGID &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# idem pour les groupes&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;CHOWN &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# chown sur les queues au démarrage&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;FOWNER &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# opérations sur fichiers sans être propriétaire&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;FSETID &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# conserver le setuid bit après écriture&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;DAC_OVERRIDE # OBLIGATOIRE &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;root dans UserNS n&amp;#39;est pas &amp;#34;vrai&amp;#34; root,&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="c"&gt;# les vérifications DAC ne sont pas contournées automatiquement&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;Ce manifest illustre plusieurs choses importantes.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord, c&amp;rsquo;est très pénible de rendre une application legacy secure avec les UserNS, et il faut faire des compromis, notamment sur les capabilities (on est loin de la feature magique qui sécurise les apps root).&lt;/p&gt;
&lt;p&gt;Ensuite, on remarque des trucs rigolos. Normalement, la policy &lt;code&gt;Restricted&lt;/code&gt; (Pod Security Standard) interdit &lt;code&gt;runAsNonRoot: false&lt;/code&gt;. Kubernetes fait une exception quand &lt;code&gt;hostUsers: false&lt;/code&gt; est présent. C&amp;rsquo;est documenté &lt;a class="link" href="https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/#integration-with-pod-security-admission-checks" target="_blank" rel="noopener"
&gt;ici&lt;/a&gt;. Sans UserNamespaces, ce pod serait rejeté par l&amp;rsquo;admission controller.&lt;/p&gt;
&lt;p&gt;De plus, on doit ajouter la &lt;strong&gt;capacité &lt;code&gt;DAC_OVERRIDE&lt;/code&gt;&lt;/strong&gt;, ce qui est contre-intuitif. root dans un UserNS n&amp;rsquo;est pas un vrai root du point de vue du kernel pour les vérifications DAC (Discretionary Access Control). Quand Postfix essaie de faire &lt;code&gt;set-permissions&lt;/code&gt; pour &lt;code&gt;chown&lt;/code&gt; ses queues, le kernel vérifie quand même les permissions ; et les refuse si &lt;code&gt;DAC_OVERRIDE&lt;/code&gt; n&amp;rsquo;est pas là. C&amp;rsquo;est exactement le genre de surprise opérationnelle invisible jusqu&amp;rsquo;au premier déploiement en production.&lt;/p&gt;
&lt;p&gt;On notera malgré tout qu&amp;rsquo;on a pu garder &lt;code&gt;readOnlyRootFilesystem: true&lt;/code&gt; et &lt;code&gt;allowPrivilegeEscalation: false&lt;/code&gt; ; le legacy ne justifie pas de tout sacrifier.&lt;/p&gt;
&lt;p&gt;Mon avis : c&amp;rsquo;est le seul cas d&amp;rsquo;usage où l&amp;rsquo;UserNS est vraiment acceptable. Pas de code tiers non maîtrisé, pas de plateforme hostile, juste du legacy bien identifié &lt;strong&gt;avec un plan de migration&lt;/strong&gt; ultérieur. Les deux autres cas sont &amp;ldquo;acceptables sous conditions&amp;rdquo;, le legacy reste le plus propre des trois.&lt;/p&gt;
&lt;h2 id="quelques-contre-arguments"&gt;Quelques contre-arguments
&lt;/h2&gt;&lt;p&gt;Je vous vois arriver avec quelques contre arguments, donc pour gagner du temps à tout le monde, je vais faire les questions et les réponses :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;C&amp;rsquo;est de la défense en profondeur.&amp;rdquo;&lt;/strong&gt;
&amp;ldquo;Vrai, mais&amp;rdquo;&amp;hellip; La défense en profondeur suppose qu&amp;rsquo;on a déjà posé les couches de base. Si vous n&amp;rsquo;avez pas encore migré vos images vers un user non-root, mettre de l&amp;rsquo;énergie sur l&amp;rsquo;UserNS est un non-sens. Et une fois en non-root, le gain marginal de l&amp;rsquo;UserNS est négligeable face à la complexité qu&amp;rsquo;il introduit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;On ne contrôle pas les images tierces.&amp;rdquo;&lt;/strong&gt;
Argument un peu faible, à mon avis : si vous avez une image blackbox de votre éditeur propriétaire, qui est codée pour tourner en root, il y a de fortes chances :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;soit qu&amp;rsquo;elle en ait réellement besoin pour fonctionner (typiquement, le cas pour certains outils de sécurité propriétaires)&lt;/li&gt;
&lt;li&gt;soit qu&amp;rsquo;elle gère mal le mapping d&amp;rsquo;UID (cf. le problème de stockage).
L&amp;rsquo;UserNS n&amp;rsquo;est pas une baguette magique qui rend n&amp;rsquo;importe quelle image tierce compatible et sécurisée.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;C&amp;rsquo;est un garde-fou contre les erreurs humaines.&amp;rdquo;&lt;/strong&gt;
Il est aussi facile d&amp;rsquo;oublier &lt;code&gt;hostUsers: false&lt;/code&gt; que d&amp;rsquo;oublier &lt;code&gt;runAsNonRoot: true&lt;/code&gt;. La vraie solution centralisée, ce sont les &lt;strong&gt;Pod Security Standards&lt;/strong&gt; ou un Admission Controller (Kyverno, OPA) qui rejettent purement et simplement les pods root. C&amp;rsquo;est plus simple, plus fiable, et ça ne casse pas le stockage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;On en a besoin pour la compliance SOC2/PCI-DSS/&amp;hellip;&amp;rdquo;&lt;/strong&gt;
Si votre compliance exige une isolation stricte entre tenants, l&amp;rsquo;UserNS sera probablement jugé insuffisant par vos auditeurs. Les VMs ou microVMs restent le standard. Utiliser l&amp;rsquo;UserNS pour la compliance, c&amp;rsquo;est choisir l&amp;rsquo;outil le plus complexe à maintenir pour un résultat discutable.&lt;/p&gt;
&lt;h2 id="où-investir-son-budget-sécurité-"&gt;Où investir son budget sécurité ?
&lt;/h2&gt;&lt;p&gt;Si on met de côté le marketing, voici où l&amp;rsquo;effort paye vraiment, du plus impactant au plus niche :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Priorité 1 — Images non-root + &lt;code&gt;nobody&lt;/code&gt; (UID 65534)&lt;/strong&gt;
Passer les images en non-root, idéalement vers l&amp;rsquo;utilisateur &lt;code&gt;nobody&lt;/code&gt; (le moins privilégié du système). Si une application est compromise sous &lt;code&gt;nobody&lt;/code&gt;, l&amp;rsquo;attaquant ne peut pratiquement rien faire, même sur le filesystem du container. À combiner avec &lt;code&gt;readOnlyRootFilesystem: true&lt;/code&gt; et &lt;code&gt;capabilities: drop: [&amp;quot;ALL&amp;quot;]&lt;/code&gt;.&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;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;securityContext&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;runAsNonRoot&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;runAsUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65534&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# nobody&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;seccompProfile&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;RuntimeDefault&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;containers&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;app&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;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;my-app:distroless&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;securityContext&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;allowPrivilegeEscalation&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 class="nt"&gt;capabilities&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;drop&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="s2"&gt;&amp;#34;ALL&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="nt"&gt;readOnlyRootFilesystem&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;&lt;strong&gt;Priorité 2 — Pod Security Standards (PSS) en mode &lt;code&gt;Baseline&lt;/code&gt; ou &lt;code&gt;Restricted&lt;/code&gt;&lt;/strong&gt;
Bloquer le root et les privilèges sans rien casser au niveau infra. Ça nécessite d&amp;rsquo;avoir déjà fait le point n°1, mais c&amp;rsquo;est gratuit, standard, et ça s&amp;rsquo;applique à tout le cluster via un label de namespace (et ça peut s&amp;rsquo;overrider par namespace si nécessaire). Plus de risque d&amp;rsquo;oubli possible. C&amp;rsquo;est déjà configuré par défaut sur plusieurs types de Kubernetes (je pense à Talos, mais ce n&amp;rsquo;est pas le seul).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Priorité 3 — MicroVMs (Kata Containers, Firecracker)&lt;/strong&gt;
Pour les workloads vraiment non-fiables. Isolation matérielle réelle, overhead devenu raisonnable sur les générations récentes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Priorité 4 — UserNamespaces&lt;/strong&gt;
When all else fail. Uniquement pour les cas légitimes identifiés ci-dessus (build, legacy, multi-tenancy hostile). C&amp;rsquo;est vraiment littéralement la &lt;strong&gt;dernière&lt;/strong&gt; chose à faire.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Les UserNamespaces dans Kubernetes 1.36 sont l&amp;rsquo;aboutissement d&amp;rsquo;un chantier qui aura pris &amp;ldquo;officiellement&amp;rdquo; cinq ans (KEP-127 date de 2021) et dont on parle quasiment depuis la genèse de Kubernetes. Pour les plateformes de build mutualisées et les SaaS multi-tenants qui font tourner du code utilisateur, c&amp;rsquo;est une brique potentiellement intéressante (notamment pour éviter qu&amp;rsquo;une app d&amp;rsquo;un client lise les apps d&amp;rsquo;un autre en cas de container escape sans élévation de privilège).&lt;/p&gt;
&lt;p&gt;Pour le reste (c&amp;rsquo;est-à-dire 99% des clusters de production) ce n&amp;rsquo;est pas là que commence la sécurité container — et c&amp;rsquo;est précisément le problème avec ce genre d&amp;rsquo;infographie.&lt;/p&gt;
&lt;p&gt;Les infographies LinkedIn qui vendent une sécurité sans effort sont dangereuses : &amp;ldquo;gardez votre image root de 800 Mo pleine d&amp;rsquo;outils, ajoutez juste &lt;code&gt;hostUsers: false&lt;/code&gt;, et vous êtes protégés&amp;rdquo;. C&amp;rsquo;est exactement &lt;strong&gt;l&amp;rsquo;inverse de la bonne démarche&lt;/strong&gt;. La vraie sécurité container se construit dans le Dockerfile, pas dans le PodSpec.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si vous activez les UserNamespaces pour sécuriser une application dont vous possédez le code source, vous avez probablement raté une étape dans votre cycle de développement sécurisé.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="références"&gt;Références
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/127-user-namespaces/README.md" target="_blank" rel="noopener"
&gt;KEP-127 : Support for User Namespaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/" target="_blank" rel="noopener"
&gt;Kubernetes docs — User Namespaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/docs/concepts/security/pod-security-standards/" target="_blank" rel="noopener"
&gt;Pod Security Standards&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2019-5736" target="_blank" rel="noopener"
&gt;CVE-2019-5736&lt;/a&gt; — runc : écriture dans &lt;code&gt;/proc/self/exe&lt;/code&gt; du process hôte depuis le container&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2022-0492" target="_blank" rel="noopener"
&gt;CVE-2022-0492&lt;/a&gt; — cgroups v1 : escape via &lt;code&gt;unshare&lt;/code&gt;, UserNS aide mais &lt;code&gt;runAsNonRoot&lt;/code&gt; aussi&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2024-21626" target="_blank" rel="noopener"
&gt;CVE-2024-21626&lt;/a&gt; — runc &amp;ldquo;Leaky Vessels&amp;rdquo; : fuite de file descriptor vers le répertoire de travail de l&amp;rsquo;hôte&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/GoogleContainerTools/distroless" target="_blank" rel="noopener"
&gt;Distroless containers — GoogleContainerTools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><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>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>101 façons de déployer Kubernetes : une nouvelle UI pour explorer les 118+ solutions</title><link>https://blog.zwindler.fr/2026/02/09/101-facons-de-deployer-kubernetes-nouvelle-ui/</link><pubDate>Mon, 09 Feb 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/02/09/101-facons-de-deployer-kubernetes-nouvelle-ui/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/02/101-kubernetes-ui-screenshot.webp" alt="Featured image of post 101 façons de déployer Kubernetes : une nouvelle UI pour explorer les 118+ solutions" /&gt;&lt;h2 id="de-google-sheet-à-une-vraie-application-web"&gt;De Google Sheet à une vraie application web
&lt;/h2&gt;&lt;p&gt;Vous vous souvenez peut-être de mes précédents articles sur ce projet : d&amp;rsquo;abord un &lt;a class="link" href="https://blog.zwindler.fr/2025/11/02/93-facons-de-deployer-kubernetes/" &gt;simple Google Sheet avec 93 méthodes&lt;/a&gt;, puis un &lt;a class="link" href="https://blog.zwindler.fr/2026/01/04/101-facons-de-deployer-kubernetes-v2/" &gt;dépôt GitHub avec plus de 100 entrées&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Aujourd&amp;rsquo;hui, je peux présenter la dernière itération de ce projet : &lt;strong&gt;une vraie interface web&lt;/strong&gt; pour explorer toutes ces solutions !&lt;/p&gt;
&lt;p&gt;👉 &lt;a class="link" href="https://zwindler.github.io/101-ways-to-deploy-kubernetes/" target="_blank" rel="noopener"
&gt;101-ways-to-deploy-kubernetes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/02/101-kubernetes-ui-screenshot.avif"
loading="lazy"
alt="Interface web du projet 101 ways to deploy Kubernetes"
&gt;&lt;/p&gt;
&lt;h2 id="pourquoi-une-ui-"&gt;Pourquoi une UI ?
&lt;/h2&gt;&lt;p&gt;Le tableau Markdown sur GitHub, c&amp;rsquo;était déjà mieux que le Google Sheet pour la collaboration, mais à peine. Difficile à parser, difficile de rajouter des colonnes sans que ça devienne trop le bazar (c&amp;rsquo;était déjà le cas, ahah), et surtout, ultra MOCHE.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/101-kubernetes-v2-screenshot.avif"
loading="lazy"
alt="Ancien tableau Markdown sur GitHub, difficile à lire"
&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc décidé de transformer tout ça en une interface moderne et intuitive, avec l&amp;rsquo;aide d&amp;rsquo;un LLM.&lt;/p&gt;
&lt;h2 id="stack-technique--astro--tailwind"&gt;Stack technique : Astro + Tailwind
&lt;/h2&gt;&lt;p&gt;Pour ce projet, j&amp;rsquo;ai choisi une stack simple mais efficace :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="https://astro.build/" target="_blank" rel="noopener"
&gt;Astro&lt;/a&gt;&lt;/strong&gt; : un framework moderne qui génère des sites statiques ultra-rapides&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="https://tailwindcss.com/" target="_blank" rel="noopener"
&gt;Tailwind CSS&lt;/a&gt;&lt;/strong&gt; : pour un design responsive sans prise de tête&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le résultat ? Un site léger, plutôt rapide, et qui fonctionne aussi bien sur &lt;strong&gt;desktop&lt;/strong&gt; que sur &lt;strong&gt;mobile&lt;/strong&gt; (même si l&amp;rsquo;expérience desktop reste plus confortable vu la quantité de données).&lt;/p&gt;
&lt;h2 id="les-fonctionnalités"&gt;Les fonctionnalités
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Des cartes pour chaque solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Fini le tableau digne d&amp;rsquo;un dev back (non, pire, un ingé kube&amp;hellip;) ! Chaque outil dispose maintenant de sa propre &amp;ldquo;carte&amp;rdquo; animée avec :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le logo du projet&lt;/li&gt;
&lt;li&gt;Le type de licence (OSS ou propriétaire)&lt;/li&gt;
&lt;li&gt;Le nombre d&amp;rsquo;étoiles GitHub&lt;/li&gt;
&lt;li&gt;Des liens directs vers le projet et des ressources tierces (blogs indépendants, REX, tutos&amp;hellip;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/02/101-kubernetes-zoom.avif"
loading="lazy"
alt="Vue détaillée d’une carte de solution Kubernetes avec logo, licence et étoiles GitHub"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Des filtres puissants&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Vous cherchez uniquement des solutions open source ? Des outils pour le développement local ? Des plateformes de management ?&lt;/p&gt;
&lt;p&gt;Les filtres par &lt;strong&gt;catégories&lt;/strong&gt; permettent de naviguer facilement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Desktop (développement local)&lt;/li&gt;
&lt;li&gt;Managed (offres cloud)&lt;/li&gt;
&lt;li&gt;Self-hosted (automatisation on-premise)&lt;/li&gt;
&lt;li&gt;Infra As Code&lt;/li&gt;
&lt;li&gt;Kubernetes OS (systèmes spécialisés)&lt;/li&gt;
&lt;li&gt;Management Platform&lt;/li&gt;
&lt;li&gt;Kubernetes in Kubernetes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et vous pouvez aussi filtrer par &lt;strong&gt;statut&lt;/strong&gt; (actif, abandonné) ou afficher uniquement les solutions &lt;strong&gt;open source&lt;/strong&gt; ou &lt;strong&gt;production ready&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Une barre de recherche&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Vous savez ce que vous cherchez ? Tapez directement le nom dans la barre de recherche pour trouver la solution en un instant.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Des tags pour affiner&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Au-delà des catégories, les tags permettent d&amp;rsquo;identifier rapidement les technologies sous-jacentes (kubeadm, k3s, k0s&amp;hellip;).&lt;/p&gt;
&lt;h2 id="le-saviez-vous--au-moins-18-outils-utilisent-kubeadm-"&gt;Le saviez-vous ? Au moins 18 outils utilisent kubeadm !
&lt;/h2&gt;&lt;p&gt;En compilant toutes ces données, j&amp;rsquo;ai découvert quelque chose de fascinant : &lt;strong&gt;au moins 18 outils&lt;/strong&gt; utilisent &lt;code&gt;kubeadm&lt;/code&gt; comme backend pour déployer Kubernetes ! 🤯&lt;/p&gt;
&lt;p&gt;Et ce n&amp;rsquo;est même pas en comptant les offres managées des cloud providers !&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est typiquement le genre d&amp;rsquo;information qu&amp;rsquo;on peut maintenant visualiser instantanément grâce à cette nouvelle interface.&lt;/p&gt;
&lt;h2 id="un-projet-collaboratif"&gt;Un projet collaboratif
&lt;/h2&gt;&lt;p&gt;Le projet reste &lt;strong&gt;100% open source&lt;/strong&gt; et collaboratif. Les données sont toujours stockées dans le dépôt GitHub, et l&amp;rsquo;UI est générée automatiquement à partir de ces données (j&amp;rsquo;ai même rajouté des previews pour les PRs).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Il manque un outil ou un provider ?&lt;/strong&gt; Vous avez repéré un bug ? N&amp;rsquo;hésitez pas à &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes/issues" target="_blank" rel="noopener"
&gt;ouvrir une issue&lt;/a&gt; ou à soumettre une Pull Request !&lt;/p&gt;
&lt;p&gt;Le projet compte maintenant &lt;strong&gt;118 solutions&lt;/strong&gt; (et je sais qu&amp;rsquo;il en manque certainement), avec pour chacune :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Des liens à jour&lt;/li&gt;
&lt;li&gt;Le statut du projet&lt;/li&gt;
&lt;li&gt;Des références externes (tutoriels, retours d&amp;rsquo;expérience&amp;hellip;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="essayez-commentez-partagez"&gt;Essayez, commentez, partagez
&lt;/h2&gt;&lt;p&gt;Rendez-vous sur &lt;a class="link" href="https://zwindler.github.io/101-ways-to-deploy-kubernetes/" target="_blank" rel="noopener"
&gt;zwindler.github.io/101-ways-to-deploy-kubernetes&lt;/a&gt; pour explorer toutes ces solutions !&lt;/p&gt;
&lt;p&gt;OK, c&amp;rsquo;est un infâme &amp;ldquo;call to action&amp;rdquo; comme on en voit sur tous les réseaux sociaux. Soit.&lt;/p&gt;
&lt;p&gt;Cependant, je ne peux pas savoir si c&amp;rsquo;est utile (ou non) si vous ne le faites pas. Je peux le laisser en l&amp;rsquo;état (ça n&amp;rsquo;est pas génant en soit, j&amp;rsquo;ai plein d&amp;rsquo;autres side projects qui n&amp;rsquo;attendent que mon temps libre) ou le faire vivre, si ça vous plait / sert.&lt;/p&gt;
&lt;p&gt;Et si vous trouvez ce projet utile, n&amp;rsquo;hésitez pas à :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Star&lt;/strong&gt; le projet sur &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes" target="_blank" rel="noopener"
&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partager&lt;/strong&gt; avec vos collègues de la communauté Cloud Native&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contribuer&lt;/strong&gt; en ajoutant des outils manquants ou en corrigeant des erreurs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Merci d&amp;rsquo;avance ! 🙏&lt;/p&gt;</description></item><item><title>Installation d'un Cluster HAT avec Raspberry Pi 5 et Pi Zero</title><link>https://blog.zwindler.fr/2026/01/27/installation-cluster-hat-raspberry-pi/</link><pubDate>Tue, 27 Jan 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/01/27/installation-cluster-hat-raspberry-pi/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/01/pi_cluster_hat_with_4_pi_zero.webp" alt="Featured image of post Installation d'un Cluster HAT avec Raspberry Pi 5 et Pi Zero" /&gt;&lt;h2 id="introduction-cest-quoi-un-cluster-hat-"&gt;Introduction, c&amp;rsquo;est quoi un Cluster Hat ?
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Qu&amp;rsquo;est ce que tu nous as encore trouvé ???&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Vous m&amp;rsquo;avez vu jouer avec des Raspberry Pi et y installer Kubernetes dessus, &lt;a class="link" href="https://blog.zwindler.fr/2025/12/23/alpine-linux-raspberry-pi-headless/" &gt;ou juste simplement Alpine Linux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Alors de quoi &lt;em&gt;diantre&lt;/em&gt; (oui, &amp;ldquo;diantre&amp;rdquo; carrément, n&amp;rsquo;ayons pas peur des mots) s&amp;rsquo;agit-il aujourd&amp;rsquo;hui ???&lt;/p&gt;
&lt;p&gt;En cherchant dans les tréfonds des Internets s&amp;rsquo;il était possible d&amp;rsquo;installer un cluster Kubernetes sur des Raspberry Pi zero, je suis tombé sur 3 personnes différentes qui avaient échoué dans cette tâche :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.andreveiga.dev/rpizero-k3s-cluster/" target="_blank" rel="noopener"
&gt;Setting up a Kubernetes cluster using Raspberry Pi Zero 2 W&amp;rsquo;s&lt;/a&gt; et &lt;a class="link" href="https://www.reddit.com/r/homelab/comments/urz31s/kubernetes_cluster_on_raspberry_pi_zero_2_ws_sort/?tl=fr" target="_blank" rel="noopener"
&gt;l&amp;rsquo;article reddit associé avec une photo&lt;/a&gt; (importante pour la suite de l&amp;rsquo;article)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/abelperezok/kubernetes-raspberry-pi-cluster-hat" target="_blank" rel="noopener"
&gt;github.com/abelperezok/kubernetes-raspberry-pi-cluster-hat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.reddit.com/r/homelab/comments/qta8yd/a_tiny_cluster_based_on_4x_raspberry_pi_zero_2_w/" target="_blank" rel="noopener"
&gt;A tiny cluster based on 4x Raspberry Pi Zero 2 W&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans les 3 cas, les gens ont tous essayé d&amp;rsquo;installer un cluster de RPi zero, plantés sur une sorte de carte additionnelle dont j&amp;rsquo;ignorais l&amp;rsquo;existence : le Cluster HAT&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/ClusterHAT-v2-in-use-sm.avif"
loading="lazy"
alt="Cluster HAT v2 en fonctionnement avec des Raspberry Pi Zero"
&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://clusterhat.com/" target="_blank" rel="noopener"
&gt;Site officiel du Cluster HAT clusterhat.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The Cluster HAT (Hardware Attached on Top) which interfaces a (Controller) Raspberry Pi A+/B+/2/3 with 4 Raspberry Pi Zeros configured to use USB Gadget mode is an ideal tool for teaching, testing or simulating small scale clusters.&lt;/p&gt;
&lt;p&gt;The Cluster HAT can be used with any mix of Pi Zero 1.2, Pi Zero 1.3 and Pi Zero W.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;USB Gadget Mode: Ethernet and Serial Console.&lt;/li&gt;
&lt;li&gt;Onboard 4 port USB 2.0 hub.&lt;/li&gt;
&lt;li&gt;Raspberry Pi Zeros powered via Controller Pi GPIO (USB optional).&lt;/li&gt;
&lt;li&gt;Individual Raspberry Pi Zero power controlled via Controller Pi GPIO (I2C).&lt;/li&gt;
&lt;li&gt;Connector for Controller Serial Console (FTDI Basic).&lt;/li&gt;
&lt;li&gt;Controller Pi can be rebooted without interrupting power to Pi Zeros, network recovers on boot.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id="problèmes"&gt;Problème(s)
&lt;/h2&gt;&lt;p&gt;Bon, forcément, c&amp;rsquo;est absurde, c&amp;rsquo;est donc un énième projet pour moi !!&lt;/p&gt;
&lt;p&gt;Premier problème : le Cluster HAT ne peut en théorie pas supporter les Pi Zero 2W. C&amp;rsquo;est en tout cas ce qu&amp;rsquo;on lit partout (sur les sites des revendeurs, et ce qu&amp;rsquo;on peut déduire de l&amp;rsquo;introduction que j&amp;rsquo;ai mise juste au dessus). Et c&amp;rsquo;est super dommage parce que c&amp;rsquo;est le modèle que j&amp;rsquo;ai (j&amp;rsquo;en ai deux) et j&amp;rsquo;ai un peu la flemme d&amp;rsquo;en acheter 4 en version 1.3 juste pour être sûr que ça fonctionne.&lt;/p&gt;
&lt;p&gt;Heureusement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Plusieurs commentaires trouvés sur le net indiquent qu&amp;rsquo;en réalité, ça fonctionne si l&amp;rsquo;alim (27w pour l&amp;rsquo;officielle) du Pi 5 était suffisamment puissante ; de plus, &lt;a class="link" href="https://8086.support/content/23/121/en/can-the-pi-zero-2-w-be-used-in-the-cluster-hat.html" target="_blank" rel="noopener"
&gt;la FAQ dit qu&amp;rsquo;en fait, c&amp;rsquo;est bon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Quentin m&amp;rsquo;en a gentiment passé 2 Pi Zero 1.3, ce qui complète ma collection. J&amp;rsquo;en aurais 2 de chaque, ça devrait le faire niveau alim.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Deuxième problème, en trouver un (Cluster HAT) ! La dernière version de ce produit a été produite il y a plusieurs années déjà. Le site officiel ne livre pas en France, le produit n&amp;rsquo;existe plus sur Kubii.fr. J&amp;rsquo;en ai trouvé encore quelques uns sur The Pi Hut (au Royaume-Uni et il n&amp;rsquo;y en a plus aujourd&amp;rsquo;hui), pour une quarantaine d&amp;rsquo;euros, livraison incluse.&lt;/p&gt;
&lt;p&gt;Dernier point, il a fallu trouver un Pi 5 à un prix raisonnable et ça fait mal d&amp;rsquo;avoir de lâcher 70€ pour un Pi 5 d&amp;rsquo;occasion avec seulement 4 Go.&lt;/p&gt;
&lt;p&gt;On en est donc, avec les accessoires (cartes SDs) et les différentes cartes à un side project à peu près 200€.&lt;/p&gt;
&lt;p&gt;Ça fait un peu cher pour un projet absurde, mais je finance ça avec un article (sur un autre sujet) qui va sortir dans un magazine dans quelques semaines. Ceux qui savent, savent ;-P.&lt;/p&gt;
&lt;h2 id="installation-matérielle"&gt;Installation matérielle
&lt;/h2&gt;&lt;p&gt;Probablement la partie la plus rigolote quand on aime (comme moi) bidouiller des composants informatiques et des cartes électroniques.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mettez les petites vis pour surélever le &amp;ldquo;Hat&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Insérez le Cluster HAT sur les ports GPIO du Raspberry Pi 5&lt;/li&gt;
&lt;li&gt;Insérez les 4 Pi Zero dans les ports USB du HAT&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Alimentation :&lt;/strong&gt; Utilisez impérativement une alimentation suffisamment puissante, idéalement l&amp;rsquo;alimentation officielle du Pi 5 (27W USB-C). Le Cluster HAT tire son énergie du Pi 5 pour alimenter les 4 Zéros.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cartes SD :&lt;/strong&gt; Il vous faut 5 cartes MicroSD au total (1 pour le Pi 5, 4 pour les Zéros). Le site parle de booter les pi zero sans carte MicroSDs (fonctionnalité expérimentale). Je regarderai peut être ?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/cluster-hat-1.avif"
loading="lazy"
alt="Cluster HAT assemblé sur un Raspberry Pi 5 avec les 4 Pi Zero"
&gt;&lt;/p&gt;
&lt;p&gt;Bon OK, c&amp;rsquo;était trivial. Mais ça prouve que le produit est bien conçu !&lt;/p&gt;
&lt;h2 id="boîtier-3d"&gt;Boîtier 3D
&lt;/h2&gt;&lt;p&gt;Comme j&amp;rsquo;ai une imprimante 3D, j&amp;rsquo;ai modifié un modèle existant pour les RPi avec un &amp;ldquo;M.2 hat&amp;rdquo; (une autre carte additionnelle, celle-ci pour connecter un SSD M.2) en faisant des gros TROUS partout pour que les pi zero puissent rentrer.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://makerworld.com/fr/models/1063966-raspberry-pi-5-ai-m-2-hat-snap-case#profileId-1052672" target="_blank" rel="noopener"
&gt;Raspberry Pi 5 AI/M.2 Hat+ Snap Case&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/pi_cluster_hat_with_4_pi_zero.avif"
loading="lazy"
alt="Cluster HAT dans son boîtier imprimé en 3D avec les 4 Pi Zero"
&gt;&lt;/p&gt;
&lt;p&gt;Je ne peux malheureusement pas partager le profil modifié car c&amp;rsquo;est contraire à la licence du fichier 3D (Standard Digital File License, qui interdit les modifications).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note :&lt;/strong&gt; j&amp;rsquo;ai passé plus de temps à chercher des modèles, les modifier et tester des prototypes, qu&amp;rsquo;à jouer avec le cluster hat lui-même.&lt;/p&gt;
&lt;h2 id="préparation-de-linstallation"&gt;Préparation de l&amp;rsquo;installation
&lt;/h2&gt;&lt;p&gt;La première chose à faire pour commencer à profiter de ce magnifique cluster de Raspberry est de commencer par installer et configurer notre Pi 5.&lt;/p&gt;
&lt;p&gt;Même s&amp;rsquo;il est théoriquement possible de tout faire soi-même, le fabricant du cluster hat conseille de partir sur les images préconfigurées disponibles sur son site &lt;a class="link" href="https://8086.net" target="_blank" rel="noopener"
&gt;8086.net&lt;/a&gt;. La dernière version disponible est téléchargeable ici :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://dist1.8086.net/clusterctrl/bookworm/2025-11-24/" target="_blank" rel="noopener"
&gt;dist1.8086.net/clusterctrl/bookworm/2025-11-24/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En parcourant la liste, on note qu&amp;rsquo;il existe :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;6 versions pour le RPi &amp;ldquo;maître&amp;rdquo; (Bookworm lite, normal et full, avec un mode CNAT ou CBRIDGE pour chaque)&lt;/li&gt;
&lt;li&gt;des images préconfigurées Rpi OS lite pour les Pi Zero (appelées Px, x étant la position sur le cluster hat), là aussi avec des variantes CBRIDGE / CNAT, armhf (32 bits) et arm64&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Par flemme, j&amp;rsquo;ai juste branché le Pi 5 sur le courant, je n&amp;rsquo;ai pas de câble Ethernet à portée de main. Dans ce cas, si comme moi vous connectez votre Pi en WiFi, il faut partir sur le mode &lt;strong&gt;CNAT&lt;/strong&gt; (NATé). Au contraire, le mode CBRIDGE (bridgé, qui nécessite donc le port Ethernet) est que vous verrez sur votre LAN les 5 machines.&lt;/p&gt;
&lt;p&gt;Deuxième info (rappel), dans mon cas, j&amp;rsquo;ai un mix de machines 32 et 64 bits, il faut donc que je fasse attention à bien sélectionner le bon OS pour les bonnes machines.&lt;/p&gt;
&lt;h2 id="installation-du-raspberry-pi-5-notre-contrôleur"&gt;Installation du Raspberry Pi 5 (notre contrôleur)
&lt;/h2&gt;&lt;p&gt;Pour le Raspberry Pi 5, dans mon cas le fichier est donc &lt;code&gt;2025-11-24-2-bookworm-ClusterCTRL-arm64-lite-CNAT.img.xz&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;wget https://dist1.8086.net/clusterctrl/bookworm/2025-11-24/2025-11-24-2-bookworm-ClusterCTRL-arm64-lite-CNAT.img.xz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="le-problème-du-mode-headless"&gt;Le problème du mode headless
&lt;/h3&gt;&lt;p&gt;Même si c&amp;rsquo;est possible, je ne veux pas brancher le Pi à un écran (faut sortir un clavier et un écran&amp;hellip; fatiguant) alors je vais tout faire de manière à être en &lt;strong&gt;headless&lt;/strong&gt;. Et manque de bol, les dernières versions du Raspberry Pi Imager (2.0.0) ne permettent pas (plus ?) de faire de customisation des OS tiers.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/clusterhat1.avif"
loading="lazy"
alt="Raspberry Pi Imager 2.0 ne permet plus de personnaliser les OS tiers"
&gt;&lt;/p&gt;
&lt;p&gt;En théorie, il va donc falloir, une fois l&amp;rsquo;image flashée sur la microSD, configurer manuellement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le WiFi&lt;/li&gt;
&lt;li&gt;un utilisateur de base&lt;/li&gt;
&lt;li&gt;activer SSH&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;ai essayé, ça a échoué, malgré un fichier &lt;code&gt;wpa_supplicant.conf&lt;/code&gt;, un &lt;code&gt;userconf.txt&lt;/code&gt; correctement formatté et un fichier &lt;code&gt;ssh&lt;/code&gt; vide.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/headless.avif"
loading="lazy"
alt="Configuration headless échouée avec wpa_supplicant et userconf"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La solution : Raspberry Pi Imager 1.9.6&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Après avoir pas mal ragé, je suis tout simplement retourné sur la version précédente de l&amp;rsquo;imager (1.9.6) qui fonctionne très bien avec l&amp;rsquo;image fournie par 8086.net.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note :&lt;/strong&gt; cependant, pour installer les OS RPi OS récents, j&amp;rsquo;ai eu pas mal d&amp;rsquo;échecs. Donc à utiliser avec parcimonie.&lt;/p&gt;
&lt;h2 id="installation-des-4-raspberry-pi-zero-les-nœuds"&gt;Installation des 4 Raspberry Pi Zero (Les Nœuds)
&lt;/h2&gt;&lt;p&gt;Ici, c&amp;rsquo;est un poil plus simple côté réseau, car en mode CNAT, les Pi Zero vont communiquer via le câble USB du Cluster HAT. MAIS il faut faire attention aux problématiques d&amp;rsquo;architecture 32 vs 64 bits, dans mon cas un peu hybride :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;P1&lt;/strong&gt; (un Pi Zero 2) : &lt;code&gt;...arm64-lite-CNAT-p1.img.xz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P2&lt;/strong&gt; (un Pi Zero 1.3) : &lt;code&gt;...armhf-lite-CNAT-p2.img.xz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P3&lt;/strong&gt; (un Pi Zero 1.3) : &lt;code&gt;...armhf-lite-CNAT-p3.img.xz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P4&lt;/strong&gt; (un Pi Zero 2) : &lt;code&gt;...arm64-lite-CNAT-p4.img.xz&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Niveau customization, on laisse donc SSH et le user/password, mais pas la peine de mettre le WiFi. On pourra d&amp;rsquo;ailleurs modifier les paramètres de boot pour supprimer côté kernel les modules Bluetooth et WiFi histoire de gagner quelques Mo de RAM.&lt;/p&gt;
&lt;h2 id="démarrage"&gt;Démarrage
&lt;/h2&gt;&lt;p&gt;Une fois que tout est prêt, que le RPi 5 fonctionne, on peut se logger en SSH et exécuter la commande :&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;sudo clusterctrl on
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;À partir de là, les LEDs du cluster hat devraient se mettre à s&amp;rsquo;exciter, et les RPi Zero devraient s&amp;rsquo;allumer progressivement. Ça fait un peu guirlande de Noël mais j&amp;rsquo;aime bien.&lt;/p&gt;
&lt;p&gt;Si on veut en allumer que certaines, c&amp;rsquo;est possible avec les commandes :&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;sudo clusterctrl on p1 &lt;span class="c1"&gt;# pour n&amp;#39;allumer que p1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo clusterctrl off p2 p3 p4 &lt;span class="c1"&gt;# pour éteindre p2, p3 et p4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="configuration-réseau"&gt;Configuration réseau
&lt;/h3&gt;&lt;p&gt;Dans le mode NAT, les adresses IP des Pi Zero sont affectées de la manière suivante :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;côté RPi 5 (le contrôleur) on a un bridge &lt;code&gt;br0&lt;/code&gt; avec pour adresse &lt;code&gt;172.19.181.254/24&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;côté Pi Zero, &lt;code&gt;172.19.181.x&lt;/code&gt; avec x allant de 1 à 4&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est aussi simple que ça. Depuis le contrôleur, on peut donc tenter de se logger en SSH :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/connect_p1.avif"
loading="lazy"
alt="Connexion SSH au Pi Zero p1 via le réseau NAT du Cluster HAT"
&gt;&lt;/p&gt;
&lt;p&gt;p1 a beau être NATé, le NAT fonctionne puisqu&amp;rsquo;on a bien accès à Internet :&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;zwindler@p1:~ $ ping 8.8.8.8
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PING 8.8.8.8 &lt;span class="o"&gt;(&lt;/span&gt;8.8.8.8&lt;span class="o"&gt;)&lt;/span&gt; 56&lt;span class="o"&gt;(&lt;/span&gt;84&lt;span class="o"&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;64&lt;/span&gt; bytes from 8.8.8.8: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;115&lt;/span&gt; &lt;span class="nv"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;12.5 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;64&lt;/span&gt; bytes from 8.8.8.8: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;115&lt;/span&gt; &lt;span class="nv"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;17.1 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;J&amp;rsquo;ai perdu pas mal de temps à flasher les images jusqu&amp;rsquo;à trouver le bon moyen d&amp;rsquo;avoir à la fois la ROM custom du cluster HAT &lt;strong&gt;et&lt;/strong&gt; les customizations OS pour tourner en headless.&lt;/p&gt;
&lt;p&gt;Mais une fois que j&amp;rsquo;avais ça, c&amp;rsquo;était relativement trivial de faire fonctionner ce petit cluster.&lt;/p&gt;
&lt;p&gt;Maintenant, il reste à trouver ce que je vais faire tourner dessus ;) j&amp;rsquo;ouvre les paris !&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://andypiper.medium.com/building-a-compact-pi-cluster-44217f6a9cf5" target="_blank" rel="noopener"
&gt;Building a compact Pi cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.reddit.com/r/homelab/comments/qta8yd/a_tiny_cluster_based_on_4x_raspberry_pi_zero_2_w/?tl=fr" target="_blank" rel="noopener"
&gt;A tiny cluster based on 4x Raspberry Pi Zero 2 W&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://medium.com/@dhuck/the-missing-clusterhat-tutorial-45ad2241d738" target="_blank" rel="noopener"
&gt;The missing ClusterHAT tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://clusterctrl.com/setup-software" target="_blank" rel="noopener"
&gt;ClusterCTRL Setup Software&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/burtyb/clusterhat-image/tree/master" target="_blank" rel="noopener"
&gt;ClusterHAT Image GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://dist1.8086.net/clusterctrl/bookworm/2025-11-24/" target="_blank" rel="noopener"
&gt;ClusterCTRL Downloads&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>101 façons de déployer Kubernetes (v2) : maintenant sur Github avec plus de méthodes, plus de détails, plus de tout</title><link>https://blog.zwindler.fr/2026/01/04/101-facons-de-deployer-kubernetes-v2/</link><pubDate>Sun, 04 Jan 2026 10:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/01/04/101-facons-de-deployer-kubernetes-v2/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/01/101-kubernetes-v2-screenshot.webp" alt="Featured image of post 101 façons de déployer Kubernetes (v2) : maintenant sur Github avec plus de méthodes, plus de détails, plus de tout" /&gt;&lt;h2 id="une-nouvelle-version-une-nouvelle-maison"&gt;Une nouvelle version, une nouvelle maison
&lt;/h2&gt;&lt;p&gt;Il y a quelques mois, je vous présentais mon &lt;a class="link" href="https://blog.zwindler.fr/2025/11/02/93-facons-de-deployer-kubernetes/" &gt;tableau recensant 93 façons différentes de déployer Kubernetes&lt;/a&gt;, sur Google Sheets.&lt;/p&gt;
&lt;p&gt;À la suite de la remarque de Ludovic Piot qui aurait aimé contribuer un ou deux outils manquants, j&amp;rsquo;ai pris un peu (beaucoup, en vrai) de temps pendant ces vacances pour &lt;strong&gt;déménager tout ça sur Github&lt;/strong&gt; : &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes" target="_blank" rel="noopener"
&gt;101-ways-to-deploy-kubernetes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/101-kubernetes-v2-screenshot2.avif"
loading="lazy"
alt="Capture d’écran du dépôt GitHub 101 ways to deploy Kubernetes v2"
&gt;&lt;/p&gt;
&lt;p&gt;Pourquoi sur un dépôt &lt;code&gt;git&lt;/code&gt; plutôt que sur un Google Sheet comme au début ? Parce que &lt;strong&gt;ça me permet facilement de rendre ce projet vraiment collaboratif&lt;/strong&gt;, notamment via des PRs (et surtout leur validation ou non).&lt;/p&gt;
&lt;p&gt;Google Sheets, c&amp;rsquo;était bien pour commencer, quand c&amp;rsquo;était un outil perso pour m&amp;rsquo;aider à écrire mon livre, mais pour la collaboration, c&amp;rsquo;était pas viable.&lt;/p&gt;
&lt;h2 id="quoi-de-neuf-dans-cette-v2-"&gt;Quoi de neuf dans cette v2 ?
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Contributions facilitées&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le projet dispose maintenant d&amp;rsquo;un &lt;strong&gt;&lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener"
&gt;CONTRIBUTING.md&lt;/a&gt;&lt;/strong&gt; qui explique comment participer.&lt;/p&gt;
&lt;p&gt;Vous connaissez une solution qui n&amp;rsquo;est pas listée ? Vous avez repéré une erreur ? Un lien cassé ? Il vous suffit de faire une Pull Request !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;De nombreuses nouvelles méthodes (j&amp;rsquo;ai pas compté mais probablement une douzaine)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le projet est passé de 93 à &lt;strong&gt;plus de 100 méthodes&lt;/strong&gt; ! J&amp;rsquo;ai continué mes explorations et ajouté une douzaine de nouvelles solutions, dont certaines que vous m&amp;rsquo;aviez suggérées.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Informations sur les licences&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai enrichi le tableau avec &lt;strong&gt;des détails sur les licences&lt;/strong&gt; de chaque projet. C&amp;rsquo;est particulièrement important pour identifier d&amp;rsquo;un coup d&amp;rsquo;oeil si un produit est réellement open source ou si c&amp;rsquo;est une solution à licence propriétaire ou commerciale.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2026/01/101-kubernetes-v2-screenshot.avif"
loading="lazy"
alt="Tableau des méthodes de déploiement Kubernetes avec informations de licence"
&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai eu le débat avec Ludovic justement, qui voulait comprendre pourquoi je n&amp;rsquo;avais pas listé &amp;ldquo;MKE (Mirantis Kubernetes Engine)&amp;rdquo;. Je lui avais répondu à l&amp;rsquo;époque que c&amp;rsquo;était parce que je ne pouvais pas avoir accès à une version d&amp;rsquo;essai en tant que &amp;ldquo;random des Internets&amp;rdquo; sans passer par un commercial, mais l&amp;rsquo;argument n&amp;rsquo;était pas hyper solide.&lt;/p&gt;
&lt;p&gt;Pour trouver une règle &amp;ldquo;unique&amp;rdquo; à indiquer dans le &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener"
&gt;CONTRIBUTING.md&lt;/a&gt;, j&amp;rsquo;ai donc réintégré une partie des solutions que j&amp;rsquo;avais &amp;ldquo;censurées&amp;rdquo;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;While the heart of the cloud native ecosystem is open source, both open source and closed source (proprietary) tools are accepted in this list. What matters is whether the tool helps deploy Kubernetes clusters, not its licensing model.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nombreuses références externes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai fait un &lt;strong&gt;important travail&lt;/strong&gt; sur la partie &amp;ldquo;références externes&amp;rdquo;. L&amp;rsquo;idée du répo, au delà de juste fournir une liste à la prévert, est de référencer des articles de tiers indiquant &lt;strong&gt;comment&lt;/strong&gt; mettre en place ces solutions et bénéficier des retours d&amp;rsquo;expérience de la communauté.&lt;/p&gt;
&lt;p&gt;Dans l&amp;rsquo;idée de rendre le projet plus international (ça sera tout aussi utile aux anglophones qu&amp;rsquo;aux petits frenchies de ma communauté), j&amp;rsquo;ai récupéré en priorité des liens en anglais, pour le plus de solutions possibles de cette liste. Il en reste encore qui n&amp;rsquo;en ont pas, mais ça progresse.&lt;/p&gt;
&lt;p&gt;Note importante : au-delà de la documentation officielle (qui existe très souvent pour la grande majorité des projets), j&amp;rsquo;ai en priorité voulu recenser :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Des tutoriels de qualité de la communauté&lt;/li&gt;
&lt;li&gt;Des articles de blog détaillés&lt;/li&gt;
&lt;li&gt;Des retours d&amp;rsquo;expérience concrets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="un-projet-toujours-sous-cc-by-sa-40"&gt;Un projet toujours sous CC BY-SA 4.0
&lt;/h2&gt;&lt;p&gt;Le contenu reste sous licence &lt;a class="link" href="https://creativecommons.org/licenses/by-sa/4.0/deed.fr" target="_blank" rel="noopener"
&gt;CC BY-SA 4.0 (Attribution - Share Alike)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Cela signifie que vous êtes libre de :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Partager&lt;/strong&gt; : copier et redistribuer le contenu&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adapter&lt;/strong&gt; : remixer, transformer et créer à partir de ce matériel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sous les conditions suivantes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Attribution&lt;/strong&gt; : vous devez créditer le projet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partage dans les mêmes conditions&lt;/strong&gt; : si vous adaptez le contenu, vous devez le distribuer sous la même licence&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="comment-contribuer-"&gt;Comment contribuer ?
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est simple :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Forkez le dépôt &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes" target="_blank" rel="noopener"
&gt;101-ways-to-deploy-kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ajoutez ou modifiez le contenu (en suivant le format du tableau)&lt;/li&gt;
&lt;li&gt;Soumettez une Pull Request&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lisez le &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener"
&gt;CONTRIBUTING.md&lt;/a&gt; pour plus de détails sur le processus et les bonnes pratiques.&lt;/p&gt;
&lt;p&gt;Toutes les contributions sont les bienvenues : nouvelles entrées, corrections, enrichissements, suggestions&amp;hellip;&lt;/p&gt;
&lt;h2 id="et-la-suite-"&gt;Et la suite ?
&lt;/h2&gt;&lt;p&gt;Le projet va continuer à évoluer au fil des découvertes et de vos contributions.&lt;/p&gt;
&lt;p&gt;Je continue aussi à explorer de nouvelles solutions et à enrichir les informations existantes. Et qui sait, peut-être qu&amp;rsquo;on dépassera les 150 méthodes d&amp;rsquo;ici la fin de l&amp;rsquo;année ? 😄&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est le moment où je fais mon influenceur et que je vous demande de ne pas hésiter à :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Star&lt;/strong&gt; le projet sur Github si vous le trouvez utile ⭐⭐⭐&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contribuer&lt;/strong&gt; en ajoutant vos découvertes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partager&lt;/strong&gt; autour de vous si vous le trouvez utile&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rendez-vous sur &lt;a class="link" href="https://github.com/zwindler/101-ways-to-deploy-kubernetes" target="_blank" rel="noopener"
&gt;github.com/zwindler/101-ways-to-deploy-kubernetes&lt;/a&gt; !&lt;/p&gt;</description></item><item><title>Transformer Proxmox VE en node Kubernetes avec LXC et lxcri</title><link>https://blog.zwindler.fr/2025/11/17/transformer-proxmox-ve-en-node-kubernetes-avec-lxc-et-lxcri/</link><pubDate>Mon, 17 Nov 2025 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2025/11/17/transformer-proxmox-ve-en-node-kubernetes-avec-lxc-et-lxcri/</guid><description>&lt;img src="https://blog.zwindler.fr/2025/11/proxmox-k8s.webp" alt="Featured image of post Transformer Proxmox VE en node Kubernetes avec LXC et lxcri" /&gt;&lt;h2 id="il-ne-savait-pas-que-cétait-complètement-stupide-alors-il-la-fait"&gt;Il ne savait pas que c&amp;rsquo;était complètement stupide, alors il l&amp;rsquo;a fait
&lt;/h2&gt;&lt;p&gt;Quel mensonge&amp;hellip; &lt;strong&gt;Bien sûr, je sais très bien que c&amp;rsquo;est débile&lt;/strong&gt;. Raison pour laquelle cette idée est donc irrésistible.&lt;/p&gt;
&lt;p&gt;Si vous suivez le blog, vous savez que j&amp;rsquo;utilise beaucoup Proxmox VE (les articles qui attirent le plus sur le blog sont d&amp;rsquo;ailleurs des articles sur cette technologie de virtualisation).&lt;/p&gt;
&lt;p&gt;Proxmox VE, c&amp;rsquo;est très cool ; on peut faire des VMs (QEMU) avec, mais aussi, si on n&amp;rsquo;a pas de VT-x ou qu&amp;rsquo;on veut des &amp;ldquo;lightweight VMs&amp;rdquo; (terme dont on a abusé à outrance), on peut installer des OS complets dans des containers avec LXC.&lt;/p&gt;
&lt;p&gt;Mais au delà de ces deux solutions, les devs du projet Proxmox VE sont assez rigides (et pas que là dessus). C&amp;rsquo;est d&amp;rsquo;ailleurs pour ça que j&amp;rsquo;ai posté quelques &amp;ldquo;hacks&amp;rdquo; pour contourner les limitations de Proxmox, notamment &lt;a class="link" href="https://blog.zwindler.fr/2022/11/07/containers-docker-dans-proxmox-avec-lxc/" target="_blank" rel="noopener"
&gt;cet article où j&amp;rsquo;explique comment lancer des containers Docker (via LXC) dans Proxmox VE (normalement pas possible)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Et si on allait encore plus loin dans le hack idiot ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Et si, non seulement on exécutait des containers OCI dans Proxmox VE, mais qu&amp;rsquo;EN PLUS, on transformait nos hôtes Proxmox VE en ✨️ &lt;strong&gt;Nodes Kubernetes&lt;/strong&gt; ✨️ ???&lt;/p&gt;
&lt;h2 id="on-sy-prend-comment-"&gt;On s&amp;rsquo;y prend comment ?
&lt;/h2&gt;&lt;p&gt;Le but n&amp;rsquo;est évidemment pas d&amp;rsquo;installer Kubernetes à côté de Proxmox VE, mais réutiliser le plus de choses possibles. Typiquement, je vais réutiliser la techno de &amp;ldquo;virtu&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Proxmox VE supportant 2 technos de virtualisation différentes (qemu et LXC), il faut choisir. Et comme j&amp;rsquo;aime bien LXC et que j&amp;rsquo;ai déjà expérimenté sur le hack Docker =&amp;gt; LXC sur Proxmox VE, &lt;strong&gt;la question était vite répondue&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Toute la difficulté de l&amp;rsquo;exercice est de trouver comment rendre LXC &amp;ldquo;CRI-compatible&amp;rdquo;. Et on a de la chance, Linux Containers, la fondation qui chapeaute LXC et Incus (ex LXD, &amp;ldquo;refermé&amp;rdquo; par Canonical), a écrit il y a quelques années un CRI pour LXC appelé &lt;a class="link" href="https://github.com/lxc/lxcri" target="_blank" rel="noopener"
&gt;lxcri&lt;/a&gt;. Et bien sûr, comme c&amp;rsquo;est un projet à l&amp;rsquo;utilité discutable, le truc n&amp;rsquo;est plus maintenu depuis 2021.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;avais d&amp;rsquo;ailleurs essayé de l&amp;rsquo;utiliser, passé pas mal de temps dessus en février 2024 (j&amp;rsquo;ai essuyé bug sur bug, suis passé par des forks, &amp;hellip;) pour échouer lamentablement sur un problème de compatibilité avec ma version de LXC (5+ sous Proxmox 8, 6 en Proxmox 9), entre autres bugs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;lxcri is a wrapper around LXC which can be used as a drop-in container runtime replacement for use by CRI-O.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On va donc quand même avoir besoin de 3 trucs en plus sur notre serveur Proxmox pour que mon idée débile fonctionne :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;lxcri&lt;/strong&gt; comme container runtime de base niveau&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cri-o&lt;/strong&gt; comme container runtime de haut niveau&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;kubelet&lt;/strong&gt; pour piloter le runtime et communiquer avec le control plane&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="cest-parti"&gt;C&amp;rsquo;est parti
&lt;/h2&gt;&lt;p&gt;OK. lxcri s&amp;rsquo;appuie donc sur &lt;a class="link" href="https://cri-o.io/" target="_blank" rel="noopener"
&gt;cri-o&lt;/a&gt;, le container runtime de Red Hat. On commence donc par installer cri-o :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/cri-o/cri-o/blob/main/install.md" target="_blank" rel="noopener"
&gt;https://github.com/cri-o/cri-o/blob/main/install.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note : mon serveur Proxmox VE 8 de l&amp;rsquo;époque utilisait Debian 12 comme OS de base. On devrait donc se baser là-dessus pour la variable $OS (comme l&amp;rsquo;indique la doc). Cependant, à l&amp;rsquo;heure où j&amp;rsquo;ai testé la première fois, les dépôts de CRI-O étaient en cours de migration, avec une documentation pas à jour et des releases manquantes. Le répo &lt;a class="link" href="https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o/" target="_blank" rel="noopener"
&gt;download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o&lt;/a&gt; n&amp;rsquo;avait pas le PATH Debian_12, ni la version 1.29 de Kubernetes&amp;hellip; &lt;strong&gt;J&amp;rsquo;avais BIEN ragé&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/cri-o/cri-o/issues/7657" target="_blank" rel="noopener"
&gt;https://github.com/cri-o/cri-o/issues/7657&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/blog/2023/10/10/cri-o-community-package-infrastructure/" target="_blank" rel="noopener"
&gt;https://kubernetes.io/blog/2023/10/10/cri-o-community-package-infrastructure/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On ne devrait plus avoir de soucis maintenant :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;KUBERNETES_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v1.32
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;CRIO_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v1.32
&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;# créer un répertoire pour les keyrings (il n&amp;#39;existe pas toujours sur une install fraiche)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mkdir -p /usr/share/keyrings
&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;# répo de kubernetes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://pkgs.k8s.io/core:/stable:/&lt;span class="nv"&gt;$KUBERNETES_VERSION&lt;/span&gt;/deb/Release.key &lt;span class="p"&gt;|&lt;/span&gt; sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/&lt;/span&gt;&lt;span class="nv"&gt;$KUBERNETES_VERSION&lt;/span&gt;&lt;span class="s2"&gt;/deb/ /&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; sudo tee /etc/apt/sources.list.d/kubernetes.list
&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;# répo de crio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://download.opensuse.org/repositories/isv:/cri-o:/stable:/&lt;span class="nv"&gt;$CRIO_VERSION&lt;/span&gt;/deb/Release.key &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg
&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://download.opensuse.org/repositories/isv:/cri-o:/stable:/&lt;/span&gt;&lt;span class="nv"&gt;$CRIO_VERSION&lt;/span&gt;&lt;span class="s2"&gt;/deb/ /&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; sudo tee /etc/apt/sources.list.d/cri-o.list
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install cri-o
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A partir de là, on a cri-o. On pourrait le configurer mais si on fait ça, il utilisera runc et on n&amp;rsquo;utiliserait pas LXC en tant que runtime.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ce n&amp;rsquo;est pas le but de ce hack !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est donc à ce moment là qu&amp;rsquo;on rebascule sur la documentation de lxcri :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/lxc/lxcri/blob/main/doc/setup.md" target="_blank" rel="noopener"
&gt;github.com/lxc/lxcri&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ya-pas-de-release"&gt;Ya pas de release
&lt;/h2&gt;&lt;p&gt;Et oui&amp;hellip; il faut préalablement &lt;strong&gt;builder&lt;/strong&gt; le binaire &lt;code&gt;lxcri&lt;/code&gt; et le déposer dans le /usr/local de notre serveur Proxmox VE. Il n&amp;rsquo;y a pas de binaire précompilé dans le projet, &lt;a class="link" href="https://github.com/lxc/lxcri/issues/57" target="_blank" rel="noopener"
&gt;c&amp;rsquo;est une issue ouverte juste avant qu&amp;rsquo;il ne soit abandonné 😬😬&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Et pour que ce soit encore plus fun, le processus de build passe par un Dockerfile, ce qui est rigolo puisqu&amp;rsquo;on n&amp;rsquo;a pas Docker sur notre node Proxmox&amp;hellip;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc essayé de &lt;a class="link" href="https://github.com/lxc/lxcri/blob/main/README.md" target="_blank" rel="noopener"
&gt;builder lxcri&lt;/a&gt; depuis une machine avec Docker, en suivant la commande indiquée sur le GitHub. Patatra. (Déjà, il manque un &amp;ldquo;.&amp;rdquo; en fin de commande &lt;code&gt;docker build&lt;/code&gt; dans la doc) ça fail misérablement à la compilation&amp;hellip;&lt;/p&gt;
&lt;p&gt;Note : je n&amp;rsquo;ai plus l&amp;rsquo;erreur en question, probablement un problème de dépendances. C&amp;rsquo;est relou parce qu&amp;rsquo;on va devoir faire plein de choses à la main&amp;hellip; Mais ne vous embêtez pas à &lt;code&gt;git clone&lt;/code&gt;, on va partir sur un fork (d&amp;rsquo;un fork).&lt;/p&gt;
&lt;p&gt;En allant jeter un œil au Dockerfile, on se rend vite compte qu&amp;rsquo;on ne fait que lancer un script (install.sh)&amp;hellip;&lt;/p&gt;
&lt;p&gt;Quand on appelle Docker avec le buildarg &lt;code&gt;installcmd=install_runtime&lt;/code&gt;, on lance la fonction &lt;code&gt;install_runtime&lt;/code&gt;, qui appelle &lt;code&gt;install_runtime_noclean&lt;/code&gt;, qui lance &lt;code&gt;add_lxc&lt;/code&gt; puis &lt;code&gt;add_lxcri&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Je veux juste compiler &lt;strong&gt;lxc&lt;/strong&gt; (pour les bindings) et &lt;strong&gt;lxcri&lt;/strong&gt;. Je vais donc le faire à la main. Pour ça, il faut golang 1.16 (ça date&amp;hellip;).&lt;/p&gt;
&lt;p&gt;Il y a pas mal de code pété un peu partout et plusieurs issues ouvertes dans lesquelles les mainteneurs conseillent de partir sur un fork :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/drachenfels-de/lxcri" target="_blank" rel="noopener"
&gt;https://github.com/drachenfels-de/lxcri&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;ai passé pas mal de temps à le débugger, et au final j&amp;rsquo;ai fait mon propre fork (&lt;a class="link" href="https://github.com/zwindler/lxcri%29" target="_blank" rel="noopener"
&gt;https://github.com/zwindler/lxcri)&lt;/a&gt;, qui nécessite golang 1.22+ et qui ajoute un gros paquet de fixes.&lt;/p&gt;
&lt;h2 id="prérequis-pour-builder-lxcri"&gt;Prérequis pour builder lxcri
&lt;/h2&gt;&lt;p&gt;On va installer un paquet de trucs :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt update
sudo apt install curl git meson pkg-config cmake libdbus-1-dev docbook2x
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On installe Golang si on ne l&amp;rsquo;a pas (peu de chance que vous ayez Golang sur un node Proxmox VE) :&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;wget https://go.dev/dl/go1.25.4.linux-amd64.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo rm -rf /usr/local/go &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo tar -C /usr/local -xzf go1.25.4.linux-amd64.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/usr/local/go/bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; go version go1.25.4 linux/amd64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Je vais aussi devoir &lt;strong&gt;récupérer et compiler lxc&lt;/strong&gt;. Pendant un moment, j&amp;rsquo;étais bloqué sur la version 4.0.12 (très précisément) car la 4.0.6 qu&amp;rsquo;on trouve sur Debian 11 ne fonctionne &lt;strong&gt;pas&lt;/strong&gt; avec le code de lxcri (j&amp;rsquo;ai mangé plein de bugs).&lt;/p&gt;
&lt;p&gt;Mais la bonne nouvelle, c&amp;rsquo;est que comme je suis un try-harder de l&amp;rsquo;espace, à force de fixes sur mon fork, celui-ci fonctionne avec la dernière version de lxc (6.x), ce qui tombe bien parce que c&amp;rsquo;est la version sur mon Proxmox VE 9 à jour.&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;git clone https://github.com/lxc/lxc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; lxc
&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;meson setup -Dprefix&lt;span class="o"&gt;=&lt;/span&gt;/usr -Dsystemd-unitdir&lt;span class="o"&gt;=&lt;/span&gt;PATH build
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Build targets in project: &lt;span class="m"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; lxc 6.0.0
&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; User defined options
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; prefix : /usr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; systemd-unitdir: PATH
&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; Found ninja-1.12.1 at /usr/bin/ninja
&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;meson compile -C build
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; INFO: autodetecting backend as ninja
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; INFO: calculating backend &lt;span class="nb"&gt;command&lt;/span&gt; to run: /usr/bin/ninja -C /root/lxc/build
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ninja: Entering directory &lt;span class="sb"&gt;`&lt;/span&gt;/root/lxc/build&lt;span class="err"&gt;&amp;#39;&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;544/544&lt;span class="o"&gt;]&lt;/span&gt; Linking target src/lxc/tools/lxc-monitor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ça a buildé plein de trucs, c&amp;rsquo;est cool. Mais les fichiers qui m&amp;rsquo;intéressent sont ici :&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;find . -name &lt;span class="s2"&gt;&amp;#34;lxc.pc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./build/meson-private/lxc.pc
&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;ls build/*lxc*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;build/liblxc.so build/liblxc.so.1 build/liblxc.so.1.8.0 build/lxc.spec
&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;build/liblxc.so.1.8.0.p:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;liblxc.so.1.8.0.symbols
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Je mets ça aux bons endroits dans mon Proxmox VE avec un :&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;sudo make install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ldconfig
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="builder-comme-jamais"&gt;Builder comme jamais
&lt;/h2&gt;&lt;p&gt;Ok, c&amp;rsquo;est parti pour jouer &lt;strong&gt;avec mon fork&lt;/strong&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;git clone https://github.com/zwindler/lxcri.git lxcri.zwindler
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; lxcri.zwindler
&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;make build
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; go build -ldflags &lt;span class="s1"&gt;&amp;#39;-X main.version=8805687-dirty -X github.com/lxc/lxcri.defaultLibexecDir=/usr/local/libexec/lxcri&amp;#39;&lt;/span&gt; -o lxcri ./cmd/lxcri
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cc -Werror -Wpedantic -o lxcri-start cmd/lxcri-start/lxcri-start.c &lt;span class="k"&gt;$(&lt;/span&gt;pkg-config --libs --cflags lxc&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; go build -o lxcri-init ./cmd/lxcri-init
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# this is paranoia - but ensure it is statically compiled&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ! ldd lxcri-init 2&amp;gt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; go build -o lxcri-hook ./cmd/lxcri-hook
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; go build -o lxcri-hook-builtin ./cmd/lxcri-hook-builtin
&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;ls -alrt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; total &lt;span class="m"&gt;15288&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -rwxr-xr-x &lt;span class="m"&gt;1&lt;/span&gt; debian debian &lt;span class="m"&gt;7108288&lt;/span&gt; Nov &lt;span class="m"&gt;16&lt;/span&gt; 20:06 lxcri
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -rwxr-xr-x &lt;span class="m"&gt;1&lt;/span&gt; debian debian &lt;span class="m"&gt;17520&lt;/span&gt; Nov &lt;span class="m"&gt;16&lt;/span&gt; 20:06 lxcri-start
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -rwxr-xr-x &lt;span class="m"&gt;1&lt;/span&gt; debian debian &lt;span class="m"&gt;2942584&lt;/span&gt; Nov &lt;span class="m"&gt;16&lt;/span&gt; 20:06 lxcri-init
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -rwxr-xr-x &lt;span class="m"&gt;1&lt;/span&gt; debian debian &lt;span class="m"&gt;2834743&lt;/span&gt; Nov &lt;span class="m"&gt;16&lt;/span&gt; 20:06 lxcri-hook
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -rwxr-xr-x &lt;span class="m"&gt;1&lt;/span&gt; debian debian &lt;span class="m"&gt;2519097&lt;/span&gt; Nov &lt;span class="m"&gt;16&lt;/span&gt; 20:06 lxcri-hook-builtin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si on était sur une machine de dev, on pourrait envoyer les binaires sur le serveur Proxmox VE (lxcri dans /usr/local/bin, le reste dans /usr/local/libexec/lxcri).&lt;/p&gt;
&lt;p&gt;Dans mon cas, je suis directement sur la machine qui build et qui run, je fais donc :&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;$ sudo make install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; mkdir -p /usr/local/bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cp -v lxcri /usr/local/bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;lxcri&amp;#39;&lt;/span&gt; -&amp;gt; &lt;span class="s1"&gt;&amp;#39;/usr/local/bin/lxcri&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; mkdir -p /usr/local/libexec/lxcri
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cp -v lxcri-start lxcri-init lxcri-hook lxcri-hook-builtin /usr/local/libexec/lxcri
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;lxcri-start&amp;#39;&lt;/span&gt; -&amp;gt; &lt;span class="s1"&gt;&amp;#39;/usr/local/libexec/lxcri/lxcri-start&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;lxcri-init&amp;#39;&lt;/span&gt; -&amp;gt; &lt;span class="s1"&gt;&amp;#39;/usr/local/libexec/lxcri/lxcri-init&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;lxcri-hook&amp;#39;&lt;/span&gt; -&amp;gt; &lt;span class="s1"&gt;&amp;#39;/usr/local/libexec/lxcri/lxcri-hook&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;lxcri-hook-builtin&amp;#39;&lt;/span&gt; -&amp;gt; &lt;span class="s1"&gt;&amp;#39;/usr/local/libexec/lxcri/lxcri-hook-builtin&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;et on peut continuer :&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;$ /usr/local/bin/lxcri &lt;span class="nb"&gt;help&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; lxcri - lxcri is a OCI compliant runtime wrapper &lt;span class="k"&gt;for&lt;/span&gt; lxc
&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;USAGE:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; lxcri &lt;span class="o"&gt;[&lt;/span&gt;global options&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; options&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;arguments...&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VERSION:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;8805687&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="on-reprend-crio"&gt;On reprend crio
&lt;/h2&gt;&lt;p&gt;Ok, on a buildé lxcri et on a toutes les dépendances pour l&amp;rsquo;utiliser. On peut donc repartir sur la doc officielle de lxcri &amp;ldquo;&lt;a class="link" href="https://github.com/lxc/lxcri/blob/main/doc/setup.md" target="_blank" rel="noopener"
&gt;setup.md&lt;/a&gt;&amp;rdquo; dans l&amp;rsquo;idée de configurer CRI-O, pour qu&amp;rsquo;il n&amp;rsquo;utilise pas runc, &lt;strong&gt;uniquement lxcri&lt;/strong&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;sudo tee /etc/crio/crio.conf.d/10-crio.conf &amp;gt; /dev/null &lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;[crio.image]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;signature_policy = &amp;#34;/etc/crio/policy.json&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;[crio.runtime]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;default_runtime = &amp;#34;lxcri&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;[crio.runtime.runtimes.lxcri]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;runtime_path = &amp;#34;/usr/local/bin/lxcri&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;runtime_type = &amp;#34;oci&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;runtime_root = &amp;#34;/var/lib/lxc&amp;#34; #proxmox lxc folder
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;inherit_default_runtime = false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;runtime_config_path = &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;container_min_memory = &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;monitor_path = &amp;#34;/usr/libexec/crio/conmon&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;monitor_cgroup = &amp;#34;system.slice&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;monitor_exec_cgroup = &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;privileged_without_host_devices = false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A noter, la doc d&amp;rsquo;installation &lt;code&gt;setup.md&lt;/code&gt; nous dit générer une conf propre avec le binaire crio et la commande config, mais ça ne marche pas vraiment et on se retrouve à lancer runc ou crun sans le vouloir. J&amp;rsquo;écrase tout, c&amp;rsquo;est plus simple.&lt;/p&gt;
&lt;p&gt;Et maintenant, on peut lancer crio :&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;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; crio &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo systemctl start crio
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A partir de là, on a un serveur disposant d&amp;rsquo;un CRI &lt;em&gt;a priori&lt;/em&gt; fonctionnel.&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;systemctl status crio
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;● crio.service - Container Runtime Interface &lt;span class="k"&gt;for&lt;/span&gt; OCI &lt;span class="o"&gt;(&lt;/span&gt;CRI-O&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Loaded: loaded &lt;span class="o"&gt;(&lt;/span&gt;/lib/systemd/system/crio.service&lt;span class="p"&gt;;&lt;/span&gt; enabled&lt;span class="p"&gt;;&lt;/span&gt; vendor preset: enabled&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Active: active &lt;span class="o"&gt;(&lt;/span&gt;running&lt;span class="o"&gt;)&lt;/span&gt; since Mon 2025-11-17 12:02:37 UTC&lt;span class="p"&gt;;&lt;/span&gt; 12s ago
&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;journalctl -u crio
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Nov &lt;span class="m"&gt;17&lt;/span&gt; 20:29:43 instance-2025-11-16-15-31-55 systemd&lt;span class="o"&gt;[&lt;/span&gt;1&lt;span class="o"&gt;]&lt;/span&gt;: Starting Container Runtime Interface &lt;span class="k"&gt;for&lt;/span&gt; OCI &lt;span class="o"&gt;(&lt;/span&gt;CRI-O&lt;span class="o"&gt;)&lt;/span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Nov &lt;span class="m"&gt;17&lt;/span&gt; 20:29:43 instance-2025-11-16-15-31-55 crio&lt;span class="o"&gt;[&lt;/span&gt;11906&lt;span class="o"&gt;]&lt;/span&gt;: &lt;span class="nv"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2025-11-17T20:29:43.61758425Z&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Using runtime handler lxcri version 8805687&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yeeees :D&lt;/p&gt;
&lt;p&gt;On peut donc installer kubelet, puis l&amp;rsquo;ajouter à un cluster Kubernetes existant&lt;/p&gt;
&lt;h2 id="fast-forward"&gt;Fast forward
&lt;/h2&gt;&lt;p&gt;J&amp;rsquo;avais la flemme de monter un cluster propre avec &lt;a class="link" href="https://blog.zwindler.fr/2023/12/17/kubeadmcfg-introduction-api-kubeadm/" target="_blank" rel="noopener"
&gt;kubeadm ou autre, et d&amp;rsquo;enrôler ensuite un node à la main avec le token+sha&lt;/a&gt;. J&amp;rsquo;aurais aussi pu rejouer avec mon PoC rigolo des &lt;a class="link" href="https://blog.zwindler.fr/2025/07/20/kubernetes-sur-clever-cloud-linux/" target="_blank" rel="noopener"
&gt;workers &amp;ldquo;linux&amp;rdquo; de Clever Cloud pour créer un control plane&lt;/a&gt; chez Clever, puis enrôler à la main avec le node bootstrap token (j&amp;rsquo;ai fait les choses plutôt bien dans ce PoC).&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc rejoué à l&amp;rsquo;arrache avec mon projet &lt;a class="link" href="https://github.com/zwindler/demystifions-kubernetes/tree/main" target="_blank" rel="noopener"
&gt;demystifions-kubernetes&lt;/a&gt;, qui permet de monter un cluster mono node à la main en lançant juste des binaires.&lt;/p&gt;
&lt;p&gt;Une fois le control plane bootstrapé (etcd, api-server, controller-manager, scheduler), on s&amp;rsquo;arrête AVANT la partie containerd (on a déjà configuré CRI-O) et on lance le kubelet à la main :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo bin/kubelet --kubeconfig admin.conf --container-runtime-endpoint --container-runtime-endpoint=unix:///var/run/crio/crio.sock --fail-swap-on=false --cgroup-driver=&amp;#34;systemd&amp;#34;
[...]
I1117 13:41:58.741561 88434 kubelet_node_status.go:78] &amp;#34;Successfully registered node&amp;#34; node=&amp;#34;instance-2025-11-16-15-31-55&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On progresse ! Essayons de voir l&amp;rsquo;état de santé du cluster et de déployer un pod :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ kubectl get nodes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; NAME STATUS ROLES AGE VERSION
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; instance-2025-11-16-15-31-55 Ready &amp;lt;none&amp;gt; 13m v1.34.2
&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 create deployment web --image&lt;span class="o"&gt;=&lt;/span&gt;zwindler/vhelloworld
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; deployment.apps/web 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 get pods
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; NAME READY STATUS RESTARTS AGE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; web-6c8cc48c68-cdbtj 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 4m17s
&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 logs web-6c8cc48c68-cdbtj
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt;Vweb&lt;span class="o"&gt;]&lt;/span&gt; Running app on http://localhost:8081/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt;Vweb&lt;span class="o"&gt;]&lt;/span&gt; We have &lt;span class="m"&gt;3&lt;/span&gt; workers
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Great success!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;A partir de là, CRI-O partage le moteur LXC de Proxmox VE de manière fonctionnelle.&lt;/p&gt;
&lt;p&gt;Pour s&amp;rsquo;en convaincre, on peut lancer la commande lxc-ls, qui nous affichera un mix de &amp;ldquo;vrais&amp;rdquo; containers LXC (créés avec Proxmox, les 151 et 200) et de containers de pods (les STOPPED sont les init containers de cilium) :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ lxc-ls -f
NAME STATE AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED
151 STOPPED 0 - - - true
200 STOPPED 0 - - - true
3045d1f3abcc069b1009acc869a27b09cf9531d0701a3f9d5a760213d57c7b20 STOPPED 0 - - - false
78057100e5d0613944c2be859dfd09f790eeba88bceebea0e04970841c9bd950 RUNNING 0 - 10.0.0.90 - false
8e5506beea9a50214c46f31fa7fa6d04397963cb912aa397261359865d40a0cd RUNNING 0 - 10.0.0.167, 10.244.0.1, 203.0.113.42, 192.168.1.10 2001:db8::1 false
abdcd5be0a27417aa9e11cc2b27882bc86a63d94f547f909332ca7470a5e075a STOPPED 0 - - - false
aeaed9c2f56328bb6196ede4a78f06c4bb2d3edf5dca7912cf38407b9bf6610a STOPPED 0 - - - false
ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d RUNNING 0 - 10.0.0.167, 10.244.0.1, 203.0.113.42, 192.168.1.10 2001:db8::1 false
edb9d42e35a4029cfec5bed5597746bdf67fbe438f21446230252265ed1c849d STOPPED 0 - - - false
$ ps -ef |grep ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d
root 2674943 1 0 22:32 ? 00:00:00 /usr/libexec/crio/conmon -b /run/containers/storage/overlay-containers/ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d/userdata -c ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d --exit-dir /var/run/crio/exits -l /var/log/pods/kube-system_cilium-operator-56d6cd6767-dps78_6082ea2d-331e-4262-b8b3-d3f88eb4e446/cilium-operator/1.log --log-level info -n k8s_cilium-operator_cilium-operator-56d6cd6767-dps78_kube-system_6082ea2d-331e-4262-b8b3-d3f88eb4e446_1 -P /run/containers/storage/overlay-containers/ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d/userdata/conmon-pidfile -p /run/containers/storage/overlay-containers/ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d/userdata/pidfile --persist-dir /var/lib/containers/storage/overlay-containers/ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d/userdata -r /usr/local/bin/lxcri --runtime-arg --root=/var/lib/lxc --socket-dir-path /var/run/crio --syslog -u ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d -s
root 2674951 2674943 0 22:32 ? 00:00:00 /usr/local/libexec/lxcri/lxcri-start ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d /var/lib/lxc /var/lib/lxc/ed9aaf5a0fd4b11be121296290472d6d71d9016e4c6bb0d05fb2e4a7f4b7a85d/config
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Rien ne nous empêcherait ensuite de pousser le vice, et de faire un petit script qui récupère toutes les informations des containers, et crée les fichiers &lt;code&gt;/etc/pve/lxc/xxx.conf&lt;/code&gt; associés de manière à les afficher dans l&amp;rsquo;UI, comme je l&amp;rsquo;avais fait dans l&amp;rsquo;article :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2022/11/07/containers-docker-dans-proxmox-avec-lxc/" target="_blank" rel="noopener"
&gt;Lancer des containers Docker avec Proxmox VE (et LXC)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En théorie, si cette étape avait du sens, je pourrais créer les releases moi-même sur mon fork pour faciliter l&amp;rsquo;installation.&lt;/p&gt;
&lt;p&gt;Enfin, je pourrais essayer de contribuer mes modifications pour que tout soit mergé sur le projet principal, à ceci près que &lt;a class="link" href="https://github.com/lxc/lxcri/issues/67#issuecomment-2055724867" target="_blank" rel="noopener"
&gt;les mainteneurs refusent les PRs car le projet n&amp;rsquo;est plus maintenu&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Mais j&amp;rsquo;avoue qu&amp;rsquo;après probablement une quinzaine d&amp;rsquo;heures de debug, de Golang, de compilation C, réparties sur 2 ans, j&amp;rsquo;ai un peu été au bout de ma patience pour cette &amp;ldquo;blague&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;OK, c&amp;rsquo;était débile, maintenant que j&amp;rsquo;ai réussi, dodo X_X.&lt;/p&gt;</description></item></channel></rss>