Optimierung von Docker-Containern: Fehlerbehebung bei Leistungsengpässen

Läuft Ihr Docker-Container langsam? Dieser wichtige Leitfaden zeigt, wie Sie häufige Leistungsengpässe in containerisierten Anwendungen identifizieren und beheben. Lernen Sie, Docker-Überwachungstools wie `docker stats` effektiv einzusetzen, hohe CPU-/Speicherauslastung zu diagnostizieren, die I/O-Leistung durch Kenntnis des Speichertreibers zu optimieren und Best Practices wie Multi-Stage-Builds für einen schnelleren, effizienteren Betrieb anzuwenden.

Optimierung von Docker-Containern: Fehlerbehebung bei Leistungsengpässen

Wenn ein Docker-Container langsam ist, liegt die Ursache selten allein am Container. Das Problem ist meist eine Ebene tiefer: CPU-Drosselung, Speicherdruck, langsame Schreibvorgänge, DNS-Verzögerungen, laute Nachbarn auf dem Host oder eine Anwendung, die bereits vor der Containerisierung ineffizient war.

Der schnellste Weg, Zeit zu verschwenden, ist, Docker-Flags zu ändern, bevor man weiß, welche Ressource eingeschränkt ist. Beginnen Sie mit Beweisen, isolieren Sie einen Engpass, ändern Sie eine Sache und messen Sie erneut.

Beginnen Sie mit der Triage, nicht mit der Optimierung

docker stats bietet eine schnelle Live-Ansicht:

docker stats
docker stats --no-stream

Verwenden Sie es, um grundlegende Fragen zu beantworten:

  • Ist die CPU hoch und anhaltend?
  • Ist der Speicher nahe am konfigurierten Limit?
  • Steigt der Block-I/O während langsamer Anfragen?
  • Ist die Prozessanzahl unerwartet hoch?
  • Ist der Netzwerk-I/O auf die Arbeitslast abgestimmt?

Überprüfen Sie dann den Containerstatus und die Protokolle:

docker logs --tail 100 <container_name_or_id>
docker inspect <container_name_or_id> --format 'OOM={{.State.OOMKilled}} Exit={{.State.ExitCode}} Restarting={{.State.Restarting}}'

Schauen Sie sich auch den Host an. Ein Container kann harmlos aussehen, während der Host auslagert oder die Festplatte gesättigt ist:

top
free -m
vmstat 1
iostat -xz 1

iostat erfordert möglicherweise das Paket sysstat. Wenn Sie Docker Desktop verwenden, denken Sie daran, dass sich eine VM zwischen Ihrem Host-Betriebssystem und den Linux-Containern befindet, was das Datei- und Netzwerkverhalten verändert.

CPU-Engpässe

Hohe CPU-Auslastung kann bedeuten, dass die Anwendung ausgelastet, unterdimensioniert oder gedrosselt ist. Das sind unterschiedliche Probleme.

Überprüfen Sie die konfigurierten CPU-Einstellungen:

docker inspect <container> --format '{{json .HostConfig.NanoCpus}} {{json .HostConfig.CpuQuota}} {{json .HostConfig.CpuPeriod}}'

Wenn der Container zu stark begrenzt ist, können sich Anfragen stauen, obwohl der Host noch freie CPU hat. Versuchen Sie eine kontrollierte Erhöhung:

docker run -d --name api --cpus="2" my-api:latest

Wenn die CPU nach der Erhöhung des Limits hoch bleibt, profilieren Sie die Anwendung. Beispielsweise könnte ein Node-Dienst in der JSON-Serialisierung stecken bleiben, ein Python-Worker könnte unter der GIL CPU-gebunden sein und ein Java-Dienst könnte Zeit mit der Garbage Collection verbringen. Docker kann das nicht von selbst beheben.

Speicherdruck und OOM-Kills

Speicherprobleme zeigen sich oft als Neustarts, Latenzspitzen oder das Verschwinden des Prozesses unter Last.

Überprüfen Sie, ob Docker einen OOM-Kill festgestellt hat:

docker inspect <container> --format '{{.State.OOMKilled}}'

Wenn der Speicher langsam ansteigt und nie fällt, suchen Sie nach einem Leck oder einem unbegrenzten Cache. Wenn der Speicher bei bestimmten Anfragen ansteigt, reproduzieren Sie diesen Pfad unter Last. Wenn eine Laufzeitumgebung ein eigenes Heap-Limit hat, gleichen Sie es mit dem Container ab. Eine JVM, die das tatsächliche Container-Budget nicht versteht, kann sich schlecht verhalten; moderne JVMs sind containerbewusst, aber die Heap-Einstellungen verdienen dennoch eine Überprüfung.

Speicherlimits sollten Spielraum lassen. Ein Container, der bei normalem Datenverkehr 950 MB eines 1-GB-Limits verwendet, ist nicht gesund. Garbage Collection, temporäre Puffer, TLS, Komprimierung und Anforderungsspitzen benötigen alle Platz.

