Zum Inhalt springen
CASOON

Kubernetes für den Einstieg: Von Pods zu Deployments

Kernkonzepte, erster Cluster und die häufigsten Anfängerfehler – ohne Vendor-Lock-in

12 Minuten
Kubernetes für den Einstieg: Von Pods zu Deployments
#Kubernetes #DevOps #Cloud #Infrastruktur

Kubernetes ist eines der meistgenannten Werkzeuge in Cloud-Native-Diskussionen – und gleichzeitig eines, bei dem der Einstieg unverhältnismäßig viel Zeit kostet. Nicht weil das Konzept schwer wäre, sondern weil die Dokumentation riesig ist und man schnell in Details versinkt, die für den ersten produktiven Cluster noch keine Rolle spielen.

Dieser Artikel geht den direkten Weg: Konzepte so weit wie nötig, erstes Deployment so früh wie möglich, und eine ehrliche Einschätzung, wann Kubernetes mehr Aufwand macht als es löst.

Pod, ReplicaSet, Deployment – warum man alle drei kennen muss

Viele Einsteiger beginnen mit Pods und fragen sich dann, warum Deployments existieren. Die Antwort liegt im Failure Mode.

Ein Pod ist die kleinste deploybare Einheit in Kubernetes. Er enthält einen oder mehrere Container, ein gemeinsames Netzwerk und gemeinsamen Storage. Wenn ein Pod stirbt – weil der Container abstürzt, der Node ausfällt oder jemand ihn löscht –, ist er weg. Kein Neustart, keine Selbstheilung. Ein direkt erstellter Pod ist Einweg.

Ein ReplicaSet löst das erste Problem: Es sorgt dafür, dass immer eine bestimmte Anzahl identischer Pods läuft. Stirbt einer, erstellt das ReplicaSet sofort einen neuen. Aber ReplicaSets direkt zu verwalten ist umständlich, weil sie keine Rolling Updates unterstützen – ein Update bedeutet alten ReplicaSet löschen, neuen erstellen, Downtime.

Ein Deployment ist die Abstraktion darüber. Es verwaltet ReplicaSets automatisch, führt Rolling Updates durch (erst neue Pods hochfahren, dann alte beenden) und erlaubt Rollbacks mit einem einzigen Befehl. In der Praxis erstellt man fast immer Deployments, nie direkt Pods oder ReplicaSets.

Ein Service ist die Networking-Schicht. Pods bekommen dynamische IP-Adressen, die sich bei jedem Neustart ändern. Ein Service stellt eine stabile IP und einen DNS-Namen bereit und leitet Traffic an alle passenden Pods weiter – unabhängig davon, wie viele gerade laufen oder welche IP sie haben.

Das Zusammenspiel: Deployment sorgt dafür, dass drei Pods laufen. Service leitet den eingehenden Traffic an alle drei weiter. Stirbt ein Pod, erstellt das Deployment einen neuen, der Service aktualisiert seine Endpunkte automatisch.

Lokaler Einstieg ohne Cloud-Kosten: minikube vs kind

Bevor ein Managed Service sinnvoll wird, lohnt sich ein lokaler Cluster. Zwei Tools dominieren hier:

Merkmalminikubekind
GrundprinzipVirtuelle Maschine (oder Container)Kubernetes-Nodes als Docker-Container
Startzeit~1-2 Minuten~30 Sekunden
RessourcenverbrauchHoch (eigene VM)Gering
Addon-SystemJa (Ingress, Dashboard etc.)Manuell via Manifeste
Multi-Node-ClusterMöglich, aber komplexEinfach konfigurierbar
EmpfehlungEinzelentwickler, einfacher EinstiegCI-Umgebungen, Multi-Node-Tests

Für den Einstieg ist minikube die einfachere Wahl. kind ist besser, sobald man Cluster-Topologien testen oder Kubernetes in CI einsetzen will.

# minikube installieren (macOS via Homebrew)
brew install minikube

# Cluster starten
minikube start --cpus=2 --memory=4096

# Status prüfen
minikube status

# kubectl ist bereits konfiguriert – Cluster-Info anzeigen
kubectl cluster-info

Vom Dockerfile zum laufenden Service: erstes Deployment

Ein konkretes Beispiel ist besser als abstrakte Erklärungen. Annahme: Es gibt eine Node.js-App, die auf Port 3000 lauscht, und ein entsprechendes Docker-Image myapp:1.0.

Schritt 1: Deployment erstellen

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: myapp:1.0
          ports:
            - containerPort: 3000
          resources:
            requests:
              memory: "64Mi"
              cpu: "100m"
            limits:
              memory: "128Mi"
              cpu: "250m"
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 5

Schritt 2: Service erstellen

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
  type: ClusterIP

Schritt 3: Anwenden und prüfen

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

# Status beobachten
kubectl get deployments
kubectl get pods
kubectl get services

# Logs eines Pods ansehen
kubectl logs -l app=myapp

