K8s Debug Bridge
Leitet Live-Traffic von einem Kubernetes/OpenShift-Dienst zu einem lokalen Rechner zum Debuggen um – unter Verwendung von ausschließlich standardmäßigem kubectl/oc exec, Bashs eingebautem /dev/tcp und dem nc im Bridge-Pod – ohne zusätzliche CLI oder Agenteninstallation. Mechanismus: Ein temporärer „Bridge-Pod“ wird gestartet, der spec.selector des Zieldienstes wird vorübergehend auf das Label dieses Pods umgestellt, und ein Reverse-Tunnel verbindet den Pod und den lokalen Port. Funktioniert auf jeder Kubernetes-Distribution (vanilla K8s, OpenShift, EKS/GKE/AKS) – verwendet oc falls verfügbar, sonst kubectl (siehe scripts/lib-kube-cli.sh). Die README.md erläutert die Beweggründe und die Abwägungen gegenüber Tools wie Telepresence und mirrord.
Dieser Skill ist nicht an eine einzelne Sprache/ein einzelnes Framework gebunden: Die Bridge transportiert nur Traffic. VS Code + Air + Delve (Go) ist nur ein Beispiel für eine Integration – wenn du in einer anderen Sprache arbeitest, schließe deinen eigenen Watcher/Debugger an (Node: nodemon+inspector, Python: watchdog+debugpy usw.) über denselben Tunnel an.
Wenn die lokale Seite des Benutzers noch nicht eingerichtet ist – kein Reloader, kein Debugger verbunden, oder sie möchten sie an ihr eigenes Projekt anpassen – lese zu diesem Zeitpunkt docs/local-debug-setup.md und folge ihr. Gib deren Inhalt nicht aus dem Gedächtnis wieder und lese sie nicht, wenn die lokale App bereits läuft und zuhört.
Dieser Skill erstellt eine echte Ressource in deinem Cluster und ändert eine echte Ressource: Er erstellt einen Pod und ändert vorübergehend den spec.selector eines Dienstes. Dies sind keine Simulationen – dies ist echter Cluster-Zustand. Es muss bei jedem Schritt einen Rollback-Plan geben – ändere niemals den Selektor und vergiss ihn.
Voraussetzungen – vor dem Fragen des Benutzers prüfen
Frage den Benutzer nicht nach dem Namespace/Dienstnamen und übergib ihn blind an bridge-up.sh – ein falsch geschriebener Name führt mitten im Skript zu einem Fehler. Führe zuerst scripts/check-env.sh [namespace] aus (schreibgeschützt, ändert nichts):
- Ohne Argumente: Zeigt den Login-Status und zugängliche Namespaces an. Wenn nicht eingeloggt, stoppe hier und sage dem Benutzer, er solle
oc login/kubectl config use-contextausführen. - Mit einem Namespace: Überprüft, ob der Namespace existiert, und listet die darin enthaltenen Dienste auf (Name + Port + targetPort, einschließlich bereits gebrückter).
Zeige diese Ausgabe dem Benutzer und lass ihn den Zieldienst aus der Liste auswählen – lass ihn nicht raten. Dann:
- Das Bridge-Pod-Image – immer fragen, niemals annehmen. Wähle keinen Standardwert und führe ihn aus: Frage den Benutzer direkt „Welches Image soll der Bridge-Pod verwenden?“ und warte auf eine Antwort. Das Image muss von diesem spezifischen Cluster genehmigt und erreichbar sein, und nur der Benutzer weiß, welches das ist – viele Cluster ziehen nur aus einer genehmigten internen Registry, sodass ein geratenes Image beim Pull fehlschlagen kann, während der Dienst bereits umgeleitet wurde.
nicolaka/netshootist ein Beispiel, das angeboten werden kann, aber kein Fallback, der stillschweigend verwendet wird. Was auch immer sie wählen, es mussncausliefern: Die Pod-Seite verwendetncanstelle vonsocat, weil gehärtete Images oftsocatentfernen, während Nmapsncerhalten bleibt.tunnel.shprüft aufnc, bevor der Tunnel geöffnet wird, und stoppt mit einer klaren Fehlermeldung, wenn das Image es nicht enthält. - Pod-Manifest: Der Bridge-Pod wird aus
assets/bridge-pod.yamlgerendert. Wenn der Cluster imagePullSecrets, einen securityContext, nodeSelector/tolerations oder andere Ressourcenlimits erfordert, bearbeite diese Datei – arbeite nicht in den Skripten darum herum. Behalte dasdebug: "__BRIDGE_NAME__"-Label und den Containernamendebug-bridgebei; die anderen Skripte adressieren den Pod darüber. - Der Port: Verwende den numerischen
targetPortdes Dienstes aus der Ausgabe voncheck-env.sh, NICHT einen beliebigen freien Port. Wenn der Dienst einen benannten targetPort verwendet (z. B.http), löse ihn in seinen numerischen Container-Port auf. kube-proxy leitet Traffic nach der Umleitung des Selektors an dentargetPortdes Pods weiter – wenn du eine andere Nummer wählst, erreicht der Traffic den Bridge-Pod nie. - Ausgehende Abhängigkeiten: Diese Bridge behandelt nur eingehenden Traffic (Cluster → Lokal). Wenn lokaler Code andere Cluster-Dienste aufrufen muss (
.svc.cluster.local), verwende externe Routes/Ingress oder lokale Port-Forwards. - Geheimnisse und Umgebung: Führe niemals
oc exec ... env,printenv,setoder einen ähnlichen Befehl gegen einen Anwendungs-Pod aus. Werte können mehrzeilige Anmeldeinformationen sein und in die Terminal-/Tool-Ausgabe gelangen, selbst wenn ein Befehl nur Namen auszuwählen scheint. Kopiere niemals Pod-Umgebungsvariablen in einen lokalen Befehl, die Shell-Historie oder eine.env. Erfordere einen vorhandenen lokalen Secret-Management-Workflow oder lass den Benutzer die benötigte lokale Umgebungsdatei außerhalb der Reihe bereitstellen. Überprüfe nur, ob die lokale Anwendung startet; gib ihre Umgebung nicht aus.
Die lokale Anwendung muss bereits laufen, bevor du den Dienst berührst. Dies ist eine Vorbedingung, kein späterer Schritt: Der Tunnel liefert Traffic an 127.0.0.1:<port>. Wenn dort nichts lauscht, wenn der Selektor umschaltet, schlägt jede abgefangene Anfrage mit connection-refused/502 fehl, während die echten Pods bereits außer Betrieb sind. Lass den Benutzer seine App starten (mit einer sicher bereitgestellten lokalen Umgebungsdatei – niemals einer, die aus einem Cluster-Pod ausgelesen wurde) und bestätige den Listener unmittelbar vor Schritt 2:
lsof -nP -iTCP:<port> -sTCP:LISTEN
Ablauf
-
Bridge einrichten: Sage dem Benutzer klar, bevor du etwas ausführst: „Ich werde einen echten Pod namens
<bridge-pod>im Namespace<namespace>erstellen (Image:<image>) – er verbraucht Cluster-Ressourcen und wird gelöscht, wenn du fertig bist.“ Nach Zustimmung führescripts/bridge-up.sh <namespace> <service> <bridge-pod> <image> [port]aus – dies erstellt den Bridge-Pod und speichert den ursprünglichen Selektor sowohl im lokalen Zustand als auch alsk8s-debug-bridge/owner-Annotation am Dienst (kein python3/jq erforderlich, reines Bash). Überspringe diesen Schritt nicht – leite niemals um, ohne den ursprünglichen Selektor gespeichert zu haben, sonst gibt es keinen Weg zurück. Wenn der Dienst bereits von jemand anderem gebrückt wird, schlägt dieses Skript fehl und stoppt (Annotation-Check) – zeige dem Benutzer in diesem Fall den Besitzer aus der Fehlermeldung, versuche es nicht selbst erneut. -
Dienst umleiten: Bevor der Selektor des Zieldienstes auf das Label des Bridge-Pods umgestellt wird, sage dem Benutzer klar und hole eine explizite Zustimmung ein: „Ab diesem Moment geht der GESAMTE Traffic zu
<service>zu deinem Bridge-Pod – die echten Pods erhalten keinen Traffic mehr.“ Nach Zustimmung führescripts/redirect-service.sh <namespace> <service> --yesaus –--yesüberspringt die eigene interaktiveread-Eingabeaufforderung des Skripts (da du bereits im Chat zugestimmt hast); ohne--yeswartet das Skript auf stdin und kann hängen bleiben. Das Skript ersetzt die vollständige Selektor-Map nur durchdebug=<bridge-pod>; es darf dieses Label nicht mit dem ursprünglichen Selektor zusammenführen, da der Bridge-Pod die ursprünglichen Labels der Anwendung nicht trägt. -
Tunnel starten:
scripts/tunnel.sh <namespace> <service> <bridge-pod> <port>– dieses Skript blockiert, gehört daher in ein eigenes Terminal oder einen Hintergrundprozess. Es reinigt sich selbst:trapfängt EXIT/INT/TERM ab, sodass Strg+C oder jede andere Beendigung automatischcleanup.shaufruft. Wenn du es selbst im Hintergrund gestartet hast, gibt es kein Strg+C zum Drücken – stoppe es mitkill -TERM <pid>, was der trap ebenfalls behandelt; notiere die PID beim Start und teile sie dem Benutzer mit. Die manuelle Ausführung voncleanup.shist nur erforderlich, wenn der Prozess unfangbar getötet wird (kill -9). Es überträgt jeweils eine Verbindung und erstellt automatisch einen neuen Pod-seitigen Listener, nachdem jede Verbindung geschlossen wurde. Lasse daher denselben Tunnel zwischen aufeinanderfolgenden Debug-Anfragen laufen. Wenn der Tunnel wiederholtWARNING: could not carry a connectionprotokolliert, lauscht nichts auf127.0.0.1:<port>– die lokale App ist ausgefallen, wird noch gebaut, oder ein Hot Reload hat den Port freigegeben. Behebe das, anstatt den Tunnel neu zu starten. -
Lokales Debuggen: Der Port, den du brückst, ist der Port, auf dem die Anwendung bedient (der
targetPortdes Dienstes), niemals der Port, auf dem ihr Debugger lauscht. Wenn der Benutzer sagt „Mein Debugger ist auf 2345, brücke das“, verwechselt er die beiden: Korrigiere ihn, frage, auf welchem Port die Anwendung selbst bedient, und brücke diesen. Traffic, der an einen Debugger-Port gesendet wird, kommt als 502 zurück, die wie ein defekter Tunnel aussehen. Die lokale App läuft bereits und ist verifiziert (siehe Vorbedingung oben); sie erhält den getunnelten Traffic. Der Debugger ihrer Sprache (z. B. Delve, ein Node-Inspector) hängt sich separat an diese laufende Anwendung an, normalerweise auf einem anderen Port – z. B. in Go: Air + Delve auf Port 2345, während die App selbst auf dem getunnelten<port>lauscht. Ein Hot Reload kann den Listener kurzzeitig entfernen; während er fehlt, gibt die Bridge Verbindungsfehler/502 zurück. Wenn sich die lokale App nicht rechtzeitig erholt, stoppe den Tunnel und bereinige den Dienst, anstatt den gesamten Traffic auf eine fehlerhafte Bridge zu lenken. Die Bridge transportiert den HTTP-Pfad unverändert: Konfiguriere das Basispräfix der lokalen App so, dass es dem an den Dienst gelieferten Pfad entspricht; sie reproduziert keine Ingress-Umschreibung. Traffic kann von jedem In-Cluster-Aufrufer des Dienstes kommen (z. B. einem Dashboard-Nginx-Proxy); eine öffentliche Route/Ingress ist nicht erforderlich. Wenn die Ausführung über das Timeout des Aufrufers hinaus pausiert wurde, setze sie fort und löse eine neue Anfrage aus. Nach einem Hot Reload, der Delve neu startet, muss sich die IDE möglicherweise erneut verbinden. Wenn Delve mit--accept-multiclientgestartet wurde, kann der Agent zusätzlich zu VS Code einen terminalbasierten Delve-Client an Port2345anhängen, um die aktuelle Quellzeile, den Stack und Variablen zu inspizieren. Melde Beobachtungen dem Benutzer; hole explizite Zustimmung ein, bevor du die Ausführung fortsetzt oder den Debugger-Zustand anderweitig änderst. -
Wenn fertig: Strg+C im Tunnel-Terminal oder
kill -TERM <pid>, wenn du es im Hintergrund gestartet hast – der Dienst-Selektor und der Bridge-Pod werden automatisch zurückgesetzt, einschließlich der Wiederherstellung der vollständigen ursprünglichen Selektor-Map. Wenn der trap nicht ausgelöst wurde (z. B.kill -9), mussscripts/cleanup.sh <namespace> <service>manuell ausgeführt werden; erinnere den Benutzer daran, bevor er die Sitzung verlässt.
Sicherheitshinweis
Diese Methode installiert keine dauerhafte Komponente im Cluster (kein Mutating Webhook, Sidecar-Injector oder clusterweites RBAC) – nur einen temporären Pod, der klar durch seinen Namen/Label gekennzeichnet ist (debug=<bridge-pod-name>), und Standard-exec-Berechtigungen. Dennoch gibt es zwei reale Risiken, warne den Benutzer davor:
- Den Dienst-Selektor zurückzusetzen vergessen – ein dauerhafter Ausfall in Produktion/Staging.
exec-Berechtigungen, die über RBAC missbraucht werden können – dieser Skill sollte nur in deinem eigenen Namespace, auf deinem eigenen Bridge-Pod und niemals mit Cluster-Admin ausgeführt werden.
Ausführliche Schritte und Fehlerbehebung findest du in der README.md.
Überprüfe vor Beendigung der Sitzung, ob noch eine ungereinigte Bridge vorhanden ist. Der Zustand befindet sich neben dem scripts/-Verzeichnis, im eigenen Ordner dieses Skills – nicht im Projektverzeichnis des Benutzers. Löse ihn relativ zu den Skripten auf, die du ausführst, z. B.:
find "$(dirname <path-to>/scripts)/.state" -name '*.env'
Ein nacktes find .state aus dem Projektverzeichnis des Benutzers findet stillschweigend nichts und meldet einen sauberen Zustand, selbst wenn noch eine Bridge aktiv ist. Wenn dort eine .env-Datei existiert, teile dem Benutzer mit, welchen Namespace/Dienst sie benennt, und schlage scripts/cleanup.sh <namespace> <service> vor. Dies ist eine Erinnerung, kein deterministisches Gate – achte darauf, sie nicht zu vergessen.