Précédemment, dans “GenAI et dev”
Dans mon article précédent, je vous parlais de mon retour d’expérience avec PodSweeper, un projet développé avec OpenCode et Claude Opus. Le bilan était nuancé : vitesse brute impressionnante, mais race conditions, gestion d’erreur laxiste, et nécessité constante de supervision humaine (entre autres déconvenues).
Aujourd’hui, je vous parle d’un deuxième projet, bien plus simple, né d’un vrai besoin en production. Et le constat est assez différent.
L’incident qui a tout déclenché
Vous connaissez peut-être cette situation : un pod en production, en état de fonctionnement, qui utilise un PVC en ReadWriteOnce. Vous avez besoin d’aller regarder le contenu de ce volume. Bonne pratique de production : le pod en question ne dispose pas de shell (on réduit la surface d’attaque). Pas de /bin/sh, pas de /bin/bash, rien.
Pas grave, on a kubectl debug pour ça, me direz-vous !
Ok, créons un conteneur éphémère dans le pod et… ah non. kubectl debug ne permet pas de monter les volumes du pod dans le conteneur éphémère. Il n’expose tout simplement pas l’option volumeMounts pour les conteneurs éphémères.
On pourrait couper le pod, ce qui permet de monter le PVC RWO dans un autre pod avec un shell, mais on n’a pas envie, c’est de la prod.
Mon collègue Maxime (encore lui !) me propose une méthode de contournement. La procédure manuelle, c’est :
- Créer un conteneur éphémère sur le pod avec
kubectl debug - Construire à la main un patch JSON pour ajouter des
volumeMountsau conteneur éphémère qu’on vient de créer - Dans un autre terminal, se brancher
au cul du camionsur l’API de kube en direct aveckubectl proxy - Appliquer le patch via un curl sur l’API Kubernetes
- Attendre que le conteneur soit prêt, s’attacher au conteneur
- Se dire qu’il y avait quand même plus simple comme métier que patcheur de container à coup de curl.
Pour ceux qui pensent que c’est jouable, “téma la gueule du patch” comme disent (disaient ?) les jeunes :
curl http://localhost:8001/api/v1/namespaces/<namespace>/pods/<pod>/ephemeralcontainers \
-X PATCH \
-H 'Content-Type: application/strategic-merge-patch+json' \
-d '{
"spec": {
"ephemeralContainers": [
{
"name": "debugger",
"image": "ubuntu",
"command": ["/bin/sh"],
"targetContainerName": "<target-container>",
"stdin": true,
"tty": true,
"volumeMounts": [
{
"name": "<volume-name>",
"mountPath": "/debug/<volume-name>"
}
]
}
]
}
}'
Si vous pensez que c’est error prone, vous avez raison. Et si vous pensez que c’est pénible à faire sous pression pendant un incident de prod, vous avez encore plus raison.
Side note
Les plus aguerri·es d’entre vous dans les versions récentes de Kubernetes ont peut être entendu parler du KEP-2590, qui introduit un (relativement) nouveau flag --subresource de kubectl. En effet, les containers éphémères créés par la commande kubectl debug ne sont pas des resources (un pod, un deployment, …) mais des subresources.
Jusqu’à cette version 1.33, il était littéralement impossible de réaliser des opérations sur les subresources avec kubectl, seulement les resources.
Kubernetes v1.33: Octarine apporte le support du flag –subresource, mais seulement pour 3 subresources pour l’instant status, scale and resize
- https://kubernetes.io/blog/2025/04/23/kubernetes-v1-33-release/#subresource-support-in-kubectl
- https://kubernetes.io/docs/reference/kubectl/conventions/#subresources
Pas de solution de ce côté-là non plus…
Le prompt de réunion
J’avais cette idée en tête depuis l’incident. Lundi matin, au début d’une réunion (pendant que les gens faisaient encore des blagues), j’ai lancé OpenCode avec Claude Opus et j’ai tapé un prompt qui décrivait :
- Le problème :
kubectl debugne monte pas les volumes PVC s’ils sont en RWO - La procédure manuelle que je faisais à la main (les étapes douloureuses décrites un peu plus haut)
- Ce que je voulais : un outil qui automatise tout ça
OpenCode + Opus m’ont posé 2 questions (et j’ai ajouté une consigne) :
- “Quel langage ?” → Go (je persiste)
- “CLI seule ou TUI ?” → Les deux (mode non-interactif pour le scripting, TUI pour l’usage quotidien)
- Et je lui ai demandé de toujours vérifier à chaque fois qu’il estime avoir fini de lancer le linter
golangci-lint(trauma de mon précédent test avec opencode)
Il est parti de lui-même sur l’idée d’une TUI avec Bubble Tea. Puis j’ai arrêté de regarder & j’ai suivi ma réunion.
~30 minutes plus tard, fin de la réunion, j’ai regardé le résultat. Le POC était fonctionnel.
Ce qu’Opus a produit en 30 minutes
Sans aucune intervention de ma part, le LLM a scaffoldé un projet Go complet :
- Structure propre :
cmd/pour le CLI Cobra,pkg/k8s/pour toute l’interaction Kubernetes,pkg/tui/pour la TUI Bubble Tea - Le coeur du sujet : un appel direct à l’API Kubernetes pour patcher le subresource
ephemeralcontainersdu pod avec un strategic merge patch incluant lesvolumeMounts - Mode non-interactif :
-n namespace -p pod -v volume:/mount/path, prêt pour le scripting - TUI complète : navigation vim-style (
j/k), filtrage fuzzy (/), multi-sélection de volumes, spinner de chargement… - Makefile avec tout un tas de targets (vet, lint, test, build, install)
Ce que je lui ai demandé d’ajouter :
- Discovery intelligente : au lieu de scanner tous les pods de tous les namespaces (lent sur un de mes gros cluster), l’outil liste d’abord les PVCs cluster-wide (un seul appel API) pour identifier les namespaces pertinents, PUIS les pods dans le namespace sélectionné. On réduit drastiquement le nombre d’appels à l’API server de kube et la TUI ne montre que les namespaces et pods qui ont des volumes PVC
- Héritage du
SecurityContext: le conteneur éphémère copie lesecurityContextdu conteneur cible, ce qui lui permet de passer les PodSecurity policies (restricted,baseline…). C’est un cas que je n’avais pas envisagé dans mon prompt initial et qui a planté immédiatement sur un cluster bien configuré.
Sans ces petites améliorations le tool était déjà utilisable (sauf pour le cas où vous avez configuré des SecurityContext), mais c’est nettement plus confortable à l’usage (parce que oui, je l’utilise).
Le code de la mécanique centrale (le patch de l’API) fait quelques centaines de lignes de Go propre. Il récupère le pod, construit le patch JSON avec le conteneur éphémère et ses volumeMounts, l’applique via Patch() sur le subresource, attend que le conteneur soit Running, puis lance kubectl attach. Rien de sorcier, c’est juste ce qu’il faut, bien fait du premier coup.
ephemeralContainer := corev1.EphemeralContainer{
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
Name: containerName,
Image: opts.Image,
Command: []string{"/bin/sh"},
Stdin: true,
TTY: true,
VolumeMounts: mounts,
SecurityContext: targetSecurityContext,
},
TargetContainerName: targetContainer,
}
...
_, err = c.Clientset.CoreV1().Pods(opts.Namespace).Patch(
ctx,
opts.PodName,
types.StrategicMergePatchType,
patchBytes,
metav1.PatchOptions{},
"ephemeralcontainers",
)
Les itérations suivantes (30 minutes-ish)
Une fois le POC validé, j’ai enchaîné quelques prompts ciblés. Je lui ai demandé ce qu’on pouvait améliorer. Il a proposé (et implémenté) :
- quelques corrections mineures de code qu’il avait mal codé (mais non bloquant)
- Warnings : si le SecurityContext hérité a
readOnlyRootFilesystem,runAsNonRoot, ou autre mesure de sécurité, l’outil prévient l’utilisateur avant l’attach - Ajouté une CI complète à base de
goreleaseret de github actions - Ajouté de la documentation
La cerise sur le Macdo : publication sur Krew
Une fois que j’avais une version propre, j’ai demandé à Opus comment faciliter l’installation du plugin. Il m’a proposé brew, ou alors Krew, le gestionnaire de plugins kubectl.
Je lui ai demandé si le processus d’acceptation du plugin était complexe. Il a dit que non, je lui ai dit “chiche !”.
Il a réalisé l’intégralité du processus :
- Création d’un fork de krew-index
- Rédaction du manifeste Krew (le fichier
.yamlqui décrit le plugin) - Prise en compte de toutes les bonnes pratiques demandées par les maintainers de krew-index (format des URLs de téléchargement, descriptions courtes, checksums SHA256, etc.)
- Préparation de la PR, directement sur krew-index
J’ai juste regardé. La PR a été mergée 12 heures plus tard par les mainteneurs.
Résultat : kubectl krew install debug-pvc fonctionne.
Mini échec - déléguer aussi la démo
Fort de ce succès avec la PR pour krew-index, je me suis demandé si on ne pourrait pas faire une démo visuelle (un GIF à ajouter dans le README.md du projet qui montre comment ça fonctionne) avec le LLM.
Je lui ai demandé s’il pouvait le faire avec asciinema et le LLM m’a répondu que “oui” (oui, je discute avec le LLM comme avec mes collègues). Banco, on a essayé.
Le résultat était presque bon, j’aurais pu m’en contenter si j’étais pas quelqu’un de pénible : c’était un poil lent. J’ai l’impression la réactivité du LLM dans ses actions était inégale, ce qui rendait le rendu un peu désagréable au visionnage. J’aurais pu insister jusqu’à avoir quelque chose de correct, mais j’ai finalement enregistré moi-même une vidéo, convertie en GIF avec ffmpeg. C’était plus fluide et plus rapide que d’itérer.