# In minikube: Service im Browser öffnen
minikube service myapp-service

Ein erfolgreich laufendes Deployment sieht so aus:

NAME     READY   UP-TO-DATE   AVAILABLE   AGE
myapp    3/3     3            3           45s

Wenn READY nicht 3/3 zeigt, ist das der Startpunkt für die Fehlersuche.

Wenn Pods nicht starten: die drei häufigsten Fehler

Fehler 1: Image Pull Policy und lokale Images

Beim lokalen Entwickeln baut man Images lokal – und wundert sich dann, warum Kubernetes sie nicht findet.

# Fehlermeldung
Events:
  Warning  Failed    2s    kubelet  Failed to pull image "myapp:1.0": 
    rpc error: code = Unknown desc = Error response from daemon: 
    pull access denied for myapp, repository does not exist

Kubernetes versucht standardmäßig, Images von einer Registry zu pullen. Lokale Images existieren nur im lokalen Docker-Daemon, nicht in dem, den minikube verwendet.

# Lösung für minikube: Image in minikube laden
minikube image load myapp:1.0

# Alternativ: imagePullPolicy auf Never setzen (nur lokal sinnvoll)
containers:
  - name: myapp
    image: myapp:1.0
    imagePullPolicy: Never  # Nur für lokale Entwicklung

Fehler 2: Fehlende Resource Limits führen zu OOMKill

Ohne definierte Limits kann ein Pod unbegrenzt Speicher verbrauchen. Kubernetes hat dann keine andere Wahl, als den Container zu killen, wenn der Node unter Druck gerät.

# Fehlermeldung im Pod-Status
Status: OOMKilled

# Details
kubectl describe pod myapp-xyz-abc
# ...
# Last State: Terminated
#   Reason: OOMKilled
#   Exit Code: 137

Resource Limits sind deshalb keine optionale Optimierung, sondern grundlegendes Cluster-Hygiene:

resources:
  requests:
    memory: "64Mi"    # Was Kubernetes reserviert für Scheduling
    cpu: "100m"       # 100 Millicores = 0,1 CPU-Kern
  limits:
    memory: "128Mi"   # Absolutes Maximum – bei Überschreitung: OOMKill
    cpu: "250m"       # CPU wird gedrosselt, nicht gekillt

requests beeinflussen das Scheduling: Kubernetes platziert Pods nur auf Nodes, die genug freie Ressourcen haben. limits sind die harte Grenze zur Laufzeit. Wer keine requests definiert, bekommt Pods, die auf überlasteten Nodes landen und dort andere Workloads destabilisieren.

Fehler 3: Liveness Probe zu aggressiv konfiguriert

Eine Liveness Probe prüft, ob ein Container noch läuft und neugestartet werden soll. Das Problem: Wenn die Probe zu früh oder zu häufig feuert, restartet Kubernetes einen Container, der eigentlich nur noch am Starten ist.

# Typisches Symptom: Pod startet, läuft kurz, wird neu gestartet
kubectl get pods
NAME             READY   STATUS             RESTARTS   AGE
myapp-xyz-abc   0/1     CrashLoopBackOff   5          3m
# Ursache finden
kubectl describe pod myapp-xyz-abc
# Events zeigen oft:
# Liveness probe failed: Get "http://10.0.0.5:3000/health": dial tcp: connection refused

Der Fix liegt in initialDelaySeconds – der Zeit, die Kubernetes wartet, bevor die erste Probe läuft:

livenessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 30   # Mindestens so lang wie die App zum Starten braucht
  periodSeconds: 10          # Wie oft die Probe ausgeführt wird
  failureThreshold: 3        # Wie viele Fehler bis zum Neustart
readinessProbe:
  httpGet:
    path: /ready
    port: 3000
  initialDelaySeconds: 10    # Darf früher sein als Liveness
  periodSeconds: 5

Liveness und Readiness Probes haben unterschiedliche Bedeutungen: Liveness entscheidet über Neustart, Readiness entscheidet über Traffic-Routing. Ein Pod, der die Readiness Probe nicht besteht, bekommt keinen Traffic vom Service – wird aber nicht neugestartet. Das ist das richtige Verhalten beim Startup.

Namespaces, ConfigMaps und Secrets: der nächste Schritt

Sobald mehrere Anwendungen im Cluster laufen, wird Isolation wichtig. Namespaces trennen Ressourcen logisch:

# Namespace erstellen
kubectl create namespace staging

# Deployment in einem Namespace erstellen
kubectl apply -f deployment.yaml -n staging

# Alle Pods in einem Namespace
kubectl get pods -n staging

Konfigurationswerte gehören nicht in Images. ConfigMaps und Secrets entkoppeln Konfiguration von Code:

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  DATABASE_HOST: "postgres-service"
  LOG_LEVEL: "info"
---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: myapp-secrets
type: Opaque
data:
  DATABASE_PASSWORD: cGFzc3dvcmQxMjM=  # base64-kodiert
