Fehlerbehebung bei langsamen Docker-Containern: Eine Schritt-für-Schritt-Leistungsanleitung
Finden Sie heraus, warum Docker-Container langsam sind, indem Sie CPU, Arbeitsspeicher, Festplatten-I/O, Netzwerk, Limits, Mounts und Protokollierung überprüfen.
Fehlerbehebung bei langsamen Docker-Containern: Eine Schritt-für-Schritt-Leistungsanleitung
Wenn ein Docker-Container langsam erscheint, beginnen Sie nicht damit, das Image neu zu erstellen oder zufällige Laufzeit-Flags zu ändern. Entscheiden Sie zuerst, was „langsam“ bedeutet. Ist die API-Antwortzeit hoch? Hinkt ein Worker hinterher? Ist der Start langsam? Sind Builds langsam? Ist der Host überlastet? Jeder Punkt weist auf eine andere Lösung hin.
Ein Container ist keine magische Isolation von der Physik. Er verwendet weiterhin Host-CPU, Host-Arbeitsspeicher, Host-Speicher, Host-Netzwerk und den von Ihnen bereitgestellten Anwendungscode. Docker fügt Steuerelemente und Namespaces um diese Ressourcen hinzu, macht aber keine langsame Abfrage schnell oder eine gesättigte Festplatte untätig.
Beginnen Sie mit einer schnellen Live-Ansicht:
docker stats
Beobachten Sie den Container, während Sie die Verlangsamung reproduzieren. Eine einzelne Momentaufnahme ist weniger nützlich als zu sehen, was sich unter Last ändert. Wenn die CPU springt und hoch bleibt, haben Sie eine CPU-Spur. Wenn der Speicher ansteigt, bis der Container stirbt, folgen Sie dem Speicherpfad. Wenn sich BLOCK I/O stark bewegt, während Anfragen stocken, verdient der Speicher Aufmerksamkeit. Wenn der Container ruhig aussieht, Benutzer aber immer noch Latenz sehen, schauen Sie sich die App, Netzwerkaufrufe, Datenbank oder vorgelagerte Dienste an.
Zuerst Container- und Host-Gesundheit vergleichen
Ein langsamer Container kann einfach auf einem langsamen Host leben. Überprüfen Sie beide Ebenen.
docker stats <container>
top
free -h
df -h
Unter Linux ist iostat -xz 1 hilfreich, falls verfügbar. Hohe Festplattenauslastung oder lange Wartezeiten können langsame Datenbanken, Paketinstallationen und protokollintensive Dienste erklären. Überprüfen Sie unter Docker Desktop auch die CPU und den Arbeitsspeicher, die der Docker-VM zugewiesen sind. Ein Mac mit viel Arbeitsspeicher kann Container dennoch aushungern, wenn Docker Desktop zu niedrig eingestellt ist.
Wenn jeder Container langsam ist, ist der Host der Verdächtige. Wenn ein Container langsam ist, während Nachbarn in Ordnung sind, konzentrieren Sie sich auf diese Arbeitslast, ihre Limits, Mounts und Abhängigkeiten.
CPU-Engpässe
In docker stats kann die CPU 100 % überschreiten, da Docker die Nutzung über Kerne hinweg meldet. Ein Container, der 200 % verwendet, nutzt ungefähr zwei Kerne. Die wichtige Frage ist, ob dies für die Arbeitslast erwartet wird.
Überprüfen Sie Laufzeitlimits:
docker inspect <container> --format 'NanoCPUs={{.HostConfig.NanoCpus}} CpuQuota={{.HostConfig.CpuQuota}} CpuPeriod={{.HostConfig.CpuPeriod}} Cpuset={{.HostConfig.CpusetCpus}}'
Wenn ein Dienst mit --cpus=0.5 gestartet wurde, kann er unter normalem Datenverkehr gedrosselt werden. In Kubernetes oder Compose kann dasselbe Problem in CPU-Limits versteckt sein. Ein Worker, der Aufträge auf einem Laptop schnell verarbeitet hat, kann in CI kriechen, weil er nur eine halbe CPU bekommt.
Für CPU auf Anwendungsebene profilieren Sie den Prozess, anstatt zu raten. Verwenden Sie für Node integrierte CPU-Profilerstellung oder Clinic-ähnliche Tools. Verwenden Sie für Python py-spy, wo erlaubt. Verwenden Sie für Java JFR oder async-profiler. Wenn Sie keine Tools in einem Produktionsimage installieren können, führen Sie dasselbe Image in einer Staging-Umgebung aus oder verwenden Sie ein Debug-Container-Muster.
Häufige CPU-Ursachen sind enge Polling-Schleifen, teure JSON-Serialisierung, Regex-Rückverfolgung, Bildverarbeitung, Komprimierung und zu viele Worker-Threads, die um zu wenige Kerne kämpfen. Mehr CPU hilft nur, wenn die App sie nutzen kann und der Host Kapazität hat.
Speicherdruck und OOM-Kills
Speicherprobleme zeigen sich als steigende Speichernutzung, häufige Garbage Collection, Swap-Aktivität auf dem Host oder plötzliche Beendigungen. Bestätigen Sie den OOM-Status:
docker inspect <container> --format 'exit={{.State.ExitCode}} oom={{.State.OOMKilled}} memory={{.HostConfig.Memory}}'
Wenn OOMKilled=true, hat der Container seine Speichersituation überschritten. Das kann ein explizites --memory-Limit, ein Docker Desktop VM-Limit oder hostweiter Druck sein.
Verwenden Sie docker stats, während Sie realistischen Datenverkehr senden. Wenn der Speicher wächst, ohne sich zu stabilisieren, vermuten Sie ein Leck, einen unbegrenzten Cache, einen Warteschlangenaufbau oder eine Arbeitslast, die zu viele Daten auf einmal lädt. Wenn der Speicher während des Starts springt und sich dann beruhigt, ist das Limit möglicherweise einfach zu niedrig für die Laufzeit.
Sprachstandards sind wichtig. Java, Node und einige Anwendungsserver können Speicher je nach Version und Konfiguration innerhalb von Containern unterschiedlich reservieren oder verwenden. Legen Sie explizite Heap- oder Speicheroptionen fest, wenn Sie vorhersagbares Verhalten benötigen. Beispielsweise benötigt ein Java-Dienst möglicherweise containerbewusste Heap-Prozentsätze; ein Node-Dienst benötigt möglicherweise --max-old-space-size; eine Datenbank benötigt Cache-Einstellungen, die Platz für den Prozess und das Dateisystem lassen.
Setzen Sie Speicherlimits nicht so eng, dass die App die ganze Zeit mit Garbage Collection verbringt. Ein Container, der nie abstürzt, aber ständig pausiert, ist immer noch defekt.
Festplatten-I/O und langsame Bind-Mounts
Speicherprobleme sind leicht zu übersehen, weil CPU- und Speicherdiagramme normal aussehen. In Docker kommt Festplattenlangsamkeit oft von einer von vier Stellen: starker Anwendungs-I/O, übermäßige Protokolle, der Speichertreiber oder Bind-Mounts auf Docker Desktop.
Überprüfen Sie die Docker-Ansicht:
docker stats <container>
docker logs --tail 20 <container>
Wenn Protokolle extrem laut sind, hat der Logging-Treiber Arbeit zu tun. JSON-Datei-Protokolle können schnell wachsen, wenn keine Rotation konfiguriert ist. Bei einem vielbeschäftigten Dienst kann das Protokollieren jedes Anforderungstextes oder jeder Debug-Zeile zu einem echten Leistungsproblem werden.
Überprüfen Sie die Protokollierungseinstellungen:
docker inspect <container> --format '{{json .HostConfig.LogConfig}}'
Erwägen Sie für lokale und kleine Server-Setups die Protokollrotation in der Daemon-Konfiguration oder der Compose-Datei. Für Produktionsplattformen senden Sie Protokolle an das Protokollierungssystem der Plattform und halten Sie das Anwendungsprotokollvolumen absichtlich gering.
Bind-Mounts verdienen besondere Aufmerksamkeit unter macOS und Windows. Ein Quellbaum, der vom Host in einen Linux-Container gemountet wird, durchquert eine Virtualisierungsschicht. Das ist praktisch für die Entwicklung, kann aber für Abhängigkeitsordner, Datenbanken oder schreibintensive Verzeichnisse viel langsamer sein als ein benannter Volume.
Zum Beispiel kann ein Node-Entwicklungscontainer langsam sein, wenn node_modules auf einem Bind-Mount lebt. Ein besseres Muster ist, Quellcode per Bind-Mount zu mounten, aber Abhängigkeiten in einem benannten Volume zu behalten:
services:
app:
volumes:
- .:/app
- node_modules:/app/node_modules
volumes:
node_modules:
Bevorzugen Sie für Datenbanken benannte Volumes gegenüber Bind-Mounts, es sei denn, Sie haben einen bestimmten Backup- oder Inspektionsworkflow, der Host-Pfade erfordert.
Netzwerklatenz und Abhängigkeitsverlangsamung
Ein Container kann „langsam“ sein, weil er auf einen anderen Dienst wartet. Der lokale Prozess kann gesund sein, während DNS, eine Datenbank, Redis, eine API oder ein Proxy langsam ist.
Testen Sie von innerhalb des Containers:
docker exec -it <container> sh
curl -w '
lookup:%{time_namelookup} connect:%{time_connect} start:%{time_starttransfer} total:%{time_total}
' -o /dev/null -s http://service:8080/health
Diese curl -w-Ausgabe trennt DNS-Lookup, TCP-Verbindung, erstes Byte und Gesamtzeit. Wenn DNS-Lookup langsam ist, überprüfen Sie /etc/resolv.conf und die DNS-Einstellungen des Docker-Daemons. Wenn die Verbindung langsam ist oder fehlschlägt, überprüfen Sie Netzwerke, Firewalls und Service-Bindung. Wenn die Zeit bis zum ersten Byte langsam ist, hat der vorgelagerte Dienst die Verbindung akzeptiert, aber Zeit für die Antwort gebraucht.
Verwenden Sie für Container-zu-Container-Datenverkehr ein benutzerdefiniertes Bridge-Netzwerk, damit Container sich gegenseitig nach Namen auflösen können:
docker network create appnet
docker run -d --name api --network appnet my-api
docker run --rm --network appnet curlimages/curl http://api:8080/health
Führen Sie keine Benchmarks über veröffentlichte Host-Ports durch, wenn der echte Datenverkehr Container-zu-Container ist. Testen Sie den Pfad, den die Produktion verwendet.
Startleistung ist ein separates Problem
Langsamer Start kommt oft von Image-Pull-Zeit, Abhängigkeitsinstallation beim Containerstart, Datenbankmigrationen oder Anwendungs-Warmup.
Ein Container sollte nicht jedes Mal Pakete installieren, wenn er startet. Wenn Ihr Entrypoint npm install, pip install, apt-get ausführt oder Binärdateien bei jedem Booten herunterlädt, verschieben Sie diese Arbeit in den Image-Build, es sei denn, es gibt einen starken Grund dagegen.
Überprüfen Sie Startprotokolle mit Zeitstempeln, wenn Ihre App sie bereitstellt. Wenn nicht, fügen Sie beim Debuggen einfache Zeitstempel um Entrypoint-Schritte hinzu:
date; echo 'starting migrations'
# migration command
date; echo 'starting server'
# server command
Für Images, die über ein Netzwerk gezogen werden, ist die Image-Größe wichtig. Multi-Stage-Builds, .dockerignore und kleinere Laufzeit-Basen verbessern die Kaltstart- und Bereitstellungsgeschwindigkeit. Aber sobald das Image bereits vorhanden ist und der Container läuft, ist die Image-Größe normalerweise weniger wichtig als CPU, Speicher, I/O und Anwendungsverhalten.
Build-Leistung ist nicht Laufzeitleistung
Langsame Docker-Builds sind frustrierend, aber sie sind eine andere Klasse von Problemen. Wenn Codeänderungen die Abhängigkeitsinstallation bei jedem Build erzwingen, korrigieren Sie die Layer-Reihenfolge:
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
Kopieren Sie nicht das gesamte Repository vor der Installation von Abhängigkeiten, es sei denn, Sie möchten, dass jede Quellcodeänderung den Abhängigkeits-Layer ungültig macht.
Halten Sie auch den Build-Kontext klein:
.git
node_modules
coverage
dist
*.log
BuildKit-Cache-Mounts können bei wiederholten Abhängigkeits-Downloads helfen, aber stellen Sie zuerst sicher, dass die Dockerfile korrekt geordnet ist. Ein Cache-Mount kann eine Dockerfile, die den Cache zu früh ungültig macht, nicht vollständig retten.
Ressourcenlimits können den Host schützen und der App schaden
CPU- und Speicherlimits sind nützlich, weil ein Container nicht einen Host lahmlegen sollte. Sie können auch künstliche Langsamkeit erzeugen, wenn sie aus einem Beispiel kopiert werden, ohne die Arbeitslast zu messen.
Überprüfen Sie Limits:
docker inspect <container> --format '{{json .HostConfig}}' | jq '{Memory, NanoCpus, CpuQuota, CpuPeriod, BlkioWeight}'
Wenn jq nicht verfügbar ist, überprüfen Sie den Container normal und suchen Sie nach HostConfig.
Für Compose überprüfen Sie die tatsächliche gerenderte Konfiguration:
docker compose config
Dies fängt Limits, die von Override-Dateien oder Umgebungsvariablen geerbt wurden. Eine häufige Überraschung ist eine Dev-Override-Datei, die niedrige Limits setzt und versehentlich in einer Testumgebung verwendet wird.
Ein praktischer Diagnoseablauf
Verwenden Sie diesen Ablauf, wenn die Beschwerde einfach „der Container ist langsam“ ist:
- Reproduzieren Sie das langsame Verhalten und führen Sie
docker statswährend der Reproduktion aus. - Überprüfen Sie Host-CPU, Arbeitsspeicher, Festplatte und Docker Desktop VM-Limits.
- Überprüfen Sie Container-CPU- und Speicherlimits.
- Lesen Sie Protokolle auf Wiederholungen, Verbindungszeitüberschreitungen, Migrationen, Debug-Protokollierung oder OOM-Hinweise.
- Testen Sie Abhängigkeiten von innerhalb des Containers mit
curl,digoder einem zweckgebauten Debug-Image. - Überprüfen Sie Mounts: Verschieben Sie schreibintensive Pfade nach Möglichkeit zu benannten Volumes.
- Profilieren Sie die Anwendung, wenn Ressourcendiagramme auf Code zurückverweisen.
Die besten Korrekturen sind tendenziell spezifisch: Erhöhen Sie ein zu niedriges Speicherlimit, hören Sie auf, große Nutzlasten zu protokollieren, verschieben Sie Datenbankdaten von einem Bind-Mount, beheben Sie einen langsamen DNS-Pfad, ordnen Sie Dockerfile-Layer neu oder optimieren Sie die Anwendungslaufzeit. Allgemeine „Docker optimieren“-Ratschläge sind weniger nützlich als zu beweisen, welche Ressource tatsächlich langsam ist.