Veröffentlicht am

Docker zu Kubernetes Migration: 10 häufige Fehler vermeiden

Teilen:
Authors

TL;DR

  • Die meisten Migrationsfehler entstehen nicht bei der Containerisierung selbst, sondern beim Uebergang von Docker-Compose-Konventionen zu Kubernetes-Konzepten
  • Fehlende Resource Limits sind der haeufigste Grund fuer instabile Cluster nach der Migration -- ein einzelner Container ohne Limits kann den gesamten Node lahmlegen
  • Stateful Workloads (Datenbanken, Message Queues) erfordern PersistentVolumes, StatefulSets und durchdachte Backup-Strategien statt einfacher Volume-Mounts
  • Health Checks sind in Kubernetes nicht optional: Ohne Liveness und Readiness Probes routet der Service Traffic an Container, die noch nicht bereit sind
  • Eine schrittweise Migration mit parallelem Betrieb (Docker Compose + Kubernetes) reduziert das Risiko erheblich

Container Migration Fehler: 10 haeufige Probleme bei Docker-zu-Kubernetes

Die Containerisierung mit Docker war der erste Schritt. Jetzt steht die Migration zu Kubernetes an -- und hier scheitern ueberraschend viele Projekte. Nicht weil Kubernetes zu komplex ist, sondern weil Teams Docker-Compose-Gewohnheiten 1:1 uebertragen. Dieser Artikel zeigt die 10 haeufigsten Fehler und wie Sie sie vermeiden.


Fehler 1: Resource Limits nicht setzen

Das Problem

In Docker Compose laufen Container standardmaessig ohne Ressourcen-Beschraenkung. Das funktioniert auf einem einzelnen Host, weil der Kernel den Speicher verteilt. In Kubernetes fuehrt das zu einer Katastrophe: Der Scheduler kann Pods nicht sinnvoll platzieren, und ein einzelner Container ohne Memory Limit kann einen Out-of-Memory-Kill des gesamten Nodes ausloesen.

Die Loesung

# Falsch: Keine Limits (Docker-Compose-Gewohnheit)
containers:
- name: api
  image: myapp:v1

# Richtig: Requests und Limits definiert
containers:
- name: api
  image: myapp:v1
  resources:
    requests:
      memory: "256Mi"
      cpu: "250m"
    limits:
      memory: "512Mi"
      cpu: "500m"

Faustregel: Requests auf den typischen Verbrauch setzen, Limits auf das Doppelte. Nach 2 Wochen Betrieb die tatsaechlichen Werte aus dem Monitoring ablesen und anpassen.

Requests und Limits sind kein Nice-to-have. Ohne Requests kann der Scheduler nicht entscheiden, auf welchem Node der Pod laufen soll. Ohne Limits gibt es kein Sicherheitsnetz bei Memory Leaks.


Fehler 2: Docker-Volumes direkt auf PersistentVolumes abbilden

Das Problem

In Docker Compose definieren Sie ein Volume mit einer Zeile:

# docker-compose.yml
volumes:
  - ./data:/app/data

Teams uebertragen das auf Kubernetes mit einem hostPath-Volume. Das funktioniert auf einem Single-Node-Cluster, bricht aber sofort, wenn der Pod auf einem anderen Node neu gestartet wird -- die Daten sind weg.

Die Loesung

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: managed-premium
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  template:
    spec:
      containers:
      - name: api
        volumeMounts:
        - name: data
          mountPath: /app/data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: app-data

Verwenden Sie immer PersistentVolumeClaims mit einer StorageClass. Bei Managed Kubernetes (EKS, AKS, GKE) sind Storage Classes vorkonfiguriert. Fuer Datenbanken nutzen Sie StatefulSets statt Deployments, damit jeder Pod sein eigenes Volume erhaelt.


Fehler 3: Health Checks vergessen

Das Problem

Docker Compose hat einen optionalen healthcheck, den viele Teams ignorieren. In Kubernetes sind Health Checks kritisch: Ohne Readiness Probe routet der Service Traffic an Pods, die noch starten. Ohne Liveness Probe erkennt Kubernetes nicht, wenn eine Anwendung haengt.

Die Loesung

containers:
- name: api
  livenessProbe:
    httpGet:
      path: /healthz
      port: 8080
    initialDelaySeconds: 15
    periodSeconds: 10
    failureThreshold: 3
  readinessProbe:
    httpGet:
      path: /ready
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 5
    failureThreshold: 3
  startupProbe:
    httpGet:
      path: /healthz
      port: 8080
    failureThreshold: 30
    periodSeconds: 10

Drei Probe-Typen verstehen:

  • startupProbe: Gibt der Anwendung Zeit zum Starten (z.B. Java-Apps mit langer Startzeit)
  • readinessProbe: Entscheidet, ob der Pod Traffic empfangen soll
  • livenessProbe: Entscheidet, ob der Pod neu gestartet werden soll