# Im Container referenzieren
envFrom:
  - configMapRef:
      name: myapp-config
  - secretRef:
      name: myapp-secrets

Wann Kubernetes die falsche Wahl ist

Das muss gesagt werden: Kubernetes ist für viele Projekte überdimensioniert.

Ein einzelner Service, ein kleines Team, ein Projekt mit überschaubarem Traffic – hier ist der Overhead von Kubernetes (Cluster-Management, Netzwerkkonfiguration, Zertifikatsverwaltung, Node-Maintenance) höher als der Nutzen. Ein VPS mit Docker Compose oder ein einfacher PaaS-Dienst löst dasselbe Problem mit einem Bruchteil der Komplexität.

Kubernetes lohnt sich, wenn:

  • mehrere Services voneinander unabhängig skaliert werden müssen
  • Rolling Deployments ohne Downtime erforderlich sind
  • das Team groß genug ist, um Cluster-Kenntnisse aufzubauen und zu halten
  • Compliance oder Datenschutz eine eigene Infrastruktur verlangen

Für lokale Hardware oder eigene Server im Mittelstand, wo Cloud-Kosten zu hoch sind aber Kubernetes-Funktionalität gebraucht wird, ist k3s eine sinnvolle Alternative – ein vollwertiges Kubernetes, das auf einem Raspberry Pi läuft und mit einem einzigen Binary installiert wird.

Rolling Updates und Rollbacks funktionieren sofort

Einer der größten Vorteile von Deployments zeigt sich beim Update:

# Image-Version aktualisieren
kubectl set image deployment/myapp myapp=myapp:2.0

# Update-Status beobachten
kubectl rollout status deployment/myapp

# Wenn etwas schiefgeht: Rollback
kubectl rollout undo deployment/myapp

# Rollback auf eine bestimmte Version
kubectl rollout undo deployment/myapp --to-revision=2

Kubernetes startet beim Rolling Update neue Pods mit der neuen Version und beendet alte erst, wenn die neuen die Readiness Probe bestehen. Kein Traffic geht verloren, kein manuelles Eingreifen nötig.

Wann ein Managed Service die bessere Wahl wird

Den eigenen Cluster zu betreiben bedeutet: Control Plane-Updates, Node-Maintenance, etcd-Backups, Netzwerk-Konfiguration, Zertifikatsrotation. Das ist Arbeit, die nicht direkt Mehrwert für die Anwendung bringt.

Managed Kubernetes (EKS bei AWS, GKE bei Google, AKS bei Azure) übernimmt die Control Plane komplett. Man zahlt mehr, gewinnt aber Betriebszeit zurück. Für die meisten Teams ohne dedizierten Platform-Engineer ist das die realistischere Wahl.

Die Entscheidung hängt an einer Frage: Ist das Betreiben von Infrastruktur eine Kernkompetenz des Teams – oder eine notwendige Nebenaufgabe, die so wenig Aufmerksamkeit wie möglich brauchen soll?

Auf Produktion: was vor dem ersten Go-live nicht fehlen darf

Ein lokales Deployment läuft. Für Produktion fehlen noch ein paar Dinge:

  • Ingress Controller – um externen HTTP/HTTPS-Traffic zu routen (nginx-ingress oder Traefik sind verbreitet)
  • TLS-Zertifikate – cert-manager mit Let’s Encrypt automatisiert das vollständig
  • Horizontal Pod Autoscaler – skaliert Pods automatisch anhand von CPU/Memory-Metriken
  • PodDisruptionBudgets – stellt sicher, dass bei Node-Maintenance nicht alle Pods gleichzeitig enden
  • Monitoring – ohne Metriken und Alerts bleibt ein Produktionscluster blind

Dieser letzte Punkt – Monitoring und Observability – ist eigener Aufwand. Kubernetes liefert Metriken, aber das Aufbauen von Dashboards und Alerting ist eine eigene Disziplin. Prometheus und Grafana sind der Standard dafür, und wie man sie vernünftig aufsetzt, ohne sofort in Alert-Fatigue zu ertrinken, ist ein eigenes Thema.

Von hier weiter denken

Kubernetes lohnt sich als Investment, wenn man die Grundkonzepte erst einmal verinnerlicht hat. Pod, ReplicaSet, Deployment, Service – diese vier Konzepte decken 80 Prozent der alltäglichen Arbeit ab. Die restlichen 20 Prozent (Ingress, Autoscaling, Storage, Network Policies) kommen mit dem Bedarf.

Wer auf eigener Hardware oder in einem Heimlabor startet: k3s bietet denselben Kubernetes-API-Kompatibilitätslevel mit einem deutlich schlankeren Footprint – ideal für Proxmox-VMs oder ältere Hardware, auf der ein vollwertiger k8s-Cluster zu schwergewichtig wäre.

Der Einstieg kostet Zeit. Aber die Konzepte sind solide und stabil – was man heute über Deployments lernt, gilt in drei Jahren noch genauso.