Behebung von Eingabe-/Ausgabeleistungsproblemen (I/O)

Langsamer Festplattenzugriff betrifft Datenbanken, Warteschlangen, Suchmaschinen, Cache-Warmups und ausführliche Protokollierung. Finden Sie zunächst heraus, ob Schreibvorgänge in die beschreibbare Ebene des Containers, ein benanntes Volume oder einen Bind-Mount erfolgen.

docker inspect <container> --format '{{json .Mounts}}'
docker info --format 'StorageDriver={{.Driver}}'

Bei modernen Linux-Docker-Installationen ist overlay2 der übliche Standard-Speichertreiber. Er ist normalerweise eine gute Wahl, aber das Schreiben großer, veränderlicher Daten in die Containerebene ist dennoch ein schlechtes Muster.

Verwenden Sie benannte Volumes für persistente Anwendungsdaten:

docker volume create app-data
docker run -d --name app -v app-data:/var/lib/app my-image

Verwenden Sie Bind-Mounts, wenn Sie einen bestimmten Host-Pfad benötigen, testen Sie sie jedoch. Docker Desktop-Bind-Mounts unter macOS und Windows können viel langsamer sein als nativer Linux-Dateisystemzugriff, da Dateioperationen eine Virtualisierungsgrenze überschreiten.

Für temporäre Hochgeschwindigkeitsdateien kann /dev/shm helfen, ist aber speichergestützt und begrenzt:

docker run --shm-size=512m my-image

Dies ist üblich für Browser, Test-Runner und Anwendungen, die gemeinsamen Speicher benötigen. Es ist kein Ersatz für echten Speicher.

Optimierung der Image-Größe und Build-Leistung

Die Image-Größe wirkt sich hauptsächlich auf Build-, Pull-, Scan- und Bereitstellungszeit aus. Sie macht die Bearbeitung von Anfragen normalerweise nicht schneller, sobald der Container läuft, ist aber betrieblich dennoch wichtig.

Verwenden Sie Multi-Stage-Builds, damit Compiler, Paket-Caches und Testtools nicht im Laufzeit-Image landen:

FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app

FROM alpine:3.20
COPY --from=builder /app/app /app
CMD ["/app"]

Ordnen Sie Dockerfile-Anweisungen für die Cache-Wiederverwendung:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

Das Ändern des Quellcodes sollte keine Abhängigkeits-Downloads erzwingen, es sei denn, die Abhängigkeitsdateien haben sich geändert.

Überlegungen zur Netzwerkleistung

Netzwerkverlangsamungen sehen oft wie Anwendungsverlangsamungen aus. Testen Sie von innerhalb des Containers:

docker exec -it <container> sh
time getent hosts api.example.com
time wget -qO- https://api.example.com/health

Wenn die DNS-Auflösung langsam oder unzuverlässig ist, überprüfen Sie /etc/resolv.conf im Container und vergleichen Sie es mit dem Host. Sie können DNS-Server zur Laufzeit bereitstellen:

docker run -d --name web --dns 1.1.1.1 my-image

Tun Sie dies als diagnostische oder strategische Entscheidung, nicht als zufällige Korrektur. In Unternehmensnetzwerken kann internes DNS erforderlich sein.

Das Standard-Bridge-Netzwerk von Docker fügt NAT und iptables-Verarbeitung hinzu. Für die meisten Webanwendungen ist der Overhead akzeptabel. Host-Netzwerke können den Overhead unter Linux reduzieren:

docker run --network host my-image

Es entfernt auch die Netzwerk-Namespace-Isolation und ändert die Port-Handhabung. Verwenden Sie es, wenn Sie einen tatsächlichen Bedarf gemessen haben.

Eine Feld-Checkliste

Wenn ein Container langsam ist, arbeiten Sie diese Liste ab:

  1. Reproduzieren Sie die Verlangsamung mit einer bestimmten Anfrage, einem Job oder einer Arbeitslast.
  2. Erfassen Sie docker stats --no-stream, Protokolle und die Ausgabe von docker inspect.
  3. Überprüfen Sie Host-CPU, Speicher, Swap, Festplatten-I/O und Netzwerk.
  4. Identifizieren Sie die eingeschränkte Ressource, bevor Sie Limits ändern.
  5. Verschieben Sie persistente oder umfangreiche Schreibvorgänge auf Volumes.
  6. Vergleichen Sie das Verhalten von Bind-Mounts unter Linux mit Docker Desktop, wenn die lokale Entwicklung der einzige langsame Ort ist.
  7. Profilieren Sie die Anwendung, wenn Containermetriken die Verlangsamung nicht erklären.
  8. Ändern Sie eine Einstellung und messen Sie erneut.

Die nützliche Denkweise ist einfach: Docker bietet Isolation und Paketierung, ersetzt aber nicht die normale Systemarbeit. CPU, Speicher, Festplatte und Netzwerk bestimmen immer noch, wie schnell sich der Dienst anfühlt.