Fuer eine tiefere Betrachtung von Health Checks im Vergleich zu Docker siehe Docker Healthcheck vs. Kubernetes Liveness.


Fehler 4: Networking-Unterschiede ignorieren

Das Problem

In Docker Compose erreichen Sie Services ueber ihren Service-Namen: http://api:8080. Das funktioniert in Kubernetes aehnlich, aber mit wichtigen Unterschieden:

  • Service-Namen sind Namespace-spezifisch: api.production.svc.cluster.local
  • Ports muessen im Service-Objekt definiert werden, nicht nur im Container
  • Port-Mapping funktioniert anders als in Docker Compose

Die Loesung

apiVersion: v1
kind: Service
metadata:
  name: api
  namespace: production
spec:
  selector:
    app: api
  ports:
  - port: 8080        # Port, unter dem der Service erreichbar ist
    targetPort: 8080   # Port im Container
    protocol: TCP

Haeufige Netzwerk-Fehler:

Docker ComposeKubernetesLoesung
ports: "8080:8080"Service + IngressService fuer interne, Ingress fuer externe Erreichbarkeit
links: [db]Service Discovery via DNSAutomatisch ueber Service-Namen
network_mode: hosthostNetwork: trueVermeiden -- nutzen Sie NodePort oder LoadBalancer
Feste IPsClusterIP (dynamisch)Immer DNS-Namen verwenden, nie IPs

Fehler 5: Stateful Workloads als Deployments laufen lassen

Das Problem

Datenbanken, Message Queues und andere Stateful-Anwendungen laufen in Docker Compose als normale Services. In Kubernetes werden sie oft als Deployment migriert -- das ist falsch. Deployments garantieren keine stabile Netzwerk-Identitaet und keine geordnete Skalierung.

Die Loesung

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:16
        ports:
        - containerPort: 5432
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: managed-premium
      resources:
        requests:
          storage: 50Gi

StatefulSet vs. Deployment:

EigenschaftDeploymentStatefulSet
Pod-NamenZufaellig (api-7b9f4-xyz)Stabil (postgres-0, postgres-1)
SkalierungParallelGeordnet (0, 1, 2...)
VolumesGeteilt oder keineEigenes Volume pro Pod
DNSNur ueber ServiceIndividuell pro Pod

Bedenken Sie: Fuer Produktions-Datenbanken ist ein Managed Service (RDS, Cloud SQL, Azure Database) oft die bessere Wahl als ein selbst betriebenes StatefulSet.


Fehler 6: Environment Variables unsicher uebergeben

Das Problem

In Docker Compose stehen Umgebungsvariablen in einer .env-Datei oder direkt im docker-compose.yml. Teams uebertragen das auf Kubernetes und schreiben Passwoerter direkt in die Deployment-YAML:

# Falsch: Passwoerter im Klartext
env:
- name: DB_PASSWORD
  value: "mein-super-geheimes-passwort"

Diese YAML-Dateien landen im Git-Repository, werden in CI/CD-Logs angezeigt und sind fuer jeden mit Cluster-Zugriff lesbar.

Die Loesung

# Richtig: Secrets referenzieren
env:
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: app-secrets
      key: db-password

Noch besser: Einen External Secrets Operator verwenden, der Secrets aus HashiCorp Vault, AWS Secrets Manager oder Azure Key Vault synchronisiert. So liegen Passwoerter nie im Kubernetes-Cluster.

Fuer Details zu Secrets-Management im Enterprise-Kontext siehe Kubernetes Security Hardening.


Fehler 7: Kein Graceful Shutdown implementieren

In Docker Compose druecken Sie Ctrl+C und der Container stoppt. In Kubernetes werden Pods regelmaessig neu gestartet -- durch Rolling Updates, Node-Drains oder OOM-Kills. Ohne SIGTERM-Handling gehen laufende Requests verloren.

spec:
  terminationGracePeriodSeconds: 30
  containers:
  - name: api
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh", "-c", "sleep 5"]

Der preStop-Sleep gibt dem Ingress Controller Zeit, den Pod aus dem Routing zu entfernen, bevor SIGTERM gesendet wird. Die Anwendung hat dann terminationGracePeriodSeconds Zeit zum Herunterfahren, danach folgt SIGKILL.


Fehler 8: Logging nicht anpassen

Docker-Anwendungen schreiben oft in Log-Dateien (/var/log/app.log). In Kubernetes verschwinden Log-Dateien beim Pod-Neustart und fuellen das Node-Filesystem.

Regel: Schreibe auf stdout/stderr, nicht in Dateien. Kubernetes sammelt stdout/stderr automatisch (kubectl logs deployment/api). Fuer zentrale Log-Aggregation setzen Sie Fluent Bit (DaemonSet) mit einem Log-Backend (Elasticsearch, Loki) auf.


