31. Oktober 2026 · 12 Min. Lesezeit

kubectl debug: Pods ohne Shell untersuchen

Grafische Titelkarte zum Beitrag „kubectl debug: Pods ohne Shell untersuchen“ mit einem stilisierten Motiv: ein Container-Symbol, dessen Pfeil an einer Schranke endet, daneben eine Uhr und ein Stapel aus vier Zeilen.
Grafik: HumanITy

kubectl debug hängt an einen laufenden Pod einen zusätzlichen Container mit einem Image deiner Wahl, etwa busybox, ohne den Pod neu zu starten. Dieser Debug-Container heißt in Kubernetes ephemeral container. Mit --target teilt er sich den Prozess-Namespace mit deinem Anwendungscontainer: Du siehst dessen Prozesse, erreichst sein Dateisystem über /proc/<PID>/root und testest das Netzwerk aus Sicht des Pods. Genau das brauchst du bei distroless-Images, in denen kubectl exec mangels Shell scheitert. Für abstürzende Pods erzeugt kubectl debug --copy-to eine veränderbare Kopie, und kubectl debug node/<name> öffnet eine Shell auf dem Node. Welche Rechte der Debug-Container bekommt, steuern Profile; seit kubectl v1.36 ist general der Standard.

Ich bin Kubestronaut mit allen fünf CNCF-Zertifizierungen und Autor des Kubernetes-Praxisbuchs bei Rheinwerk Computing. Alle Beiträge zum Thema findest du auf der Seite Kubernetes.

Alle Ausgaben unten stammen aus einem lokalen Testcluster vom 30.10.2026: minikube v1.39.0 mit Docker-Treiber, Kubernetes v1.37.0, containerd 2.3.4, kubectl v1.37.2. Sie sind gekürzt, Pod-Namen sind Beispiele.

Warum kubectl exec bei distroless nicht reicht

Distroless-Images enthalten laut ihrem Projekt keine Paketverwaltung, keine Shell und keine anderen Programme, die du aus einer normalen Linux-Distribution kennst. Das macht sie klein und schwerer angreifbar, aber auch schwer zu untersuchen. Der Testpod läuft mit gcr.io/distroless/python3-debian13:nonroot und startet einen kleinen Webserver:

kubectl run shop-api --image=gcr.io/distroless/python3-debian13:nonroot \
  --restart=Never --command -- python3 -m http.server 8080
kubectl exec -it shop-api -- sh
error: Internal error occurred: ... OCI runtime exec failed: exec failed:
unable to start container process: exec: "sh": executable file not found in $PATH

Auch kubectl exec shop-api -- ls / scheitert so. Den allgemeinen Ablauf mit get, describe, logs und exec beschreibe ich in kubectl Befehle: die wichtigsten im Alltag. Hier geht es um den Fall danach.

kubectl debug mit ephemeral container: der Grundbefehl

kubectl debug -it shop-api --image=busybox:1.37 --target=shop-api -c debugger -- sh
Targeting container "shop-api". If you don't see processes from this container it may be because the container runtime doesn't support this feature.
All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.
If you don't see a command prompt, try pressing enter.

--image wählt die Werkzeugkiste, --target den Container, dessen Prozesse du sehen willst, -c den Namen des Debug-Containers. Darin laufen dann diese Befehle:

ps
wget -qO- http://localhost:8080/ | head -5
ls /proc/1/root/
PID   USER     TIME  COMMAND
    1 65532     0:00 python3 -m http.server 8080
   19 root      0:00 sh
   26 root      0:00 ps
<!DOCTYPE HTML>
<html lang="en">
...
bin  boot  dev  etc  home  lib  proc  root  run  sbin  sys  tmp  usr  var

PID 1 ist der Python-Prozess des distroless-Containers mit der Benutzer-ID 65532. localhost:8080 antwortet, weil sich die Container eines Pods das Netzwerk teilen, und unter /proc/1/root/ liegt das Dateisystem des Anwendungscontainers.

Ohne --target zeigte ps im Test nur die Prozesse des Debug-Containers selbst (sh und ps), den Python-Prozess nicht. Die Container-Laufzeit muss --target laut Dokumentation unterstützen; containerd 2.3.4 tat es.

Nimm die zweite Warnung ernst: Alles, was du in der Sitzung tippst, landet im Log des Debug-Containers und ist mit kubectl logs shop-api -c debugger lesbar, auch Passwörter.

Was danach im Pod bleibt

Ein ephemeral container lässt sich laut Dokumentation nach dem Hinzufügen weder ändern noch entfernen, und er wird nie automatisch neu gestartet. Ports, Probes und resources sind für ihn nicht erlaubt. Nach exit steht er in kubectl describe pod unter „Ephemeral Containers“ auf Terminated, der Pod selbst läuft weiter:

Ephemeral Containers:
  debugger:
    ...
    Image:         busybox:1.37
    ...
    Command:
      sh
    State:          Terminated
      Reason:       Completed
      Exit Code:    0

Jeder Aufruf fügt einen Eintrag hinzu, nach dem Test standen sieben im Pod. Weg sind sie erst, wenn der Pod ersetzt wird, etwa beim nächsten Rollout.