Le diff avec PodSweeper
La différence de ressenti entre ce projet et PodSweeper est frappante. Là où PodSweeper était un combat constant (race conditions, amnésie du LLM, fonctionnalités hors-spécifications), kubectl-debug-pvc s’est déroulé sans accroc notable.
Pourquoi ? Je pense que ça tient à la nature du projet :
- Une seule chose à faire, clairement définie : patcher un subresource Kubernetes avec des volumeMounts
- Pas de logique concurrente : on fait une requête API, on attend, on s’attache
- Un domaine bien documenté : l’API Kubernetes, Cobra, Bubble Tea. Le LLM connaît tout ça par coeur
- Pas d’état partagé complexe : pas de goroutines qui se marchent dessus, pas de graceful shutdown à gérer, de microservices multiples
- Scope limité : le projet entier tient dans une quinzaine de fichiers, CI et documentation incluses
C’est probablement le terrain de jeu idéal pour un LLM. Un problème bien cadré, un domaine technique bien balisé, une solution linéaire.
Qu’est-ce que j’en pense ?
Objectivement, ce genre de projet “simple” (une chose à faire, simple à comprendre) marche vraiment super bien avec OpenCode + Opus. Je pense que c’est ce genre à cause (grâce) de ce genre de petits projets que la hype est tellement forte autour du développement agentique. On a une idée en tête qu’on avait la flemme de dev, on teste, ça marche. “WOW”.
Mais je ne veux pas non plus minimiser le travail réalisé. Le fait qu’un outil fonctionnel, propre, publié sur Krew et utilisable par n’importe qui ait pu émerger d’un prompt lancé en début de réunion, c’est quand même assez dingue.
Un peu flippant aussi, de se dire qu’un nombre hallucinant de micro tools vont apparaître dans les prochains mois, avec un niveau de qualité probablement inégal.
Le projet
Le code est disponible ici :
Installation :
# Via Krew (recommandé)
kubectl krew install debug-pvc
# Utilisation interactive (TUI)
kubectl debug-pvc
# Utilisation non-interactive
kubectl debug-pvc -n my-namespace -p my-pod-0 -v data:/debug/data
Si vous aussi vous avez déjà galéré à inspecter un PVC sur un pod sans shell, essayez. Et si vous trouvez des bugs, vous pourrez blâmer le LLM 😄.
