Kubernetes porté dans le navigateur
(ngrok.com)- webernetes est un projet qui transpose une partie de Kubernetes en TypeScript pour faire tourner un cluster dans le navigateur ; il a été réalisé en 2 mois, avec 552 commits, 629 fichiers et près de 100 000 lignes
- Il ne s’agit pas d’une compilation directe de Kubernetes en WebAssembly : le projet réimplémente une partie de kubelet, plusieurs contrôleurs, un CNI et un runtime de conteneurs basés sur le navigateur, ainsi qu’une API de manipulation du cluster
- Il ne récupère pas les images depuis de vrais registres d’images : celles-ci sont définies via une API TypeScript. L’objectif n’est pas une distribution de production, mais la création de contenus Kubernetes interactifs
- La majeure partie du code a été écrite par un LLM, mais chaque ligne a été relue par un humain, et le projet est validé par 204 tests d’intégration exécutant les mêmes tests que k3s, ainsi que par 1 855 tests unitaires portés depuis la base de code Go de Kubernetes
- Pendant le portage, le LLM a répété des erreurs comme des simplifications, la création arbitraire de helpers et l’omission de tests ; pour tirer parti de la génération rapide de code, il faut l’accompagner de revue et de tests
Ce que webernetes exécute dans le navigateur
- webernetes est un projet qui porte partiellement Kubernetes en TypeScript afin de faire tourner un cluster dans le navigateur
- Le cluster de démonstration fonctionne entièrement dans le navigateur et effectue une grande partie des tâches d’un véritable cluster Kubernetes
-
Cycle de vie des Pods
- DNS et réseau du cluster
- Garbage collection des conteneurs
- Attribution d’adresses IP
- Suivi des
Deploymentet desReplicaSet - Les points bleus de la démo représentent les Pods qui s’envoient des requêtes entre eux
-
Un portage partiel plutôt qu’une compilation WebAssembly
- Kubernetes n’a pas été compilé en WebAssembly
- Même un simple programme Go
hello, world!compilé en WebAssembly pèse environ 540 KiB en gzip, tandis que webernetes pèse environ 140 KiB en gzip - Compiler tout Kubernetes en WebAssembly nécessiterait probablement des transferts de plusieurs mégaoctets, et provoquerait aussi des erreurs à la compilation en raison d’appels à des API système indisponibles dans le navigateur
- webernetes est composé des éléments suivants
- Un portage partiel du binaire Kubernetes kubelet nécessaire à l’exécution des Pods et aux probes
- Le portage de plusieurs contrôleurs Kubernetes, notamment le scheduler de Pods, le namespace controller, kube-proxy et le deployment controller
- Une Container Network Interface (CNI) basée sur le navigateur pour la communication entre Pods
- Un runtime de conteneurs basé sur le navigateur permettant à kubelet d’exécuter les conteneurs via la Container Runtime Interface (CRI)
- Une API de cluster webernetes pour des opérations comme l’application de manifests et le watch des ressources
Définition des images et mode d’utilisation de l’API
- Pour rester compact, webernetes ne récupère pas de vraies images depuis des registres comme Docker Hub
- Il dispose plutôt d’un registre basé sur le navigateur, et les images sont définies via une API TypeScript
- L’image d’exemple
HelloWorldhérite dew8s.BaseImageet, dansexec, renvoie"Hello, world!"aux requêtes HTTP sur le port 8080 - Le flux d’utilisation du cluster est le suivant
- Créer un cluster avec
new w8s.Cluster() - Enregistrer l’image avec
cluster.registerImage(HelloWorld) - Appliquer un manifest
Deploymentapps/v1aveccluster.apply() - Lister les Pods avec
cluster.api.corev1.listNamespacedPod() - Surveiller les changements de Pods avec
cluster.informer("pods", ...) - Observer les événements de requêtes et de réponses entre Pods avec
cluster.on("request")etcluster.on("response") - Envoyer une requête HTTP à l’IP d’un Pod via le réseau du cluster avec
cluster.fetch()
- Créer un cluster avec
- D’autres exemples se trouvent dans les exemples du dépôt webernetes
Usages et limites actuelles
- L’objectif de webernetes est de créer du contenu Kubernetes interactif
- Ce n’est pas une distribution Kubernetes prête pour la production, et il n’a pas besoin d’exécuter de vraies images
- Il suffit que les créateurs de contenu puissent configurer certains workloads pour illustrer les concepts Kubernetes qu’ils souhaitent expliquer
- Les fonctionnalités actuellement non prises en charge incluent notamment
- ConfigMaps
- Secrets
- Ressources des Pods
- Volumes persistants
- De nombreuses fonctionnalités Kubernetes qui n’ont pas encore été nécessaires
- D’autres fonctionnalités Kubernetes seront implémentées à l’avenir, au fil des besoins de création de contenu
Pourquoi ne pas avoir tout confié au LLM malgré son utilisation
- Presque tout le code de webernetes a été écrit par un LLM
- Pour assurer la fiabilité du projet, deux approches ont été menées en parallèle
- Relire personnellement chaque ligne de code
- Écrire des centaines de tests pour vérifier que webernetes se comporte comme un vrai cluster
- La revue manuelle a permis d’acquérir la conviction que la majeure partie du code est identique, ligne par ligne, à la base de code Go de Kubernetes
- Les tests servent à vérifier que cette similarité lexicale se traduit bien par un comportement identique
- Les erreurs restantes après revue relèvent de la responsabilité de l’auteur du projet, qui demande d’ouvrir une issue en cas de problème
Pourquoi le code porté nécessitait une revue
- Dans des cas où un compilateur C a été écrit avec un LLM, ou où Bun a été porté de Zig vers Rust, il existait un moyen de vérifier automatiquement l’exactitude
- Anthropic disposait de compilateurs C existants auxquels comparer le résultat
- Bun disposait d’une vaste suite de tests, jugée assez fiable pour fusionner plus d’un million de lignes de nouveau code Rust sans revue manuelle
- webernetes ne disposait pas de cette base
- Si une suite de tests était nécessaire, il fallait l’écrire soi-même
- Pour comparer avec un vrai Kubernetes, il fallait aussi concevoir la méthode de comparaison
- La majorité du code de webernetes a été portée depuis la base de code Go de Kubernetes, et l’auteur a utilisé un LLM en estimant que ce serait plus rapide que de le saisir à la main
- Pendant le portage, le LLM a répété des erreurs
- Simplification : il pouvait remplacer par un simple
Mapdes éléments comme les LRU cache, expiring cache, FIFO cache ou transforming cache de Kubernetes, sans les implémenter correctement, ce qui aurait pu produire un comportement incorrect - Nettoyage excessif : il pouvait créer des fonctions helper absentes du code Go d’origine, rendant la revue plus difficile ou introduisant des différences subtiles
- Omissions : lors du portage des table tests de Go, il omettait souvent arbitrairement des cas de test
- Simplification : il pouvait remplacer par un simple
- Pour faire confiance au résultat d’un portage par LLM, il faut relire sa sortie ; l’auteur du projet estime ne pas connaître de raccourci utilisable
Tests comparés à un vrai cluster
- Même si le code ressemble côte à côte à l’original, les runtimes Go et JavaScript sont différents, ce qui peut entraîner des comportements divergents
- webernetes avait aussi besoin de versions JavaScript des channels, des mutexes, de l’instruction
selectde Go et d’autres comportements typiques de Go - Le même code de test est exécuté à la fois sur webernetes et sur un cluster k3s afin de comparer les comportements
- La cible de compatibilité API retenue est le client Kubernetes JavaScript officiel avec types TypeScript, kubernetes-client/javascript
- Le harnais de test change d’environnement d’exécution via
kubernetes.describe(..)pnpm test:nodeexécute les tests en environnement Node contre k3spnpm test:browserexécute les tests contre webernetes dans un navigateur headless
- Les tests d’intégration vérifient non seulement le code porté, mais aussi que le runtime de conteneurs personnalisé basé sur le navigateur et le réseau de cluster se comportent comme ceux d’un vrai cluster
- Lorsqu’un bug est trouvé, l’auteur commence par écrire un test qui passe sur k3s mais échoue sur webernetes, puis utilise cette boucle de feedback, avec l’aide du LLM, pour comprendre la cause et la corriger
- Au moment de la rédaction, webernetes compte 204 tests d’intégration et 1 855 tests unitaires
Pourquoi revue et tests doivent aller ensemble
- Le code produit par un LLM a besoin de bons tests et d’un bon code, comme une PR écrite par un humain
- La différence en 2026 est qu’avec un collègue humain on s’attendait dans une certaine mesure à un bon travail, tandis qu’avec un LLM il est plus sûr de partir du principe qu’il ne fera pas du bon travail
- Si même le code de test n’est pas relu, il est difficile de savoir sur quels critères de réussite le LLM travaille
- Même en relisant tout le code, sans tests, il est difficile de croire qu’un humain peut raisonner sur toutes les possibilités
- Comme le LLM ne se fatigue pas et saisit rapidement, il est utile de lui faire proposer des edge cases auxquels un humain n’aurait pas pensé, puis, s’ils sont valides, de lui faire écrire les tests
- La combinaison du goût et de la compréhension humains avec la capacité de rédaction rapide du LLM est perçue comme le plus grand changement depuis le début de la carrière de l’auteur en 2012
Taille du projet et usage des tokens
- Le premier commit a été ajouté le 21 avril dans l’actuel dépôt webernetes
- Une partie du travail initial ayant été réalisée dans une branche du dépôt situé derrière ce site de blog, le graphique ne reflète pas entièrement la réalité
- Le graphique des lignes de code indique 126 642 lignes pour la semaine du 15 juin
- Les quelque 100 000 lignes mentionnées au début excluent le code non TypeScript, les commentaires et la demo app
- Le nombre total de lignes par semaine a augmenté comme suit
- Semaine du 20 avril : 11 640 lignes
- Semaine du 27 avril : 20 660 lignes
- Semaine du 4 mai : 25 048 lignes
- Semaine du 11 mai : 30 417 lignes
- Semaine du 18 mai : 42 301 lignes
- Semaine du 25 mai : 54 155 lignes
- Semaine du 1er juin : 79 704 lignes
- Semaine du 8 juin : 98 532 lignes
- Semaine du 15 juin : 126 642 lignes
- Dans les sessions Codex et Claude, le volume de cached input tokens était bien plus important que celui des autres tokens, un phénomène particulièrement marqué lorsque de longues fenêtres de contexte étaient souvent remplies
- La semaine du 15 juin, 104 155 857 uncached input tokens, 2 196 467 968 cached input tokens et 6 420 826 output tokens ont été utilisés
Pic de la dernière semaine et coûts
- La dernière semaine, l’ajout de la prise en charge des Deployments dans la demo app s’est révélé plus important que prévu
- La première tentative de portage du LLM omettait une grande partie des fonctionnalités nécessaires dans les composants requis
- Ensuite, plusieurs agents ont été mobilisés pour identifier les chaînes de dépendances, chaque composant a été porté par davantage de sous-agents, puis d’autres sous-agents ont effectué les revues
- Cette approche a permis d’aller plus vite qu’un travail manuel, mais son efficacité en tokens était très mauvaise, et une revue manuelle restait nécessaire à la fin
- Le coût des tokens LLM converti en API a augmenté semaine après semaine, atteignant 1 811,64 $ la semaine du 15 juin
- Sur l’ensemble du projet, le temps de l’auteur est resté l’élément le plus coûteux jusqu’à la fin
Ressources finales et participation
- Une série de vidéos documente aussi le processus de création
- Les vidéos montrent aussi l’optimisme initial mal placé, ainsi qu’une façon de travailler presque entièrement mains libres grâce au contrôle vocal et au suivi oculaire
- L’auteur invite à essayer le projet et à ouvrir des issues, ou à le contacter à
s.rose@ngrok.comsi vous avez créé quelque chose ou si vous êtes bloqué
1 commentaires
Avis sur Hacker News
Par exemple, j’ai créé ceci : https://kubernetes-made-simple.vercel.app/ et je pourrais maintenant l’y ajouter.
Mais le site est chouette.
Ce serait bien de développer davantage Gateway et, si possible, de mentionner aussi les CRD.
Si tu devais citer une chose que la plupart des gens comprennent mal à propos de k8s, et qui rend l’apprentissage inutilement difficile, ce serait quoi ?
Si je me souviens bien, au début j’utilisais Katacoda, puis une autre plateforme similaire, et c’était très utile parce qu’elle lançait à la volée, pour chaque utilisateur, une nouvelle instance avec une configuration précise.
Cela dit, ici ça me semble aujourd’hui mieux adapté à l’enseignement des concepts ou de l’architecture. Le vrai plaisir commence quand on commence à bien maîtriser kubectl.
Les autres plateformes similaires semblent aussi avoir disparu faute de quelqu’un pour en payer les coûts, et c’est dommage.
J’espère que ceci pourra servir d’alternative. Il y a un risque que ça diverge de la réalité et vieillisse mal, mais les fondamentaux resteront presque toujours valables.
Ça me semble être la bonne façon d’envisager l’ingénierie assistée par LLM. L’IA peut générer une quantité surprenante de code, mais la vraie valeur réside dans la discipline de revue et les tests autour.
L’angle “Kubernetes dans le navigateur” est chouette, mais le plus intéressant, c’est le workflow, en particulier le fait de tester le comportement par rapport à k8s au lieu de se fier à “ça a l’air plausible”. Je me demande combien d’équipes appliquent déjà ce niveau de validation au code écrit par l’IA, et c’est peut-être la direction que tout le monde prendra dans les prochaines années.
Malheureusement, toutes les tâches de programmation n’offrent pas cette possibilité.
https://youtu.be/t7L2iROVaRg?is=xoV4aiCXcYMVvVDL
La complexité supplémentaire ou les pertes de performance devront être justifiées dans une certaine mesure, mais pour certains cas d’usage, cela semble pouvoir largement compenser.
C’est assez proche de la distinction de Fred Brooks entre complexité essentielle et complexité accidentelle.
Bien sûr, si on utilise kube pour quelque chose qui pourrait être fait plus simplement, kube devient vite de la complexité accidentelle.
Par analogie, si quelqu’un porte DOOM dans le navigateur, ça veut dire qu’on peut désormais y jouer dans le navigateur. Mais on ne peut pas vraiment exécuter dans le navigateur les bases de données qu’on voit dans l’onglet, si ?
Lancer ruby2d ne veut pas dire qu’on obtient soudainement la prise en charge de Ruby côté client. Pour que ça tourne réellement dans un onglet de navigateur, il faut toutes sortes de travaux personnalisés.
À l’inverse, les services backend conteneurisés classiques peuvent réellement être déplacés ici ou là et exécutés à peu près n’importe où.
Du coup, je ne saisis pas bien l’intérêt ; corrigez-moi si je me trompe. Ça ne semble pas non plus correspondre à l’affirmation faite.
Il n’exécute pas de vraies images de conteneurs. Il vaudrait peut-être mieux parler de “Kubernetes simulé”.
Ce qui a été porté, c’est le control plane. Le scheduler, kube-proxy, le deployment controller, etc., ont été portés depuis le vrai code source Go, et ils utilisent la même API client pour tester la concordance de comportement avec k3s. Le “rendu” est une application de démo qui visualise les requêtes entre Pods sous forme de points qui se déplacent.
C’était amusant.