Technisch schreibt kubectl über die Unterressource pods/ephemeralcontainers, laut API des Testclusters mit den Verben get, patch und update. Wer dort patch darf, kann in fremde Pods hineinschauen. Vergib das Recht so gezielt wie pods/exec; wie das geht, steht in Kubernetes RBAC: Rollen und Rechte.

Profile: welche Rechte der Debug-Container bekommt

Mit --profile legst du fest, welchen securityContext kubectl setzt. Die Hilfe von kubectl v1.37.2 nennt general, baseline, restricted, netadmin und sysadmin, mit general als Standard. Laut Changelog von Kubernetes v1.36 hat sich der Standard von legacy auf general geändert, legacy soll mit v1.39 entfallen. Im Test lief --profile=legacy noch, mit einer Warnung. Die Doku-Seite zum Debuggen laufender Pods nennt am 30.10.2026 noch legacy als Standard.

Profil Was kubectl im Test gesetzt hat
general Capability SYS_PTRACE; am Node Host-Namespaces für PID, Netzwerk und IPC, Node-Dateisystem unter /host
baseline nichts zusätzlich; am Node keine Host-Namespaces und kein /host
restricted capabilities: drop: ALL, runAsNonRoot: true, seccompProfile: RuntimeDefault, allowPrivilegeEscalation: false
netadmin Capabilities NET_ADMIN und NET_RAW (geprüft am Node, dort mit Host-Netzwerk)
sysadmin privileged: true (geprüft am Node)

Drei Befunde aus dem Test, die in der Praxis Zeit kosten:

general scheitert an Pod Security baseline. Im Namespace shop mit dem Label pod-security.kubernetes.io/enforce=baseline lehnte der API-Server den Debug-Container ab:

Error from server (Forbidden): pods "api" is forbidden: violates PodSecurity "baseline:latest":
non-default capabilities (container "dbg-general" must not include "SYS_PTRACE" in securityContext.capabilities.add)

Mit --profile=baseline lief er, ps zeigte die Prozesse des Ziels weiterhin. Mehr zu den Stufen in Pod Security Standards in Kubernetes.

baseline sieht Prozesse, aber kein fremdes Dateisystem. Ohne SYS_PTRACE meldete ls /proc/1/root als root im Debug-Container Permission denied, weil der Zielprozess unter einer anderen Benutzer-ID läuft.

restricted mit einem root-Image startet nie. busybox läuft als root, restricted verlangt runAsNonRoot. Der Debug-Container blieb in CreateContainerConfigError hängen, mit der Meldung „container has runAsNonRoot and image will run as root“. Und weil sich ephemeral containers nicht entfernen lassen, bleibt dieser Eintrag im Pod stehen. Die Lösung ist ein eigenes Profil mit --custom, einer Teil-Spezifikation des Containers als YAML. Erst mit Benutzer- und Gruppen-ID des Ziels (hier 65532) war das Dateisystem lesbar; nur mit runAsUser kam wieder Permission denied:

# nonroot.yaml
securityContext:
  runAsUser: 65532
  runAsGroup: 65532
kubectl debug shop-api --image=busybox:1.37 --target=shop-api \
  --profile=restricted --custom=nonroot.yaml -c dbg-nonroot -- sh -c 'id; ls /proc/1/root/'
uid=65532 gid=65532 groups=65532
bin  boot  dev  etc  home  lib  proc  root  run  sbin  sys  tmp  usr  var

Laut Dokumentation darf --custom die Felder name, image, command, lifecycle und volumeDevices nicht ändern, und nur den Container, nicht den Pod.

kubectl debug --copy-to: eine Kopie zum Untersuchen

Stürzt der Anwendungscontainer sofort ab oder willst du seinen Befehl ändern, hilft ein ephemeral container wenig. Dann erzeugt --copy-to einen neuen Pod nach dem Vorbild des alten, im einfachsten Fall mit zusätzlichem Werkzeug-Container:

kubectl debug shop-api --copy-to=shop-api-debug --image=busybox:1.37 -c debugger -- sleep 3600
kubectl exec shop-api-debug -c debugger -- ps
PID   USER     TIME  COMMAND
    1 65535     0:00 /pause
    7 65532     0:00 python3 -m http.server 8080
   13 root      0:00 sleep 3600
   19 root      0:00 ps

--share-processes ist laut Hilfe mit true voreingestellt, die Kopie hatte shareProcessNamespace: true. Wichtig: Die Kopie trug keine Labels (Original: run=shop-api), denn --keep-labels steht auf false. Ein Service schickt ihr also keinen Verkehr. Probes übernimmt sie ebenfalls nur mit --keep-liveness, --keep-readiness und --keep-startup.

Der stärkere Fall ist ein Pod im CrashLoop. Im Test beendete sich der absichtlich fehlerhafte Container worker sofort mit „CONFIG_PATH fehlt“. Distroless bietet zu jedem Image eine debug-Variante mit busybox-Shell, für nonroot heißt sie debug-nonroot. --set-image tauscht das Image in der Kopie, --container mit Befehl nach -- ersetzt den Startbefehl:

kubectl debug worker --copy-to=worker-debug --container=worker \
  --set-image=worker=gcr.io/distroless/python3-debian13:debug-nonroot \
  -- sh -c 'id; ls -la /config; sleep 3600'
kubectl logs worker-debug -c worker
uid=65532(nonroot) gid=65532(nonroot) groups=65532(nonroot)
ls: /config: No such file or directory

Die Kopie läuft, und die Ursache ist sichtbar: /config fehlt. Wie du den Fehler im Original eingrenzt, steht in CrashLoopBackOff: Pods richtig debuggen. Lösch die Kopien danach mit kubectl delete pod shop-api-debug worker-debug, sonst laufen sie weiter.

kubectl debug node: eine Shell auf dem Node

Liegt der Fehler unter dem Pod, beim kubelet, bei containerd oder im Netzwerk des Nodes, startet kubectl debug node/<name> einen Pod auf genau diesem Node:

kubectl debug node/kubectl-debug-test -it --image=busybox:1.37
Creating debugging pod node-debugger-kubectl-debug-test-226bz with container debugger on node kubectl-debug-test.

Mit dem Standardprofil general lief der Pod im Test mit hostPID, hostNetwork und hostIPC und hatte das Dateisystem des Nodes unter /host eingebunden, ohne privilegiert zu sein. ls /host/var/log/pods zeigte die Log-Ordner aller Pods auf dem Node. Die Dokumentation nennt weitere Logs unter /host/var/log, etwa kubelet.log, und warnt, dass chroot /host ohne privilegierten Pod scheitern kann. Im minikube-Test lief chroot /host crictl ps trotzdem und listete die Container des Nodes; verlass dich in anderen Clustern nicht darauf und nimm bei Bedarf --profile=sysadmin, das im Test privileged: true setzte.

Nach dem Ende bleibt der Pod stehen, als Completed oder, wenn der Befehl scheiterte, als Error; nach vier Aufrufen waren es vier. Lösch ihn mit kubectl delete pod <name>. Und wer so debuggen darf, kommt an das Dateisystem des Nodes: Vergib das Recht, Pods anzulegen, bewusst.

Welcher Weg wann

Situation Befehl
Pod läuft, Image ohne Shell kubectl debug -it <pod> --image=busybox --target=<container>
Pod stürzt ab oder Startbefehl soll anders sein kubectl debug <pod> --copy-to=<kopie> --container=<container> --set-image=... -- sh
Namespace mit Pod Security baseline --profile=baseline
Pod läuft als non-root, Namespace restricted --profile=restricted --custom=<datei> mit runAsUser und runAsGroup des Ziels
Fehler unterhalb des Pods kubectl debug node/<node> -it --image=busybox

Häufige Fragen

Was ist ein ephemeral container?

Ein Container, den Kubernetes einem laufenden Pod ohne Neustart hinzufügt, ohne Ports, Probes und resources. Stabil ist die Funktion seit Kubernetes v1.25.

Warum sehe ich mit ps im Debug-Container nur seine eigenen Prozesse?

Weil --target fehlt. Ohne Ziel bekommt der Debug-Container einen eigenen Prozess-Namespace. Mit --target=<container> siehst du die Prozesse des Anwendungscontainers, sofern die Container-Laufzeit das unterstützt.

Kann ich einen ephemeral container wieder löschen?

Nein. Er bleibt im Pod, bis der Pod ersetzt wird. Experimentier mit Profilen deshalb an einem Testpod oder einer Kopie.

Welches Profil nutzt kubectl debug standardmäßig?

In kubectl v1.37.2 ist es general, seit v1.36 Standard. Ältere kubectl-Versionen nutzen legacy. Prüf deine Version mit kubectl debug --help, dort steht der Standardwert bei --profile.

Wie du weitermachst

kubectl debug ist der Schritt, wenn describe, logs und exec nicht mehr weiterhelfen. Startet der Container gar nicht erst, weil das Image fehlt, lies ImagePullBackOff: Ursachen und Lösung. Willst du die Befehle gefahrlos ausprobieren, setz dir einen Cluster nach Kubernetes lokal: Cluster auf dem Rechner auf.

Mein Vorschlag für den nächsten distroless-Pod: erst kubectl debug -it <pod> --image=busybox --target=<container>, dann ps und /proc/1/root. Die Grundlagen zu Pods und zur Kommandozeile stehen in meinem Kubernetes-Praxisbuch bei Rheinwerk, alle Optionen in der Kubernetes-Dokumentation zum Debuggen laufender Pods.

Kevin Welter

Kevin Welter

Entwickler, IT-Architekt, Fachbuchautor (Kubernetes, Cloud-Infrastrukturen) und Speaker. Betreibt sein Business mit einer KI-Belegschaft aus vierzehn KI-Mitarbeitern und zeigt Selbstständigen in seiner Community, wie sie ihren ersten KI-Mitarbeiter einstellen.

Mehr über KI-Mitarbeiter

Kubernetes von den Grundlagen bis zum produktionsreifen Cluster

Zum Buch