<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cilium on Zwindler's Reflection</title><link>https://blog.zwindler.fr/tags/cilium/</link><description>Recent content in Cilium 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/tags/cilium/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>Flannel et NetworkPolicies : comment ajouter le support avec Cilium en CNI chaining</title><link>https://blog.zwindler.fr/2026/03/15/flannel-networkpolicies-cilium-cni-chaining/</link><pubDate>Sun, 15 Mar 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/03/15/flannel-networkpolicies-cilium-cni-chaining/</guid><description>&lt;img src="https://blog.zwindler.fr/2026/03/flannel-cilium-chaining.webp" alt="Featured image of post Flannel et NetworkPolicies : comment ajouter le support avec Cilium en CNI chaining" /&gt;&lt;h2 id="flannel-flannel-flannel"&gt;Flannel, flannel, flannel&amp;hellip;
&lt;/h2&gt;&lt;p&gt;Flannel est un CNI simple et populaire 😢.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est le CNI par défaut de k3s, celui que la moitié des tutos kubeadm utilisent, et on le retrouve aussi dans pas mal d&amp;rsquo;offres managées.&lt;/p&gt;
&lt;p&gt;OK, il est simple, il route les paquets entre les pods, il supporte VXLAN et WireGuard, il se configure en 2 minutes. Que demander de plus ?&lt;/p&gt;
&lt;p&gt;Ben justement. Il y a un truc que flannel ne fait &lt;strong&gt;pas&lt;/strong&gt; : les NetworkPolicies. (Et c&amp;rsquo;est très grave).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Flannel is focused on networking. For network policy, other projects such as Calico can be used.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;[Edit]&lt;/strong&gt; Contrairement à ce que j&amp;rsquo;écris ici, flannel supporte en fait les NetworkPolicies depuis au moins 2 ans, via l&amp;rsquo;implémentation de référence &lt;a class="link" href="https://github.com/kubernetes-sigs/kube-network-policies" target="_blank" rel="noopener"
&gt;kube-network-policies&lt;/a&gt; du projet Kubernetes. L&amp;rsquo;option n&amp;rsquo;est pas mise en avant dans le README et ce n&amp;rsquo;est probablement pas plus recommandé en production, mais elle existe. Voir la &lt;a class="link" href="https://github.com/flannel-io/flannel/blob/master/Documentation/netpol.md" target="_blank" rel="noopener"
&gt;documentation officielle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Et le piège, c&amp;rsquo;est que si vous n&amp;rsquo;avez pas lu cette petite ligne dans le &lt;strong&gt;README.md&lt;/strong&gt;, rien ne vous le dit explicitement.&lt;/p&gt;
&lt;p&gt;Vous pouvez parfaitement créer des objets &lt;code&gt;NetworkPolicy&lt;/code&gt; dans votre cluster, &lt;code&gt;kubectl apply&lt;/code&gt; ne bronchera pas, &lt;code&gt;kubectl get netpol&lt;/code&gt; vous les listera gentiment. Sauf que&amp;hellip; elles ne sont pas enforced. Le trafic passe quand même. Votre deny-all ne deny rien du tout.&lt;/p&gt;
&lt;h2 id="on-vérifie-pour-être-bien-sûr"&gt;On vérifie pour être bien sûr
&lt;/h2&gt;&lt;p&gt;Avant de résoudre quoi que ce soit, vérifions que le problème existe. En partant du prérequis qu&amp;rsquo;on a un cluster Kubernetes qui a flannel comme CNI et que tout est fonctionnel, on va déployer deux pods dans deux namespaces différents : un client (curl) et un serveur (nginx).&lt;/p&gt;
&lt;p&gt;On vérifie que le client peut joindre le serveur :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n netpol-test-a client -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -s --max-time &lt;span class="m"&gt;5&lt;/span&gt; http://server.netpol-test-b.svc.cluster.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On obtient la page d&amp;rsquo;accueil nginx. Jusque-là, tout est normal. Maintenant, on applique une NetworkPolicy &lt;em&gt;deny-all&lt;/em&gt; sur le namespace du serveur :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 01-deny-all-ingress.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;networking.k8s.io/v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;NetworkPolicy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;deny-all-ingress&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;netpol-test-b&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;podSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;policyTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Ingress&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f 01-deny-all-ingress.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et on re-teste :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n netpol-test-a client -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -s --max-time &lt;span class="m"&gt;5&lt;/span&gt; http://server.netpol-test-b.svc.cluster.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Résultat : la page nginx s&amp;rsquo;affiche toujours.&lt;/strong&gt; La NetworkPolicy est bien créée (&lt;code&gt;kubectl get netpol -n netpol-test-b&lt;/code&gt; la montre), mais elle n&amp;rsquo;est pas enforced. Le trafic passe comme si de rien n&amp;rsquo;était.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est le comportement attendu avec flannel (par défaut). Flannel ne fait que du routage L3 (overlay VXLAN ou WireGuard). Il n&amp;rsquo;implémente pas de contrôleur NetworkPolicy. Les objets existent dans etcd, mais personne ne les traduit en règles de filtrage.&lt;/p&gt;
&lt;p&gt;On nettoie la policy avant de passer à la suite :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl delete -f 01-deny-all-ingress.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="les-alternatives-pour-ajouter-le-support"&gt;Les alternatives pour ajouter le support
&lt;/h2&gt;&lt;p&gt;Pendant longtemps j&amp;rsquo;ai pensé que c&amp;rsquo;était une fatalité. Je pense toujours que n&amp;rsquo;est pas un bon choix pour n&amp;rsquo;importe quelle production.&lt;/p&gt;
&lt;p&gt;MAIS récemment, j&amp;rsquo;ai découvert qu&amp;rsquo;il était possible de chaîner les CNI au sein d&amp;rsquo;un même cluster, et ainsi d&amp;rsquo;avoir un CNI qui gère la majorité des tâches (ici Flannel) et un autre qui se charge d&amp;rsquo;autres tâches, comme par exemple enforcer des Netpols.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est d&amp;rsquo;ailleurs le principe de Canal, que je connaissais de nom mais que je n&amp;rsquo;avais jamais exploré. En fait, c&amp;rsquo;est ni plus ni moins qu&amp;rsquo;un manifeste qui déploie Flannel comme CNI principal avec Calico pour l&amp;rsquo;enforcing des NetOK fine, it&amp;rsquo;s truepols !&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Canal was the name of Tigera and CoreOS’s project to integrate Calico and flannel.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note : &lt;a class="link" href="https://github.com/projectcalico/canal?tab=readme-ov-file" target="_blank" rel="noopener"
&gt;le projet GitHub a été archivé en octobre 2025&lt;/a&gt; mais en théorie ça devrait encore fonctionner, si on trouve la doc correcte (pas trouvé, les liens sont KO, mais j&amp;rsquo;ai pas vraiment cherché non plus).&lt;/p&gt;
&lt;p&gt;Vous avez donc compris le principe, on va ajouter un composant qui va &lt;strong&gt;watch&lt;/strong&gt; les objets NetworkPolicy et les traduire en règles de filtrage effectives (iptables, eBPF, nftables&amp;hellip;), &lt;strong&gt;sans toucher au flannel existant&lt;/strong&gt;. C&amp;rsquo;est ce qu&amp;rsquo;on appelle le &amp;ldquo;CNI chaining&amp;rdquo; ou le mode &amp;ldquo;policy-only&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Il existe plusieurs solutions :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Calico (Canal)&lt;/strong&gt;, Historiquement, la combinaison flannel + Calico s&amp;rsquo;appelle &amp;ldquo;Canal&amp;rdquo;, dont je viens de parler. &lt;strong&gt;Mais&lt;/strong&gt; le manifeste Canal officiel embarque son propre flannel dans le même DaemonSet que calico-node. Si votre flannel est déjà installé et géré (par vous, par un opérateur, par un provider&amp;hellip;), vous ne voulez probablement pas le remplacer. Et l&amp;rsquo;opérateur Tigera (la méthode Helm &amp;ldquo;officielle&amp;rdquo;) ne supporte pas non plus le déploiement en mode policy-only sur un flannel existant. Bref, c&amp;rsquo;est faisable mais ça nécessite un peu d&amp;rsquo;effort. Flemme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;kube-router&lt;/strong&gt;, kube-router peut fonctionner en mode firewall-only (&lt;code&gt;--run-firewall=true&lt;/code&gt;) et n&amp;rsquo;a besoin que d&amp;rsquo;iptables/ipset. C&amp;rsquo;est d&amp;rsquo;ailleurs ce que k3s utilise par défaut pour les NetworkPolicies. C&amp;rsquo;est la solution la plus légère (a priori ~50 Mo de RAM par node). Vérifiez que votre kernel dispose du module &lt;code&gt;ip_set&lt;/code&gt;, sinon ça ne fonctionnera pas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cilium en mode CNI chaining&lt;/strong&gt;, C&amp;rsquo;est la solution que j&amp;rsquo;ai retenue et qu&amp;rsquo;on va détailler. Cilium s&amp;rsquo;attache aux interfaces veth créées par flannel et ajoute ses programmes eBPF pour le policy enforcement. Pas de dépendance à iptables ou ipset, et en bonus on récupère Hubble pour l&amp;rsquo;observabilité réseau.&lt;/p&gt;
&lt;h2 id="installer-cilium-en-mode-cni-chaining"&gt;Installer Cilium en mode CNI chaining
&lt;/h2&gt;&lt;h3 id="prérequis"&gt;Prérequis
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Un cluster Kubernetes fonctionnel avec flannel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;helm&lt;/code&gt; v3+&lt;/li&gt;
&lt;li&gt;Un kernel &amp;gt;= 4.19 (idéalement &amp;gt;= 5.10 pour toutes les features eBPF)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="récupérer-la-configuration-cni-de-flannel"&gt;Récupérer la configuration CNI de flannel
&lt;/h3&gt;&lt;p&gt;Cilium en mode chaining doit connaître la configuration CNI existante. On va la récupérer depuis un node :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl debug node/&lt;span class="k"&gt;$(&lt;/span&gt;kubectl get nodes -o &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{.items[0].metadata.name}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -it --image&lt;span class="o"&gt;=&lt;/span&gt;busybox -- cat /host/etc/cni/net.d/10-flannel.conflist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sur mon cluster, ça donne :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cbr0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cniVersion&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.3.1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;plugins&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;flannel&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;delegate&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hairpinMode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;isDefaultGateway&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;portmap&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;capabilities&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;portMappings&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Notez le champ &lt;code&gt;name&lt;/code&gt;&lt;/strong&gt; (ici &lt;code&gt;cbr0&lt;/code&gt;). On en aura besoin.&lt;/p&gt;
&lt;h3 id="créer-le-configmap-de-chaining"&gt;Créer le ConfigMap de chaining
&lt;/h3&gt;&lt;p&gt;On va créer un ConfigMap qui reprend la config flannel et y ajoute le plugin &lt;code&gt;cilium-cni&lt;/code&gt; en chaining :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ConfigMap&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;cni-configuration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kube-system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cni-config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|-&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;name&amp;#34;: &amp;#34;cbr0&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;cniVersion&amp;#34;: &amp;#34;0.3.1&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;plugins&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;type&amp;#34;: &amp;#34;flannel&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;delegate&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;hairpinMode&amp;#34;: true,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;isDefaultGateway&amp;#34;: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;type&amp;#34;: &amp;#34;portmap&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;capabilities&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;portMappings&amp;#34;: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;type&amp;#34;: &amp;#34;cilium-cni&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;chaining-mode&amp;#34;: &amp;#34;generic-veth&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; }&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Attention&lt;/strong&gt; : le champ &lt;code&gt;name&lt;/code&gt; doit correspondre à celui de votre &lt;em&gt;conflist&lt;/em&gt; flannel. Si le vôtre s&amp;rsquo;appelle &lt;code&gt;cni0&lt;/code&gt; ou autre chose, adaptez.&lt;/p&gt;
&lt;p&gt;Avant d&amp;rsquo;appliquer, vérifiez aussi que votre CNI utilise bien des interfaces &lt;strong&gt;veth&lt;/strong&gt; (c&amp;rsquo;est le cas par défaut avec flannel, mais mieux vaut s&amp;rsquo;en assurer). Depuis un node :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ip -d link &lt;span class="p"&gt;|&lt;/span&gt; grep veth
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Vous devriez voir des interfaces de type &lt;code&gt;veth&lt;/code&gt; correspondant à vos pods, par exemple :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;103: lxcb3901b7f9c02@if102: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; ...
veth addrgenmode eui64 numtxqueues 1 numrxqueues 1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Si c&amp;rsquo;est bien le cas, le mode &lt;code&gt;generic-veth&lt;/code&gt; de Cilium fonctionnera. On applique :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f cilium-cni-configmap.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="installer-cilium-via-helm"&gt;Installer Cilium via Helm
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm repo add cilium https://helm.cilium.io/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm repo update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Voici les values pour le mode chaining :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# cilium-values.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;cni&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;chainingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;generic-veth&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;customConf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;configMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;cni-configuration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;routingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;native&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;enableIPv4Masquerade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;enableIPv6Masquerade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;hubble&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Les points importants :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cni.chainingMode: generic-veth&lt;/code&gt;, c&amp;rsquo;est le mode chaining, Cilium s&amp;rsquo;attache aux interfaces veth existantes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cni.customConf: true&lt;/code&gt; + &lt;code&gt;cni.configMap&lt;/code&gt;, on fournit notre propre config CNI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;routingMode: native&lt;/code&gt;, flannel gère le routage, pas Cilium&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enableIPv4Masquerade: false&lt;/code&gt;, flannel gère le masquerading&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hubble.enabled: true&lt;/code&gt;, l&amp;rsquo;observabilité réseau, c&amp;rsquo;est le gros bonus de Cilium&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm install cilium cilium/cilium --version 1.19.1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --namespace kube-system &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -f cilium-values.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On attend que tout soit prêt :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl rollout status daemonset/cilium -n kube-system --timeout&lt;span class="o"&gt;=&lt;/span&gt;120s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="vérification"&gt;Vérification
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n kube-system ds/cilium -- cilium status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ce qui nous intéresse :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Kubernetes: Ok 1.35 (v1.35.0) [linux/amd64]
CNI Chaining: generic-veth
Cilium: Ok 1.19.1
Hubble: Ok
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;La ligne &lt;code&gt;CNI Chaining: generic-veth&lt;/code&gt; confirme que Cilium fonctionne en mode &lt;em&gt;chaining&lt;/em&gt; et ne remplace pas flannel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; : les pods qui existaient avant l&amp;rsquo;installation de Cilium ne sont pas automatiquement gérés par Cilium. Il faut les redémarrer pour que Cilium attache ses programmes eBPF. Pensez à faire un &lt;code&gt;kubectl rollout restart&lt;/code&gt; de vos workloads de test (ou à les recréer).&lt;/p&gt;
&lt;h2 id="tester-les-networkpolicies"&gt;Tester les NetworkPolicies
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est le moment de vérité. On re-applique notre deny-all :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f 01-deny-all-ingress.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n netpol-test-a client -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -s --max-time &lt;span class="m"&gt;5&lt;/span&gt; http://server.netpol-test-b.svc.cluster.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Résultat : timeout&lt;/strong&gt; ! Cette fois, la NetworkPolicy est bien enforced. Le trafic est bloqué.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai enchaîné avec les autres scénarios classiques de NetworkPolicy, et tout fonctionne :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Allow ingress sélectif par namespace&lt;/strong&gt;, en ajoutant une policy qui autorise le trafic depuis &lt;code&gt;netpol-test-a&lt;/code&gt; uniquement, le curl passe depuis ce namespace mais reste bloqué depuis &lt;code&gt;default&lt;/code&gt;. L&amp;rsquo;isolation par namespace fonctionne.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deny-all egress&lt;/strong&gt;, en bloquant tout le trafic sortant du client, même la résolution DNS est bloquée (timeout immédiat).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Allow egress sélectif&lt;/strong&gt;, en autorisant uniquement le DNS (port 53) et le serveur (port 80 dans le namespace &lt;code&gt;netpol-test-b&lt;/code&gt;), le curl vers le serveur passe mais &lt;code&gt;curl http://example.com&lt;/code&gt; reste bloqué.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bref, ingress, egress, sélectif par namespace, tout marche comme attendu.&lt;/p&gt;
&lt;h2 id="bonus--hubble-lobservabilité-réseau"&gt;Bonus : Hubble, l&amp;rsquo;observabilité réseau
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est pour moi le vrai atout de Cilium par rapport aux alternatives. Hubble permet de voir en temps réel les flux réseau et les verdicts de policy :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n kube-system ds/cilium -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; hubble observe --namespace netpol-test-b --last &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;Mar 15 13:20:42.287: netpol-test-a/client:40066 (ID:9745) -&amp;gt;
netpol-test-b/server:80 (ID:22271)
policy-verdict:none ALLOWED (TCP Flags: SYN)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On voit le pod source, le pod destination, le port, l&amp;rsquo;identité Cilium, et le verdict de policy. Quand vous debuggez une NetworkPolicy qui ne se comporte pas comme prévu, c&amp;rsquo;est vraiment pratique.&lt;/p&gt;
&lt;h2 id="combien-ça-coûte-en-ressources-"&gt;Combien ça coûte en ressources ?
&lt;/h2&gt;&lt;p&gt;Sur mon cluster (3 nodes), voici ce que Cilium consomme juste après installation :&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Composant&lt;/th&gt;
&lt;th&gt;Par node&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;cilium agent&lt;/td&gt;
&lt;td&gt;oui (DaemonSet)&lt;/td&gt;
&lt;td&gt;~160 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cilium-envoy&lt;/td&gt;
&lt;td&gt;oui (DaemonSet)&lt;/td&gt;
&lt;td&gt;~22 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cilium-operator&lt;/td&gt;
&lt;td&gt;non (2 replicas)&lt;/td&gt;
&lt;td&gt;~42 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hubble-relay&lt;/td&gt;
&lt;td&gt;non (1 replica)&lt;/td&gt;
&lt;td&gt;~16 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hubble-ui&lt;/td&gt;
&lt;td&gt;non (1 replica)&lt;/td&gt;
&lt;td&gt;~21 Mo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Soit environ &lt;strong&gt;180 Mo par node&lt;/strong&gt; pour l&amp;rsquo;agent + envoy. J&amp;rsquo;ai pas de point de comparaison par rapport à Calico ou kube-router, mais ça me semble acceptable, et le fait de pouvoir avoir une vision complète sur la totalité des flux avec Hubble justifie largement le surcoût (à mon avis).&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Si jamais vous n&amp;rsquo;avez pas le choix et que vous devez composer avec flannel, et que vous voulez boucher le trou béant de sécurité que représente l&amp;rsquo;absence d&amp;rsquo;enforcement des Netpols, sachez donc qu&amp;rsquo;il est maintenant possible :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;d&amp;rsquo;activer l&amp;rsquo;ajout de l&amp;rsquo;implémentation officielle (même si je ne l&amp;rsquo;ai pas testée, ça doit marcher)&lt;/li&gt;
&lt;li&gt;de chaîner un autre CNI pour le faire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;À défaut de l&amp;rsquo;avoir en CNI pour tout (tout bon cluster Kubernetes a Cilium comme CNI), Cilium en mode CNI chaining (generic-veth) est une solution plutôt sympa pour combler ce manque. Il ne touche pas au flannel existant, il s&amp;rsquo;y greffe. Et en bonus, vous récupérez Hubble pour l&amp;rsquo;observabilité réseau, ce qui est franchement appréciable.&lt;/p&gt;
&lt;p&gt;Have fun :)&lt;/p&gt;</description></item><item><title>Un nouveau champ `log` pour les network policies Cilium : une idée de use case</title><link>https://blog.zwindler.fr/2025/11/03/cilium-policy-log-field-limitation-fr/</link><pubDate>Mon, 03 Nov 2025 12:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2025/11/03/cilium-policy-log-field-limitation-fr/</guid><description>&lt;img src="https://blog.zwindler.fr/2025/10/cilium-hubble.webp" alt="Featured image of post Un nouveau champ `log` pour les network policies Cilium : une idée de use case" /&gt;&lt;h2 id="tldr"&gt;TL;DR
&lt;/h2&gt;&lt;p&gt;Cilium 1.18 a ajouté un champ &lt;code&gt;log&lt;/code&gt; aux CiliumNetworkPolicies pour taguer les verdicts de flux (FORWARDED, DROPPED, AUDIT, &amp;hellip;) avec des labels personnalisés. Dans l&amp;rsquo;idée, c&amp;rsquo;est la fonctionnalité parfaite pour éviter de logger le trafic bloqué que l&amp;rsquo;on connait dans nos dashboards de monitoring !&lt;/p&gt;
&lt;p&gt;Mais il y a un hic, sans rapport avec cette fonctionnalité, qui rend cette idée inutilisable : on ne peut pas l&amp;rsquo;utiliser avec &lt;code&gt;egressDeny&lt;/code&gt; + &lt;code&gt;toFQDNs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;ET, il y a un bug, qui fait que le &amp;ldquo;log&amp;rdquo; n&amp;rsquo;est visible que sur le trafic &amp;ldquo;autorisé&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Je vous raconte&amp;hellip;&lt;/p&gt;
&lt;h2 id="le-problème--monitor-all-the-things-mais-pas-trop"&gt;Le problème : monitor all the things (mais pas trop)
&lt;/h2&gt;&lt;p&gt;Comme toute bonne équipe &amp;ldquo;ops&amp;rdquo; qui se respecte, nous monitorons/loggons les flux réseau de notre cluster Kubernetes avec Hubble pour une analyse ultérieure et de l&amp;rsquo;alerting. Nous (en particulier mon collègue Nicolas Nativel) poussons tous les flux &lt;code&gt;AUDIT&lt;/code&gt; et &lt;code&gt;DROPPED&lt;/code&gt; vers un dashboard Grafana pour pouvoir rapidement repérer quand quelque chose est bloqué et décider :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C&amp;rsquo;est légitime ? → On ouvre le flux&lt;/li&gt;
&lt;li&gt;C&amp;rsquo;est suspect ? → On déclenche l&amp;rsquo;alarme 🚨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2025/10/cilium-hubble.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Ça fonctionne plutôt bien&amp;hellip; jusqu&amp;rsquo;à ce qu&amp;rsquo;on commence à bloquer &lt;strong&gt;explicitement&lt;/strong&gt; des choses qu&amp;rsquo;on &lt;em&gt;sait&lt;/em&gt; devoir être bloquées.&lt;/p&gt;
&lt;p&gt;Dans notre cas, nous voulions empêcher une application tierce d&amp;rsquo;envoyer les données de &amp;ldquo;télémétrie&amp;rdquo; (ouais, appelons ça comme ça 😏). On parle d&amp;rsquo;appels HTTPS vers des domaines de tracking externes.&lt;/p&gt;
&lt;p&gt;Le problème ? Si on bloque simplement ces flux, ils apparaîtront comme &lt;code&gt;DROPPED&lt;/code&gt; dans Hubble, déclencheront notre monitoring, et on se retrouvera avec des alertes pour quelque chose qu&amp;rsquo;on a &lt;em&gt;intentionnellement&lt;/em&gt; bloqué.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est du bruit dont on ne veut pas.&lt;/p&gt;
&lt;h2 id="et-donc-ce-champ-log-des-network-policies-de-cilium-118-"&gt;Et donc, ce champ log des network policies de Cilium 1.18 ?
&lt;/h2&gt;&lt;p&gt;Bonne nouvelle ! Cilium 1.18 a introduit exactement ce dont nous avions besoin : la possibilité d&amp;rsquo;ajouter des champs de log personnalisés aux verdicts sur les network policies.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cf. &lt;a class="link" href="https://isovalent.com/blog/post/cilium-1-18/#hubble-flow-policy-log-field" target="_blank" rel="noopener"
&gt;l&amp;rsquo;annonce dans le blogpost officiel&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L&amp;rsquo;idée est simple : vous ajoutez un champ &lt;code&gt;log.value&lt;/code&gt; à votre CiliumNetworkPolicy :&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;cilium.io/v2&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;CiliumNetworkPolicy&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;my-policy&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;endpointSelector&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;matchLabels&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;app&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&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;egress&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;toFQDNs&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;matchName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;example.com&amp;#34;&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;log&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;my-custom-log-tag&amp;#34;&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;Ensuite, quand vous observez les flux dans Hubble, vous pouvez &lt;strong&gt;les filtrer&lt;/strong&gt; en utilisant &lt;a class="link" href="https://kubernetes.io/docs/reference/using-api/cel/" target="_blank" rel="noopener"
&gt;CEL (Common Expression Language)&lt;/a&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;hubble observe &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --verdict AUDIT &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --not &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cel-expression &lt;span class="s2"&gt;&amp;#34;(_flow.policy_log.endsWith(&amp;#39;my-custom-log-tag&amp;#39;))&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --print-raw-filters
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Output :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;allowlist:
- &amp;#39;{&amp;#34;verdict&amp;#34;:[&amp;#34;AUDIT&amp;#34;]}&amp;#39;
denylist:
- &amp;#39;{&amp;#34;experimental&amp;#34;:{&amp;#34;cel_expression&amp;#34;:[&amp;#34;(_flow.policy_log.endsWith(&amp;#39;&amp;#39;my-custom-log-tag&amp;#39;&amp;#39;))&amp;#34;]}}&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Parfait ! Vous savez maintenant taguer les calls que vous avez explicitement bloqués et les exclure de votre monitoring. 🎉&lt;/p&gt;
&lt;h2 id="le-plan--bloquer-la-télémétrie-élégamment"&gt;Le plan : bloquer la télémétrie élégamment
&lt;/h2&gt;&lt;p&gt;À l&amp;rsquo;aide de cette nouvelle fonctionnalité, nous avons élaboré notre stratégie :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Utiliser &lt;code&gt;egressDeny&lt;/code&gt; pour bloquer explicitement les domaines de télémétrie&lt;/li&gt;
&lt;li&gt;Ajouter un champ de log personnalisé : &lt;code&gt;app-explicit-traffic-blocked&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Configurer Hubble pour filtrer les verdicts de flux avec ce tag&lt;/li&gt;
&lt;li&gt;Profit ! 🎉&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Voici ce que nous avons essayé :&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;cilium.io/v2&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;CiliumNetworkPolicy&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;app-external-block-policy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;my-namespace&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;endpointSelector&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;matchLabels&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;app.kubernetes.io/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;my-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="c"&gt;# note: egressDeny prend la précédence sur les règles egress&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;# https://docs.cilium.io/en/stable/security/policy/language/#deny-policies&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;egressDeny&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="c"&gt;# Bloquer tout le trafic externe et le logger avec un champ de log arbitraire&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;# Ceci est utilisé pour empêcher l&amp;#39;app d&amp;#39;envoyer des données de télémétrie à l&amp;#39;extérieur&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;# sans déclencher d&amp;#39;alerte AUDIT/DROPPED&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;# fonctionnalité ajoutée dans cilium 1.18.0 https://github.com/cilium/cilium/pull/39902&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;toFQDNs&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;matchPattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;*.telemetry.example.com&amp;#34;&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;toPorts&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;ports&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;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;443&amp;#34;&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;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;TCP&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;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;80&amp;#34;&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;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;TCP&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;log&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;app-explicit-traffic-blocked&amp;#34;&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;Ça devrait fonctionner, non ? On utilise &lt;code&gt;egressDeny&lt;/code&gt; (qui &lt;a class="link" href="https://docs.cilium.io/en/stable/security/policy/language/#deny-policies" target="_blank" rel="noopener"
&gt;prend la précédence sur les autres règles&lt;/a&gt;, ce qui est une bonne chose !), et on le tague avec notre log personnalisé.&lt;/p&gt;
&lt;h2 id="retour-à-la-réalité"&gt;Retour à la réalité
&lt;/h2&gt;&lt;p&gt;Et puis&amp;hellip; &lt;strong&gt;patatra&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;En lisant la &lt;a class="link" href="https://docs.cilium.io/en/stable/security/policy/language/#deny-policies" target="_blank" rel="noopener"
&gt;documentation Cilium sur les deny policies&lt;/a&gt;, nous sommes tombés sur cette petite note de rien du tout :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Deny policies do not support:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;policy enforcement at L7, i.e., specifically denying an URL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;toFQDNs&lt;/strong&gt;, i.e., specifically denying traffic to a specific domain name.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Attendez, quoi ?&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;ne peut pas&lt;/strong&gt; utiliser &lt;code&gt;toFQDNs&lt;/code&gt; avec &lt;code&gt;egressDeny&lt;/code&gt;. Tout notre plan vient de s&amp;rsquo;effondrer 😱.&lt;/p&gt;
&lt;h2 id="pourquoi-cest-un-problème-déjà-"&gt;Pourquoi c&amp;rsquo;est un problème, déjà ?
&lt;/h2&gt;&lt;p&gt;Le problème, c&amp;rsquo;est le modèle de précédence dans Cilium :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les règles &lt;code&gt;egressDeny&lt;/code&gt; prennent la précédence sur les règles &lt;code&gt;egress&lt;/code&gt; (by design, et c&amp;rsquo;est bien !)&lt;/li&gt;
&lt;li&gt;Mais si on utilise &lt;code&gt;egressDeny&lt;/code&gt;, on ne peut pas utiliser &lt;code&gt;toFQDNs&lt;/code&gt; pour cibler intelligemment le domaine incriminé, on doit bloquer par IP ou CIDR&lt;/li&gt;
&lt;li&gt;Ces services de télémétrie utilisent &lt;em&gt;probablement&lt;/em&gt; des IPs dynamiques pour leurs endpoints (bonne chance pour maintenir une liste&amp;hellip;)&lt;/li&gt;
&lt;li&gt;Et si on bloque tout le trafic 80/443 dans &lt;code&gt;egressDeny&lt;/code&gt;, on ne peut pas faire d&amp;rsquo;exceptions pour le trafic légitime dans les règles &lt;code&gt;egress&lt;/code&gt; car&amp;hellip; &lt;em&gt;deny&lt;/em&gt; prend la précédence sur &lt;em&gt;allow&lt;/em&gt; !&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On est coincé entre le marteau et l&amp;rsquo;enclume :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Utiliser &lt;code&gt;egress&lt;/code&gt; avec &lt;code&gt;toFQDNs&lt;/code&gt; → ça marche, mais on ne peut pas &lt;strong&gt;bloquer&lt;/strong&gt;, seulement autoriser&lt;/li&gt;
&lt;li&gt;Utiliser &lt;code&gt;egressDeny&lt;/code&gt; avec des IPs → on va jouer au chat et à la souris avec des plages IP qui changent&lt;/li&gt;
&lt;li&gt;Utiliser &lt;code&gt;egressDeny&lt;/code&gt; pour bloquer tout le 80/443 → on bloque tout, y compris le trafic légitime&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="solutions-de-contournement-potentielles"&gt;Solutions de contournement potentielles
&lt;/h2&gt;&lt;p&gt;En attendant que Cilium supporte &lt;code&gt;toFQDNs&lt;/code&gt; dans les policies &lt;code&gt;egressDeny&lt;/code&gt;, voici quelques approches alternatives que vous pourriez envisager :&lt;/p&gt;
&lt;h3 id="trouver-un-moyen-de-désactiver-la-télémétrie-directement-dans-lapp"&gt;Trouver un moyen de désactiver la télémétrie directement dans l&amp;rsquo;app
&lt;/h3&gt;&lt;p&gt;C&amp;rsquo;est la meilleure option, mais malheureusement pas toujours sur la table.&lt;/p&gt;
&lt;h3 id="blocage-basé-sur-le-dns"&gt;Blocage basé sur le DNS
&lt;/h3&gt;&lt;p&gt;Configurer le serveur DNS pour retourner NXDOMAIN pour les domaines de télémétrie, comme un serveur &lt;a class="link" href="https://pi-hole.net/" target="_blank" rel="noopener"
&gt;pi-hole&lt;/a&gt; personnel le ferait avec les pubs. L&amp;rsquo;application échouera à résoudre le domaine et n&amp;rsquo;enverra pas de données.&lt;/p&gt;
&lt;h3 id="utiliser-egressdeny-basé-sur-les-ips-avec-un-overhead-de-maintenance"&gt;Utiliser egressDeny basé sur les IPs (avec un overhead de maintenance)
&lt;/h3&gt;&lt;p&gt;Résoudre les FQDNs de télémétrie vers leurs plages IP actuelles et les bloquer avec &lt;code&gt;egressDeny&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;egressDeny&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;toCIDRSet&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;cidr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;203.0.113.0&lt;/span&gt;&lt;span class="l"&gt;/24 &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Exemple de plage IP de télémétrie&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;toPorts&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;ports&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;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;443&amp;#34;&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;Si la liste n&amp;rsquo;évolue pas trop souvent, c&amp;rsquo;est une bonne option.&lt;/p&gt;
&lt;h2 id="ok-mais-imaginons-quil-ny-ait-pas-de-trafic-légitime-peut-on-utiliser-la-fonctionnalité-pour-ajouter-un-log-sur-le-trafic-droppé-"&gt;OK, mais imaginons qu&amp;rsquo;il n&amp;rsquo;y ait pas de trafic légitime. Peut-on utiliser la fonctionnalité pour ajouter un log sur le trafic droppé ?
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Malheureusement non, pas pour le moment.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Il y a un bug dans cette nouvelle fonctionnalité de Cilium qui ne log le champ &lt;code&gt;policy_log&lt;/code&gt; que sur les flux &amp;ldquo;autorisés&amp;rdquo;, pas sur les flux audit/dropped.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/cilium/cilium/issues/42044" target="_blank" rel="noopener"
&gt;Policy log does not work for DROPPED/AUDIT flow&lt;/a&gt; ouverte par mon collègue Nicolas :&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;When defining a CiliumNetworkPolicy with the spec.log field configured, I expect the relevant hubble flows to have the policy_log field. It works for allowed flow.&lt;/p&gt;
&lt;p&gt;But for denied/audited flow resulting from the rule (implicit or explicit), policy_log is never available.&lt;/p&gt;
&lt;p&gt;Note: I observe the same issue with &lt;code&gt;--print-policy-names&lt;/code&gt; option of hubble, the k8s:io.cilium.k8s.policy.derived-from label is not set for denied flows (but correctly set for allowed flows).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;et une autre issue &lt;a class="link" href="https://github.com/cilium/cilium/issues/41912" target="_blank" rel="noopener"
&gt;[Hubble CLI] &amp;ndash;print-policy-names flag does not do anything&lt;/a&gt; ouverte par quelqu&amp;rsquo;un d&amp;rsquo;autre.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Puisque 2 tickets sont ouverts et que les mainteneurs ont commencé à répondre dessus, on peut espérer que ce soit corrigé un jour.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Dans notre cas d&amp;rsquo;usage, nous n&amp;rsquo;avons finalement pas utilisé cette nouvelle fonctionnalité de Cilium. Mais donner la possibilité d&amp;rsquo;ajouter des détails (et permettre de filtrer dessus également) est toujours une feature sympa.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Un grand merci à mon collègue Nicolas Nativel, qui a fait la majorité du travail autour des CiliumNetworkPolicies, incluant les dashboards, le travail exploratoire sur cette fonctionnalité, et a pris le temps de créer l&amp;rsquo;issue sur le repo Cilium.&lt;/strong&gt;&lt;/p&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://isovalent.com/blog/post/cilium-1-18/#hubble-flow-policy-log-field" target="_blank" rel="noopener"
&gt;Blogpost officiel pour la sortie de Cilium 1.18 - Hubble flow policy log field&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.danielstechblog.io/ciliums-new-hubble-flow-policy-log-field/" target="_blank" rel="noopener"
&gt;Daniel&amp;rsquo;s Tech Blog - Cilium&amp;rsquo;s new Hubble flow policy log field&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.cilium.io/en/stable/security/policy/language/#deny-policies" target="_blank" rel="noopener"
&gt;Docs Cilium - Deny Policies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/cilium/cilium/pull/39902" target="_blank" rel="noopener"
&gt;GitHub PR #39902 - Add policy log field&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/cilium/cilium/issues/42044" target="_blank" rel="noopener"
&gt;GitHub Issue #42044 - Policy log does not work for DROPPED/AUDIT flow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>J'ai testé pour vous : k8e (Kubernetes Easy Engine)</title><link>https://blog.zwindler.fr/2025/09/19/test-k8e/</link><pubDate>Fri, 19 Sep 2025 16:00:00 +0000</pubDate><guid>https://blog.zwindler.fr/2025/09/19/test-k8e/</guid><description>&lt;img src="https://blog.zwindler.fr/2025/09/k8e-logo.webp" alt="Featured image of post J'ai testé pour vous : k8e (Kubernetes Easy Engine)" /&gt;&lt;h2 id="tu-ne-faisais-pas-un-livre-sur-kubernetes-toi-"&gt;Tu ne faisais pas un livre sur Kubernetes, toi ?
&lt;/h2&gt;&lt;p&gt;Oui ! Et j&amp;rsquo;ai une bonne nouvelle : mon livre &amp;ldquo;Kubernetes : 50 solutions pour les postes de développement et les clusters de production&amp;rdquo;, édité par &lt;a class="link" href="https://www.editions-eyrolles.com/" target="_blank" rel="noopener"
&gt;Eyrolles&lt;/a&gt;, sortira le &lt;strong&gt;16 octobre 2025&lt;/strong&gt; ! Vous pouvez suivre l&amp;rsquo;état d&amp;rsquo;avancement du projet sur &lt;a class="link" href="https://50ndk.zwindler.fr" target="_blank" rel="noopener"
&gt;50ndk.zwindler.fr&lt;/a&gt;. Je ferai une annonce propre quand j&amp;rsquo;aurai la couverture définitive à vous montrer :3.&lt;/p&gt;
&lt;p&gt;En attendant la sortie, je &amp;ldquo;libère&amp;rdquo; un autre chapitre qui avait été abandonné lors de la sélection finale du livre : celui sur &lt;a class="link" href="https://github.com/xiaods/k8e" target="_blank" rel="noopener"
&gt;&lt;strong&gt;k8e&lt;/strong&gt; (Kubernetes Easy Engine)&lt;/a&gt;. Si vous suivez le blog attentivement, vous vous souviendrez peut-être que j&amp;rsquo;avais fait pareil pour &lt;a class="link" href="https://blog.zwindler.fr/2025/05/26/test-k8s-tew" &gt;k8s-tew (K8S : the easier way)&lt;/a&gt;, qui n&amp;rsquo;avait aussi pas eu la chance de figurer dans la liste des 50 méthodes qui ont leur place dans mon livre :-P.&lt;/p&gt;
&lt;h2 id="mais-revenons-à-k8e-"&gt;Mais revenons à k8e !
&lt;/h2&gt;&lt;p&gt;k8e est un wrapper pour k3s qui permet d&amp;rsquo;installer facilement un cluster multi-nodes avec &lt;a class="link" href="https://cilium.io/" target="_blank" rel="noopener"
&gt;Cilium&lt;/a&gt; configuré comme CNI et en mode &lt;em&gt;kube-proxy replacement&lt;/em&gt;. Dans la philosophie, c&amp;rsquo;est vraiment pas beaucoup plus que k3s avec un gros preflight check. On est même pas sur le niveau fonctionnel d&amp;rsquo;un &lt;code&gt;k3sup&lt;/code&gt; ou d&amp;rsquo;un &lt;code&gt;k0ctl&lt;/code&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Les développeurs mettent en avant plusieurs fonctionnalités clés :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;✅ Key Features&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supports airgap images package for k8s components&lt;/li&gt;
&lt;li&gt;10-year valid certificate, supports cluster backup and upgrade&lt;/li&gt;
&lt;li&gt;No dependency on Ansible, HAProxy, or Keepalived; a binary tool with zero dependencies&lt;/li&gt;
&lt;li&gt;Natively supports Cilium network&lt;/li&gt;
&lt;li&gt;No kube-proxy component&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Le projet se présente donc comme une alternative simplifiée pour déployer k3s avec Cilium directement intégré, ce qui évite les étapes manuelles de configuration post-installation. J&amp;rsquo;ai d&amp;rsquo;ailleurs écrit un article fin 2023 sur ces fameuses opérations manuelles, toujours disponible ici : &lt;a class="link" href="https://blog.zwindler.fr/2023/09/01/k3s-et-cilium-rapide-et-facile" &gt;k3s et cilium rapide et facile&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="prérequis"&gt;Prérequis
&lt;/h2&gt;&lt;p&gt;Comme k8e s&amp;rsquo;appuie sur k3s, les prérequis sont globalement les mêmes que pour k3s. Cependant, il y a une exigence supplémentaire importante : Cilium utilisant eBPF pour ses fonctionnalités de bas niveau, il faut un noyau Linux &lt;strong&gt;relativement&lt;/strong&gt; récent&lt;/p&gt;
&lt;p&gt;Bon, ça c&amp;rsquo;était ce qu&amp;rsquo;on disait pour les premières versions de cilium. Maintenant &lt;strong&gt;Linux kernel &amp;gt;= 4.19.57&lt;/strong&gt; c&amp;rsquo;est un vieux vieux kernel !&lt;/p&gt;
&lt;p&gt;Pour mes tests, j&amp;rsquo;ai utilisé 4 machines virtuelles dans le même LAN :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;k8e1 (control plane, 192.168.1.11)&lt;/li&gt;
&lt;li&gt;k8e2 (control plane, 192.168.1.12)&lt;/li&gt;
&lt;li&gt;k8e3 (control plane, 192.168.1.13)&lt;/li&gt;
&lt;li&gt;k8e4 (worker, 192.168.1.14)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L&amp;rsquo;installation nécessite un accès SSH avec des privilèges sudo/root sur tous les nœuds.&lt;/p&gt;
&lt;h2 id="installation-du-premier-nœud"&gt;Installation du premier nœud
&lt;/h2&gt;&lt;p&gt;Le &lt;a class="link" href="https://getk8e-site.pages.dev/docs/install/200-quick-start/" target="_blank" rel="noopener"
&gt;Getting started&lt;/a&gt; est un peu rude car on tombe sur une page en chinois simplifié par défaut&amp;hellip;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;installation de k8e se fait via un script bash, comme beaucoup d&amp;rsquo;outils de la CNCF (RIP la sécurité). Pour le premier nœud (qui sera notre premier node control plane), on utilise la commande suivante :&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;denis@k8e1:~$ curl -sfL https://getk8e-site.pages.dev/install.sh &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;API_SERVER_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.11 &lt;span class="nv"&gt;K8E_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;superSecureToken &lt;span class="nv"&gt;INSTALL_K8E_EXEC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;server --cluster-init&amp;#34;&lt;/span&gt; sh -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le script réalise plusieurs vérifications préalables (&amp;ldquo;preflight checks&amp;rdquo;) pour s&amp;rsquo;assurer que l&amp;rsquo;environnement est compatible. C&amp;rsquo;est notamment là qu&amp;rsquo;il vérifie la version du noyau Linux pour la compatibilité eBPF.&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="o"&gt;[&lt;/span&gt;2025-09-19 12:28:45&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;WARN&lt;span class="o"&gt;]&lt;/span&gt; System memory is less than 4GB. This may affect performance.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Finding latest version from GitHub
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;v1.31.2+k8e1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Downloading package https://github.com/xiaods/k8e/releases/download/v1.31.2+k8e1/k8e as /home/denis/k8e
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2025/09/k8e-prereqs.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;À la fin du processus, on obtient un message de confirmation :&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="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;&lt;span class="o"&gt;[&lt;/span&gt;2025-09-19 12:35:33&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;INFO&lt;span class="o"&gt;]&lt;/span&gt; systemd: Starting k8e
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;2025-09-19 12:35:41&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;INFO&lt;span class="o"&gt;]&lt;/span&gt; Installing cilium network cni/operator
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ℹ️ Using Cilium version 1.15.6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;🔮 Auto-detected cluster name: default
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;🔮 Auto-detected kube-proxy has not been installed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ℹ️ Cilium will fully replace all functionalities of kube-proxy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;2025-09-19 12:35:42&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;INFO&lt;span class="o"&gt;]&lt;/span&gt; Installation completed successfully
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;2025-09-19 12:35:42&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;INFO&lt;span class="o"&gt;]&lt;/span&gt; Performing cleanup...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Point qui a été amélioré depuis la dernière fois que j&amp;rsquo;avais testé, k8e copie lui-même le kubeconfig, il n&amp;rsquo;y a plus besoin d&amp;rsquo;aller le récupérer dans &lt;code&gt;/etc/k8e/k8e.yaml&lt;/code&gt; (idem k3s, il est traditionnellement dans &lt;code&gt;/etc/rancher/k3s/k3s.yaml&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;denis@k8e1:~$ 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;k8e1 Ready control-plane,etcd,master 62m v1.31.2+k8e1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cependant, un rapide coup d&amp;rsquo;œil permet de voir &amp;ldquo;comment&amp;rdquo; k8e le fait :&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;denis@k8e1:~$ env
&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="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/k8e/k8e.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;denis@k8e1:~$ ls -l /etc/k8e/k8e.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-rw-r--r-- &lt;span class="m"&gt;1&lt;/span&gt; root root &lt;span class="m"&gt;2961&lt;/span&gt; Sep &lt;span class="m"&gt;19&lt;/span&gt; 12:35 /etc/k8e/k8e.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et là, je suis désolé, mais c&amp;rsquo;est :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2025/09/heretique.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Le kubeconfig cluster-admin à poil en 644, c&amp;rsquo;est NON. La doc de k8e est même encore pire, elle conseille du 666 (toujours plus).&lt;/p&gt;
&lt;h2 id="ajout-des-nœuds-supplémentaires"&gt;Ajout des nœuds supplémentaires
&lt;/h2&gt;&lt;p&gt;Une fois le premier nœud installé, on peut ajouter les autres membres du control plane. Pour les nœuds supplémentaires du control plane :&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;denis@k8e2:~$ curl -sfL https://getk8e-site.pages.dev/install.sh &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;K8E_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;superSecureToken &lt;span class="nv"&gt;K8E_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://192.168.1.11:6443 &lt;span class="nv"&gt;INSTALL_K8E_EXEC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;server&amp;#34;&lt;/span&gt; sh -s -
&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;denis@k8e3:~$ curl -sfL https://getk8e.com/install.sh &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;K8E_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;superSecureToken &lt;span class="nv"&gt;K8E_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://192.168.1.11:6443 &lt;span class="nv"&gt;INSTALL_K8E_EXEC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;server&amp;#34;&lt;/span&gt; sh -s -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et pour le worker node :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;denis@k8e4:~$ curl -sfL https://getk8e-site.pages.dev/install.sh &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;K8E_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;superSecureToken &lt;span class="nv"&gt;K8E_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://192.168.1.11:6443 sh -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On peut ensuite vérifier que tous les nœuds sont bien présents :&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;denis@k8e1:~$ 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;k8e1 Ready control-plane,etcd,master 72m v1.31.2+k8e1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;k8e2 Ready control-plane,etcd,master 2m30s v1.31.2+k8e1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;k8e3 Ready control-plane,etcd,master 2m18s v1.31.2+k8e1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;k8e4 Ready &amp;lt;none&amp;gt; 75s v1.31.2+k8e1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="vérification-de-cilium"&gt;Vérification de Cilium
&lt;/h2&gt;&lt;p&gt;Une des particularités de k8e est donc l&amp;rsquo;intégration native de Cilium. On peut vérifier que Cilium fonctionne correctement en utilisant sa CLI directement sur un des nœuds du control plane (installée par défaut) :&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;denis@k8e1:~$ cilium status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /¯¯&lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /¯¯&lt;span class="se"&gt;\_&lt;/span&gt;_/¯¯&lt;span class="se"&gt;\ &lt;/span&gt; Cilium: OK
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="se"&gt;\_&lt;/span&gt;_/¯¯&lt;span class="se"&gt;\_&lt;/span&gt;_/ Operator: OK
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /¯¯&lt;span class="se"&gt;\_&lt;/span&gt;_/¯¯&lt;span class="se"&gt;\ &lt;/span&gt; Envoy DaemonSet: disabled &lt;span class="o"&gt;(&lt;/span&gt;using embedded mode&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="se"&gt;\_&lt;/span&gt;_/¯¯&lt;span class="se"&gt;\_&lt;/span&gt;_/ Hubble Relay: disabled
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="se"&gt;\_&lt;/span&gt;_/ ClusterMesh: disabled
&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;DaemonSet cilium Desired: 4, Ready: 4/4, Available: 4/4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Deployment cilium-operator Desired: 1, Ready: 1/1, Available: 1/1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Containers: cilium Running: &lt;span class="m"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cilium-operator Running: &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cluster Pods: 3/3 managed by Cilium
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Helm chart version: 1.15.6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Image versions cilium quay.io/cilium/cilium:v1.15.6: &lt;span class="m"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cilium-operator quay.io/cilium/operator-generic:v1.15.6: &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="avantages-et-inconvénients"&gt;Avantages et Inconvénients
&lt;/h3&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Les plus&lt;/th&gt;
&lt;th&gt;Les moins&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;➕ Installation automatisée de Cilium sur k3s&lt;/td&gt;
&lt;td&gt;➖ Permissions &lt;code&gt;/etc/k8e/k8e.yaml&lt;/code&gt; en &amp;ldquo;world readable&amp;rdquo; dans la doc officielle (dangereux !)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;➖ Version de Kubernetes en retard par rapport aux dernières versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;➖ N&amp;rsquo;apporte pas énormément de valeur ajoutée par rapport à k3s seul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;➖ Documentation limitée par rapport aux projets plus matures&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Dans les points positifs, le projet, sans être populaire, est quand même plutôt suivi avec pas mal de contributions externes et une &amp;ldquo;vie&amp;rdquo; (commits réguliers). k8e est un outil pour les fainéants qui veulent installer un cluster k3s avec Cilium plus rapidement (oui c&amp;rsquo;est un point positif).&lt;/p&gt;
&lt;p&gt;Mais, à quel prix ?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Un kubeconfig à 644, lisible par tous les utilisateurs unix des control planes&lt;/li&gt;
&lt;li&gt;Une installation en &lt;code&gt;curl | bash&lt;/code&gt; (exactement comme &lt;code&gt;k3s&lt;/code&gt;, à ceci près que j&amp;rsquo;ai plus confiance en &lt;strong&gt;Rancher labs&lt;/strong&gt; qu&amp;rsquo;en &lt;strong&gt;xiaods&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Des versions datées (kube 1.31, cilium 1.15)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pas la peine que je vous dise ce que j&amp;rsquo;en pense, je pense que vous avez compris.&lt;/p&gt;</description></item><item><title>Migration du routage de cilium de iptables vers eBPF... à chaud !</title><link>https://blog.zwindler.fr/2023/10/20/migration-routage-cilium-iptables-ebpf/</link><pubDate>Fri, 20 Oct 2023 06:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2023/10/20/migration-routage-cilium-iptables-ebpf/</guid><description>&lt;img src="https://blog.zwindler.fr/2023/10/cilium-logo.webp" alt="Featured image of post Migration du routage de cilium de iptables vers eBPF... à chaud !" /&gt;&lt;h2 id="contexte"&gt;Contexte
&lt;/h2&gt;&lt;p&gt;Il se pourrait que j&amp;rsquo;aie configuré des clusters Kubernetes de prod avec &lt;a class="link" href="https://cilium.io/" target="_blank" rel="noopener"
&gt;cilium&lt;/a&gt; en mode &lt;code&gt;iptables&lt;/code&gt; et non pas &lt;code&gt;eBPF&lt;/code&gt;. Mais vous n&amp;rsquo;avez aucune preuve&amp;hellip;&lt;/p&gt;
&lt;p&gt;Cependant, dans l&amp;rsquo;hypothèse hautement improbable où j&amp;rsquo;aurais pu faire une boulette pareille, voilà comment je m&amp;rsquo;y serai pris pour résoudre le problème.&lt;/p&gt;
&lt;p&gt;#trollface&lt;/p&gt;
&lt;p&gt;Imaginons donc qu&amp;rsquo;en vérifiant la configuration de Cilium, voici ce que vous avez trouvé :&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="l"&gt;kubectl -n cilium exec -it cilium-aaaaa -- cilium status&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="p"&gt;[&lt;/span&gt;&lt;span class="l"&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="nt"&gt;Host Routing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Legacy&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;Masquerading: IPTables [IPv4: Enabled, IPv6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Disabled]&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="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;...]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Damned!&lt;/p&gt;
&lt;p&gt;A y regarder de plus près, les containers cilium-agent dans vos pods cilium prennent beaucoup de CPU et de RAM et commencent à engorger vos workers&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;C&amp;rsquo;est la cata&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="pourquoi-ça-"&gt;Pourquoi ça ?
&lt;/h2&gt;&lt;p&gt;Les premières implémentations du &lt;em&gt;réseau imaginaire&lt;/em&gt; de Kubernetes (les fameux CNI plugins) utilisaient &lt;code&gt;iptables&lt;/code&gt;. Or, on sait depuis très longtemps, notamment dans le cas d&amp;rsquo;usage de Kubernetes, qu&amp;rsquo;&lt;code&gt;iptables&lt;/code&gt; est assez mauvais pour gérer de grandes quantités de règles.&lt;/p&gt;
&lt;p&gt;Dans le cas particulier de Kubernetes, le nombre de règles à tendance à augmenter de manière exponentielle avec la taille du cluster et le CPU consommé pour router des paquets réseaux avec&amp;hellip;&lt;/p&gt;
&lt;p&gt;À un moment donné, parcourir la liste des règles prend tout le CPU d&amp;rsquo;un nœud donné et le rend non réactif.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Allo patron ? On est mal.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;C&amp;rsquo;est une des raisons pour lesquelles j&amp;rsquo;aime beaucoup cilium comme CNI plugin, les développeurs font partie des premiers à avoir misé sur &lt;a class="link" href="https://ebpf.io/" target="_blank" rel="noopener"
&gt;eBPF&lt;/a&gt; comme remplacement d&amp;rsquo;iptables (même s&amp;rsquo;il existe d&amp;rsquo;autres implémentations / d&amp;rsquo;autres technos qui règles ce problème).&lt;/p&gt;
&lt;h2 id="comment-en-est-on-arrivé-là-"&gt;Comment en est on arrivé là ?
&lt;/h2&gt;&lt;p&gt;A vrai dire, j&amp;rsquo;ai juste lu la doc et fait confiance&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We introduced eBPF-based host-routing in Cilium 1.9 to fully bypass iptables and the upper host stack, and to achieve a faster network namespace switch compared to regular veth device operation. &lt;strong&gt;This option is automatically enabled if your kernel supports it&lt;/strong&gt;. To validate whether your installation is running with eBPF host-routing, run cilium status in any of the Cilium pods and look for the line reporting the status for &amp;ldquo;Host Routing&amp;rdquo; which should state &amp;ldquo;BPF&amp;rdquo;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Grosso modo, &lt;a class="link" href="https://docs.cilium.io/en/stable/operations/system_requirements/" target="_blank" rel="noopener"
&gt;selon la doc officielle, si on dispose du kernel correct et des modules qui vont bien&lt;/a&gt;, l&amp;rsquo;installation de cilium est censée activer automatiquement le mode eBPF&amp;hellip; Sauf qu&amp;rsquo;on voit bien un peu plus haut que ce n&amp;rsquo;est pas le cas, malgré un kernel récent (6.2).&lt;/p&gt;
&lt;p&gt;En creusant un peu plus les logs, voilà ce qu&amp;rsquo;on peut trouver :&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 -n cilium logs cilium-7b5cp
&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;&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;BPF host routing requires enable-bpf-masquerade. Falling back to legacy host routing (enable-host-legacy-routing=true).&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;subsys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;daemon
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bon&amp;hellip; visiblement, il manque une option dans la chart Helm.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&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; kubeProxyReplacement: strict
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ bpf:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ masquerade: true
&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Problem solved, fin de l&amp;rsquo;article ?&lt;/p&gt;
&lt;h2 id="problème"&gt;Problème
&lt;/h2&gt;&lt;p&gt;Alors oui, forcément, on peut modifier la chart comme un⋅e bourrin⋅e et fin de l&amp;rsquo;histoire.&lt;/p&gt;
&lt;p&gt;Mais admettons qu&amp;rsquo;on ait pas envie de couper le trafic de production&amp;hellip; Comment on fait ?&lt;/p&gt;
&lt;p&gt;La solution la plus clean qui me vient en tête est la suivante :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on &lt;code&gt;drain&lt;/code&gt; un node&lt;/li&gt;
&lt;li&gt;on lui change sa configuration&lt;/li&gt;
&lt;li&gt;on l&amp;rsquo;&lt;code&gt;uncordon&lt;/code&gt; pour lui remettre un peu de trafic dessus&lt;/li&gt;
&lt;li&gt;on vérifie que la nouvelle configuration fonctionne
ET&lt;/li&gt;
&lt;li&gt;on vérifie que les nodes peuvent se parler entre eux entre ceux en &lt;code&gt;iptables&lt;/code&gt; et ceux en &lt;code&gt;eBPF&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bah oui, ça serait dommage d&amp;rsquo;avoir la moitié du cluster qui peut pas communiquer avec l&amp;rsquo;autre moitié&amp;hellip;&lt;/p&gt;
&lt;h2 id="vérifier-la-connectivité"&gt;Vérifier la connectivité
&lt;/h2&gt;&lt;p&gt;Ce qui est cool avec cilium (je vous ai déjà dit que j&amp;rsquo;aime cilium ?), c&amp;rsquo;est qu&amp;rsquo;on a déjà du tooling pour tout tester.&lt;/p&gt;
&lt;p&gt;La CLI cilium dispose d&amp;rsquo;une sous commande &lt;code&gt;cilium connectivity test&lt;/code&gt; qui lance des dizaines de tests internes/externes pour tester que tout est OK.&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;➜ ~ cilium -n cilium connectivity &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ℹ️ Monitor aggregation detected, will skip some flow validation steps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✨ &lt;span class="o"&gt;[&lt;/span&gt;node&lt;span class="o"&gt;]&lt;/span&gt; Deploying echo-same-node service...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✨ &lt;span class="o"&gt;[&lt;/span&gt;node&lt;span class="o"&gt;]&lt;/span&gt; Deploying DNS &lt;span class="nb"&gt;test&lt;/span&gt; server configmap...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✨ &lt;span class="o"&gt;[&lt;/span&gt;node&lt;span class="o"&gt;]&lt;/span&gt; Deploying same-node deployment...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✨ &lt;span class="o"&gt;[&lt;/span&gt;node&lt;span class="o"&gt;]&lt;/span&gt; Deploying client deployment...
&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;✅ All &lt;span class="m"&gt;32&lt;/span&gt; tests &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;265&lt;/span&gt; actions&lt;span class="o"&gt;)&lt;/span&gt; successful, &lt;span class="m"&gt;2&lt;/span&gt; tests skipped, &lt;span class="m"&gt;0&lt;/span&gt; scenarios skipped.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Il existe aussi une commande &lt;code&gt;cilium-health&lt;/code&gt;, embarquée dans le container cilium-agent, qui permet de remonter des statistiques périodiques de latences entre tous les nodes du cluster. Utile !&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 -n cilium &lt;span class="nb"&gt;exec&lt;/span&gt; -it cilium-ccccc -c cilium-agent -- cilium-health status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Probe time: 2023-09-12T14:47:03Z
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Nodes:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; node-02 &lt;span class="o"&gt;(&lt;/span&gt;localhost&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Host connectivity to 172.31.0.152:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ICMP to stack: OK, &lt;span class="nv"&gt;RTT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;860.424µs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HTTP to agent: OK, &lt;span class="nv"&gt;RTT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;110.142µs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Endpoint connectivity to 10.0.2.56:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ICMP to stack: OK, &lt;span class="nv"&gt;RTT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;783.861µs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HTTP to agent: OK, &lt;span class="nv"&gt;RTT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;256.419µs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; node-01:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Host connectivity to 172.31.0.151:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ICMP to stack: OK, &lt;span class="nv"&gt;RTT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;813.324µs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HTTP to agent: OK, &lt;span class="nv"&gt;RTT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;553.445µs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Endpoint connectivity to 10.0.1.53:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ICMP to stack: OK, &lt;span class="nv"&gt;RTT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;865.976µs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HTTP to agent: OK, &lt;span class="nv"&gt;RTT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3.440655ms
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et enfin, on peut juste regarder la commande &lt;code&gt;cilium status&lt;/code&gt; qui nous dit qui est &amp;ldquo;reachable&amp;rdquo; (là encore, dans le container cilium-agent)&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 -n cilium &lt;span class="nb"&gt;exec&lt;/span&gt; -ti cilium-ddddd -- cilium status
&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;Host Routing: Legacy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Masquerading: IPTables &lt;span class="o"&gt;[&lt;/span&gt;IPv4: Enabled, IPv6: Disabled&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;Cluster health: 5/5 reachable &lt;span class="o"&gt;(&lt;/span&gt;2023-09-14T12:01:51Z&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="changer-la-configuration-dun-node"&gt;Changer la configuration d&amp;rsquo;un node
&lt;/h2&gt;&lt;p&gt;On a de la chance, car, depuis la version 1.13 de cilium (la dernière en date est la 1.14), il est possible d&amp;rsquo;appliquer des configurations différentes pour un sous ensemble de nodes (documentation officielle du &lt;a class="link" href="https://docs.cilium.io/en/stable/configuration/per-node-config/#per-node-configuration" target="_blank" rel="noopener"
&gt;Per-node configuration&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Note : avant ça, c&amp;rsquo;était quand même possible, &lt;em&gt;de manière temporaire&lt;/em&gt;, en éditant la ConfigMap de cilium à la main, puis en redémarrant les pods concernés pour prise en compte.&lt;/p&gt;
&lt;p&gt;Il existe maintenant une CRD pour le faire, qui s&amp;rsquo;appelle &lt;strong&gt;CiliumNodeConfig&lt;/strong&gt;. Les seules choses qu&amp;rsquo;on a à faire, c&amp;rsquo;est ajouter un label sur un node (io.cilium.enable-ebpf: &amp;ldquo;true&amp;rdquo;) et ajouter le manifest suivant sur notre 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;cat &amp;gt; cilium-fix.yaml &lt;span class="s"&gt;&amp;lt;&amp;lt; EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;apiVersion: cilium.io/v2alpha1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;kind: CiliumNodeConfig
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;metadata:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; namespace: cilium
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; name: cilium-switch-from-iptables-ebpf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;spec:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; nodeSelector:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; matchLabels:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; io.cilium.enable-ebpf: &amp;#34;true&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; defaults:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; enable-bpf-masquerade: &amp;#34;true&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;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f cilium-fix.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl label node node-05 --overwrite &lt;span class="s1"&gt;&amp;#39;io.cilium.enable-ebpf=true&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si jamais on est en production, le plus propre est donc de &lt;code&gt;kubectl drain&lt;/code&gt; le Node préalablement.&lt;/p&gt;
&lt;p&gt;Par curiosité, j&amp;rsquo;ai quand même essayé de le faire &amp;ldquo;à chaud&amp;rdquo;, pour le fun.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai déployé un daemonset contenant &lt;a class="link" href="https://github.com/zwindler/vhelloworld" target="_blank" rel="noopener"
&gt;une app en V(lang) qui répond simplement le nom du container dans une page web&lt;/a&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="l"&gt;cat &amp;gt; vhelloworld-daemonset.yaml &amp;lt;&amp;lt; EOF&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;apps/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;DaemonSet&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;vhelloworld-daemonset&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;selector&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;matchLabels&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;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;vhelloworld&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;template&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;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;labels&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;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;vhelloworld&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;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;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;vhelloworld&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;zwindler/vhelloworld: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;ports&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;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8081&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;imagePullPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Always&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="l"&gt;EOF&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;kubectl apply -f vhelloworld-daemonset.yaml&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 get pods -o wide
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vhelloworld-daemonset-q4h44 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 3m31s 10.0.4.87 nodekube-05 &amp;lt;none&amp;gt; &amp;lt;none&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vhelloworld-daemonset-w97pv 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 3m31s 10.0.3.85 nodekube-04 &amp;lt;none&amp;gt; &amp;lt;none&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Puis j&amp;rsquo;ai créé des containers contenant un shell et &lt;code&gt;curl&lt;/code&gt; pour vérifier périodiquement et depuis plusieurs nodes que les containers sont bien accessibles :&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 run -it --image curlimages/curl:latest curler -- /bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;If you don&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;t see a &lt;span class="nb"&gt;command&lt;/span&gt; prompt, try pressing enter.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~ $ curl http://10.0.4.87:8081
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello from vhelloworld-daemonset-g2zdw
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~ $ curl http://10.0.3.85:8081
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello from vhelloworld-daemonset-65k2n
&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="k"&gt;while&lt;/span&gt; true&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; date
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl http://10.0.4.87:8081
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl http://10.0.3.85:8081
&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sleep &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Une fois le label appliqué sur un node, son pod cilium tué (via un &lt;code&gt;kubectl delete&lt;/code&gt;), les pods sont restés accessibles&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;Fri Sep &lt;span class="m"&gt;15&lt;/span&gt; 14:05:34 UTC &lt;span class="m"&gt;2023&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello from vhelloworld-daemonset-g2zdw
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello from vhelloworld-daemonset-65k2n
&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;Fri Sep &lt;span class="m"&gt;15&lt;/span&gt; 14:05:35 UTC &lt;span class="m"&gt;2023&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello from vhelloworld-daemonset-g2zdw
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello from vhelloworld-daemonset-65k2n
&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;Fri Sep &lt;span class="m"&gt;15&lt;/span&gt; 14:05:36 UTC &lt;span class="m"&gt;2023&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello from vhelloworld-daemonset-g2zdw
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello from vhelloworld-daemonset-65k2n
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A un moment donné, cilium a redémarré, remarqué la présence de pods existants, et pris le relais avec eBPF !&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;evel&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;Rewrote endpoint BPF program&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;containerID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8b7be1b032 &lt;span class="nv"&gt;datapathPolicyRevision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nv"&gt;desiredPolicyRevision&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;endpointID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1018&lt;/span&gt; &lt;span class="nv"&gt;identity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4773&lt;/span&gt; &lt;span class="nv"&gt;ipv4&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.0.3.85 &lt;span class="nv"&gt;ipv6&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;k8sPodName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;default/vhelloworld-daemonset-w97pv &lt;span class="nv"&gt;subsys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;endpoint
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;Restored endpoint&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;endpointID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1018&lt;/span&gt; &lt;span class="nv"&gt;ipAddr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[10.0.3.85 ]&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;subsys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;endpoint
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="ca-fonctionne-mais-est-ce-que-cest-mieux-en-termes-de-consommation-de-ressources-"&gt;Ca fonctionne, mais est ce que c&amp;rsquo;est mieux en termes de consommation de ressources ?
&lt;/h2&gt;&lt;p&gt;Ca, c&amp;rsquo;était &lt;strong&gt;la&lt;/strong&gt; bonne nouvelle. Je m&amp;rsquo;attendais à des gains car le cluster était vraiment pas loin de souffrir.&lt;/p&gt;
&lt;p&gt;Les containers cilium prenaient 10% des CPU de mes nodes, et plusieurs Go de RAM en mode &lt;code&gt;iptables&lt;/code&gt;. Ca aurait rapidement pu monter bien plus haut, si j&amp;rsquo;avais agrandi le cluster au-delà des 50 nodes / 2000 pods.&lt;/p&gt;
&lt;p&gt;Le passage au mode eBPF a permis de retrouver des niveaux totalement indolores (1% de CPU par node, 1-2% de RAM) par rapport aux configurations de mes machines.&lt;/p&gt;
&lt;p&gt;Pas mal, hein ?&lt;/p&gt;
&lt;h2 id="bonus"&gt;Bonus
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://isovalent.com/blog/post/tutorial-migrating-to-cilium-part-1/" target="_blank" rel="noopener"
&gt;Un tutoriel bien velu pour migrer totalement de CNI à chaud (de flannel à cilium)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>k3s et cilium rapide et facile</title><link>https://blog.zwindler.fr/2023/09/01/k3s-et-cilium-rapide-et-facile/</link><pubDate>Fri, 01 Sep 2023 10:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2023/09/01/k3s-et-cilium-rapide-et-facile/</guid><description>&lt;img src="https://blog.zwindler.fr/2023/09/k3s_cilium.webp" alt="Featured image of post k3s et cilium rapide et facile" /&gt;&lt;h2 id="contexte"&gt;Contexte
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://k3s.io/" target="_blank" rel="noopener"
&gt;K3s&lt;/a&gt; est une distribution de Kubernetes éditée par Rancher (et certifiée par la CNCF) que je trouve super pratique car légère et supportant plusieurs plateformes (en particulier ARM):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;x86_64&lt;/li&gt;
&lt;li&gt;armhf&lt;/li&gt;
&lt;li&gt;arm64/aarch64&lt;/li&gt;
&lt;li&gt;s390x&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je l&amp;rsquo;ai déjà utilisée par le passé (&lt;a class="link" href="https://blog.zwindler.fr/2019/03/21/deployer-en-5-minutes-un-cluster-kubernetes-sur-arm-avec-k3s-et-ansible/" target="_blank" rel="noopener"
&gt;ici&lt;/a&gt;) pour faire un cluster kubernetes avec des vieux Raspberry Pi. A l&amp;rsquo;époque, on pouvait même faire tourner les nodes &amp;ldquo;workers&amp;rdquo; sur des RPi 1 (je ne sais pas si c&amp;rsquo;est toujours possible).&lt;/p&gt;
&lt;p&gt;La particularité de k3s est que tout est intégré dans un seul binaire. Nécessairement, des choix techniques ont été fais pour rendre l&amp;rsquo;installation et l&amp;rsquo;utilisation la plus légère possible. Parmi ces choix techniques, il y a l&amp;rsquo;utilisation de &lt;a class="link" href="https://github.com/flannel-io/flannel" target="_blank" rel="noopener"
&gt;flannel comme &amp;ldquo;CNI plugin&amp;rdquo;&lt;/a&gt; (pour faire simple, le réseau virtuel qui permet aux containers de parler en eux).&lt;/p&gt;
&lt;p&gt;Je ne suis vraiment PAS fan de flannel qui m&amp;rsquo;a posé beaucoup de soucis en production, qui ne supporte pas les &amp;ldquo;Network Policies&amp;rdquo;, qui a pendant plusieurs années utilisé comme backend etcd2 alors que ce logiciel était marqué comme obsolète (deprecated) depuis plusieurs années, &amp;hellip;&lt;/p&gt;
&lt;p&gt;A l&amp;rsquo;inverse, je suis très fan de &lt;a class="link" href="https://cilium.io/" target="_blank" rel="noopener"
&gt;cilium&lt;/a&gt;, qui a beaucoup de fonctionnalités sympa grâce à l&amp;rsquo;usage de eBPF en termes de performance et de sécurité et a pas mal de traction dans l&amp;rsquo;écosystème.&lt;/p&gt;
&lt;h2 id="prérequis"&gt;Prérequis
&lt;/h2&gt;&lt;p&gt;Dans ce tutoriel, je pars du principe que vous avez installé un serveur avec Ubuntu 22.04.&lt;/p&gt;
&lt;p&gt;Cilium est très friand d&amp;rsquo;eBPF, dont il tire la plupart de ces fonctionnalités. Et comme eBPF est quelque chose d&amp;rsquo;assez récent dans le kernel Linux, le mieux est de mettre à jour notre serveur et de lui mettre un kernel plus récent :&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 apt update -y
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ sudo apt upgrade -y
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ sudo apt install curl linux-image-generic-hwe-22.04 -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Une fois que c&amp;rsquo;est fait, on reboot le serveur pour la prise en compte du nouveau kernel.&lt;/p&gt;
&lt;h2 id="installation-de-k3s"&gt;Installation de k3s
&lt;/h2&gt;&lt;p&gt;On installe donc &lt;a class="link" href="https://docs.k3s.io/installation" target="_blank" rel="noopener"
&gt;k3s&lt;/a&gt;, en remplaçant flannel par cilium (voir documentation &lt;a class="link" href="https://docs.k3s.io/installation/network-options" target="_blank" rel="noopener"
&gt;Installation - Network options&lt;/a&gt;). La doc conseille de retirer le support des network-policy. Je me demande comment c&amp;rsquo;est géré puisque flannel n&amp;rsquo;est pas censé les supporter :thinking_face:.&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;$ curl -sfL https://get.k3s.io &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;INSTALL_K3S_EXEC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;--flannel-backend=none --disable &amp;#34;traefik&amp;#34;&amp;#39;&lt;/span&gt; sh -s - --disable-network-policy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On vérifie que k3s est démarré et fonctionne :&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 k3s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;● k3s.service - Lightweight Kubernetes
&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;/etc/systemd/system/k3s.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 Fri 2023-09-01 12:32:55 UTC&lt;span class="p"&gt;;&lt;/span&gt; 1min 13s ago
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Docs: https://k3s.io
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Process: &lt;span class="m"&gt;1724&lt;/span&gt; &lt;span class="nv"&gt;ExecStartPre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/bin/sh -xc ! /usr/bin/systemctl is-enabled --quiet nm-cloud-setup.service &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exited, &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0/SUCCESS&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Process: &lt;span class="m"&gt;1726&lt;/span&gt; &lt;span class="nv"&gt;ExecStartPre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/sbin/modprobe br_netfilter &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exited, &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0/SUCCESS&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Process: &lt;span class="m"&gt;1727&lt;/span&gt; &lt;span class="nv"&gt;ExecStartPre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/sbin/modprobe overlay &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exited, &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0/SUCCESS&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Par défaut, le &amp;ldquo;kubeconfig&amp;rdquo; d&amp;rsquo;administration est déposé dans le fichier &lt;code&gt;/etc/rancher/k3s/k3s.yaml&lt;/code&gt;, dont le propriétaire est root. Il existe un flag &lt;code&gt;--write-kubeconfig-mode&lt;/code&gt; dans k3s pour modifier les droits de ce fichier mais cela le rendrait &amp;ldquo;world readable&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Pour éviter de faire ça, on récupère le kube/config pour notre utilisateur &amp;ldquo;ubuntu&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Comme le binaire k3s embarque tout, dont kubectl, on va aussi devoir lui dire de ne pas utiliser ce fichier en forçant la variable d&amp;rsquo;environnement $KUBECONFIG.&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;$ mkdir ~/.kube
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ sudo chown ubuntu:ubuntu ~/.kube/config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ chmod &lt;span class="m"&gt;600&lt;/span&gt; ~/.kube/config
&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;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.kube/config
&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="s1"&gt;&amp;#39;export KUBECONFIG=~/.kube/config&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc
&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="s1"&gt;&amp;#39;source &amp;lt;(kubectl completion bash)&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc
&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="s1"&gt;&amp;#39;alias k=kubectl&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&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 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;kube01 NotReady control-plane,master 70s v1.27.4+k3s1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ici, kube01 est marqué NOTREADY. C&amp;rsquo;est normal car on a pas de CNI plugin (donc pas de réseau interne).&lt;/p&gt;
&lt;h2 id="ajout-dun-second-node"&gt;Ajout d&amp;rsquo;un second node
&lt;/h2&gt;&lt;p&gt;Sur le premier node, récupérez le token pour que le second puisse rejoindre le 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;cat /var/lib/rancher/k3s/server/node-token
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Une fois que c&amp;rsquo;est fait, connectez vous sur le node à rajouter au cluster, installez le kernel &lt;code&gt;hwe&lt;/code&gt;, rebootez&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt update
sudo apt upgrade
sudo apt install curl linux-image-generic-hwe-22.04 -y
sudo reboot
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Une fois que le node est à jour et prêt à rejoindre le cluster, lancez la commande suivante :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;IP_MASTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;IP.DE.VOTRE.MASTER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;K3STOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;le:token:du:master
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sfL https://get.k3s.io &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;K3S_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IP_MASTER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:6443 &lt;span class="nv"&gt;K3S_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;K3STOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; sh -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Au bout de quelques secondes, votre node va apparaître dans la liste des nodes.&lt;/p&gt;
&lt;h2 id="cni-plugin--ingresscontroller"&gt;CNI plugin + ingressController
&lt;/h2&gt;&lt;p&gt;Nos nodes sont toujours en &amp;ldquo;Not Ready&amp;rdquo;. On installe helm et on ajoute le CNI plugin :&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;$ curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 &lt;span class="p"&gt;|&lt;/span&gt; bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nv"&gt;CILIUM_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1.14.2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ helm repo add cilium https://helm.cilium.io/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ helm upgrade --install cilium cilium/cilium --version&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CILIUM_VERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --set global.tag&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;v&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CILIUM_VERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --set global.containerRuntime.integration&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;containerd&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --set global.containerRuntime.socketPath&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/var/run/k3s/containerd/containerd.sock&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --set global.kubeProxyReplacement&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;strict&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --set global.bpf.masquerade&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --set ingressController.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --set ingressController.default&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --namespace cilium &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --create-namespace
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note: pour le fun, j&amp;rsquo;ai aussi ajouté le support de l&amp;rsquo;ingressController de cilium. Comme ça c&amp;rsquo;est fait :D.&lt;/p&gt;
&lt;p&gt;Au bout de quelques secondes, notre cluster devrait devenir opérationnel. On a aussi metric server (pour les stats de base).&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 STATUS RESTARTS AGE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system cilium-operator-86dbc96dd6-hlgtt 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 56s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system cilium-operator-86dbc96dd6-h7sq6 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 56s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system cilium-pxrrz 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 56s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system cilium-psjr4 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 56s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system svclb-cilium-ingress-c27a13c6-x7tld 2/2 Running &lt;span class="m"&gt;0&lt;/span&gt; 32s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system local-path-provisioner-957fdf8bc-5w9n9 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 6m9s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system coredns-77ccd57875-zlvpc 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 6m9s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system svclb-cilium-ingress-c27a13c6-8dbf6 2/2 Running &lt;span class="m"&gt;0&lt;/span&gt; 34s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kube-system metrics-server-648b5df564-xr29p 1/1 Running &lt;span class="m"&gt;0&lt;/span&gt; 6m9s
&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;Voilà pour ce très court article sur k3s et cilium.&lt;/p&gt;
&lt;p&gt;Ce setup, rapide à installer sur une install fraîche est ma base pour beaucoup d&amp;rsquo;expérimentations avec Kubernetes.&lt;/p&gt;</description></item><item><title>Kubeadm, Ubuntu 22.04 et cilium - mes petites galères récentes</title><link>https://blog.zwindler.fr/2023/06/06/kubeadm-ubuntu-cilium-mes-petites-galeres/</link><pubDate>Tue, 06 Jun 2023 12:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2023/06/06/kubeadm-ubuntu-cilium-mes-petites-galeres/</guid><description>&lt;img src="https://blog.zwindler.fr/2017/06/kubernetes2.webp" alt="Featured image of post Kubeadm, Ubuntu 22.04 et cilium - mes petites galères récentes" /&gt;&lt;h2 id="kubernetes-le-retour-de-la-vengeance"&gt;Kubernetes, le retour de la vengeance
&lt;/h2&gt;&lt;p&gt;J&amp;rsquo;ai été pas mal pris ces derniers temps, autant côté pro que côté perso, et j&amp;rsquo;ai un peu délaissé mes clusters Kubernetes (qui vivent leur vie).&lt;/p&gt;
&lt;p&gt;Aussi, quand j&amp;rsquo;ai eu le temps de m&amp;rsquo;y remettre, j&amp;rsquo;ai perdu pas mal de temps sur plusieurs &amp;ldquo;petites&amp;rdquo; bêtises liés, la plupart du temps à des modifications introduites dans les composants de ma stacks.&lt;/p&gt;
&lt;p&gt;Une preuve (si c&amp;rsquo;était nécessaire) qu&amp;rsquo;il est important de se maintenir à la page, à jour et surtout de bien lire les changelogs.&lt;/p&gt;
&lt;p&gt;Voici donc la liste des soucis que j&amp;rsquo;ai rencontrés et de comment on les corrige / contourne.&lt;/p&gt;
&lt;h2 id="cilium-et-kernel-ubuntu-minimal"&gt;cilium et kernel Ubuntu Minimal
&lt;/h2&gt;&lt;p&gt;L&amp;rsquo;an dernier, j&amp;rsquo;ai décidé de concevoir un nouveau talk inspiré de &amp;ldquo;dessine moi un cluster&amp;rdquo; de Jérôme Petazzoni. Dans ce talk dont j&amp;rsquo;ai déjà parlé ici, je construis en live coding un &amp;ldquo;cluster&amp;rdquo; Kubernetes brique par brique, binaire par binaire.&lt;/p&gt;
&lt;p&gt;Et pour le CNI, j&amp;rsquo;avais choisi Cilium que j&amp;rsquo;utilise par ailleurs et que j&amp;rsquo;aime bien.&lt;/p&gt;
&lt;p&gt;Sauf que 2 jours avant de monter sur scène, j&amp;rsquo;ai changé d&amp;rsquo;hébergeur un peu en urgence (problème de disponibilité) et patatra, le talk ne marchait plus. Cilium avait l&amp;rsquo;air de s&amp;rsquo;installer correctement mais les pods finissaient par crasher, rendant toute communication avec les Pods / Services de mon cluster impossible&amp;hellip;&lt;/p&gt;
&lt;p&gt;En creusant un peu, je suis tombé sur ces logs dans le container de l&amp;rsquo;agent cilium sur mon node :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;level=warning msg=&amp;#34;+ tc qdisc replace dev cilium_vxlan clsact&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;RTNETLINK answers: Operation not supported&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;+ true&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;++ grep -v &amp;#39;pref 1 bpf chain 0 $\\|pref 1 bpf chain 0 handle 0x1&amp;#39;&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;++ tc filter show dev cilium_vxlan ingress&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;RTNETLINK answers: Operation not supported&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;Dump terminated&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;+ &amp;#39;[&amp;#39; -z &amp;#39;&amp;#39; &amp;#39;]&amp;#39;&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;+ cilium bpf migrate-maps -s bpf_overlay.o&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;+ tc filter replace dev cilium_vxlan ingress prio 1 handle 1 bpf da obj bpf_overlay.o sec from-overlay&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;RTNETLINK answers: Operation not supported&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;We have an error talking to the kernel&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;+ cilium bpf migrate-maps -e bpf_overlay.o -r 1&amp;#34; subsys=datapath-loader
level=warning msg=&amp;#34;+ return 1&amp;#34; subsys=datapath-loader
level=fatal msg=&amp;#34;Error while creating daemon&amp;#34; error=&amp;#34;error while initializing daemon: failed while reinitializing datapath: exit status 1&amp;#34; subsys=daemon
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et le message important là dedans, c&amp;rsquo;est &lt;code&gt;RTNETLINK answers: Operation not supported&lt;/code&gt;, qui m&amp;rsquo;a permis de trouver &lt;a class="link" href="https://github.com/cilium/cilium/issues/20858#issuecomment-1212024027" target="_blank" rel="noopener"
&gt;cette issue sur le dépôt github de cilium&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Lorsque j&amp;rsquo;avais changé d&amp;rsquo;hébergeur, je n&amp;rsquo;avais pas fait attention mais les machines Ubuntu livrées l&amp;rsquo;étaient avec une install &amp;ldquo;minimal&amp;rdquo; sur laquelle il manque des capabilities, en particulier &lt;code&gt;CONFIG_NET_CLS_ACT=y&lt;/code&gt; qui est un des prérequis de cilium.&lt;/p&gt;
&lt;p&gt;Cependant, l&amp;rsquo;installation échoue silencieusement (ou alors c&amp;rsquo;est discret). Un gros warning aurait été le bienvenu&amp;hellip;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;erreur disparaît quand on change de kernel ou d&amp;rsquo;image de base.&lt;/p&gt;
&lt;h2 id="containerd-et-cgroups-systemd"&gt;containerd et cgroups systemd
&lt;/h2&gt;&lt;p&gt;En essayant de créer un nouveau cluster Kubernetes avec &lt;code&gt;kubeadm&lt;/code&gt; sur des machines Ubuntu 22.04, je me suis retrouvé assez vite avec un problème bizarre et très vite bloquant : les noeuds de mon cluster etcd tombaient les uns à la suite des autres, provoquant on crash du cluster.&lt;/p&gt;
&lt;p&gt;Alors que tout semble ok, le container s&amp;rsquo;arrête avec un laconique &lt;code&gt;received signal; shutting down&lt;/code&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mi"&gt;2022-02-05&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;42.582666628&lt;/span&gt;&lt;span class="err"&gt;Z&lt;/span&gt; &lt;span class="err"&gt;stderr&lt;/span&gt; &lt;span class="err"&gt;F&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;level&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;info&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;ts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2022-02-05T00:46:42.582Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;caller&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;embed/serve.go:188&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;serving client traffic securely&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;address&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;172.31.58.179:2379&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="mi"&gt;2022-02-05&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;42.582757689&lt;/span&gt;&lt;span class="err"&gt;Z&lt;/span&gt; &lt;span class="err"&gt;stderr&lt;/span&gt; &lt;span class="err"&gt;F&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;level&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;info&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;ts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2022-02-05T00:46:42.582Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;caller&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;etcdmain/main.go:47&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;notifying init daemon&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="mi"&gt;2022-02-05&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;42.58276643&lt;/span&gt;&lt;span class="err"&gt;Z&lt;/span&gt; &lt;span class="err"&gt;stderr&lt;/span&gt; &lt;span class="err"&gt;F&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;level&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;info&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;ts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2022-02-05T00:46:42.582Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;caller&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;etcdmain/main.go:53&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;successfully notified init daemon&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="mi"&gt;2022-02-05&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;42.582906352&lt;/span&gt;&lt;span class="err"&gt;Z&lt;/span&gt; &lt;span class="err"&gt;stderr&lt;/span&gt; &lt;span class="err"&gt;F&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;level&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;info&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;ts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2022-02-05T00:46:42.582Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;caller&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;embed/serve.go:188&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;serving client traffic securely&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;address&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;127.0.0.1:2379&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="mi"&gt;2022-02-05&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="mf"&gt;2.302978572&lt;/span&gt;&lt;span class="err"&gt;Z&lt;/span&gt; &lt;span class="err"&gt;stderr&lt;/span&gt; &lt;span class="err"&gt;F&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;level&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;info&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;ts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2022-02-05T00:56:02.302Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;caller&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;osutil/interrupt_unix.go:64&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;received signal; shutting down&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;signal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;terminated&amp;#34;&lt;/span&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;En réalité, il s&amp;rsquo;agit d&amp;rsquo;une modification d&amp;rsquo;un paramètre de &lt;code&gt;containerd&lt;/code&gt; que je n&amp;rsquo;avais jamais eu besoin de modifier lors de mes installations précédentes mais qui semble nécessaire maintenant.&lt;/p&gt;
&lt;p&gt;En effet, quand on génère la configuration par défaut de container avec la commande &lt;code&gt;containerd config default&lt;/code&gt;, la valeur SystemdCgroup est à &lt;strong&gt;false&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai perdu un peu de temps mais heureusement je suis assez vite tombé sur &lt;a class="link" href="https://github.com/etcd-io/etcd/issues/13670#issuecomment-1337727102" target="_blank" rel="noopener"
&gt;cette issue&lt;/a&gt;, ainsi que sur &lt;a class="link" href="https://kubernetes.io/docs/setup/production-environment/container-runtimes/#containerd-systemd" target="_blank" rel="noopener"
&gt;la documentation officielle qui indique bien la bonne configuration à faire&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="kubeadm-et-plusieurs-interfaces"&gt;&lt;code&gt;kubeadm&lt;/code&gt; et plusieurs interfaces
&lt;/h2&gt;&lt;p&gt;Jusqu&amp;rsquo;à présent, je n&amp;rsquo;avais travaillé qu&amp;rsquo;avec des nodes qui n&amp;rsquo;avaient qu&amp;rsquo;une seule IP principale. Récemment, j&amp;rsquo;ai dû configurer &lt;code&gt;kubeadm&lt;/code&gt; sur des machines avec plusieurs IPs/VLAN.&lt;/p&gt;
&lt;p&gt;Lorsque j&amp;rsquo;ai commencé à essayer de configurer kubeadm pour qu&amp;rsquo;il en tienne compte, je me suis emmêlé les pinceaux&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;avais bien remarqué la présence d&amp;rsquo;un flag &lt;code&gt;--api-advertise&lt;/code&gt; dans la commande &lt;code&gt;kubeadm init&lt;/code&gt; mais je ne pouvais pas utiliser ce flag car j&amp;rsquo;utilise des fichiers de configuration &lt;em&gt;kubeadmcfg&lt;/em&gt; de manière à pouvoir customiser de manière plus poussée mes clusters, et l&amp;rsquo;usage de flags conjointement avec le &lt;code&gt;--config&lt;/code&gt; n&amp;rsquo;est pas supporté&lt;/p&gt;
&lt;p&gt;Mon erreur avait été d&amp;rsquo;insérer un le bloc &lt;strong&gt;localAPIEndpoint&lt;/strong&gt; (l&amp;rsquo;équivalent du &lt;code&gt;--api-advertise&lt;/code&gt; mais dans la config) dans la &lt;strong&gt;ClusterConfiguration&lt;/strong&gt; alors qu&amp;rsquo;il faut créer un autre manifest dans le même fichier avec pour kind &lt;strong&gt;InitConfiguration&lt;/strong&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="nn"&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="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;kubeadm.k8s.io/v1beta3&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;InitConfiguration&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;localAPIEndpoint&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;advertiseAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10.0.0.4&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;bindPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6443&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="nn"&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="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;kubeadm.k8s.io/v1beta3&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;ClusterConfiguration&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;controlPlaneEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10.10.10.10&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="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;...]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cette valeur qui a priori semble ne concerner que l&amp;rsquo;API server override en fait tous les composants dans votre control plane (etcd, scheduler, controller manager) donc pas besoin de les configurer en plus.&lt;/p&gt;
&lt;p&gt;A noter, pour tout ce qui est utilisation d&amp;rsquo;une Virtual IP qui &lt;em&gt;loadbalancerait&lt;/em&gt; les requêtes vers vos APIservers, c&amp;rsquo;est bien &lt;strong&gt;controlPlaneEndpoint&lt;/strong&gt; qu&amp;rsquo;il faut utiliser.&lt;/p&gt;
&lt;h2 id="et-alors-kubelet-et-plusieurs-interfaces-"&gt;Et alors, &lt;code&gt;kubelet&lt;/code&gt; et plusieurs interfaces ?
&lt;/h2&gt;&lt;p&gt;Si jamais vous voulez modifier les IPs des composants du control plane, il est probable que vous voudrez aussi modifier celle du &lt;code&gt;kubelet&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pour modifier l&amp;rsquo;&lt;em&gt;InternalIP&lt;/em&gt; de vos nodes, il est nécessaire de modifier les paramètres dans le &lt;code&gt;kubelet.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A partir de là, vous avez deux possibilités. La plus simple si vous avez plusieurs hostnames pour chaque IP, est d&amp;rsquo;utiliser le paramètre &lt;code&gt;--hostname-override&lt;/code&gt; qui va permettre d&amp;rsquo;à la fois changer le hostname par défaut de la machine quand elle s&amp;rsquo;enregistre dans Kubernetes, mais aussi L&amp;rsquo;InternalIP.&lt;/p&gt;
&lt;p&gt;Dans mon cas, ça ne m&amp;rsquo;aidait pas car l&amp;rsquo;IP qui m&amp;rsquo;intéressait n&amp;rsquo;avait pas de résolution DNS et je ne voulais pas modifier le hostname tel qu&amp;rsquo;il était.&lt;/p&gt;
&lt;p&gt;Pour modifier l&amp;rsquo;interface sur laquelle &lt;code&gt;kubelet&lt;/code&gt; écoute et l&amp;rsquo;InternalIP, il est nécessaire de modifier 2 paramètres : &lt;code&gt;--address&lt;/code&gt; (pour l&amp;rsquo;interface d&amp;rsquo;écoute) et &lt;code&gt;--node-ip&lt;/code&gt; (modifier l&amp;rsquo;InternalIP)&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 ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zwindler-01 Ready control-plane 28m v1.24.14 10.0.0.4 &amp;lt;none&amp;gt; Ubuntu 22.04.2 LTS 5.19.0-42-generic containerd://1.6.21
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zwindler-02 Ready control-plane 28m v1.24.14 10.0.0.5 &amp;lt;none&amp;gt; Ubuntu 22.04.2 LTS 5.19.0-42-generic containerd://1.6.21
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zwindler-03 Ready control-plane 27m v1.24.14 10.0.0.6 &amp;lt;none&amp;gt; Ubuntu 22.04.2 LTS 5.19.0-42-generic containerd://1.6.21
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zwindler-04 Ready node 27m v1.24.14 10.0.0.7 &amp;lt;none&amp;gt; Ubuntu 22.04.2 LTS 5.19.0-42-generic containerd://1.6.21
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zwindler-05 Ready node 27m v1.24.14 10.0.0.8 &amp;lt;none&amp;gt; Ubuntu 22.04.2 LTS 5.19.0-42-generic containerd://1.6.21
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="ubuntu-2204-et-manageforeignroutes"&gt;Ubuntu 22.04 et ManageForeignRoutes
&lt;/h2&gt;&lt;p&gt;Je ne me suis pas le seul à s&amp;rsquo;être mangé de plein fouet cette modification de systemd sur les Ubuntu récents, puisque cette modification est la &amp;ldquo;rootcause&amp;rdquo; de l&amp;rsquo;incident global de mars de Datadog (vous pouvez aller &lt;a class="link" href="https://www.datadoghq.com/blog/engineering/2023-03-08-deep-dive-into-platform-level-impact/" target="_blank" rel="noopener"
&gt;lire le postmortem ici&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Quand j&amp;rsquo;installais cilium sur des serveurs Ubuntu 22.04, instantanément, il n&amp;rsquo;était plus possible de ping et/ou contacter les IPs locale de la machine, mais aucun problème pour contacter le reste du monde.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
^C
--- 10.0.0.1 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 4076ms
$ ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=63 time=0.285 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=63 time=0.233 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=63 time=0.248 ms
$ traceroute 10.0.0.1
traceroute to 10.0.0.1 (10.0.0.1), 30 hops max, 60 byte packets
1 * * *
2 zwindler-01 (10.0.0.1) 0.084 ms 0.079 ms 0.060 ms
3 * * *
4 zwindler-01 (10.0.0.1) 0.075 ms 0.097 ms 0.081 ms
5 * * *
6 zwindler-01 (10.0.0.1) 0.121 ms 0.137 ms 0.145 ms
7 * * *
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Je montre avec ping, mais c&amp;rsquo;était pareil avec tous les ports. Et donc, toutes les liveness des composants du control plane crashaient puisque je ne pouvais plus contacter les IPs locales !&lt;/p&gt;
&lt;p&gt;En réalité, si on creuse les &lt;a class="link" href="https://docs.cilium.io/en/v1.13/operations/system_requirements/#systemd-based-distributions" target="_blank" rel="noopener"
&gt;system requirements de cilium&lt;/a&gt;, c&amp;rsquo;est écrit !&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/06/systemd-networkd.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Par défaut, systemd-networkd supprime toutes les routes qu&amp;rsquo;il ne connaît pas et rentre en conflit direct avec cilium, d&amp;rsquo;où les problématiques de routage cheloues.&lt;/p&gt;
&lt;p&gt;Et c&amp;rsquo;est exactement le même problème qu&amp;rsquo;a rencontré Datadog, puisqu&amp;rsquo;ils utilisent eux aussi Ubuntu 22.04 et cilium. Quand les updates sont passées, ils se sont retrouvés avec tous leurs clusters (worldwide) en vrac.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;on start-up of v248, systemd-networkd flushes all IP rules it does not know about. Five months after this initial change, an additional commit introduced in v249 made it possible to opt out of this behavior using a new &lt;code&gt;ManageForeignRoutingPolicyRules&lt;/code&gt; setting. Both these changes were backported to systemd v248 and v247.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Kubecon Europe 2021 – Récap’ du jeudi</title><link>https://blog.zwindler.fr/2021/05/06/kubecon-europe-2021-recap-du-jeudi/</link><pubDate>Thu, 06 May 2021 17:00:00 +0000</pubDate><guid>https://blog.zwindler.fr/2021/05/06/kubecon-europe-2021-recap-du-jeudi/</guid><description>&lt;img src="https://blog.zwindler.fr/2021/05/old.webp" alt="Featured image of post Kubecon Europe 2021 – Récap’ du jeudi" /&gt;&lt;h2 id="deuxième-jour-de-kubecon"&gt;&lt;strong&gt;Deuxième jour de Kubecon&lt;/strong&gt;
&lt;/h2&gt;&lt;p&gt;Hier, je vous faisais un &lt;a class="link" href="https://blog.zwindler.fr/2021/05/05/kubecon-europe-2021-recap-du-mercredi/" &gt;petit résumé de la première journée de Kubecon !&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Si vous ne l’avez pas lu, je vous conseille de commencer par là, car je ne vais pas refaire la petite intro avec le contexte.&lt;/p&gt;
&lt;p&gt;Aujourd’hui, les talks ont été très condensés et j’ai fait un gros focus réseau / sécu, comme vous pourrez le voir dans la suite de l’article :)&lt;/p&gt;
&lt;h2 id="keynotes"&gt;Keynotes
&lt;/h2&gt;&lt;p&gt;Aujourd’hui, c’était moins dense en termes de nombre de keynotes.&lt;/p&gt;
&lt;p&gt;La première keynote a été réalisée par Stephen Augustus, Co-Chair &amp;amp; Head of Open Source chez Cisco. Il a donné quelques nouvelles sur le projet Kubernetes. Le plus gros morceau est le fait qu’il ait été acté que la release cycle allait passer à 15 semaines, ce qui fait 3 releases par an maintenant au lieu de 4. Il a également parlé du fait que les SIGs doivent opt-in les modifications qu’ils veulent inclure dans chaque release et a aussi acté le retour des &lt;a class="link" href="https://github.com/kubernetes/community/blob/master/events/community-meeting.md" target="_blank" rel="noopener"
&gt;community meetings&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Les deux talks qui ont suivi étaient sans intérêt, avec un talk sponsorisé de Veeam très très basique sur la sécurité et un talk sur Linkerd qui sauve le monde du COVID-19, que j’ai trouvé plus que déplacé. Je ne me suis pas privé pour le dire sur Twitter d’ailleurs (lol).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/old.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Le talk suivant (sponsorisé aussi) rattrapait un peu le niveau, avec un focus sur &lt;a class="link" href="https://knative.dev/" target="_blank" rel="noopener"
&gt;Knative&lt;/a&gt; réalisé par Brenda Chan de chez VMware. Je n’ai pas encore regardé en détail ce qu’on pouvait faire avec knative et ce talk m’a donné envie de creuser le sujet.&lt;/p&gt;
&lt;p&gt;La dernière keynote était assez intéressante, avec un REX sur l’intégration des projets cloud native par les équipes de Deutsche Telekom. Après avoir présenté les problèmes auxquels ses équipes ont du faire face, Vuk Gojnic a présenté &lt;a class="link" href="https://github.com/telekom/das-schiff" target="_blank" rel="noopener"
&gt;Das schiff&lt;/a&gt;, la solution qu’ils ont implémenté pour déployer des clusters Kubernetes distribués (as a service).&lt;/p&gt;
&lt;h2 id="what-do-you-mean-k8s-doesnt-have-users-how-do-i-manage-user-access-then"&gt;&lt;strong&gt;What Do You Mean K8s Doesn’t Have Users? How Do I Manage User Access Then?&lt;/strong&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://kccnceu2021.sched.com/event/iE4h/what-do-you-mean-k8s-doesnt-have-users-how-do-i-manage-user-access-then-jussi-nummelin-mirantis-inc" target="_blank" rel="noopener"
&gt;Page de la session&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://static.sched.com/hosted_files/kccnceu2021/2a/KubeConEU2021_UserMgmt.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La journée a commencé plutôt soft avec un talk assez généraliste sur l’AuthN/AuthZ pr Jussi Nummelin de chez Mirantis. Il a pointé du doigt un vrai problème dans Kubernetes : il n’y a pas de gestion des utilisateurs humains a proprement parlé.&lt;/p&gt;
&lt;p&gt;C’est d’ailleurs écrit noir sur blanc dans la documentation officielle de Kubernetes !&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is assumed that a cluster-independent service manages normal users
&amp;ndash;&lt;a class="link" href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/" target="_blank" rel="noopener"
&gt;kubernetes.io/docs/reference/access-authn-authz/authentication/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Tout ce qui est authentification dans Kubernetes passe soit par certificat x509, soit par des tokens de service account soit par des webhook. Tout ceci est donc plutôt réservé aux pods, comptes de services et pas du tout adapté pour des personnes en chair et en os.&lt;/p&gt;
&lt;p&gt;Je ne parlerai même pas de l’authentification ‘static token file’ qui est un réel scandale qui ne devrait même pas être utilisée en lab.&lt;/p&gt;
&lt;p&gt;La seule option viable est donc bien l’implémentation d’une authentification tierce via un connecter OIDC comme Dex, par exemple.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/dex-horizontal-color.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/dexidp/dex" target="_blank" rel="noopener"
&gt;github.com/dexidp/dex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="falcomg-thats-awesome---new-things-fixed-things-and-you-panel"&gt;&lt;strong&gt;FalcOMG That’s AWESOME - New Things, Fixed Things, and YOU Panel&lt;/strong&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://kccnceu2021.sched.com/event/iE69/falcomg-thats-awesome-new-things-fixed-things-and-you-panel-leo-didonato-leonardo-grasso-sysdig-rajakavitha-kodhandapani-linode-thomas-labarussias-qonto?iframe=no" target="_blank" rel="noopener"
&gt;Page de la session&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[Slides](&lt;a class="link" href="https://static.sched.com/hosted_files/kccnceu2021/a1/FalcOMG" target="_blank" rel="noopener"
&gt;https://static.sched.com/hosted_files/kccnceu2021/a1/FalcOMG&lt;/a&gt; That)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cette session avait pour but de présenter tout un tas de choses à propos du &lt;a class="link" href="https://falco.org/" target="_blank" rel="noopener"
&gt;projet Falco&lt;/a&gt;. Pour rappel, Falco est un cloud-native security runtime, initié par Sysdig en 2016. Il utilise eBPF (encore lui) pour détecter voire empêcher les workloads suspects sur vos machines Linux (pas que Kubernetes donc). Les dernières parties qui manquaient ont été données par sysdig en février 2021 (kernel modules + ebpf probe + runtime security).&lt;/p&gt;
&lt;p&gt;Après une longue introduction du projet (donné à la CNCF en 2018, accepté en tant qu’Incubating en 2020), deux mainteneurs (Leonardo Grasso et Leonardo Di Donato) ont présentés le processus de release ainsi que la façon dont les artefacts (cf falco open infra) sont générés automatiquement (&lt;a class="link" href="https://download.falco.org/?prefix=driver/ae104eb20ff0198a5dcb0c91cc36c86e7c3f25c7/" target="_blank" rel="noopener"
&gt;plus de 3500&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;L’équipe de falco est également très impliquée dans l’accès à cette technologie pour tous et recherche activement des traducteurs pour traduire le site et la documentation de falco dans le plus de langues possible. Le processus d’internationalisation a été présenté par Radhika Puthiyetath, technical writer chez sysdig. C’était assez intéressant de voir que le processus mis en place était assez simple et donc accessible au plus grand nombre.&lt;/p&gt;
&lt;p&gt;Enfin, l’outil &lt;a class="link" href="https://github.com/falcosecurity/falcosidekick" target="_blank" rel="noopener"
&gt;falco sidekick&lt;/a&gt; a été présenté par Thomas Labarussias, SRE chez Qonto. Il s’agit d’un démon permettant de collecter l’ensemble des événements transmis par falco (présent sur tous vos nodes kube par exemple) et de les forwarder à une 30aines d’outputs différents en fonction de certaines règles.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/falcosidekick_color.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;On peut ainsi mettre en place de l’alerting ou loguer les événements dans des SIEM ou des moteurs de recherche (Elasticsearch) pour traitements ultérieurs par exemple.&lt;/p&gt;
&lt;h2 id="uncovering-a-sophisticated-kubernetes-attack-in-real-time"&gt;&lt;strong&gt;Uncovering a Sophisticated Kubernetes Attack in Real-Time&lt;/strong&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://kccnceu2021.sched.com/event/iE2u/uncovering-a-sophisticated-kubernetes-attack-in-real-time-jed-salazar-natalia-reka-ivanko-isovalent?iframe=no" target="_blank" rel="noopener"
&gt;Page de la session&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://static.sched.com/hosted_files/kccnceu2021/3e/UncoveringASophisticatedAttackInRealTime_JedSalazar_NataliaRekaIvanko_06052021.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Troisième talk de la journée sur la sécurité !!! Natália Réka Ivánkó (Security Engineer chez Isovalent) et Jed Salazar (Manager, Platform Security chez Tesla) nous on présenté un panel assez large de risques de sécurité et de vecteurs d’attaques accessibles depuis un cluster Kubernetes.&lt;/p&gt;
&lt;p&gt;Après avoir balayé très rapidement les solutions classiques pour réduire les risques (Distroless base images, OPA, pas de shells pour réduire la surface d’attaques, analyse statique des images et analyse au runtime), ils ont cherché à comprendre ce qu’on pouvait faire de plus.&lt;/p&gt;
&lt;p&gt;On peut aussi appliquer à la sécurité le mindset « devops » (on parle parfois de devsecops) en considérant que ce qu’on a mis en place en termes de sécurité doit être (1) testé et (2) observé.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/trust_but_verify.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Et selon eux, le meilleur outil pour le faire, c’est eBPF. Ils nous ont donc ensuite présenté plusieurs scenarii d’attaques dans lesquels &lt;a class="link" href="https://cilium.io/" target="_blank" rel="noopener"
&gt;Cilium&lt;/a&gt; (eBPF-based Networking, Observability, and Security) était utilisé pour connecter, surveiller et sécuriser les événements sur le réseau de kubernetes.&lt;/p&gt;
&lt;p&gt;Le talk était riche et dense et il est extrêmement dur pour moi de le résumer ici. Je serais probablement amené à en reparler sur le blog tant le sujet est pléthorique.&lt;/p&gt;
&lt;h2 id="how-to-break-your-kubernetes-cluster-with-networking"&gt;&lt;strong&gt;How to Break your Kubernetes Cluster with Networking&lt;/strong&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://kccnceu2021.sched.com/event/iE5i/how-to-break-your-kubernetes-cluster-with-networking-thomas-graf-isovalent?iframe=no" target="_blank" rel="noopener"
&gt;Page de la session&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://static.sched.com/hosted_files/kccnceu2021/d0/Kubecon%20EU%2021%20-%20Breaking%20Kubernetes%20with%20Networking.pdf" target="_blank" rel="noopener"
&gt;Slides&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J’ai vraiment adoré le talk précédent mais alors celui là c’est le meilleur que j’ai vu depuis le début de cet Kubecon. Thomas Graf, en plus d’être un excellent pédagogue est un excellent speaker. C’est le CTO d’Isovalent (tiens tiens tiens, encore eux !) et il est core member du projet Cilium (encore lui !).&lt;/p&gt;
&lt;p&gt;Là aussi, le talk était extrêmement dense et Thomas Graf s’est employé à brosser toutes les façons de casser son cluster Kubernetes avec le réseau. Cela va de bête erreurs DNS à l’utilisation de kube-proxy + iptables en passant par des CRD watchers qui DDoS votre API server et en finissant par des histoires d’horreur de changement de CNI (ou double run) qui cassent tout le réseau du cluster (kids, don’t do this at home).&lt;/p&gt;
&lt;p&gt;Une fois de plus, le talk était tellement dense qu’il est difficile de le résumer. Ce qu’on peut en retenir simplement, c’est que le mieux est encore de rester le plus simple possible dans la limite de vos besoins et de ne pas ajouter trop de couches juste parce qu’elles sont « shiny ». (Et je suis évidemment à 100% d’accord).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Oooooooh! Shiny ones!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="les-talks-que-jaurai-voulu-voir"&gt;&lt;strong&gt;Les talks que j’aurai voulu voir&lt;/strong&gt;
&lt;/h2&gt;&lt;p&gt;Je l’ai déjà dit hier, choisir c’est renoncer.&lt;/p&gt;
&lt;p&gt;Si j’avais pu, je serai également allé voir &lt;strong&gt;BuildKit CLI for kubectl: A New Way to Build Container Images&lt;/strong&gt; par curiosité pour l’outil, que je ne connais pas. Mais les dieux du scheduling en ont voulu autrement (c’était pendant la présentation sur Falco).&lt;/p&gt;
&lt;h2 id="fin-de-la-journée"&gt;&lt;strong&gt;Fin de la journée&lt;/strong&gt;
&lt;/h2&gt;&lt;p&gt;Si on peut appeler ça la fin de la journée puisque le dernier talk a fini vers 15h ;-) mais en vrai c’était tellement intense il y avait pas mal à débriefer.&lt;/p&gt;
&lt;p&gt;Maintenant qu’on est au 2/3 de la Kubecon, je vois 2 sujets prioritaires qu’il va falloir que j’investigue sérieusement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la partie eBPF, avec un vrai choix technique côté CNI (test de Cilium à faire absolument) et évaluation des outils de sécus basés sur des programmes BPF&lt;/li&gt;
&lt;li&gt;la partie CI/CD avec flux, flagger, litmus (mais plutôt réservée pour mes copains ingés CI/CD, je ne ferai que suivre le mouvement)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On verra si la journée de demain changera un peu ces priorité ou pas (probablement que non).&lt;/p&gt;
&lt;p&gt;Et en attendant, have fun :)&lt;/p&gt;</description></item></channel></rss>