Docker-Images optimieren
Was dieser Artikel über Docker-Image-Optimierung behandelt
- Warum kleine Images sinnvoll sind.
- Wie Layer und Caching funktionieren.
- Multi-Stage-Builds und .dockerignore.
- Basis-Image-Auswahl.
- Sicherheits- und Performance-Best-Practices.
Einleitung: Docker-Images optimieren
Grosse Docker-Images verlangsamen Builds, Deployments und Downloads. Sie erhöhen die Angriffsfläche, weil mehr Software installiert ist, und belegen unnötig Speicher. Durch gezielte Optimierung lassen sich Images oft von mehreren Gigabyte auf einige hundert Megabyte reduzieren, ohne Funktionalität zu verlieren.
Dieser Artikel zeigt, wie man Docker-Images klein, schnell und sicher macht.
Wichtige Begriffe
- Layer: Schicht in einem Docker-Image.
- Caching: Wiederverwendung unveränderter Layer.
- Multi-Stage-Build: Mehrere Bauabschnitte in einem Dockerfile.
- .dockerignore: Datei, die unerwünschte Dateien vom Build ausschliesst.
- Base Image: Grundlage des Images.
- Distroless: Image ohne Shell und unnötige Tools.
- Squashing: Zusammenfassen mehrerer Layer.
Warum Images optimieren?
- Schnellere Builds und Deployments.
- Weniger Speicherbedarf.
- Kleinere Angriffsfläche.
- Weniger Bandbreite beim Verteilen.
- Einfachere Wartung und Prüfung.
Basis-Image wählen
Gross vs. klein
| Basis | Grösse | Vorteil | Nachteil |
|---|---|---|---|
| ubuntu | ~78 MB | Viele Tools | Relativ gross |
| debian:slim | ~30 MB | Ausgewogen | Weniger Tools |
| alpine | ~5 MB | Sehr klein | musl-Kompatibilität |
| distroless | unter 5 MB | Minimal | Kein Shell, schwerer Debugging |
Empfehlung
- Für Python:
python:3.11-slim. - Für Node.js:
node:20-alpineoderslim. - Für Go:
golang:alpineoderscratchfür fertige Binaries. - Für kritische Umgebungen:
distroless.
Multi-Stage-Builds
# Builder
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Runtime
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
Nur die Laufzeitdateien landen im finalen Image.
Layer und Caching optimieren
Jede Anweisung im Dockerfile erzeugt einen Layer. Häufig geänderte Befehle sollten am Ende stehen:
# Abhängigkeiten zuerst, ändern sich selten
COPY package*.json ./
RUN npm ci
# Quellcode zuletzt, ändert sich oft
COPY . .
So wird das Layer-Caching effektiv genutzt.
.dockerignore
Verhindert, dass unnötige Dateien ins Build-Context gelangen:
node_modules
.git
*.log
.env
Dockerfile
.dockerignore
README.md
tests/
Befehle zusammenfassen
Statt:
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
besser:
RUN apt-get update && apt-get install -y curl && apt-get clean && rm -rf /var/lib/apt/lists/*
Das erzeugt einen einzigen Layer und entfernt Caches.
Nicht benötigte Tools entfernen
- Build-Tools wie Compiler nur in Builder-Stages.
apt-get cleanund Listen entfernen.npm cistattnpm install.- Keine Dokumentation oder Beispiele im Image.
- Keine Testverzeichnisse im Produktions-Image.
Non-Root-User
FROM node:20-slim
RUN useradd -m appuser
USER appuser
Das reduziert das Risiko bei Container-Breakouts.
Labels und Metadaten
LABEL maintainer="team@example.com"
LABEL org.opencontainers.image.source="https://github.com/user/repo"
LABEL org.opencontainers.image.version="1.0.0"
Healthchecks einbauen
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:3000/health || exit 1
Image analysieren
Tools zur Analyse:
docker history mein-image:latest
dive mein-image:latest
dive zeigt, welche Layer viel Speicher belegen.
Beispiel: Optimiertes Dockerfile
# Builder
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
# Runtime
FROM python:3.11-slim
WORKDIR /app
RUN useradd -m appuser
COPY --from=builder /root/.local /home/appuser/.local
COPY --chown=appuser:appuser . .
USER appuser
ENV PATH=/home/appuser/.local/bin:$PATH
HEALTHCHECK CMD curl -f http://localhost:8000/health || exit 1
CMD ["python", "main.py"]
Sicherheit
- Regelmässig neue Basis-Images nutzen.
- Images auf Schwachstellen scannen.
- Keine Secrets im Image.
- Minimale Packages installieren.
- Read-only Filesystem erwägen.
latestvermeiden.
Typische Stolpersteine
- COPY . . zu früh: Invalidiert Cache bei jeder Codeänderung.
- Keine .dockerignore: Build-Kontext wird riesig.
- Build-Tools im finalen Image: Grösser und unsicherer.
- Als root laufen: Erhöhtes Risiko.
- Zu viele Layer: Jeder RUN erzeugt einen.
- Grosse Basis-Images: Unnötig, wenn slim reicht.
- Kein apt clean: Layer wird unnötig gross.
Weiterführende Links und Infos
- BotServ.de Docker Befehle
- BotServ.de Docker CI/CD
- BotServ.de Docker Sicherheit
- BotServ.de Docker Volumes
FAQ: Docker-Images optimieren
Wie klein sollte ein Image sein? So klein wie möglich, ohne Funktion zu verlieren. Oft unter 100 MB für einfache Apps.
Ist Alpine immer besser? Nicht immer. musl kann bei manchen Libraries zu Problemen führen.
Brauche ich Multi-Stage? Bei kompilierten Sprachen und Node.js meist ja.
Was ist distroless? Ein extrem minimales Image ohne Shell, nur Laufzeitdateien.
Wie prüfe ich die Imagegrösse?
Mit docker images, docker history oder dive.
Quellen und weiterführende Literatur
- Docker Best Practices: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
- Distroless Images: https://github.com/GoogleContainerTools/distroless
- dive: https://github.com/wagoodman/dive
- Docker BuildKit: https://docs.docker.com/build/buildkit/
Zusammenfassung: Docker-Images optimieren
Optimierte Docker-Images sind kleiner, schneller und sicherer. Wichtige Massnahmen sind die Wahl eines schlanken Basis-Images, Multi-Stage-Builds, Layer-Caching, eine gute .dockerignore und das Entfernen unnötiger Tools. Non-Root-User, Healthchecks und Labels runden das Image ab. Wer regelmässig Images scannt und mit dive analysiert, behält die Kontrolle über Grösse und Sicherheit.