Fehler 9: ConfigMaps und Deployments nicht verknuepfen

Kubernetes startet Pods nicht automatisch neu, wenn sich eine referenzierte ConfigMap aendert. Drei Loesungen:

Option A: Checksumme als Annotation im Pod-Template -- bei jeder ConfigMap-Aenderung aendert sich die Annotation und erzwingt einen Rolling Update.

Option B: Den Stakater Reloader Controller installieren und die Annotation reloader.stakater.com/auto: "true" setzen. Der Reloader ueberwacht ConfigMaps und startet betroffene Deployments automatisch neu.

Option C: Versionierte ConfigMap-Namen mit Hash-Suffix (z.B. app-config-v2a3f) und Referenz im Deployment aktualisieren.


Fehler 10: Keine Rollback-Strategie

Das Problem

In Docker Compose rollt man zurueck, indem man das vorherige Image manuell startet. In Kubernetes existieren eingebaute Rollback-Mechanismen, die viele Teams nicht nutzen.

Die Loesung

# Deployment-Historie anzeigen
kubectl rollout history deployment/api

# Zum vorherigen Release zurueckrollen
kubectl rollout undo deployment/api

# Zu einer bestimmten Revision zurueckrollen
kubectl rollout undo deployment/api --to-revision=3

# Rollout-Status pruefen
kubectl rollout status deployment/api

Voraussetzung: Setzen Sie revisionHistoryLimit auf mindestens 5 (Standard: 10), damit genuegend alte ReplicaSets fuer ein Rollback vorgehalten werden.

Fuer eine umfassende Migrationsstrategie, die auch Legacy-Systeme abdeckt, empfehlen wir Kubernetes Migration Legacy Applications.


Migrations-Checkliste

Pre-Migration: Services inventarisiert, Stateful vs. Stateless identifiziert, Health-Check-Endpoints implementiert, Logging auf stdout/stderr umgestellt, SIGTERM-Handler eingebaut.

Kubernetes-Setup: Resource Requests/Limits definiert, PersistentVolumeClaims erstellt, Secrets in External Secrets Store, NetworkPolicies fuer Namespace-Isolation, Ingress konfiguriert.

Post-Migration: Monitoring und Alerting aktiv, Rollback getestet, Load-Tests durchgefuehrt, Backup-Strategie verifiziert, Runbooks erstellt.


Haeufige Fragen

Kann ich Docker Compose und Kubernetes parallel betreiben?

Ja, und das ist sogar empfehlenswert. Betreiben Sie die alte Docker-Compose-Umgebung parallel zur neuen Kubernetes-Umgebung. Migrieren Sie Service fuer Service und leiten Sie den Traffic schrittweise um. So koennen Sie bei Problemen sofort zurueckschalten.

Soll ich Kompose verwenden, um docker-compose.yml zu konvertieren?

Kompose ist ein Startpunkt, aber kein Produktions-Tool. Die generierten YAML-Dateien sind selten produktionsreif -- es fehlen Resource Limits, Health Checks und Security-Konfigurationen. Nutzen Sie Kompose als Grundlage und ueberarbeiten Sie die Ergebnisse manuell.

Wie migriere ich Datenbanken?

Datenbanken sind der schwierigste Teil. Drei Optionen: (1) Managed Database Service nutzen (empfohlen), (2) StatefulSet mit Operator (z.B. CloudNativePG fuer PostgreSQL), (3) Lift-and-Shift als StatefulSet ohne Operator (nur fuer einfache Setups). Planen Sie fuer die Datenbank-Migration extra Zeit ein.

Wie lange dauert eine Migration von Docker Compose zu Kubernetes?

Fuer 5-10 Services: 6-10 Wochen. Das umfasst Cluster-Setup, Anwendungsanpassung, Migration und Stabilisierung. Die reine Konvertierung der YAML-Dateien dauert Tage, nicht Wochen -- der Aufwand steckt in Health Checks, Monitoring, Security und Testing.


Naechste Schritte

  1. Inventarisieren Sie alle Services aus Ihrer docker-compose.yml
  2. Identifizieren Sie Stateful Services und planen Sie deren Storage-Strategie
  3. Implementieren Sie Health-Check-Endpoints in allen Anwendungen
  4. Starten Sie mit einem unkritischen Service als Pilot
  5. Fuer Compliance-Anforderungen bei der Migration beachten Sie unseren Artikel Kubernetes Compliance Luecken

Weiterfuehrende Artikel:


Sie planen eine Container-Migration zu Kubernetes und moechten typische Fehler vermeiden? Sprechen Sie uns an fuer ein unverbindliches Assessment unter /kontakt.

Kubernetes-Beratung gesucht?

Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen