La plupart des équipes Kubernetes déboguent leur plateforme au seul endroit où elle ne tourne pas : leur ordinateur portable. La production est à 82 % en Kubernetes (enquête annuelle Cloud Native de la CNCF, 2025, contre 66 % en 2023), et pourtant la boucle de dev locale reste souvent un fichier Docker Compose qui, discrètement, diverge du vrai cluster sur les namespaces, le RBAC et la découverte de services. Nous avons reconstruit notre boucle interne autour d’un vrai cluster Kubernetes local piloté par Tilt, et cela a changé la vitesse — et l’honnêteté — de nos itérations sur un backend multi-services. Voici le montage, le motif, et le bug qu’il a attrapé et qu’un mock n’aurait jamais révélé.

À retenir

  • Faites tourner la vraie cible de déploiement en local : un vrai cluster Kubernetes sur Kind reproduit namespaces, RBAC et découverte de services que Docker Compose ne fait qu’approximer.
  • Le Live Update de Tilt synchronise le code dans un conteneur en cours d’exécution « en secondes, pas en minutes » (tilt.dev, 2026), ce qui rend viable un substrat local plus lourd.
  • Livrez un Tiltfile autonome par service, exécutable seul ou composable dans un Tiltfile racine, pour qu’un nouvel arrivant démarre juste le service qu’il touche.
  • Testez l’auth contre la vraie stack d’identité avec curl, pas un mock — cela a révélé un bug de configuration cliente qu’une auth mockée aurait masqué.
  • Oubliez Bitnami : ses images gratuites et stables ont été dépréciées en 2025, donc s’y épingler, c’est du pourrissement silencieux.

Pourquoi un vrai cluster en local plutôt que Docker Compose ?

Parce que la plateforme que vous livrez est Kubernetes, et Kubernetes a des surfaces que Docker Compose ne peut pas simuler. Namespaces, bindings RBAC, service accounts, découverte de services par DNS et contrôle d’admission sont tous natifs dans un cluster et tout simplement absents de Compose. Quand ils cassent, une boucle locale basée sur Compose reste au vert et le bug ne se révèle qu’après déploiement — l’endroit le plus coûteux pour le trouver.

Kubernetes est désormais le substrat de production par défaut : la CNCF (2025) situe l’usage en production à 82 % et l’adoption cloud-native à 98 %. Si c’est votre cible, la parité local/prod n’est pas un luxe ; c’est la différence entre une boucle interne rapide et une boucle externe lente, dictée par les déploiements. La même discipline de platform engineering qui standardise la production mérite d’être appliquée au poste de travail.

Le compromis, honnêtement : un vrai cluster est plus lourd que docker compose up. Il vous faut un cluster local rapide et un outil qui garde le cycle édition-exécution serré malgré ce substrat supplémentaire. C’est exactement la brèche que comblent Kind et Tilt.

Qu’est-ce que Tilt, et comment corrige-t-il la boucle interne Kubernetes ?

Tilt est une boîte à outils open source « pour soigner les douleurs du développement microservices » (tilt-dev/tilt, 9,9k étoiles GitHub, v0.37.5, 2026). Il surveille vos fichiers, reconstruit les images de conteneurs et met votre cluster à jour automatiquement — transformant une séquence manuelle build-push-deploy en un seul tilt up qui reste vivant pendant que vous éditez.

La fonctionnalité qui justifie son existence, c’est le Live Update. Plutôt que de reconstruire une image et de déployer un nouveau pod à chaque sauvegarde, Live Update synchronise les fichiers modifiés directement dans le conteneur en cours d’exécution et relance l’étape de build sur place. La formule de Tilt : déployer le code dans les conteneurs « en secondes, pas en minutes » (tilt.dev, 2026). Pour un langage compilé comme Go, cela signifie que les fichiers .go atterrissent dans le pod et que le binaire y recompile — sans aller-retour d’image.

Le Tiltfile lui-même est écrit en Starlark, un dialecte de Python : il dispose donc de vraies fonctions, boucles et conditionnelles, plutôt que de templating YAML. Cela compte dès que votre environnement local dépasse un service et quelques dépendances partagées à câbler ensemble.

Comment amorcer un cluster local avec ctlptl et Kind ?

Commencez par un cluster qui se crée lui-même. Nous utilisons Kind — « des clusters Kubernetes locaux avec des nœuds en conteneurs Docker » (kubernetes-sigs/kind, 15,4k étoiles, un installeur certifié conforme CNCF, 2026) — épaulé par ctlptl pour rattacher un registre d’images local. Un petit script le rend idempotent, créant le cluster uniquement s’il n’y en a pas déjà un :

#!/usr/bin/env bash
# scripts/ensure-cluster.sh — cluster local + registre, idempotent
set -euo pipefail

CLUSTER_NAME="klocal"

if ctlptl get cluster "kind-${CLUSTER_NAME}" >/dev/null 2>&1; then
  echo "cluster kind-${CLUSTER_NAME} déjà actif — réutilisation"
  exit 0
fi

# ctlptl crée le cluster Kind ET un registre local où Tilt peut pousser
ctlptl create registry ctlptl-registry --port=5005
ctlptl create cluster kind --name="${CLUSTER_NAME}" --registry=ctlptl-registry

Câbler cela comme un local_resource Tilt fait qu’un contributeur lance tilt up et obtient un cluster, un registre et son service — sans README rempli de commandes prérequises à lancer à la main d’abord.

Piège : Laissez ctlptl posséder à la fois le cluster et le registre. Si vous créez un cluster Kind sans registre où Tilt puisse pousser, chaque build retombe sur kind load, bien plus lent qu’une image poussée, ce qui ruine l’intérêt d’une boucle serrée.

Pourquoi un Tiltfile par service est-il le bon motif ?

Chaque service livre son propre Tiltfile autonome, conçu pour tourner seul ou être intégré plus tard dans un Tiltfile racine via le include() de Tilt. C’est le cœur réutilisable de tout le montage, et il repose sur trois règles.

D’abord, l’indépendance des chemins : chaque chemin du Tiltfile est résolu par rapport au répertoire du Tiltfile lui-même, si bien qu’il se comporte à l’identique qu’il soit lancé depuis la racine du service ou inclus depuis un parent. Ensuite, un interrupteur de dépendances : un booléen regroupe l’infrastructure partagée — Postgres, une stack d’identité, une gateway — pour qu’un futur Tiltfile racine les fournisse une seule fois au lieu que chaque service en démarre une copie. Enfin, le mode autonome par défaut : le tilt up de base dans n’importe quel répertoire de service amène un environnement fonctionnel pour ce service seul.

# Tiltfile — autonome, incluable, indépendant du chemin
config.define_bool('skip_deps')  # un Tiltfile racine le met à True pour gérer les deps partagées
cfg = config.parse()

# s'assurer qu'un cluster existe avant toute chose
local_resource('cluster', cmd='./scripts/ensure-cluster.sh',
               labels=['00-bootstrap'])

if not cfg.get('skip_deps', False):
    k8s_yaml('deploy/postgres.dev.yaml')     # YAML upstream simple, pas Bitnami
    k8s_yaml('deploy/ory.dev.yaml')          # stack d'identité pour l'auth locale

docker_build('local/klastro-api', '.',
             dockerfile='Dockerfile.dev')
k8s_yaml('deploy/api.dev.yaml')

Le bénéfice, c’est l’onboarding : un nouveau contributeur qui touche un service le clone, lance tilt up, et devient productif sans monter toute la plateforme. Quand il a besoin de la stack complète, un Tiltfile racine fait include() de chaque enfant et met skip_deps=True pour que les dépendances partagées ne montent qu’une fois. Cela reflète la structure d’un monorepo à sous-modules sans coupler les services entre eux.

Comment le Live Update garde-t-il la boucle Go rapide ?

En synchronisant la source et en recompilant dans le pod, au lieu de reconstruire l’image. L’étape docker_build déclare un bloc live_update qui copie les fichiers .go modifiés dans le conteneur et y lance la compilation, si bien que le cycle sauvegarde-vers-binaire-actif passe d’un build d’image complet à quelques secondes.

docker_build(
    'local/klastro-api', '.',
    dockerfile='Dockerfile.dev',
    live_update=[
        sync('.', '/app'),                      # pousse la source modifiée dans le pod
        run('go build -o /app/server ./cmd/api',# recompile sur place
            trigger=['./**/*.go']),
    ],
)

# Rediriger Delve pour attacher un débogueur au process dans le cluster
k8s_resource('klastro-api', port_forwards=['8080:8080', '2345:2345'])

Rediriger Delve sur 2345 permet d’attacher le débogueur de votre IDE à un process qui tourne dans le cluster, contre le vrai DNS des services et la vraie config — pas un binaire lié localement qui court-circuite la moitié de la plateforme. Vous obtenez des points d’arrêt sur le chemin de code qui s’exécutera réellement en production. Quand la panne que vous traquez est non déterministe — le genre que nous chassons dans la fraîcheur du cache des contrôleurs — déboguer contre le vrai substrat fait la différence entre reproduire et deviner.

Pourquoi éviter les images et charts Bitnami ?

Parce que le catalogue Bitnami gratuit et stable disparaît. Au 28 août 2025, Bitnami a déplacé ses images Docker Hub versionnées vers un dépôt bitnamilegacy qui ne reçoit plus aucune mise à jour, correctif ni support, le catalogue public étant réduit à un sous-ensemble communautaire « dernière version seulement » (bitnami/charts #35164, 2025). La suppression de l’ancien catalogue a été repoussée à fin septembre 2025 pour laisser aux équipes le temps de migrer (Northflank, 2025).

Pour un environnement local, épingler Postgres ou une stack d’identité sur un tag Bitnami signifie désormais l’un de deux modes d’échec : une image legacy figée qui cesse silencieusement d’être patchée, ou une image « dernière version seulement » qui bouge sous vos pieds entre deux rebuilds. Aucun n’est reproductible. Nous déployons plutôt les dépendances partagées depuis du YAML upstream simple ou les charts des projets eux-mêmes, ce qui garde la stack locale épinglable et libre de toute dépendance à un abonnement.

Piège : Les charts et images Bitnami ont été la voie de moindre résistance pendant des années : ils sont ancrés dans d’innombrables tutoriels et dépôts de démarrage. Auditez vos manifestes deploy/ locaux à la recherche de références docker.io/bitnami/* avant qu’elles ne deviennent une image legacy qui ne se mettra plus jamais à jour.

Comment tester les flux d’auth avant même un frontend ?

Avec curl contre la vraie stack d’identité — et cela vaut la peine précisément parce que cela attrape des bugs qu’un mock ne peut pas voir. Avant qu’un quelconque frontend existe, nous exercions les endpoints protégés en générant un vrai jeton de session depuis le fournisseur d’identité et en le passant en en-tête X-Session-Token à travers la gateway, droit vers l’API :

# 1. Générer un vrai jeton de session depuis la stack d'identité (Ory Kratos)
SESSION_TOKEN=$(curl -s -X POST \
  "http://localhost:4433/self-service/login/api" \
  -H 'Content-Type: application/json' \
  -d '{"method":"password","identifier":"[email protected]","password":"…"}' \
  | jq -r '.session_token')     # ory_st_…

# 2. Appeler un endpoint protégé À TRAVERS la gateway avec ce jeton
curl -s "http://localhost:8080/v1/orgs" \
  -H "X-Session-Token: ${SESSION_TOKEN}" | jq

Lancer cela contre la vraie stack d’autorisation plutôt qu’un stub a révélé un vrai bug d’intégration : la bibliothèque cliente de contrôle des permissions résolvait l’adresse du service d’autorisation comme si le host:port avait besoin d’un schéma d’URL, et échouait la résolution. Un montage local à auth mockée aurait renvoyé un joyeux 200 et masqué le défaut jusqu’à un environnement partagé. Tester la vraie chaîne — la même raison pour laquelle nous prenons au sérieux les flux d’accès privilégié — a transformé un futur incident de production en correctif local de cinq minutes.

Combien ça coûte, et quand est-ce que ça vaut le coup ?

Le coût est réel : un cluster local consomme plus de RAM qu’un Compose, et la plomberie initiale Tiltfile-plus-ctlptl représente une après-midi de travail. La parité en vaut la peine quand votre cible de production est Kubernetes et que vos bugs vivent dans les surfaces de plateforme — auth, RBAC, découverte de services, admission — que Compose ne peut pas représenter. Pour un unique service sans état et sans comportement propre au cluster, Compose reste peut-être le bon choix, plus léger.

Le seuil de rentabilité arrive vite pour un backend multi-services. Dès que vous avez plus de deux services, des dépendances partagées et la moindre auth dans le chemin de requête, l’écart « ça marche sur ma machine » entre Compose et le cluster cesse d’être un désagrément et devient la source de vos incidents d’intégration. Un Tiltfile par service composable en une racine pour toute la stack donne à chaque ingénieur le plus petit environnement dont il a besoin, et le complet quand il le veut.

Conclusion

Le développement Kubernetes en local ne consiste pas à faire tourner toute la plateforme sur votre portable pour le plaisir — il s’agit de refermer l’écart entre l’endroit où vous écrivez le code et celui où il s’exécute. Tilt et Kind rendent un vrai cluster assez rapide pour être votre boucle interne, Live Update garde le cycle Go en secondes, et un Tiltfile par service, autonome mais incluable, ramène l’onboarding à un seul tilt up.

Deux habitudes portent l’essentiel de la valeur. Testez contre la vraie chose — vrai cluster, vraie stack d’identité, vrai curl — parce que les mocks masquent exactement les bugs d’intégration qui coûtent le plus cher ensuite. Et gardez vos dépendances épinglables et libres de dépréciations surprises, ce qui en 2025 veut dire tourner le dos au catalogue legacy de Bitnami. Investissez l’après-midi dans le Tiltfile ; vous rachetez la parité chaque jour qui suit.


Ismail Kaboubi dirige l’ingénierie cloud et plateforme chez Edixos.

Réponses directes

Questions fréquentes

Faut-il un vrai cluster Kubernetes local ou Docker Compose ?

Utilisez un vrai cluster quand votre cible de production est Kubernetes. Docker Compose approxime le réseau et ignore namespaces, RBAC et découverte de services : les bugs sur ces surfaces n'apparaissent qu'après déploiement. Un cluster Kind local reproduit le vrai substrat, au prix d'outils comme Tilt et ctlptl pour garder la boucle rapide.

Que fait réellement le Live Update de Tilt ?

Live Update synchronise les fichiers modifiés directement dans un conteneur en cours d'exécution et relance l'étape de build sur place, au lieu de reconstruire et redéployer toute l'image. Tilt parle de déployer le code dans les conteneurs en secondes, pas en minutes (tilt.dev, 2026). Pour Go, cela synchronise les .go et recompile le binaire dans le pod.

Pourquoi éviter les images et charts Bitnami en développement local ?

Bitnami a déprécié son catalogue Docker Hub gratuit et stable au 28 août 2025, déplaçant les images versionnées vers un dépôt bitnamilegacy qui ne reçoit plus de mises à jour (bitnami/charts, 2025). Épingler une stack locale sur ces images, c'est du pourrissement silencieux. Les charts upstream ou images éditeur restent reproductibles sans abonnement.

Peut-on tester les flux d'authentification en local sans frontend ?

Oui. Générez un jeton de session depuis votre fournisseur d'identité et passez-le en en-tête à travers votre gateway avec curl, en appelant directement les endpoints. Cela exerce la vraie stack d'auth plutôt qu'un mock, ce qui nous a permis de détecter un bug de bibliothèque cliente qu'un montage mocké aurait totalement masqué.