Docker Compose Produktions-Deployment: 7 Schlüsselstrategien von Health Checks bis Zero-Downtime-Updates
"Funktioniert auf meiner Maschine" — Der Container-Friedhof in der Produktion
Das Mantra jedes Entwicklers: "Funktioniert auf meiner Maschine." Aber wenn Container in die Produktion gehen, beginnt der wahre Albtraum:
- Container werden lautlos OOM Killed und hinterlassen nur
Out of memoryin den Logs - Datenbank noch nicht bereit, Anwendungscontainer schreien
Connection refused - Container stürzt um 3 Uhr morgens ohne Neustart-Richtlinie ab — Dienst offline bis zum Morgen
- Log-Dateien füllen Festplatten,
docker logsgibt zig GB unstrukturierten Text aus - Produktionszugangsdaten im Klartext in
docker-compose.yml, Datenbankpasswörter offengelegt
Wenn Sie immer noch docker compose up -d als Ihre gesamte Produktionsstrategie verwenden, ist dieser Artikel für Sie.
Referenz der Kernkonzepte
| Konzept | Zweck | Wichtige Produktionskonfiguration |
|---|---|---|
| Health Check | Erkennen, ob ein Container wirklich bereit ist | healthcheck + depends_on.condition |
| Ressourcenlimits | CPU/Speicher begrenzen, Ressourcenverschwendung verhindern | deploy.resources.limits |
| Neustart-Richtlinie | Automatischer Neustart bei abnormalem Beenden | deploy.restart_policy |
| Secrets | Verschlüsselte Speicherung sensibler Daten | secrets + Docker Secret |
| Logging-Treiber | Strukturiertes Logging + Log-Rotation | logging.driver + logging.options |
| Profiles | Selektiver Dienststart nach Umgebung | profiles |
| Watch | Automatische Synchronisierung von Dateiänderungen in Container | watch (Compose Watch) |
5 Produktionsherausforderungen
Herausforderung 1: Unkontrollierbare Container-Startreihenfolge
Die Datenbank ist noch beim Initialisieren, während der Anwendungscontainer versucht, sich zu verbinden, was zu Startfehlern führt. depends_on garantiert nur die Startreihenfolge, nicht die Dienstbereitschaft.
Herausforderung 2: Unbegrenzte Ressourcenausweitung
Container ohne Ressourcenlimits sind wie Autos ohne Bremsen. Ein einziger Container mit Speicherleck kann den gesamten Host-Speicher verbrauchen und alle Dienste zum Absturz bringen.
Herausforderung 3: Das Log-Schwarze Loch
Der Standard-Log-Treiber json-file rotiert nicht. Nach 3 Monaten Laufzeit füllt /var/lib/docker die Festplatte und alle Dienste stürzen ab.
Herausforderung 4: Offenlegung sensibler Daten
Datenbankpasswörter in Klartext-environment-Blöcken, .env-Dateien in Git committet, API-Schlüssel in Images hardcodiert — das sind Zeitbomben für Produktionsvorfälle.
Herausforderung 5: Updates bedeuten Ausfallzeit
docker compose up -d stoppt standardmäßig alte Container, bevor neue gestartet werden, was den Dienst während Updates nicht verfügbar macht. Für 24/7-Dienste ist dies inakzeptabel.
7 Produktionsmuster
Muster 1: Health Checks und Abhängigkeitssortierung
Problem: depends_on steuert nur die Startreihenfolge, garantiert nicht die Dienstbereitschaft.
Lösung: healthcheck + depends_on.condition: service_healthy verwenden.
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
app:
image: myapp:latest
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "3000:3000"
Schlüsselparameter:
interval: Prüfintervall, 5-10 Sekunden für Produktion empfohlentimeout: Timeout für einzelne Prüfung, 3-5 Sekunden empfohlenretries: Aufeinanderfolgende Fehler vor Markierung als unhealthystart_period: Kulanzzeit nach Container-Start, Fehler zählen nicht zu den Versuchen
Muster 2: Ressourcenlimits und OOM-Schutz
Problem: Unbegrenzte Container konkurrieren um Host-Ressourcen. Ein entfesselter Container kann die gesamte Maschine zum Absturz bringen.
Lösung: deploy.resources verwenden, um CPU- und Speicherlimits und -reservierungen festzulegen.
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
worker:
image: myworker:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
memory: 512M
Limits vs. Reservierungen:
limits: Harte Obergrenze, überschritten → OOM Kill oder CPU-Throttlingreservations: Weiche Garantie, Scheduler versucht zu erfüllen, erzwingt aber nicht
OOM-Schutzstrategie:
services:
critical-service:
image: critical-app:latest
deploy:
resources:
limits:
memory: 1G
cap_add:
- SYS_PTRACE
Auf Host-Ebene vm.overcommit_memory konfigurieren und OOM-Richtlinien anpassen:
# Container OOM-Score prüfen
docker inspect --format='{{.State.OOMKilled}}' <container_id>
# Host OOM-Richtlinie festlegen: kritische Prozesse nicht beenden
echo -1000 > /proc/<pid>/oom_score_adj
Muster 3: Strukturiertes Logging und Log-Rotation
Problem: Der Standard-Log-Treiber json-file rotiert nicht; Festplatten füllen sich mit der Zeit.
Lösung: Log-Treiber + Rotationsrichtlinie konfigurieren. Der Treiber local wird für die Produktion empfohlen.
services:
app:
image: myapp:latest
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
tag: "{{.Name}}/{{.ID}}"
nginx:
image: nginx:1.27-alpine
logging:
driver: json-file
options:
max-size: "50m"
max-file: "10"
tag: "nginx/{{.Name}}"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
Vergleich der Log-Treiber:
| Treiber | Anwendungsfall | Vorteile | Nachteile |
|---|---|---|---|
| local | Produktionsstandard | Auto-Rotation, komprimierte Speicherung | Nur lokal |
| json-file | Strukturierte JSON-Logs benötigt | Docker-native Unterstützung | Manuelle Rotationskonfiguration erforderlich |
| syslog | Zentralisierte Log-Sammlung | Kann an Remote gesendet werden | Komplexe Konfiguration |
| fluentd | EFK-Stack-Integration | Flexibles Log-Routing | Erfordert Fluentd-Bereitstellung |
Strukturiertes Logging auf Anwendungsebene:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV TZ=UTC
ENV LOG_FORMAT=json
CMD ["node", "server.js"]
const logger = {
info: (msg, meta = {}) => {
console.log(JSON.stringify({ level: 'info', msg, ts: new Date().toISOString(), ...meta }));
},
error: (msg, meta = {}) => {
console.error(JSON.stringify({ level: 'error', msg, ts: new Date().toISOString(), ...meta }));
}
};
Muster 4: Secrets-Verwaltung
Problem: Sensible Daten im Klartext in Compose-Dateien oder Umgebungsvariablen gespeichert.
Lösung: Docker Secrets + Umgebungsvariablen mit _FILE-Suffix.
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
app:
image: myapp:latest
environment:
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/appdb
API_KEY_FILE: /run/secrets/api_key
JWT_SECRET_FILE: /run/secrets/jwt_secret
secrets:
- api_key
- jwt_secret
Verwaltung von Secrets-Dateien:
# Secrets-Verzeichnis erstellen
mkdir -p secrets
chmod 700 secrets
# Secrets-Dateien schreiben
echo "my-super-secret-password-2026" > secrets/db_password.txt
echo "ak-live-xxxx-yyyy-zzzz" > secrets/api_key.txt
echo "jwt-hs256-secret-key-here" > secrets/jwt_secret.txt
# Berechtigungen setzen: nur root-lesbar
chmod 600 secrets/*.txt
.gitignore muss enthalten:
secrets/
*.secret
.env.production
.env.staging
Vergleich Docker Secrets vs. .env:
| Funktion | Docker Secrets | .env-Dateien |
|---|---|---|
| Verschlüsselte Speicherung | Ja (im Swarm-Modus) | Nein |
| Dateiberechtigungen | Eingeschränkt (/run/secrets/) | Hängt vom Dateisystem ab |
| Audit-Trail | Ja | Nein |
| Knotenübergreifende Synchronisation | Swarm synchronisiert automatisch | Manuelle Verteilung |
| Anwendungsfall | Swarm/Einzelhost-Produktion | Entwicklung |
Muster 5: Zero-Downtime-Rolling-Updates
Problem: docker compose up -d stoppt standardmäßig alte Container, bevor neue gestartet werden.
Lösung: docker compose up --no-down + Health Checks + Reverse Proxy verwenden.
services:
app:
image: myapp:${APP_VERSION:-latest}
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
order: start-first
failure_action: rollback
rollback_config:
parallelism: 0
order: stop-first
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 5s
timeout: 3s
retries: 3
start_period: 15s
labels:
- "com.toolsku.app=true"
ports:
- "3000-3001:3000"
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 10s
timeout: 3s
retries: 3
Nginx Reverse-Proxy-Konfiguration:
upstream app_backend {
server app:3000;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_next_upstream error timeout http_502 http_503;
}
location /health {
access_log off;
return 200 'ok';
add_header Content-Type text/plain;
}
}
Zero-Downtime-Update-Skript:
#!/bin/bash
set -euo pipefail
NEW_IMAGE="myapp:v2.0.0"
echo "🚀 Starte Zero-Downtime-Update auf ${NEW_IMAGE}"
# 1. Neues Image pullen
docker compose pull app
# 2. Neue Container starten (ohne alte zu stoppen)
docker compose up -d --no-deps --scale app=2 app
# 3. Warten, bis neue Container gesund sind
echo "⏳ Warte auf gesunde neue Container..."
sleep 15
# 4. Gesundheit des neuen Containers überprüfen
for i in $(seq 1 30); do
if curl -sf http://localhost:3000/health > /dev/null 2>&1; then
echo "✅ Neuer Container ist gesund"
break
fi
if [ $i -eq 30 ]; then
echo "❌ Health Check fehlgeschlagen, Rollback..."
docker compose up -d --no-deps --scale app=1 app
exit 1
fi
sleep 2
done
# 5. Auf 1 Replikat herunterskalieren
docker compose up -d --no-deps --scale app=1 app
echo "🎉 Update erfolgreich abgeschlossen"
Muster 6: Prometheus + Grafana Monitoring-Stack
Problem: Produktion ohne Monitoring ist wie Fliegen auf Sicht — man erfährt Probleme nur durch Benutzerbeschwerden.
Lösung: Einen vollständigen Prometheus + Grafana Monitoring-Stack bereitstellen.
services:
prometheus:
image: prom/prometheus:v2.52.0
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
- '--storage.tsdb.retention.size=5GB'
deploy:
resources:
limits:
memory: 512M
networks:
- monitoring
grafana:
image: grafana/grafana:11.0.0
container_name: grafana
restart: unless-stopped
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD_FILE: /run/secrets/grafana_password
secrets:
- grafana_password
volumes:
- grafana_data:/var/lib/grafana
deploy:
resources:
limits:
memory: 256M
depends_on:
- prometheus
networks:
- monitoring
node-exporter:
image: prom/node-exporter:v1.8.0
container_name: node-exporter
restart: unless-stopped
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
networks:
- monitoring
alertmanager:
image: prom/alertmanager:v0.27.0
container_name: alertmanager
restart: unless-stopped
ports:
- "9093:9093"
volumes:
- ./monitoring/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
networks:
- monitoring
secrets:
grafana_password:
file: ./secrets/grafana_password.txt
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
driver: bridge
Prometheus-Konfiguration:
# monitoring/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'app'
static_configs:
- targets: ['app:3000']
metrics_path: /metrics
Alarmregeln:
# monitoring/alert_rules.yml
groups:
- name: container_alerts
rules:
- alert: ContainerDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Container {{ $labels.instance }} is down"
- alert: HighMemoryUsage
expr: process_resident_memory_bytes / (1024 * 1024) > 400
for: 5m
labels:
severity: warning
annotations:
summary: "Memory usage exceeds 400MB on {{ $labels.instance }}"
- alert: DiskSpaceLow
expr: node_filesystem_avail_bytes / node_filesystem_size_bytes < 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "Disk space below 10% on {{ $labels.instance }}"
Muster 7: Multi-Umgebungskonfiguration (dev/staging/prod)
Problem: Dev-, Staging- und Produktionskonfigurationen sind vermischt — die Änderung einer Umgebungskonfiguration riskiert die Beeinträchtigung anderer.
Lösung: docker-compose.override.yml + Multi-Datei-Overlay-Strategie verwenden.
Verzeichnisstruktur:
project/
├── docker-compose.yml # Basiskonfiguration
├── docker-compose.override.yml # Dev-Überschreibung (automatisch geladen)
├── docker-compose.staging.yml # Staging-Überschreibung
├── docker-compose.prod.yml # Produktions-Überschreibung
├── .env # Standard-Umgebungsvariablen
├── .env.staging # Staging-Umgebungsvariablen
├── .env.prod # Produktions-Umgebungsvariablen
├── monitoring/
│ ├── prometheus.yml
│ └── alertmanager.yml
└── secrets/
├── db_password.txt
├── api_key.txt
└── grafana_password.txt
Basiskonfiguration docker-compose.yml:
services:
app:
image: myapp:${APP_VERSION:-latest}
environment:
NODE_ENV: ${NODE_ENV:-development}
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/appdb
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
postgres_data:
networks:
app-network:
driver: bridge
Dev-Überschreibung docker-compose.override.yml (automatisch geladen):
services:
app:
build: .
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
- "9229:9229"
environment:
NODE_ENV: development
LOG_LEVEL: debug
deploy:
resources:
limits:
memory: 512M
command: node --inspect=0.0.0.0:9229 server.js
adminer:
image: adminer:latest
ports:
- "8080:8080"
networks:
- app-network
Produktions-Überschreibung docker-compose.prod.yml:
services:
app:
image: myapp:${APP_VERSION}
ports:
- "3000:3000"
environment:
NODE_ENV: production
LOG_LEVEL: info
deploy:
replicas: 2
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
postgres:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
memory: 1G
logging:
driver: local
options:
max-size: "50m"
max-file: "10"
redis:
deploy:
resources:
limits:
memory: 512M
command: redis-server --appendonly yes --maxmemory 400mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.prod.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
deploy:
resources:
limits:
memory: 128M
logging:
driver: local
options:
max-size: "50m"
max-file: "10"
volumes:
redis_data:
Startbefehle:
# Entwicklung (lädt Override automatisch)
docker compose up -d
# Staging
docker compose -f docker-compose.yml -f docker-compose.staging.yml --env-file .env.staging up -d
# Produktion
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d
5 Häufige Fallstricke
Fallstrick 1: depends_on bedeutet nicht, dass der Dienst bereit ist
❌ Falsch:
services:
app:
depends_on:
- postgres
# postgres-Container gestartet, aber DB ist möglicherweise noch nicht initialisiert
✅ Richtig:
services:
app:
depends_on:
postgres:
condition: service_healthy
postgres:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
Fallstrick 2: Keine Ressourcenlimits
❌ Falsch:
services:
app:
image: myapp:latest
# Keine Ressourcenlimits — ein Speicherleck kann den gesamten Host verbrauchen
✅ Richtig:
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
Fallstrick 3: Keine Log-Rotation
❌ Falsch:
services:
app:
image: myapp:latest
# Standard json-file-Treiber, Logs wachsen unbegrenzt
✅ Richtig:
services:
app:
image: myapp:latest
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
Fallstrick 4: Sensible Daten im Klartext
❌ Falsch:
services:
postgres:
environment:
POSTGRES_PASSWORD: "my-secret-password-123"
# Passwort im Klartext in der Compose-Datei — Katastrophe bei Git-Commit
✅ Richtig:
services:
postgres:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Fallstrick 5: Den latest-Tag verwenden
❌ Falsch:
services:
app:
image: myapp:latest
# Jeder Pull kann ein anderes Image erhalten — nicht reproduzierbar
✅ Richtig:
services:
app:
image: myapp:2.1.0
# Oder eine Variable für Versionskontrolle verwenden
image: myapp:${APP_VERSION:-2.1.0}
Fehlerbehebungsreferenz
| Fehlermeldung | Ursache | Lösung |
|---|---|---|
OOMKilled |
Container hat Speicherlimit überschritten | memory-Limit erhöhen oder App-Speicherverbrauch optimieren |
Connection refused |
Abhängiger Dienst nicht bereit | healthcheck + depends_on.condition hinzufügen |
no space left on device |
Logs/Images füllen Festplatte | logging-Rotation konfigurieren + docker system prune |
restarting-Schleife |
App stürzt beim Start ab | docker logs <id> prüfen und Konfiguration verifizieren |
permission denied |
Datei-/Verzeichnisberechtigungsproblem | user-Direktive und Volume-Berechtigungen prüfen |
port is already allocated |
Port-Konflikt | Port-Zuordnung ändern oder konfliktierenden Prozess stoppen |
unhealthy-Status |
Health Check schlägt fehl | Verifizieren, dass healthcheck-Befehl korrekt ist |
secret not found |
Secret-Datei fehlt | Sicherstellen, dass entsprechende Datei in secrets/ existiert |
Cannot connect to the Docker daemon |
Docker läuft nicht | systemctl start docker |
image pulling failed |
Image-Pull-Fehler | Netzwerk/Registry-Auth/Image-Namensschreibweise prüfen |
Erweiterte Optimierung
Docker Compose Watch für die Entwicklung
Compose Watch synchronisiert Dateiänderungen automatisch in Container, ohne Images neu zu erstellen:
services:
app:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: ./package.json
- action: sync+restart
path: ./config
target: /app/config
# Watch-Modus starten
docker compose watch
Netzwerkisolation und Sicherheit
services:
app:
networks:
- frontend
- backend
postgres:
networks:
- backend
# postgres nicht im Frontend-Netzwerk — externer Zugriff blockiert
nginx:
networks:
- frontend
ports:
- "80:80"
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
# internal:true deaktiviert externen Zugriff
Image-Optimierung mit Multi-Stage-Builds
# ---- Build-Stufe ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- Runtime-Stufe ----
FROM node:20-alpine AS runner
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
Vergleich von Orchestrierungstools
| Funktion | Docker Compose | Kubernetes | Nomad | Docker Swarm |
|---|---|---|---|---|
| Komplexität | Niedrig | Hoch | Mittel | Niedrig |
| Einzelhost-Deploy | ✅ Hervorragend | ❌ Übertrieben | ⚠️ Nutzbar | ✅ Hervorragend |
| Multi-Host-Orchestrierung | ❌ Nicht unterstützt | ✅ Kernfähigkeit | ✅ Kernfähigkeit | ⚠️ Basis |
| Auto-Scaling | ❌ | ✅ HPA/VPA | ✅ | ⚠️ Manuell |
| Rolling Updates | ⚠️ Erfordert Skripte | ✅ Nativ | ✅ Nativ | ✅ Nativ |
| Service Discovery | ⚠️ DNS | ✅ CoreDNS | ✅ Consul | ✅ DNS |
| Storage-Orchestrierung | ❌ | ✅ CSI | ✅ CSI | ⚠️ Basis |
| Lernkurve | Niedrig | Hoch | Mittel | Niedrig |
| Skalierungs-Fit | 1-10 Dienste | 100+ Dienste | 50+ Dienste | 10-50 Dienste |
| Produktionsbereit | ✅ Einzelhost | ✅ Großer Maßstab | ✅ Mittel bis groß | ⚠️ Abnehmende Community |
Empfehlung: Docker Compose für Einzelhost-/Kleinmaßstabs-Produktion verwenden, Kubernetes für großen Maßstab, Nomad wenn Sie im HashiCorp-Ökosystem sind. Docker Swarm wird zunehmend marginalisiert — nicht für neue Projekte empfohlen.
Zusammenfassung
Docker Compose Produktions-Deployment ist nicht nur
docker compose up -d. Health Checks stellen sicher, dass Dienste wirklich bereit sind, Ressourcenlimits verhindern OOM-Lawinen, Log-Rotation vermeidet Festplattenerschöpfung, Secrets schützen sensible Daten, Zero-Downtime-Updates garantieren 24/7-Verfügbarkeit, Monitoring-Stacks beseitigen blinde Flecken und Multi-Umgebungskonfigurationen halten dev/staging/prod ordnungsgemäß getrennt. Beherrschen Sie diese 7 Strategien, und Docker Compose ist vollkommen in der Lage, Produktions-Deployments im kleinen bis mittleren Maßstab durchzuführen.
Empfohlene Werkzeuge
- JSON-Formatierer - Docker Compose YAML-bezogene JSON-Konfigurationen formatieren
- Base64-Kodierung - Secrets und sensible Konfigurationsdaten kodieren
- Hash-Rechner - Checksummen für Konfigurationsdateien generieren und Deploy-Konsistenz sicherstellen
Probiere diese browser-lokalen Tools aus — keine Registrierung erforderlich →