Einleitung: Warum fortgeschrittene TypeScript-Containerisierung notwendig ist
Die Containerisierung von TypeScript-Anwendungen mit Docker ist längst kein Trend mehr – sie ist der Standard moderner Softwareentwicklung. Allerdings sind einfache Dockerfiles, die einfach das gesamte Projekt kopieren und npm start ausführen, weit vom Ideal entfernt. Auf fortgeschrittener Ebene stehen wir vor Herausforderungen wie der Optimierung der Image-Größe, der Gewährleistung von Sicherheit, der Organisation einer Microservice-Architektur und der Konfiguration der automatischen Wiederherstellung.
In diesem Artikel behandeln wir Techniken, die Ihre Docker-Pipeline für TypeScript zum Industriestandard machen: Multi-Stage Builds, Minimierung von Layern, Verwendung von .dockerignore, Docker Compose für Microservices, Healthchecks und die Arbeit mit Umgebungsvariablen. Alle Beispiele sind in TypeScript geschrieben und bereit für die Integration in reale Projekte.
1. Multi-Stage Build: Von der Entwicklung bis zur Produktion
Das Hauptproblem einfacher Dockerfiles ist, dass sie alles in das finale Image aufnehmen: Quellcode, node_modules für die Entwicklung, Build-Tools. Für TypeScript ist dies kritisch, da tsc und devDependencies in der Produktion nicht benötigt werden.
Multi-Stage Builds lösen dieses Problem: Die erste Stufe baut und kompiliert TypeScript, die zweite erstellt ein minimales Image nur mit den notwendigen Dateien.
Beispiel Dockerfile mit zwei Stufen
# Stufe 1: TypeScript-BuildFROM node:20-alpine AS builderWORKDIR /appCOPY package*.json ./RUN npm ci --only=productionCOPY tsconfig.json ./COPY src/ ./src/RUN npm run build
# Stufe 2: Production-ImageFROM node:20-alpineWORKDIR /appCOPY --from=builder /app/dist ./distCOPY --from=builder /app/node_modules ./node_modulesCOPY package.json ./EXPOSE 3000CMD ["node", "dist/index.js"]Wichtige Punkte:
- Verwendung von
npm ci– exakte Reproduktion der Abhängigkeiten aus der Lock-Datei. - Nur
dist/undnode_moduleskopieren – TypeScript-Quellcode gelangt nicht in die Produktion. - Alpine-Image – minimale Basis (ca. 5 MB).
Ein solches Image ist 3-5 Mal kleiner als ein naiver Build. Bei einem großen Projekt mit Dutzenden von Abhängigkeiten kann der Unterschied Hunderte von Megabyte betragen.
Drei-Stufen-Build mit dev-Abhängigkeiten
Manchmal müssen dev- und prod-Abhängigkeiten getrennt werden. Wir fügen eine dritte Stufe hinzu, um nur die Produktionsabhängigkeiten zu installieren:
# Stufe 1: Installation aller AbhängigkeitenFROM node:20-alpine AS depsWORKDIR /appCOPY package*.json ./RUN npm ci
# Stufe 2: BuildFROM node:20-alpine AS builderWORKDIR /appCOPY --from=deps /app/node_modules ./node_modulesCOPY . .RUN npm run build
# Stufe 3: ProductionFROM node:20-alpine AS runnerWORKDIR /appCOPY --from=builder /app/dist ./distCOPY --from=deps /app/node_modules ./node_modulesCOPY package.json ./EXPOSE 3000CMD ["node", "dist/index.js"]2. Image-Optimierung: .dockerignore, Caching und Layer
Jede COPY-, RUN- und ADD-Anweisung erstellt einen neuen Layer in Docker. Je weniger Layer und je effizienter sie gecached werden, desto schneller erfolgt der Build.
.dockerignore – Ihr erster Helfer
Erstellen Sie eine Datei .dockerignore im Stammverzeichnis des Projekts:
node_modulesdist.git.gitignore*.md.env.env.*Dockerfile.dockerignorecoverage.vscode.idea__tests__Dies verhindert das Kopieren von Müll in den Build-Kontext und beschleunigt die Übertragung von Dateien an den Docker-Daemon.
Richtige Reihenfolge der Anweisungen
Docker cached Layer. Wenn Sie zuerst package.json kopieren und dann npm ci ausführen, wird dieser Layer wiederverwendet, solange sich package.json nicht ändert. Kopieren Sie dann den Quellcode