Docker Compose Produktions-Deployment: 7 Schlüsselstrategien von Health Checks bis Zero-Downtime-Updates

DevOps

"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 memory in 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 logs gibt 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 empfohlen
  • timeout: Timeout für einzelne Prüfung, 3-5 Sekunden empfohlen
  • retries: Aufeinanderfolgende Fehler vor Markierung als unhealthy
  • start_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-Throttling
  • reservations: 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 →

#Docker#Docker Compose#生产部署#容器编排#2026#DevOps