Aufgeblähte Docker-Images verlangsamen Bereitstellungen, verschwenden Speicher, erhöhen die Angriffsfläche und kosten Geld in Bezug auf Bandbreite. Ein typisches nicht optimiertes Node.js- oder Python-Image kann mehr als 1 GB groß sein. Mit den richtigen Techniken passt dieselbe App auf weniger als 100 MB. Hier erfahren Sie, wie Sie Docker-Images drastisch verkleinern.
📋 Table of Contents
- Warum die Bildgröße wichtig ist
- Technik 1: Mehrstufige Builds (größter Gewinn)
- Technik 2: Verwenden Sie minimale Basisbilder
- Technik 3: Layer-Caching optimieren
- Technik 4: Verwenden Sie .dockerignore
- Technik 5: RUN-Befehle kombinieren und bereinigen
- Python-Beispiel: Multi-Stage + Slim
- Bildgröße messen und analysieren
- Optimierungs-Checkliste
- Häufig gestellte Fragen
- Conclusion
Warum die Bildgröße wichtig ist
- Schnellere Bereitstellungen: Kleinere Bilder ziehen und starten schneller, insbesondere beim Skalieren von
- Niedrigere Kosten: Weniger Registrierungsspeicher und Bandbreite (wichtig bei großem Maßstab)
- Kleinere Angriffsfläche: Weniger Pakete bedeuten weniger Schwachstellen
- Schnelleres CI/CD: Das Erstellen und Verschieben kleinerer Bilder beschleunigt Pipelines
Technik 1: Mehrstufige Builds (größter Gewinn)
Erstellen Sie in einer Phase mit allen Build-Tools und kopieren Sie dann nur das endgültige Artefakt in ein sauberes, minimales Image:
# 🐛 Single-stage — includes build tools, dev deps, source (1.1GB)
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
CMD ["node", "dist/index.js"]
# ✅ Multi-stage — final image has only runtime + built output (150MB)
# Build stage
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Runtime stage — clean slate
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev # production deps only
COPY --from=builder /app/dist ./dist # copy ONLY the built output
CMD ["node", "dist/index.js"]
Technik 2: Verwenden Sie minimale Basisbilder
| Basisbild | Ungefähre Größe | Notizen |
|---|---|---|
| Knoten:20 | ~1,1 GB | Vollständiges Debian – für die Produktion vermeiden |
| Knoten:20-slim | ~240 MB | Gekürztes Debian – gute Standardeinstellung |
| Knoten:20-alpin | ~180 MB | Alpine Linux – klein, verbreitet |
| gcr.io/distroless/nodejs | ~170 MB | Kein Shell-/Paketmanager – am sichersten |
# Alpine — smallest common choice
FROM node:20-alpine
# Distroless — no shell, no package manager, minimal attack surface
FROM gcr.io/distroless/nodejs20-debian12
COPY --from=builder /app/dist /app/dist
CMD ["/app/dist/index.js"]
Technik 3: Layer-Caching optimieren
# 🐛 Copying everything first breaks caching — any code change
# re-runs npm install
COPY . .
RUN npm ci
# ✅ Copy dependency files first, install, THEN copy source
COPY package*.json ./
RUN npm ci # this layer is cached unless package.json changes
COPY . . # only this layer rebuilds on code changes
RUN npm run build
Ordnen Sie die Dockerfile-Anweisungen von „am seltensten geändert“ bis „am häufigsten“ an. Abhängigkeiten ändern sich selten, die Quelle ändert sich ständig. Installieren Sie daher Abhängigkeiten, bevor Sie die Quelle kopieren, um die Cache-Treffer zu maximieren.
Technik 4: Verwenden Sie .dockerignore
# .dockerignore — exclude files that bloat the build context
node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
dist
coverage
.vscode
.idea
README.md
*.md
.DS_Store
tests
**/*.test.js
Ohne.dockerignore, COPY . . sendet Ihr gesamtes Verzeichnis (einschließlich node_modules, .git) an den Build – was die Builds verlangsamt und möglicherweise Geheimnisse preisgibt.
Technik 5: RUN-Befehle kombinieren und bereinigen
# 🐛 Each RUN creates a layer; cleanup in a separate layer doesn't shrink
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/* # too late — previous layers keep the data
# ✅ Combine into one layer with cleanup in the same RUN
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/* # cleanup in the SAME layer
Python-Beispiel: Multi-Stage + Slim
# Build stage — compile dependencies
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# Runtime stage — slim base + only installed packages
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]
# Result: ~150MB instead of ~1GB
Bildgröße messen und analysieren
# Check image size
docker images myapp
# Analyze layers to find what's taking space
docker history myapp:latest
# Use 'dive' for detailed layer analysis
dive myapp:latest
# Shows wasted space and what each layer adds
Optimierungs-Checkliste
- Verwenden Sie mehrstufige Builds – kopieren Sie nur Laufzeitartefakte in das endgültige Image
- Verwenden Sie alpine, schlanke oder distroless-Basisimages
- Abhängigkeitsdateien kopieren und vor dem Kopieren der Quelle installieren (Layer-Caching)
- Fügen Sie ein umfassendes
.dockerignore - hinzu Nur Produktionsabhängigkeiten installieren (
npm ci --omit=dev,pip --no-cache-dir) - RUN-Befehle kombinieren und auf derselben Ebene bereinigen
- Basis-Image-Versionen zur Reproduzierbarkeit anpinnen
Häufig gestellte Fragen
F: Ist Alpine immer das beste Basis-Image?
A: Normalerweise das kleinste, aber Alpine verwendet musl libc, was gelegentlich Probleme mit nativen Modulen oder DNS verursacht. Wenn Sie auf Kompatibilitätsprobleme stoßen, verwenden Sie Slim (Debian-basiert) – es ist immer noch klein und kompatibler. Distroless ist am besten for security.
Q: What is distroless and should I use it?
A: Distroless images contain only your app and its runtime — no shell, no package manager, minimal OS. This shrinks size and dramatically reduces attack surface. Great for production, but harder to debug (no shell to exec into).
Q: How much smaller can I realistically get?
A: Multi-stage builds plus a minimal base commonly reduce images by 70-90%. A 1GB Node.js image often becomes 100-150MB. Distroless can go smaller still.
Q: Does image size affect runtime performance?
A: Not directly at runtime, but smaller images pull and start faster (important for autoscaling and cold starts), use less registry storage, and have fewer vulnerabilities. The benefits are in deployment speed and security.
Q: Why does my image stay large despite cleanup commands?
A: Docker layers are immutable — if you add files in one layer and delete them in another, the deleted data still exists in the earlier layer. Clean up in the SAME RUN command, or use multi-stage builds to leave the bloat behind entirely.
Conclusion
Optimizing Docker images comes down to a few high-impact techniques: multi-stage builds (copy only what runtime needs), minimal base images (alpine, slim, or distroless), smart layer caching (dependencies before source), and a thorough .dockerignore. Together these commonly shrink images by 70-90% — from 1GB+ down to 100-150MB. Smaller images deploy faster, cost less, and expose a smaller attack surface. Apply the checklist to your Dockerfiles and measure the results withdocker history or dive.
🔗 Share this article
✍️ Leave a Comment