Fehlerbehebung: Häufige Docker-Container-Fehler schnell diagnostizieren
Meistern Sie die Kunst der schnellen Docker-Container-Fehlerbehebung mit diesem wichtigen Leitfaden. Lernen Sie den strukturierten Prozess zur Diagnose von Startfehlern mithilfe der wichtigsten Docker-Befehle kennen. Wir zeigen Ihnen, wie Sie `docker ps -a` nutzen, um Abstürze zu identifizieren, mit `docker logs` kritische Informationen extrahieren und mit `docker inspect` erweiterte Konfigurationsanalysen durchführen. Dieser Artikel bietet praktische Beispiele und gezielte Lösungen für häufige Probleme, einschließlich Exit-Code-127-Fehlern, Portkonflikten und OOMKilled-Ereignissen, damit Sie schnell die Ursache finden und den Dienst wiederherstellen können.
Fehlerbehebung: Häufige Docker-Container-Fehler schnell diagnostizieren
Wenn ein Docker-Container sofort beendet wird, beginnen Sie nicht damit, das Image neu zu erstellen oder zufällige Flags zu ändern. Finden Sie zunächst heraus, was Docker weiß: den Container-Status, den Exit-Code, die Logs und den genauen Befehl, den Docker auszuführen versucht hat. Diese vier Informationen grenzen das Problem in der Regel schnell ein.
Ein Container ist nur ein Prozess mit einer Isolierung darum herum. Wenn der Hauptprozess beendet wird, wird der Container beendet. Das kann ein Absturz, eine fehlende ausführbare Datei, ein abgeschlossener Batch-Job, eine fehlgeschlagene Health-Abhängigkeit oder der Kernel sein, der den Prozess aufgrund von zu hohem Speicherverbrauch beendet. Die folgenden Befehle helfen Ihnen, diese Fälle zu unterscheiden.
Zuerst den gestoppten Container finden
docker ps zeigt nur laufende Container an. Fehlgeschlagene Start-Container sind normalerweise versteckt, es sei denn, Sie fragen nach allen Containern:
docker ps -a
Achten Sie auf STATUS, COMMAND und NAMES:
CONTAINER ID IMAGE COMMAND STATUS NAMES
2d3f4b5c6e7a my-app:latest "/usr/bin/start" Exited (127) 2 minutes ago web-service
91aa34c0db22 worker:latest "python worker.py" Exited (0) 10 minutes ago nightly-worker
Exited (0) bedeutet oft, dass der Prozess erfolgreich abgeschlossen wurde. Das ist normal für einmalige Jobs. Bei einem Webdienst kann es bedeuten, dass der Befehl ausgeführt und beendet wurde, anstatt im Vordergrund zu bleiben.
Exit-Codes ungleich Null weisen auf einen Fehler hin, aber behandeln Sie sie als Hinweise und nicht als endgültige Antworten. Exit-Code 127 bedeutet normalerweise, dass der Befehl nicht gefunden wurde. 126 bedeutet normalerweise, dass er gefunden, aber nicht ausführbar ist. 137 bedeutet oft, dass der Prozess SIGKILL erhalten hat; in Containern hängt dies häufig, aber nicht immer, mit Speicherdruck zusammen. Bestätigen Sie dies immer mit Logs und Inspect-Ausgaben.
Logs lesen, bevor Sie etwas ändern
Docker erfasst stdout und stderr des Hauptprozesses des Containers für den Standard-Logging-Treiber. Verwenden Sie:
docker logs web-service
Nützliche Optionen:
docker logs --tail 100 web-service
docker logs --since 15m web-service
docker logs -t web-service
docker logs -f web-service
Wenn die Logs config file notfound anzeigen, überprüfen Sie Mounts und Umgebung. Wenn sie einen Anwendungs-Stacktrace zeigen, debuggen Sie die Anwendung. Wenn sie leer sind, ist der Prozess möglicherweise fehlgeschlagen, bevor er eine Ausgabe erzeugt hat, oder der Image-Entrypoint ist falsch.
Vermeiden Sie bei einer Crash-Schleife, docker logs -f als Ihr einziges Werkzeug zu verwenden. Es kann den Eindruck erwecken, dass der Fehler aktiv ist, ohne Ihnen den Status zu geben. Kombinieren Sie Logs mit docker inspect.
Den Container-Status überprüfen
docker inspect gibt ein großes JSON-Dokument zurück. Sie brauchen selten alles davon. Beginnen Sie mit formatierten Feldern:
docker inspect -f 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} error={{.State.Error}}' web-service
Überprüfen Sie dann die Befehls- und Image-Konfiguration:
docker inspect -f 'entrypoint={{json .Config.Entrypoint}} cmd={{json .Config.Cmd}} user={{.Config.User}}' web-service
Überprüfen Sie Mounts, wenn der Fehler Dateien betrifft:
docker inspect -f '{{json .Mounts}}' web-service
Wenn der Container aufgrund von Speicher beendet wurde, ist .State.OOMKilled das wichtige Feld. Wenn es true ist, kann eine Erhöhung des Speichers helfen, aber die bessere nächste Frage ist, warum der Speicher gewachsen ist. Ein größeres Limit kann ein Leck lange genug verbergen, um später zu versagen.
Nach Möglichkeit mit einer interaktiven Shell reproduzieren
Wenn das Image eine Shell enthält, überschreiben Sie den Entrypoint und überprüfen Sie das Dateisystem:
docker run --rm -it --entrypoint /bin/sh my-app:latest
Einige Images haben Bash:
docker run --rm -it --entrypoint /bin/bash my-app:latest
Überprüfen Sie im Inneren die Dateien und Befehlspfade:
ls -l /usr/bin/start
id
env
Minimale Images enthalten möglicherweise keine Shell. Verwenden Sie in diesem Fall die verfügbaren Tools des Images, erstellen Sie eine temporäre Debug-Variante neu oder überprüfen Sie das Dockerfile und die Build-Ausgabe. Fügen Sie einem Produktions-Image nicht dauerhaft Debug-Pakete hinzu, nur weil die Fehlerbehebung einmal unbequem war.
Befehl nicht gefunden: Exit 127
Exit 127 bedeutet normalerweise, dass Docker die ausführbare Datei, die von ENTRYPOINT oder CMD benannt wird, nicht finden konnte, oder ein Startskript versucht hat, einen fehlenden Befehl auszuführen.
Häufige Ursachen:
- Die ausführbare Datei wurde nie in das Image kopiert.
- Der Pfad ist auf dem Host korrekt, aber nicht innerhalb des Images.
- Das Skript verwendet
/bin/bash, aber das Image hat nur/bin/sh. - Der Befehl hängt von
PATHab, undPATHunterscheidet sich von dem, was Sie erwarten.
Überprüfen Sie den Image-Befehl:
docker inspect -f '{{json .Config.Entrypoint}} {{json .Config.Cmd}}' web-service
Wenn der Entrypoint ein Skript ist, überprüfen Sie dessen Shebang und Zeilenenden. Ein Skript mit Windows-CRLF-Zeilenenden kann mit verwirrenden „Nicht gefunden“-Meldungen fehlschlagen, da der Interpreterpfad effektiv einen Wagenrücklauf enthält.
Keine Berechtigung: Exit 126 oder Dateifehler
Exit 126 bedeutet oft, dass Docker den Befehl gefunden, aber nicht ausführen konnte. Bei Skripten fehlt der Datei möglicherweise das ausführbare Bit:
COPY start.sh /usr/local/bin/start.sh
RUN chmod 0755 /usr/local/bin/start.sh
ENTRYPOINT ["/usr/local/bin/start.sh"]
Denken Sie bei volume-gemounteten Dateien daran, dass die Host-Berechtigungen gelten. Wenn ein Container als UID 1000 ausgeführt wird und das Host-Verzeichnis root gehört und keine Schreibberechtigung hat, kann der Container dort nicht schreiben, nur weil er „innerhalb von Docker“ ist.
Überprüfen Sie den Laufzeitbenutzer:
docker inspect -f 'user={{.Config.User}}' web-service
Wenn es leer ist, werden viele Images standardmäßig als root ausgeführt, aber nicht alle. Offizielle und sicherheitsgehärtete Images verwenden oft einen Nicht-Root-Benutzer.
Port bereits vergeben
Ein Bind-Fehler tritt normalerweise auf, wenn Sie einen Host-Port veröffentlichen, der bereits verwendet wird:
docker run -p 8080:80 nginx
Docker meldet möglicherweise so etwas wie bind: address already in use. Finden Sie den Konflikt:
docker ps --format 'table {{.Names}}\t{{.Ports}}'
lsof -iTCP:8080 -sTCP:LISTEN
Stoppen Sie dann den konkurrierenden Prozess oder wählen Sie einen anderen Host-Port:
docker run -p 8081:80 nginx
Der Container-Port kann gleich bleiben. Der Host-Port ist der Teil vor dem Doppelpunkt.
Fehlende Dateien und fehlerhafte Mounts
Wenn Logs sagen, dass eine Konfigurationsdatei fehlt, vergleichen Sie, was die Anwendung erwartet, mit dem, was Docker gemountet hat:
docker inspect -f '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}' web-service
Ein häufiger Fehler ist das Mounten eines Host-Verzeichnisses über einen Pfad, der bereits Dateien im Image hatte. Der Mount verdeckt den Image-Inhalt an diesem Ziel. Wenn das Image /app/config/default.yml enthält und Sie ein leeres Host-Verzeichnis über /app/config mounten, verschwindet die Standarddatei aus der Sicht des Containers.
Überprüfen Sie auch relative Pfade. -v ./config:/app/config hängt von dem Verzeichnis ab, in dem Sie docker run ausgeführt haben, nicht von dem Verzeichnis, in dem sich das Dockerfile befindet.
Health-Check-Fehlschläge sind nicht immer Container-Abstürze
Ein Container kann laufen, aber ungesund sein:
docker ps
Sie sehen möglicherweise Up 2 minutes (unhealthy). Überprüfen Sie die Health-Ausgabe:
docker inspect -f '{{json .State.Health}}' web-service
Health-Checks schlagen oft fehl, weil die App auf einem anderen Port lauscht, nur an 127.0.0.1 bindet, länger zum Starten braucht, als der Health-Check erlaubt, oder eine Datenbank benötigt, die noch nicht bereit ist. Verwechseln Sie einen ungesunden Container nicht mit einem beendeten; der Diagnosepfad ist unterschiedlich.
Eine schnelle Fehlerbehebungssequenz
Verwenden Sie diese Reihenfolge, wenn Sie schnell eine Antwort benötigen:
docker ps -a, um den Container und den Exit-Code zu finden.docker logs --tail 100 <name>, um den Anwendungsfehler zu lesen.docker inspect -f ..., um Status, Befehl, Benutzer und Mounts zu überprüfen.- Führen Sie eine temporäre Shell im Image aus, wenn der Befehl oder das Dateisystem verdächtig ist.
- Überprüfen Sie Host-Konflikte für Ports und Berechtigungen gemounteter Verzeichnisse.
- Bauen Sie erst neu, wenn Sie wissen, ob das Problem der Image-Inhalt, die Laufzeit-Flags oder die Anwendungskonfiguration ist.
Diese Sequenz hält die Untersuchung fundiert. Docker hat normalerweise genügend Beweise; der Trick ist, sie zu lesen, bevor Sie die Szene ändern.
Überprüfen Sie die Neustartrichtlinie, bevor Sie dem vertrauen, was Sie sehen
Eine Neustartrichtlinie kann einen Container so aussehen lassen, als ob er ständig fehlschlägt oder sich ständig erholt. Überprüfen Sie sie:
docker inspect -f 'restart={{json .HostConfig.RestartPolicy}}' web-service
Wenn die Richtlinie always oder unless-stopped ist, startet Docker den Container möglicherweise nach jedem Absturz neu. docker ps zeigt ihn möglicherweise für einige Sekunden als laufend an, dann startet er erneut. Verwenden Sie in diesem Fall Logs mit Zeitstempeln und überprüfen Sie die Neustartanzahl:
docker inspect -f 'restarts={{.RestartCount}} started={{.State.StartedAt}} finished={{.State.FinishedAt}}' web-service
Eine hohe Neustartanzahl bedeutet normalerweise, dass der Hauptprozess schnell beendet wird. Die Lösung ist selten „Ändern Sie die Neustartrichtlinie“. Die Richtlinie zeigt nur den zugrunde liegenden Fehler auf.
Unterscheiden Sie Build-Zeit-Probleme von Laufzeit-Problemen
Wenn eine Datei im Container fehlt, fragen Sie, wann sie erscheinen sollte. Dateien, die im Dockerfile kopiert werden, sind Build-Zeit-Angelegenheiten. Dateien, die mit -v oder Compose-Volumes gemountet werden, sind Laufzeit-Angelegenheiten.
Build-Zeit-Überprüfungen:
docker image inspect my-app:latest
docker run --rm --entrypoint /bin/sh my-app:latest -c 'ls -la /app'
Laufzeit-Überprüfungen:
docker inspect -f '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}' web-service
Diese Aufteilung spart Zeit. Das Neuerstellen des Images wird einen fehlerhaften Host-Mount nicht beheben. Das Ändern eines Volume-Flags wird ein Dockerfile nicht reparieren, das die Binärdatei nie kopiert hat.
Umgebungsvariablen und Geheimnisse können leise fehlschlagen
Viele Anwendungen werden beendet, weil eine erforderliche Umgebungsvariable fehlt, aber der Docker-Fehler sagt nur, dass der Prozess mit Code 1 beendet wurde. Überprüfen Sie die konfigurierte Umgebung sorgfältig:
docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' web-service
Seien Sie vorsichtig, wo Sie diesen Befehl ausführen; er kann Geheimnisse ausgeben. Geben Sie in gemeinsamen Logs nur die Variablennamen aus:
docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' web-service | sed 's/=.*//'
Wenn Sie --env-file verwenden, überprüfen Sie auf CRLF-Zeilenenden, nicht in Anführungszeichen gesetzte Leerzeichen und fehlende Dateien. Docker-Env-Dateien sind keine vollständigen Shell-Skripte. Halten Sie sie einfach: KEY=value-Zeilen, Kommentare, wo von Ihrer Docker-Version unterstützt, und keine Annahmen, dass eine Shell-Erweiterung innerhalb der Datei stattfindet.
Ein praxisnaher Überprüfungsdurchlauf vor dem Ausliefern
Bevor Sie ein Skript oder eine Container-Einrichtung als abgeschlossen bezeichnen, lesen Sie es einmal so, als ob Sie die nächste Person wären, die es um 2 Uhr morgens debuggen muss. Das ändert, was Ihnen auffällt. Eine Eingabeaufforderung, die beim Schreiben des Skripts sinnvoll war, kann in einem CI-Protokoll mehrdeutig sein. Ein Docker-Dienstname, der offensichtlich schien, passt möglicherweise nicht zum Variablennamen in der Anwendung. Ein Bash-Standardwert kann für die Entwicklung sicher und für die Produktion gefährlich sein.
Ich mache gerne einen kurzen Probelauf mit absichtlich ungünstigen Werten. Verwenden Sie einen Pfad mit Leerzeichen. Verwenden Sie einen leeren optionalen Wert. Versuchen Sie einen Dateinamen, der mit einem Bindestrich beginnt. Führen Sie das Skript aus einem anderen Arbeitsverzeichnis aus. Starten Sie den Container ohne eine erwartete Umgebungsvariable. Diese Tests sind nicht ausgefallen, aber sie fangen die Annahmen, die normalerweise zuerst brechen.
Überprüfen Sie auch die Fehlermeldung. Wenn die einzige Ausgabe failed ist, hat der Rat des Artikels den Weg in die Implementierung nicht gefunden. Ein nützlicher Fehler sagt, welcher Wert verwendet wurde, welche Prüfung fehlgeschlagen ist und was der Bediener ändern kann. Das bedeutet nicht, jede Umgebungsvariable auszugeben oder Geheimnisse zu drucken. Es bedeutet, dort spezifisch zu sein, wo Spezifität hilft: der Konfigurationspfad, der fehlende Befehlsname, der Netzwerkname, der Service-Hostname oder der Port, an den der Prozess zu binden versuchte.
Die letzte Gewohnheit ist, Beispiele nahe an der Art und Weise zu halten, wie das System tatsächlich ausgeführt wird. Wenn die Produktion Compose verwendet, testen Sie mit Compose. Wenn ein Skript von systemd gestartet wird, testen Sie es mit systemd oder mit einer ähnlich minimalen Umgebung. Wenn ein Befehl sicher zum Kopieren und Einfügen sein soll, fügen Sie die Anführungszeichen, ---Trennzeichen und die Validierung im Beispiel selbst ein. Leser kopieren funktionierende Muster häufiger als Warnungen.
Dieser Überprüfungsdurchlauf ist keine Bürokratie. Es ist, wie kleine Automatisierung langweilig bleibt. Langweilig ist, was Sie von Shell-Eingabeaufforderungen, Konfigurationsladern, Variablenerweiterung, Container-Diagnose und Docker-Netzwerken wollen. Je weniger überraschend das Verhalten ist, desto einfacher ist es für den nächsten Bediener, ihm zu vertrauen.
Speichern Sie bei Docker-Fehlern den genauen Befehl, der den Container erstellt hat, wenn Sie können. docker inspect kann die aktuelle Konfiguration anzeigen, aber ein Incident-Notiz, die den ursprünglichen docker run-Befehl, den Compose-Dienst, das Image-Tag und den Env-Dateinamen enthält, ist viel einfacher später zu reproduzieren. Vermeiden Sie die Verwendung von latest während ernsthafter Fehlerbehebung. Ein sich bewegendes Tag kann einen Fehler in zwei verschiedene Untersuchungen verwandeln, weil sich das Image ändert, während Sie noch Logs lesen.
Wenn Sie ein produktionsähnliches Problem lokal diagnostizieren, ziehen Sie denselben Image-Digest oder dasselbe Tag, verwenden Sie dasselbe Volume-Layout und übergeben Sie dieselbe nicht-geheime Umgebungsform. Reproduktion schlägt Spekulation.