Das 1GB-Image-Problem
Vielleicht kennst du das: Du schreibst eine einfache Node.js App, baust ein Docker-Image und plötzlich wiegt das Ding 1,2 GB. Warum? Weil du unbewusst das gesamte Build-System, Python, Compiler und tausende Entwickler-Abhängigkeiten (devDependencies) mit in dein Produktiv-Image schleppst.
Heute zeigen wir dir, wie du das mit Multi-Stage Builds radikal optimierst.
Vorbereitung: Projekt-Setup in der Sandbox
Wir erstellen einen sauberen Test-Ordner für unser Experiment:
mkdir docker-image-expert && cd docker-image-expert
npm init -y
npm install express
touch index.js
docker-image-expert/index.js:
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Performance Rocks!'));
app.listen(3000, () => console.log('Server running on 3000'));
Der Übeltäter: Das "naive" Dockerfile
Früher haben wir oft so gebaut (Bitte NICHT so machen!):
docker-image-expert/Dockerfile.bad:
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build # Falls wir TS nutzen
CMD ["node", "dist/index.js"]
Dieses Image enthält am Ende alles: Den TypeScript-Compiler, das gesamte node_modules-Verzeichnis und alle Source-Files.
Die Lösung: Multi-Stage Builds
Wir teilen den Bauprozess in zwei Phasen auf: Build (Bauen) und Ship (Liefern).
docker-image-expert/Dockerfile:
# Stage 1: Build Phase
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# Angenommen wir bauen TS oder komprimieren Assets
# RUN npm run build
# Stage 2: Production Phase
FROM node:20-alpine
WORKDIR /app
# Wir kopieren NUR die package.json und installieren nur Prod-Dependencies
COPY package*.json ./
RUN npm install --omit=dev
# Wir kopieren NUR das gebaute Resultat aus der ersten Stage
COPY --from=builder /app/index.js .
# COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "index.js"]
Warum Alpine?
Im Vergleich zum Standard-Node-Image (basiert auf Debian) nutzt node:20-alpine die leichtgewichtige Alpine Linux Distribution. Das allein spart schon ca. 600 MB!
Der Vergleich am Terminal
Baue beide Versionen und vergleiche die Größe:
docker build -t app-bad -f Dockerfile.bad .
docker build -t app-good .
docker images | grep app-
⚠️ 📸 SCREENSHOT ANFRAGE: Hier einen Screenshot vom
docker imagesOutput einfügen, der den gewaltigen Größenunterschied zwischen den beiden Images zeigt.
Fazit
Multi-Stage Builds sind kein Luxus, sondern Standard für professionelle CI/CD-Pipelines. Sie sorgen für:
- Schnellere Deployments: Dein Server zieht 50 MB viel schneller als 1 GB.
- Mehr Sicherheit: Weniger Tools im Image bedeuten weniger Angriffsfläche.
- Geringere Kosten: Dein Registry-Speicher füllt sich nicht mehr so schnell.
Cheatsheet für Optimierer
- .dockerignore nicht vergessen: Exkludiere
node_modulesund.gitvomCOPY-Befehl! - Layer-Caching: Kopiere immer erst
package.json, bevor du den Rest kopierst. So mussnpm installnur laufen, wenn sich die Abhängigkeiten ändern. - Löschen (wenn alles fertig ist):
docker system prune(Aber Vorsicht, nicht-anutzen!)
Login