Que fait K8s Debug Bridge ?
Tunnelise le trafic en direct d'un service Kubernetes/OpenShift vers une machine locale pour
déboguer en utilisant uniquement les commandes standard kubectl/oc exec, le /dev/tcp intégré de bash et le
nc à l'intérieur du pod pont — pas besoin d'outil CLI ou d'agent supplémentaire à installer. Mécanisme : un "pod pont"
temporaire est créé, le spec.selector du service cible est temporairement basculé sur l'étiquette de ce pod, et un tunnel inverse
relie le pod et le port local. Fonctionne sur toute distribution Kubernetes (vanilla
K8s, OpenShift, EKS/GKE/AKS) — utilise oc si disponible, sinon kubectl (voir
scripts/lib-kube-cli.sh). Le fichier README.md couvre les justifications et les compromis par rapport à
des outils comme Telepresence et mirrord.
Ce n'est pas lié à un langage ou framework unique : le pont ne fait que transporter le trafic.
VS Code + Air + Delve (Go) n'est qu'un exemple d'intégration — si vous travaillez dans un autre
langage, connectez votre propre watcher/débogueur (Node : nodemon+inspector, Python :
watchdog+debugpy, etc.) via le même tunnel.
Si le côté local de l'utilisateur n'est pas encore configuré — pas de rechargement, pas de débogueur attaché, ou s'il souhaite
l'adapter à son propre projet — lisez docs/local-debug-setup.md à ce moment-là et
suivez-le. Ne reproduisez pas son contenu de mémoire, et ne le lisez pas lorsque l'application locale
est déjà en cours d'exécution et à l'écoute.
Cette compétence crée une ressource réelle dans votre cluster et modifie une ressource réelle :
elle crée un Pod et modifie temporairement le spec.selector d'un Service. Ce ne sont pas
des simulations — c'est un état réel du cluster. Il doit y avoir un plan de restauration à chaque
étape — ne modifiez jamais le sélecteur sans l'oublier.
Prérequis — vérifier avant de demander à l'utilisateur
Ne demandez pas à l'utilisateur le nom du namespace/service et ne le passez pas aveuglément à
bridge-up.sh — un nom mal orthographié échouera en plein milieu du script. Exécutez d'abord
scripts/check-env.sh [namespace] (lecture seule, ne change rien) :
- Exécuté sans argument : liste l'état de connexion et les namespaces accessibles. Si vous n'êtes pas
connecté, arrêtez-vous ici et dites à l'utilisateur d'exécuter
oc login/kubectl config use-context. - Exécuté avec un namespace : vérifie que le namespace existe et liste les Services qu'il contient (nom + port + targetPort, y compris ceux déjà pontés).
Affichez cette sortie à l'utilisateur et laissez-le choisir le Service cible dans la liste — ne le faites pas deviner. Ensuite :
- L'image du pod pont — demandez toujours, ne présumez jamais. Ne choisissez pas une valeur par défaut et exécutez-la
: demandez carrément à l'utilisateur "quelle image le pod pont doit-il utiliser ?" et attendez une
réponse. L'image doit être approuvée et accessible depuis ce cluster spécifique,
et seul l'utilisateur sait laquelle — de nombreux clusters ne tirent que depuis un
registre interne approuvé, donc une image devinée peut échouer au moment du pull avec le Service déjà
redirigé.
nicolaka/netshootest un exemple à proposer, pas un fallback à utiliser silencieusement. Quel que soit le choix, il doit fournirnc: le côté pod utilisencplutôt que socat car les images durcies retirent souventsocattout en conservant lencde Nmap.tunnel.shvérifie la présence dencavant d'ouvrir le tunnel et s'arrête avec une erreur claire si l'image ne l'a pas. - Manifeste du pod : le pod pont est rendu à partir de
assets/bridge-pod.yaml. Si le cluster nécessite imagePullSecrets, un securityContext, nodeSelector/tolerations ou des limites de ressources différentes, modifiez ce fichier — ne contournez pas cela dans les scripts. Conservez l'étiquettedebug: "__BRIDGE_NAME__"et le nom de conteneurdebug-bridge; les autres scripts adressent le pod via eux. - Le port : utilisez le
targetPortnumérique du Service depuis la sortie decheck-env.sh, PAS un port libre arbitraire. Si le Service utilise un targetPort nommé (par ex.http), résolvez-le en son port de conteneur numérique. kube-proxy transfère le trafic vers letargetPortdu pod une fois le sélecteur redirigé — si vous choisissez un numéro différent, le trafic n'atteint jamais le pod pont. - Dépendances sortantes : ce pont ne gère que le trafic entrant (Cluster → Local).
Si le code local doit appeler d'autres services du cluster (
.svc.cluster.local), utilisez des Routes/Ingress externes ou des port-forwards locaux. - Secrets et environnement : n'exécutez jamais
oc exec ... env,printenv,set, ou une commande similaire contre un pod applicatif. Les valeurs peuvent être des identifiants multilignes et fuiront dans la sortie du terminal/outil même lorsqu'une commande semble sélectionner uniquement les noms. Ne copiez jamais les valeurs d'environnement d'un pod dans une commande locale, un historique de shell ou un fichier.env. Exigez un workflow de gestion des secrets local préexistant ou demandez à l'utilisateur de fournir le fichier d'environnement local nécessaire hors bande. Vérifiez uniquement que l'application locale démarre ; n'affichez pas son environnement.
L'application locale doit déjà être en cours d'exécution avant de toucher au Service. C'est
une condition préalable, pas une étape ultérieure : le tunnel délivre le trafic vers 127.0.0.1:<port>, donc
si rien n'écoute là au moment du basculement du sélecteur, chaque requête interceptée échoue avec
connection-refused/502 pendant que les vrais pods sont déjà hors rotation. Demandez à l'utilisateur de démarrer
son application (avec un fichier d'environnement local provisionné en toute sécurité — jamais lu depuis un pod du
cluster) et confirmez l'écouteur immédiatement avant l'étape 2 :
lsof -nP -iTCP:<port> -sTCP:LISTEN
Déroulement
-
Configurer le pont : Avant d'exécuter quoi que ce soit, dites clairement à l'utilisateur : "Je vais créer un vrai Pod nommé
<bridge-pod>dans le namespace<namespace>(image :<image>) — il consomme des ressources du cluster et sera supprimé une fois que vous aurez terminé." Après approbation, exécutezscripts/bridge-up.sh <namespace> <service> <bridge-pod> <image> [port]— cela crée le pod pont et enregistre le sélecteur d'origine à la fois dans l'état local et comme annotationk8s-debug-bridge/ownersur le Service (pas besoin de python3/jq, bash simple). Ne sautez pas cette étape — ne redirigez jamais sans que le sélecteur d'origine soit enregistré, sinon il n'y a aucun moyen de revenir en arrière. Si le service est déjà ponté par quelqu'un d'autre, ce script échoue et s'arrête (vérification de l'annotation) — dans ce cas, montrez à l'utilisateur le propriétaire depuis le message d'erreur, ne réessayez pas vous-même. -
Rediriger le service : Avant de basculer le sélecteur du Service cible sur l'étiquette du pod pont, dites clairement à l'utilisateur et obtenez une approbation réelle : "À partir de ce moment, TOUT le trafic vers
<service>ira vers votre pod pont — les vrais pods cesseront de recevoir du trafic." Après approbation, exécutezscripts/redirect-service.sh <namespace> <service> --yes—--yesignore la propre invite interactivereaddu script (puisque vous avez déjà obtenu l'approbation dans le chat) ; sans--yes, le script attend sur stdin et peut se bloquer. Le script remplace la carte complète du sélecteur par uniquementdebug=<bridge-pod>; il ne doit pas fusionner cette étiquette dans le sélecteur d'origine, car le pod pont ne porte pas les étiquettes originales de l'application. -
Démarrer le tunnel :
scripts/tunnel.sh <namespace> <service> <bridge-pod> <port>— ce script bloque, donc il doit être dans son propre terminal ou un processus en arrière-plan. Il s'auto-nettoie :trapintercepte EXIT/INT/TERM, donc Ctrl+C ou toute autre terminaison appelle automatiquementcleanup.sh. Si vous l'avez démarré en arrière-plan vous-même, il n'y a pas de Ctrl+C à presser — arrêtez-le aveckill -TERM <pid>, que le trap gère également ; notez le PID lorsque vous le lancez et dites à l'utilisateur ce que c'est. Exécutercleanup.shmanuellement n'est nécessaire que si le processus est tué de manière non interceptable (kill -9). Il transporte une connexion à la fois et crée automatiquement un nouvel écouteur côté pod après chaque fermeture de connexion, donc laissez le même tunnel en cours d'exécution entre les requêtes de débogage séquentielles. Si le tunnel enregistre de manière répétéeWARNING : n'a pas pu transporter une connexion, rien n'écoute sur127.0.0.1:<port>— l'application locale est arrêtée, encore en cours de construction, ou un rechargement à chaud a libéré le port. Corrigez cela plutôt que de redémarrer le tunnel. -
Débogage local : le port que vous pontiez est le port sur lequel l'application sert (le
targetPortdu Service), jamais le port sur lequel son débogueur écoute. Si l'utilisateur dit "mon débogueur est sur 2345, pont celui-là", il confond les deux : corrigez-le, demandez sur quel port l'application elle-même sert, et pont celui-là. Le trafic envoyé vers un port de débogueur revient sous forme de 502 qui ressemblent à un tunnel cassé. L'application locale est déjà en cours d'exécution et vérifiée (voir la condition préalable ci-dessus) ; c'est elle qui reçoit le trafic tunnelisé. Le débogueur de leur langage (par ex. Delve, un inspecteur Node) s'attache à cette application en cours d'exécution séparément, généralement sur un port différent — par ex. en Go, Air + Delve sur le port 2345 pendant que l'application elle-même écoute sur le<port>tunnelisé. Un rechargement à chaud peut supprimer brièvement l'écouteur ; pendant qu'il est absent, le pont renvoie des échecs de connexion/502. Si l'application locale ne récupère pas rapidement, arrêtez le tunnel et nettoyez le Service au lieu de laisser tout le trafic pointé vers un pont défaillant. Le pont transporte le chemin HTTP inchangé : configurez le préfixe de base de l'application locale pour correspondre au chemin délivré au Service ; il ne reproduit pas une réécriture Ingress. Le trafic peut provenir de tout appelant intra-cluster du Service (par exemple, un proxy Nginx de tableau de bord) ; une Route/Ingress publique n'est pas nécessaire. Si l'exécution est en pause au-delà du délai d'attente de l'appelant, reprenez-la et déclenchez une nouvelle requête. Après un rechargement à chaud qui redémarre Delve, l'IDE peut avoir besoin de s'attacher à nouveau. Lorsque Delve a été démarré avec--accept-multiclient, l'agent peut également attacher un client Delve en terminal au port2345parallèlement à VS Code pour inspecter la ligne source actuelle, la pile et les variables. Rapportez les observations à l'utilisateur ; obtenez une approbation explicite avant de continuer l'exécution ou de modifier autrement l'état du débogueur. -
Une fois terminé : Ctrl+C dans le terminal du tunnel, ou
kill -TERM <pid>si vous l'avez démarré en arrière-plan — le sélecteur du service et le pod pont reviennent automatiquement, y compris la restauration de la carte complète du sélecteur d'origine. Si le trap n'a pas été déclenché (par ex.kill -9),scripts/cleanup.sh <namespace> <service>doit être exécuté manuellement ; rappelez-le à l'utilisateur avant qu'il ne quitte la session.
Note de sécurité
Cette méthode n'installe aucun composant permanent dans le cluster (pas de webhook mutant,
d'injecteur sidecar, ou de RBAC à l'échelle du cluster) — seulement un pod temporaire clairement marqué par
son nom/étiquette (debug=<bridge-pod-name>) et des permissions exec standard. Malgré
cela, il y a deux risques réels, prévenez l'utilisateur :
- Oublier de rétablir le sélecteur du service — une panne permanente en production/staging.
- La permission
execpouvant être abusée via RBAC — cette compétence ne doit être exécutée que dans votre propre namespace, sur votre propre pod pont, et ne doit jamais nécessiter cluster-admin.
Pour des étapes détaillées et le dépannage, consultez README.md.
Avant de terminer la session, vérifiez si un pont non nettoyé reste. L'état se trouve à côté de
scripts/, dans le propre dossier de cette compétence — pas dans le répertoire du projet de l'utilisateur.
Résolvez-le par rapport aux scripts que vous exécutez, par exemple :
find "$(dirname <chemin-vers>/scripts)/.state" -name '*.env'
Exécuter un simple find .state depuis le projet de l'utilisateur ne trouvera silencieusement rien et
signalera un état propre même si un pont est toujours actif. Si un fichier .env existe là, dites
à l'utilisateur quel namespace/service il nomme et suggérez
scripts/cleanup.sh <namespace> <service>. C'est un rappel, pas une porte déterministe
— faites attention à ne pas l'oublier.