<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Managé on Zwindler's Reflection</title><link>https://blog.zwindler.fr/tags/manag%C3%A9/</link><description>Recent content in Managé on Zwindler's Reflection</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>Licensed under CC BY-SA 4.0</copyright><lastBuildDate>Sun, 24 May 2026 18:00:00 +0200</lastBuildDate><atom:link href="https://blog.zwindler.fr/tags/manag%C3%A9/index.xml" rel="self" type="application/rss+xml"/><item><title>Test de CKE, le Kubernetes managé chez Clever Cloud (partie 2 - stockage)</title><link>https://blog.zwindler.fr/2026/05/24/test-cke-kubernetes-manage-clever-cloud-partie-2/</link><pubDate>Sun, 24 May 2026 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2026/05/24/test-cke-kubernetes-manage-clever-cloud-partie-2/</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 2 - stockage)" /&gt;&lt;h2 id="rappel-de-la-partie-1"&gt;Rappel de la partie 1
&lt;/h2&gt;&lt;p&gt;Dans la &lt;a class="link" href="https://blog.zwindler.fr/2026/05/12/test-cke-kubernetes-manage-clever-cloud-partie-1/" &gt;partie 1&lt;/a&gt;, on avait vu comment créer un cluster CKE, parcouru son anatomie (Exherbo Linux, Cilium, Materia etcd, konnectivity&amp;hellip;), mesuré les temps de boot (~57s pour le control plane, excellent), testé les NodeGroups, et activé le CSI.&lt;/p&gt;
&lt;p&gt;Dans cette deuxième partie, on va creuser ce qui touche au &lt;strong&gt;stockage&lt;/strong&gt; et à des cas d&amp;rsquo;usages &amp;ldquo;un peu&amp;rdquo; avancés : cycle de vie des volumes, resize online, snapshots, et déploiement de vCluster avec données persistantes.&lt;/p&gt;
&lt;h2 id="activer-le-csi-et-monter-son-premier-volume"&gt;Activer le CSI et monter son premier volume
&lt;/h2&gt;&lt;p&gt;Comme on l&amp;rsquo;a vu en partie 1, le CSI n&amp;rsquo;est pas activé par défaut, mais un petit coup de CLI corrige ça rapidement :&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;Cette commande déploie le provisioner Ceph RBD et crée une StorageClass &lt;code&gt;csi-rbd-sc&lt;/code&gt; qui devient la classe 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;$ kubectl get sc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;csi-rbd-sc &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt; rbd.csi.ceph.com Delete Immediate &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On notera au passage &lt;code&gt;ALLOWVOLUMEEXPANSION: true&lt;/code&gt; : on y reviendra.&lt;/p&gt;
&lt;p&gt;Créons un PVC tout bête :&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;# pvc.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;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;PersistentVolumeClaim&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;mon-pvc&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;accessModes&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;ReadWriteOnce&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;resources&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;requests&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;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;1Gi&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;storageClassName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;csi-rbd-sc&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 pvc.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ kubectl get pvc mon-pvc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME STATUS VOLUME CAPACITY ACCESS MODES
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mon-pvc Bound pvc-2c2f058c-dc85-4006-865c-3437856462f5 1Gi RWO
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le binding prend environ 30 secondes (le temps que le provisioner crée l&amp;rsquo;image RBD dans le pool Ceph et l&amp;rsquo;attache). Rien de spécial, c&amp;rsquo;est du Kubernetes standard.&lt;/p&gt;
&lt;p&gt;On monte le volume dans un pod, on écrit un fichier, on supprime le pod, on le recrée : les données sont toujours là. Jusque là, pas de surprise.&lt;/p&gt;
&lt;h2 id="vcluster--un-cas-dusage-concret-du-stockage-persistant"&gt;vCluster : un cas d&amp;rsquo;usage concret du stockage persistant
&lt;/h2&gt;&lt;p&gt;Maintenant qu&amp;rsquo;on a du stockage persistant, on peut faire des choses intéressantes. Généralement mon troll préféré, c&amp;rsquo;est de déployer Wordpress parce que évidemment Kubernetes c&amp;rsquo;est la meilleure plateforme pour déployer un Wordpress (/s).&lt;/p&gt;
&lt;p&gt;Bon, cette fois ci j&amp;rsquo;ai décidé d&amp;rsquo;innover un peu et &lt;a class="link" href="https://github.com/loft-sh/vcluster" target="_blank" rel="noopener"
&gt;on va déployer &lt;strong&gt;vCluster&lt;/strong&gt;&lt;/a&gt; (un cluster Kubernetes virtuel qui tourne dans votre cluster).&lt;/p&gt;
&lt;p&gt;Note : à quoi ça sert, me direz vous ? Et bien, les usecases sont pas aussi clairs qu&amp;rsquo;on pourrait penser. vCluster vous donne un cluster Kubernetes complet, isolé, mais qui partage l&amp;rsquo;infrastructure du cluster hôte, y compris le CSI. En théorie ça permet de donner le cluster-admin à des gens sans donner les clés de l&amp;rsquo;infra complète à tout le monde. Je suis pas hyper convaincu par cette approche. Un usecase qui me parait moins dangereux, c&amp;rsquo;est pour des usages de type CI sans devoir monter un cluster complet, mais il y a des limitations aussi.&lt;/p&gt;
&lt;p&gt;Installation :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vcluster create test-vcluster -n vcluster-test
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Deux options pour s&amp;rsquo;y connecter :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option 1 —&amp;gt; port-forward&lt;/strong&gt; (le plus simple) :&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;$ vcluster connect test-vcluster -n vcluster-test -- kubectl get nodes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME STATUS ROLES AGE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;control-plane-c367382a-22b1-4cef-b0cd-372bf83263c4-node Ready &amp;lt;none&amp;gt; 2m
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;vcluster connect&lt;/code&gt; monte un tunnel port-forward via l&amp;rsquo;API server (comme &lt;code&gt;kubectl port-forward&lt;/code&gt;) et exécute la commande dans le contexte du vCluster. Ça marche sur CKE comme sur n&amp;rsquo;importe quel cluster Kubernetes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option 2 —&amp;gt; LoadBalancer&lt;/strong&gt; (pour un accès durable sans tunnel) :&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;vcluster delete test-vcluster -n vcluster-test
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vcluster create test-vcluster -n vcluster-test --expose --connect&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le flag &lt;code&gt;--expose&lt;/code&gt; crée un Service LoadBalancer. On récupère l&amp;rsquo;IP et on génère un kubeconfig autonome :&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;LB_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;kubectl get svc -n vcluster-test test-vcluster -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;{.status.loadBalancer.ingress[0].ip}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ vcluster connect test-vcluster -n vcluster-test &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --server https://&lt;span class="nv"&gt;$LB_IP&lt;/span&gt;:443 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --insecure --print &amp;gt; /tmp/kube-vcluster.config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le flag &lt;code&gt;--insecure&lt;/code&gt; évite les soucis de certificat auto-signé, avec les risques de sécu que ça implique. A vos risques et périls&amp;hellip; 💀&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 --kubeconfig /tmp/kube-vcluster.config get nodes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME STATUS ROLES AGE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;test-vcluster Ready &amp;lt;none&amp;gt; 2m
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ce node test-vcluster contient tout notre cluster virtuel. Maintenant, la partie intéressante : créer un PVC dans le vCluster :&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 --kubeconfig /tmp/kube-vcluster.config apply -f - &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: v1
&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: PersistentVolumeClaim
&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; name: test-pvc
&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; accessModes:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; - ReadWriteOnce
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; resources:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; requests:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; storage: 1Gi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; storageClassName: csi-rbd-sc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;C&amp;rsquo;est là où c&amp;rsquo;est sympa : le vCluster délègue la création du volume au cluster hôte, qui le provisionne via Ceph RBD. On a pas besoin de reconfigurer le CSI, les storage classes, les secrets, etc. C&amp;rsquo;est totalement transparent.&lt;/p&gt;
&lt;p&gt;Petite vérification côté CKE :&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 pvc -A &lt;span class="p"&gt;|&lt;/span&gt; grep test-vcluster
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vcluster-test pvc-&amp;lt;...&amp;gt; Bound 1Gi RWO csi-rbd-sc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le PVC est bien créé dans le namespace du vCluster sur le cluster hôte.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overhead mesuré :&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pod&lt;/th&gt;
&lt;th&gt;CPU&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;&lt;code&gt;test-vcluster-0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~80m&lt;/td&gt;
&lt;td&gt;~342 Mi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;coredns (vCluster)&lt;/td&gt;
&lt;td&gt;~2m&lt;/td&gt;
&lt;td&gt;~13 Mi&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;C&amp;rsquo;est tout à fait raisonnable. 342 MiB pour un control plane K8s complet, c&amp;rsquo;est dans les clous.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion vCluster&lt;/strong&gt; : ça marche, y compris avec de la persistance. Si vous avez besoin d&amp;rsquo;isoler des environnements tout en gardant accès au CSI, vCluster sur CKE est une option fonctionnelle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : il est aussi possible d&amp;rsquo;exposer le vCluster via un LoadBalancer (flag &lt;code&gt;--expose&lt;/code&gt;) pour un accès direct sans tunnel. Pratique si le vCluster doit rester accessible après la fermeture de votre terminal.&lt;/p&gt;
&lt;h2 id="volume-expansion--agrandir-un-volume-à-chaud"&gt;Volume expansion : agrandir un volume à chaud
&lt;/h2&gt;&lt;p&gt;Vous avez une base de données qui grossit, vous avez provisionné 1Gi au départ et maintenant c&amp;rsquo;est juste. Avec &lt;code&gt;allowVolumeExpansion: true&lt;/code&gt; sur la StorageClass, on peut agrandir le volume sans downtime.&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 patch pvc mon-pvc -p &lt;span class="s1"&gt;&amp;#39;{&amp;#34;spec&amp;#34;:{&amp;#34;resources&amp;#34;:{&amp;#34;requests&amp;#34;:{&amp;#34;storage&amp;#34;:&amp;#34;2Gi&amp;#34;}}}}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Côté Ceph, l&amp;rsquo;image RBD est redimensionnée immédiatement. Le PV passe à 2Gi :&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 pv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME CAPACITY ACCESS MODES STATUS CLAIM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pvc-&amp;lt;...&amp;gt; 2Gi RWO Bound default/mon-pvc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mais le PVC côté utilisateur reste à 1Gi et le filesystem XFS n&amp;rsquo;a pas encore été agrandi. Le kubelet finira par détecter le changement et déclencher le &lt;code&gt;NodeExpandVolume&lt;/code&gt; tout seul (la synchronisation est périodique), mais on peut accélérer en supprimant et recréant le pod qui monte le volume :&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 pod mon-app --force --grace-period&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ kubectl apply -f - &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: v1
&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: Pod
&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; name: mon-app
&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; containers:
&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: app
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; image: alpine
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; command: [&amp;#34;sleep&amp;#34;, &amp;#34;3600&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; volumeMounts:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; - mountPath: /data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; name: data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; volumes:
&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: data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; persistentVolumeClaim:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; claimName: mon-pvc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dès que le volume est monté, le kubelet détecte qu&amp;rsquo;il a grandi et appelle le driver CSI pour un &lt;code&gt;NodeExpandVolume&lt;/code&gt;. Le filesystem XFS est agrandi à chaud (&lt;code&gt;xfs_growfs&lt;/code&gt;) et le PVC passe effectivement à 2Gi :&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; mon-app -- df -h /data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Filesystem Size Used Avail Use% Mounted on
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/dev/rbd0 1.9G 47M 1.9G 1% /data
&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 pvc mon-pvc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME STATUS CAPACITY ACCESS MODES
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mon-pvc Bound 2Gi RWO
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="volumesnapshots--le-plus-technique"&gt;VolumeSnapshots : le plus technique
&lt;/h2&gt;&lt;p&gt;Dernière fonctionnalité, et pas la plus simple : les snapshots de volumes.&lt;/p&gt;
&lt;p&gt;Le driver Ceph RBD supporte les snapshots nativement. On le vérifie en regardant les logs du sidecar &lt;code&gt;csi-snapshotter&lt;/code&gt; au moment de la création :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ kubectl logs -n kube-system deployment/csi-rbdplugin-provisioner -c csi-snapshotter --tail=5
I0524 19:50:53.470970 snapshot_controller.go:339] createSnapshotWrapper: Creating snapshot for content snapcontent-&amp;lt;...&amp;gt; through the plugin ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pas d&amp;rsquo;erreur côté driver, la demande est acceptée et traitée.&lt;/p&gt;
&lt;p&gt;Mais sur CKE, rien n&amp;rsquo;est pré-installé pour utiliser les snapshots. Il faut assembler les briques soi-même :&lt;/p&gt;
&lt;h3 id="étape-1--installer-les-crds-et-le-snapshot-controller"&gt;Étape 1 : installer les CRDs et le snapshot-controller
&lt;/h3&gt;&lt;p&gt;Les CRDs &lt;code&gt;volumesnapshot*&lt;/code&gt; ne sont pas déployées sur CKE. Il faut les installer depuis le projet upstream &lt;a class="link" href="https://github.com/kubernetes-csi/external-snapshotter" target="_blank" rel="noopener"
&gt;external-snapshotter&lt;/a&gt;, ainsi que le &lt;strong&gt;snapshot-controller&lt;/strong&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v8.4.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# CRDs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/&lt;span class="nv"&gt;$VERSION&lt;/span&gt;/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/&lt;span class="nv"&gt;$VERSION&lt;/span&gt;/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/&lt;span class="nv"&gt;$VERSION&lt;/span&gt;/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Snapshot-controller (RBAC + deployment)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/&lt;span class="nv"&gt;$VERSION&lt;/span&gt;/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/&lt;span class="nv"&gt;$VERSION&lt;/span&gt;/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Deux composants travaillent ensemble :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le &lt;strong&gt;snapshot-controller&lt;/strong&gt; (standalone) écoute les objets &lt;code&gt;VolumeSnapshot&lt;/code&gt; et crée les &lt;code&gt;VolumeSnapshotContent&lt;/code&gt; correspondants&lt;/li&gt;
&lt;li&gt;Le &lt;strong&gt;csi-snapshotter&lt;/strong&gt; (sidecar dans le provisioner) détecte les &lt;code&gt;VolumeSnapshotContent&lt;/code&gt; et appelle le driver Ceph RBD pour créer/supprimer les snapshots&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les deux sont nécessaires.&lt;/p&gt;
&lt;h3 id="étape-2--le-piège-des-groupsnapshot-crds"&gt;Étape 2 : le piège des GroupSnapshot CRDs
&lt;/h3&gt;&lt;p&gt;Une fois les CRDs installées, vous créez votre VolumeSnapshotClass, votre VolumeSnapshot, et&amp;hellip; rien ne se passe. Le VolumeSnapshotContent reste &lt;code&gt;readyToUse: false&lt;/code&gt; indéfiniment.&lt;/p&gt;
&lt;p&gt;Si vous regardez les logs du sidecar &lt;code&gt;csi-snapshotter&lt;/code&gt; dans le provisioner, vous ne voyez &lt;strong&gt;aucune&lt;/strong&gt; tentative de création de snapshot.&lt;/p&gt;
&lt;p&gt;Pourquoi ? Parce que le sidecar est lancé avec un feature gate activé :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;--feature-gates=CSIVolumeGroupSnapshot=true
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ce feature gate active le support des &lt;strong&gt;VolumeGroupSnapshot&lt;/strong&gt; (la possibilité de snapshotter plusieurs volumes en une fois). Mais le sidecar attend les CRDs correspondantes pour initialiser son cache. Sans ces CRDs, le cache sync est bloqué, et le controller ne traite &lt;strong&gt;aucun&lt;/strong&gt; VolumeSnapshot, pas même les snapshots unitaires (normaux, pas &amp;ldquo;group&amp;rdquo; quoi).&lt;/p&gt;
&lt;p&gt;La solution : installer les CRDs &lt;code&gt;groupsnapshot*&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 apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/&lt;span class="nv"&gt;$VERSION&lt;/span&gt;/client/config/crd/groupsnapshot.storage.k8s.io_volumegroupsnapshotclasses.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/&lt;span class="nv"&gt;$VERSION&lt;/span&gt;/client/config/crd/groupsnapshot.storage.k8s.io_volumegroupsnapshotcontents.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/&lt;span class="nv"&gt;$VERSION&lt;/span&gt;/client/config/crd/groupsnapshot.storage.k8s.io_volumegroupsnapshots.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Puis redémarrer le provisioner pour que le sidecar recharge sa config. Attention : le provisioner a une &lt;strong&gt;anti-affinity&lt;/strong&gt; qui empêche deux pods de cohabiter sur le même node. Sur un cluster ALL_IN_ONE (1 node), ça bloque le rollout. Il faut supprimer directement l&amp;rsquo;ancien pod :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl delete pod -n kube-system -l &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;csi-rbdplugin-provisioner --force
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ce problème est spécifique aux clusters ALL_IN_ONE. Sur DEDICATED_COMPUTE ou DISTRIBUTED (multi-nodes), l&amp;rsquo;anti-affinity ne sera probablement pas bloquante.&lt;/p&gt;
&lt;h3 id="étape-3--le-secret-ceph-dans-le-volumesnapshotclass"&gt;Étape 3 : le secret Ceph dans le VolumeSnapshotClass
&lt;/h3&gt;&lt;p&gt;Une fois le sidecar relancé, les logs montrent&amp;hellip; une erreur :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;rpc error: code = Internal desc = provided secret is empty
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Le driver Ceph RBD a besoin des credentials Ceph pour créer un snapshot, exactement comme pour créer un volume. Vous les trouverez dans le secret &lt;code&gt;csi-rbd-secret&lt;/code&gt; du namespace &lt;code&gt;kube-system&lt;/code&gt;. La StorageClass les référence déjà pour le provisionning :&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;parameters&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;csi.storage.k8s.io/provisioner-secret-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;csi-rbd-secret&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;csi.storage.k8s.io/provisioner-secret-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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mais &lt;strong&gt;ces paramètres ne sont pas reportés automatiquement dans le VolumeSnapshotClass&lt;/strong&gt;. Il faut les ajouter manuellement :&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;snapshot.storage.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;VolumeSnapshotClass&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;csi-rbd-snapclass&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;driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbd.csi.ceph.com&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;deletionPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Delete&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;parameters&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;clusterID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;&amp;lt;votre-cluster-id&amp;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;csi.storage.k8s.io/snapshotter-secret-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;csi-rbd-secret&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;csi.storage.k8s.io/snapshotter-secret-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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le &lt;code&gt;clusterID&lt;/code&gt; est celui de la StorageClass (visible dans &lt;code&gt;kubectl get sc csi-rbd-sc -o yaml&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="étape-4--le-snapshot-fonctionne"&gt;Étape 4 : le snapshot fonctionne
&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 apply -f - &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: snapshot.storage.k8s.io/v1
&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: VolumeSnapshot
&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; name: mon-snapshot
&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; volumeSnapshotClassName: csi-rbd-snapclass
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; source:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; persistentVolumeClaimName: mon-pvc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;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 volumesnapshot mon-snapshot
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME READYTOUSE SOURCEPVC RESTORESIZE AGE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mon-snapshot &lt;span class="nb"&gt;true&lt;/span&gt; mon-pvc 1Gi 6s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;READYTOUSE: true&lt;/code&gt; immédiatement. Le snapshot Ceph RBD est créé en quelques secondes.&lt;/p&gt;
&lt;h3 id="étape-5--restaurer-un-volume-depuis-un-snapshot"&gt;Étape 5 : restaurer un volume depuis un snapshot
&lt;/h3&gt;&lt;p&gt;Pour être VRAIMENT sûr que ça fonctionne, on peut faire un test (et oui, ça fonctionne)&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;PersistentVolumeClaim&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;pvc-restored&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;accessModes&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;ReadWriteOnce&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;resources&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;requests&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;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;1Gi&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;dataSource&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;mon-snapshot&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;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;VolumeSnapshot&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;apiGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;snapshot.storage.k8s.io&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;storageClassName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;csi-rbd-sc&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 pvc-restored.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ kubectl get pvc pvc-restored
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME STATUS VOLUME CAPACITY
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pvc-restored Bound pvc-... 1Gi
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="bilan-global"&gt;Bilan global
&lt;/h2&gt;&lt;p&gt;Deuxième partie, deuxième bilan. Qu&amp;rsquo;est-ce qu&amp;rsquo;on retient ?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les points forts :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CSI Ceph RBD&lt;/strong&gt; : là encore, un bon choix. Ceph, ça fonctionne bien dans Kubernetes. Provisionning, persistence, resize online — tout fonctionne et ce n&amp;rsquo;est pas une surprise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vCluster + CSI&lt;/strong&gt; : pour faire plaisir à Akanoa, j&amp;rsquo;ai fait du kubeception (Kubernetes in Kubernetes) et ça m&amp;rsquo;a permis de tester une feature de vCluster que j&amp;rsquo;avais jamais essayée =&amp;gt; la délégation transparente du CSI vers le CSI de l&amp;rsquo;hôte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Les points d&amp;rsquo;attention :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les snapshots ne sont pas &amp;ldquo;out of the box&amp;rdquo; : CRDs manquantes, feature gate inattendu, secret à recopier. Je ferai un petit email aux copain⋅es parce que ça me parait dommage de pas l&amp;rsquo;ajouter&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Encore merci à l&amp;rsquo;équipe Clever Cloud pour m&amp;rsquo;avoir donné accès en avant-première et pour avoir pris le temps d&amp;rsquo;échanger sur mes retours. Je ne sais pas s&amp;rsquo;il y aura un 3ème article ou non. Wait and see ;-P&lt;/p&gt;</description></item><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>Test de SKS, le Kubernetes managé chez Exoscale !</title><link>https://blog.zwindler.fr/2021/05/03/test-de-sks-le-kubernetes-manage-chez-exoscale/</link><pubDate>Mon, 03 May 2021 06:20:00 +0000</pubDate><guid>https://blog.zwindler.fr/2021/05/03/test-de-sks-le-kubernetes-manage-chez-exoscale/</guid><description>&lt;img src="https://blog.zwindler.fr/2021/05/exoscale_sks_features.webp" alt="Featured image of post Test de SKS, le Kubernetes managé chez Exoscale !" /&gt;&lt;h2 id="kewa--exoscale-a-un-kubernetes-managé-"&gt;Kewa ? Exoscale a un Kubernetes Managé ?
&lt;/h2&gt;&lt;p&gt;Lorsque &lt;a class="link" href="https://bsky.app/profile/mcorbin.bsky.social" target="_blank" rel="noopener"
&gt;Mathieu Corbin&lt;/a&gt; m’a parlé du Kubernetes managé qu’ils étaient en train de monter chez Exoscale, je lui ai tout de suite proposé de me le montrer, pour que je lui donne mon point de vue.&lt;/p&gt;
&lt;p&gt;Sans être un expert absolu des offres Kubernetes en SaaS, j’en ai quand même testé quelques uns, notamment &lt;a class="link" href="https://blog.zwindler.fr/2018/12/18/jai-teste-pour-vous-aks-la-plateforme-kubernetes-managee-dazure/" &gt;AKS d’Azure&lt;/a&gt;, &lt;a class="link" href="https://blog.zwindler.fr/2019/07/09/jai-teste-pour-vous-loffre-kubernetes-as-a-service-dovh/" &gt;OVH &lt;/a&gt;et quelques autres (GCP, Kapsule, … pour lesquels je n’ai pas fait d’article).&lt;/p&gt;
&lt;p&gt;Et puis, bon, Kubernetes, c’est un peu mon métier maintenant ;-).&lt;/p&gt;
&lt;p&gt;Au moment où j’ai rédigé le premier brouillon de cet article, on était encore en bêta fermée. Il n’y avait pas d’interface graphique dans le portail (uniquement la CLI), certains paramètres étaient pas totalement dans les règles de l’art (problèmes connus chez eux), …&lt;/p&gt;
&lt;p&gt;Et puis le temps a passé et je n’ai pas sorti l’article (comme souvent par manque de temps) avant que la solution passe GA.&lt;/p&gt;
&lt;p&gt;Heureusement, le passage en GA c’est aussi une bonne occasion pour moi de refaire le test et de comparer le boulot qui a été accompli (en peu de temps) pour gommer les quelques défauts de jeunesse que la solution pouvait encore avoir.&lt;/p&gt;
&lt;h2 id="mais-dabord-exoscale-"&gt;Mais d’abord, Exoscale ?
&lt;/h2&gt;&lt;p&gt;J’vais pas mentir, même si j’avais déjà croisé le nom d’Exoscale plusieurs fois, je n’avais jamais pris le temps de tester leur offre. Pour ceux qui l’ignorent donc, &lt;a class="link" href="https://www.exoscale.com/" target="_blank" rel="noopener"
&gt;Exoscale est un cloud provider&lt;/a&gt; avec plusieurs points de présence en Europe.&lt;/p&gt;
&lt;p&gt;Niveau taille de l’acteur, on est pas du tout comparables à un OVH (ou même Scaleway), mais ils sont suffisamment nombreux pour se permettre d’avoir une team dédiée à la release d’un Kubernetes managé. Tout en gardant suffisamment de bande passante pour quand même avoir une API ouverte et documentée, comme les grands.&lt;/p&gt;
&lt;h2 id="et-donc-sks-"&gt;Et donc, SKS ?
&lt;/h2&gt;&lt;p&gt;Cette micro introduction d’Exoscale passée, revenons donc à nos nuages.&lt;/p&gt;
&lt;p&gt;L’offre Kubernetes managée d’Exoscale s’appelle SKS (pour Scalable Kubernetes Service).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/exoscale_sks_features.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;On nous promet donc sur le site d’Exoscale un service scalable (horizontalement), qui démarre normalement en 90 secondes.&lt;/p&gt;
&lt;p&gt;La gestion du cycle de vie du control plane est entièrement gérée, on peut le déployer via CLI, API, depuis le portail et &lt;a class="link" href="https://registry.terraform.io/providers/exoscale/exoscale/latest/docs/resources/sks_cluster" target="_blank" rel="noopener"
&gt;il existe aussi un provider terraform&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Enfin, Exoscale a développé un composant permettant de faciliter l’intégration de son load balancer managé dans SKS (pour l’instant).&lt;/p&gt;
&lt;p&gt;Et comme je vous l’avais promis, &lt;a class="link" href="https://openapi-v2.exoscale.com/#endpoint-sks" target="_blank" rel="noopener"
&gt;l’API de SKS est documentée ici&lt;/a&gt; si jamais vous aimez faire des cURL.&lt;/p&gt;
&lt;h2 id="et-ça-coute-cher-"&gt;Et ça coute cher ?
&lt;/h2&gt;&lt;p&gt;Dans les offres managées de Kubernetes, il y a plusieurs clans. Ceux qui proposent un SLA sur le control plane et ceux qui n’en proposent pas. Et il y a ceux qui font payer le control plane et ceux qui ne le font pas payer.&lt;/p&gt;
&lt;p&gt;Souvent, ceux qui ne font pas payer le control plane sont également les mêmes qui ne proposent pas de SLA sur le control plane.&lt;/p&gt;
&lt;p&gt;Et pour avoir discuté avec un commercial de chez Azure en fait il y a une certaine logique à ne pas proposer de SLA sur un control plane gratuit. Un SLA étant un contrat, difficile de s’engager sur un service gratuit (et encore plus, difficile de vous rembourser ce que vous ne payer pas en cas de problème).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/sub-buzz-8705-1503572294-5.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Chez Exoscale, c’est fromage ou dessert, vous avez le choix. SKS est offert en 2 offres distinctes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une payante, avec SLA (99,95%)&lt;/li&gt;
&lt;li&gt;l’autre gratuite, sans SLA&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Classiquement, les workers sont quant à eux facturés au même prix que n’importe quelle machine IaaS chez Exoscale.&lt;/p&gt;
&lt;p&gt;Petite subtilité qui m’a fait sourire, là où les clouds providers se battaient sur qui bill à l’unité de temps la plus petite il y a 10 ans (d’abord l’heure, puis progressivement à la minute), chez Exoscale, vous êtes billé à la seconde. Difficile de faire plus précis.&lt;/p&gt;
&lt;p&gt;Pour tout ce qui concerne le pricing, je vous laisse &lt;a class="link" href="https://www.exoscale.com/pricing/" target="_blank" rel="noopener"
&gt;aller voir ici&lt;/a&gt;. N’ayant aucun intérêt financier à vous pousser vers Exoscale (ou OVH, ou Azure), je ne vais pas insister plus que ça ;-).&lt;/p&gt;
&lt;h2 id="bon-on-teste-"&gt;Bon, on teste ?
&lt;/h2&gt;&lt;p&gt;Maintenant qu’on a fait le tour de la question, et si on essayait un peu de voir ce que ça donne ?&lt;/p&gt;
&lt;p&gt;La première chose à faire est de se créer un compte sur &lt;a class="link" href="http://www.exoscale.com" target="_blank" rel="noopener"
&gt;www.exoscale.com&lt;/a&gt; et aussi télécharger la dernière version de la CLI.&lt;/p&gt;
&lt;p&gt;J’insiste bien sur la « dernière version » car pendant la bêta j’avais téléchargé la mauvaise version et l’API et les paramètres avaient changé de manière significative en peu de temps.&lt;/p&gt;
&lt;p&gt;A l’heure où j’écris l’article, la dernière version est la 1.28 (1.23 quand j’ai testé la bêta en début d’année) mais les releases sont dispos ici : &lt;a class="link" href="https://github.com/exoscale/cli/releases" target="_blank" rel="noopener"
&gt;github.com/exoscale/cli/releases&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;wget https://github.com/exoscale/cli/releases/download/v1.28.0/exoscale-cli_1.28.0_linux_amd64.tar.gz
tar xzf exoscale-cli_1.28.0_linux_amd64.tar.gz
➜ ./exo
Manage your Exoscale infrastructure easily
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A noter, il existe aussi pour les distributions linux des packages (.deb, .rpm, etc).&lt;/p&gt;
&lt;h2 id="configuration-du-compte"&gt;Configuration du compte
&lt;/h2&gt;&lt;p&gt;Maintenant qu’on a notre CLI sur notre poste, on va aller dans le portail créer une clé d’API qui va nous servir à nous authentifier.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://community.exoscale.com/documentation/tools/exoscale-command-line-interface/" target="_blank" rel="noopener"
&gt;La documentation officielle est disponible ici.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/exo_api_key_create.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Une fois que vous aurez cliqué sur « Create », l’ID de la clé ainsi que la clé apparaitra. Attention, comme toujours avec ce genre de mécanisme, la clé ne sera visible que cette fois ci. Sauvegardez là donc bien précieusement (sinon au pire il faudra en générer une nouvelle).
&lt;img src="https://blog.zwindler.fr/2021/05/exo_key_3.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;On peut ensuite configurer notre CLI&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;./exo config
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/exo_config.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Et on teste que la connexion marche bien avec la commande permettant de lister les versions de Kubernetes disponibles sur SKS&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;./exo sks versions
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/exo_cli_sks_versions.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Bon, je sais pas vous mais moi a me donne le sourire de voir que fin avril, Exoscale a déjà la 1.21 dispo :-)&lt;/p&gt;
&lt;h2 id="création-du-security-group"&gt;Création du security group
&lt;/h2&gt;&lt;p&gt;Exoscale dispose, comme tous les cloud providers, d’un système de firewalling qui permet d’appliquer des règles de sécurité à nos futures machines.&lt;/p&gt;
&lt;p&gt;Même si la création d’un groupe de sécurité n’est pas obligatoire pour instancier notre cluster SKS, c’est mieux si on le fait dès le début.&lt;/p&gt;
&lt;p&gt;Tout peut se faire via la CLI ou via le portail, comme pour le reste chez Exoscale&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;./exo firewall create sks-zwindler-sg
./exo firewall add sks-zwindler-sg -d &amp;#34;NodePort services&amp;#34; -p tcp -P 30000-32767
./exo firewall add sks-zwindler-sg -d &amp;#34;SKS Logs&amp;#34; -p tcp -P 10250
./exo firewall add sks-zwindler-sg -d &amp;#34;Calico traffic&amp;#34; -p udp -P 4789 -s sks-zwindler-sg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Si vous préférez le faie sur le portail, ça ressemblera à ça :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/sks_sg_calico.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;J’ai juste repris les valeurs par défaut dans la doc &lt;a class="link" href="https://community.exoscale.com/documentation/sks/quick-start/" target="_blank" rel="noopener"
&gt;de Quick Start&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="créer-un-cluster"&gt;Créer un cluster
&lt;/h2&gt;&lt;p&gt;La commande &lt;code&gt;exo sks create&lt;/code&gt; permet de créer notre cluster. On peut rajouter des flags pour modifier certains paramètres :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;exo sks create -h
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/exo_sks_usage.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Au delà des options pour choisir la taille du cluster et le type de node, les options intéressantes sont les 3 « &amp;ndash;no- » qui vous permettent de désactiver l’installation automatique de :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la CNI (Calico par défaut). Calico par défaut est un bon choix mais si vous préférez Cillium ou kube-router (ou flannel… nan j’déconne !).&lt;/li&gt;
&lt;li&gt;la CCM (Cloud Controller Manager), composant développé en interne par Exoscale et qui permettra à Kube d’interagir avec les autres services d’Exoscale (notamment le loadbalancer)&lt;/li&gt;
&lt;li&gt;metrics-server mais là je vois pas pourquoi vous ne voudriez pas metrics-server …&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;./exo sks create sks-zwindler --description &amp;#34;Test SKS cluster&amp;#34; --nodepool-name &amp;#34;sks-zwindler-pool&amp;#34; --nodepool-size 3 --nodepool-security-group &amp;#34;sks-zwindler-sg&amp;#34; --zone de-fra-1 --service-level pro
&amp;amp;#x2714; Creating SKS cluster &amp;#34;sks-zwindler&amp;#34;... 1m57s
&amp;amp;#x2714; Adding Nodepool &amp;#34;sks-zwindler-pool&amp;#34;... 3s
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Je ne sais pas si je n’ai pas eu de chance car il me semblait que ça allait un peu plus vite que les deux minutes que j’affiche dans cet extrait de shell. Mais 2 minutes c’est déjà plus que respectable quand on sait que chez Azure, AKS met plus de 20 minutes à poper (quand ça ne plante pas), sans compter les VMs (qui elles aussi mettent parfois 10 minutes avant d’être disponibles)&amp;hellip;&lt;/p&gt;
&lt;h2 id="kubeconfig"&gt;kubeconfig
&lt;/h2&gt;&lt;p&gt;Disponible… ok … Mais comment on s’y connecte en fait ?&lt;/p&gt;
&lt;p&gt;Pas de panique, la CLI permet de générer le kubeconfig qui va vous permettre de vous connecter à l’API server ! (ouf)&lt;/p&gt;
&lt;p&gt;Un point intéressant de l’implémentation d’Exoscale est que vous pouvez tuner un peu le kubeconfig qu’il va vous générer (là où la plupart du temps, c’est un kubeconfig admin, point). Vous pouvez notamment choisir le groupe RBAC ainsi qu’une durée de vie.&lt;/p&gt;
&lt;p&gt;Dans un environnement de production avec beaucoup d’utilisateurs, on préfèrera surement ajouter une authentification tierce (coucou OIDC), cependant, c’est quand même « nice to have ».&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;./exo sks kubeconfig sks-zwindler kube-admin --group system:masters --zone de-fra-1 &amp;gt; sks-zwindler-30d.kubeconfig
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;L’authentification se fait donc par certificat avec une durée que vous pouvez configurer en secondes (par défaut 30 jours). Bon, comme je vois que ce que je crois, j’ai fait le test avec 60 secondes et a priori ça marche ;-).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;./exo sks kubeconfig sks-zwindler kube-admin --group system:masters --zone de-fra-1 --ttl 60 &amp;gt; test-60s.kubeconfig
kubectl --kubeconfig test-60s.kubeconfig get nodes
NAME STATUS ROLES AGE VERSION
pool-afa3f-apzqy Ready &amp;lt;none&amp;gt; 6h v1.21.0
pool-afa3f-iehif Ready &amp;lt;none&amp;gt; 6h v1.21.0
pool-afa3f-ysvki Ready &amp;lt;none&amp;gt; 6h v1.21.0
#plus tard
kubectl --kubeconfig test-60s.kubeconfig get nodes
error: You must be logged in to the server (Unauthorized)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="déployer-une-application-disponible-sur-internet"&gt;Déployer une application disponible sur Internet
&lt;/h2&gt;&lt;p&gt;Comme la CCM nous permet de faire communiquer notre Kubernetes et Exoscale, on va en profiter pour créer un service Kubernetes de type Loadbalancer.&lt;/p&gt;
&lt;p&gt;Ce service Loadbalancer va commander à Exoscale un NLB chez eux, ce qui va nous permettre de router le trafic Internet directement dans notre cluster (il vaudrait mieux passer par un IngressController brancher sur ce service Loadbalancer dans la vraie vie).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl --kubeconfig sks-zwindler-30d.kubeconfig create service loadbalancer toto --tcp=80:80
kubectl --kubeconfig sks-zwindler-30d.kubeconfig get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 &amp;lt;none&amp;gt; 443/TCP 8h
toto LoadBalancer 10.111.39.139 &amp;lt;pending&amp;gt; 80:30001/TCP 22s
kubectl --kubeconfig sks-zwindler-30d.kubeconfig get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 &amp;lt;none&amp;gt; 443/TCP 8h
toto LoadBalancer 10.111.39.139 89.145.161.242 80:30001/TCP 23s
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Puis on déploie un nginx de test avec un label similaire à celui créé par défaut par le service (app=toto)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl --kubeconfig sks-zwindler-30d.kubeconfig run --image nginx --labels app=toto toto
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Très rapidement, vous devriez avoir accès à votre pod via l’IP du NLB Exoscale&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/05/exoscale_nginx.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="nettoyage"&gt;Nettoyage
&lt;/h2&gt;&lt;p&gt;Si vous avez mis votre carte de crédit, n’oubliez de supprimer votre cluster de test avant de partir ;-)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;./exo sks delete sks-zwindler -n
[+] Are you sure you want to delete Nodepool &amp;#34;sks-zwindler-pool&amp;#34;? [yN]: y
&amp;amp;#x2714; Deleting Nodepool &amp;#34;sks-zwindler-pool&amp;#34;... 6s
[+] Are you sure you want to delete SKS cluster &amp;#34;sks-zwindler&amp;#34;? [yN]: y
&amp;amp;#x2714; Deleting SKS cluster &amp;#34;sks-zwindler&amp;#34;... 18s
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et faites pareil si vous avez un instancié un Loadbalancer pour connecter vos services dans Kubernetes avec l’extérieur (j’avais oublié… oups).&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;J’avais été impressionné par l’implémentation d’OVH de Kubernetes managé, mais je dois reconnaitre que je le suis encore plus par celui d’Exoscale.&lt;/p&gt;
&lt;p&gt;Le cluster (control plane) est créé TRES vite, mais surtout les VMs popent également extrêmement vite (quelques secondes versus 10 minutes chez Azure !!!). C’est vraiment chouette !&lt;/p&gt;
&lt;p&gt;De ce que j’ai vu (et pu échanger) des entrailles de SKS, tout me parait très sain et bien conçu (sécurité, best practices). Et pourtant, Exoscale donne quand même pas mal de libertés sur la customisation du cluster (CCM, CNI, configuration kubeconfig).&lt;/p&gt;
&lt;p&gt;Les petits bugs de jeunesses (quelques erreurs de configuration du kubelet, des erreurs cosmétiques dans les commands lines, le CCM à installer soit même, …) que j’avais pu remonter à Mathieu lors de mon test en janvier/février ont toutes été gommés en moins de 2 mois.&lt;/p&gt;
&lt;p&gt;Donc, selon moi, si vous cherchez un kube managé, SKS est une alternative viable.&lt;/p&gt;</description></item></channel></rss>