Zum Inhalt springen

Kubernetes Pods und Deployments: Eine praktische Einführung

Ein praxisnaher Leitfaden zu Kubernetes Pods, Deployments und Services, der die wichtigsten Grundkonzepte für den Betrieb von Produktions-Workloads behandelt.

5 Min. Lesezeit
Kubernetes-Clusterdiagramm mit Pods, Deployments und Services

Die Kubernetes-Dokumentation ist riesig. Die API-Oberfläche ist enorm. Neue Entwicklerinnen und Entwickler starren auf eine Wand aus YAML und wissen nicht, wo sie anfangen sollen. Die Antwort sind drei Grundbausteine: Pods, Deployments und Services. Diese drei Abstraktionen decken 90 % dessen ab, was man braucht, um eine Webanwendung in Produktion zu betreiben.

Dieser Leitfaden erklärt, was jeder dieser Bausteine tut, wie sie zusammenspielen und welche Konfigurationen bei echten Workloads wirklich zählen.

Pods: die kleinste Einheit

Ein Pod besteht aus einem oder mehreren Containern, die gemeinsam auf demselben Node laufen und sich Netzwerk und Speicher teilen. Die meisten Pods enthalten nur einen einzigen Container. Das Multi-Container-Muster ist für Sidecars gedacht: einen Logging-Agenten, einen Proxy oder einen Init-Container, der vor dem eigentlichen Hauptprozess Einrichtungsschritte ausführt.

ymlyaml
# A minimal Pod definition — rarely used directly in production
apiVersion: v1
kind: Pod
metadata:
  name: api-server
  labels:
    app: api
    version: v1
spec:
  containers:
    - name: api
      image: myregistry/api-server:1.4.2
      ports:
        - containerPort: 3000
      env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
      resources:
        requests:
          memory: "128Mi"
          cpu: "100m"
        limits:
          memory: "256Mi"
          cpu: "500m"

Man erstellt Pods so gut wie nie direkt. Stirbt ein Pod, startet ihn nichts neu. Deployments verwalten den Lebenszyklus der Pods – deshalb sind sie die eigentliche Arbeitseinheit in Produktionsumgebungen.

Deployments: den Lebenszyklus von Pods verwalten

Ein Deployment beschreibt den gewünschten Zustand: welches Container-Image verwendet wird, wie viele Replicas laufen sollen und wie Updates ausgerollt werden. Der Deployment-Controller gleicht die tatsächliche Situation fortlaufend mit diesem gewünschten Zustand ab.

ymlyaml
# ❌ Running pods directly — no restart, no scaling, no rollback
apiVersion: v1
kind: Pod
metadata:
  name: api-server-1
spec:
  containers:
    - name: api
      image: myregistry/api-server:1.4.2
---
apiVersion: v1
kind: Pod
metadata:
  name: api-server-2
spec:
  containers:
    - name: api
      image: myregistry/api-server:1.4.2
ymlyaml
# ✅ A Deployment manages pods with automatic restart, scaling, and rollback
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
  labels:
    app: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: api
        version: v1
    spec:
      containers:
        - name: api
          image: myregistry/api-server:1.4.2
          ports:
            - containerPort: 3000
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 20

Die wichtigsten Felder in diesem Deployment:

  • replicas: 3: drei Instanzen des Pods ausführen
  • maxSurge: 1: erlaubt während Updates einen zusätzlichen Pod über der gewünschten Anzahl
  • maxUnavailable: 0: die Anzahl der verfügbaren Replicas darf während Updates nie unter den Sollwert fallen
  • readinessProbe: teilt Kubernetes mit, wann der Pod Traffic annehmen kann
  • livenessProbe: teilt Kubernetes mit, wann der Pod feststeckt und neu gestartet werden muss

Rolling Updates und Rollbacks

Deployments führen Updates aus, indem sie alte Pods schrittweise durch neue ersetzen. Die Einstellungen maxSurge und maxUnavailable steuern dabei Geschwindigkeit und Verfügbarkeit während des Updates.

shbash
# Update the image — triggers a rolling update
kubectl set image deployment/api-server \
  api=myregistry/api-server:1.5.0
 
# Watch the rollout progress
kubectl rollout status deployment/api-server
 
# Check rollout history
kubectl rollout history deployment/api-server
 
# Rollback to the previous version
kubectl rollout undo deployment/api-server
 
# Rollback to a specific revision
kubectl rollout undo deployment/api-server --to-revision=3

Der Rolling-Update-Prozess mit maxSurge: 1, maxUnavailable: 0 und 3 Replicas läuft so ab:

Step 1: [v1] [v1] [v1] [v2]     ← new Pod created (4 total)
Step 2: [v1] [v1] [v2] [v2]     ← old Pod removed, new created
Step 3: [v1] [v2] [v2] [v2]     ← continuing replacement
Step 4: [v2] [v2] [v2]          ← rollout complete (3 total)

Traffic wird nur an Pods weitergeleitet, die die Readiness-Probe bestehen – so landen Nutzerinnen und Nutzer nie bei einem Pod, der noch startet.

Services: Pods nach außen freigeben

Pods erhalten flüchtige IP-Adressen, die sich bei jedem Neustart ändern. Ein Service bietet eine stabile Netzwerkidentität: einen festen DNS-Namen und eine feste IP, die Traffic an gesunde Pods weiterleiten, die einem Label-Selector entsprechen.

ymlyaml
# ClusterIP Service — internal access only
apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  type: ClusterIP
  selector:
    app: api
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
---
# Other pods can now reach the API at http://api-service:80
# Kubernetes DNS resolves api-service to the ClusterIP
ymlyaml
# LoadBalancer Service — external access via cloud load balancer
apiVersion: v1
kind: Service
metadata:
  name: api-external
spec:
  type: LoadBalancer
  selector:
    app: api
  ports:
    - protocol: TCP
      port: 443
      targetPort: 3000

Die verschiedenen Service-Typen erfüllen unterschiedliche Zwecke:

  • ClusterIP: nur intern erreichbar. Für die Kommunikation zwischen Microservices.
  • NodePort: macht den Service auf der IP jedes Nodes über einen festen Port erreichbar. Nützlich für die Entwicklung.
  • LoadBalancer: stellt einen Load Balancer in der Cloud bereit. Der Standard für öffentlich erreichbare Services.

Requests und Limits für Ressourcen

Ohne Ressourcenkonfiguration kann ein einzelner Pod sämtliche Ressourcen eines Nodes beanspruchen und anderen Workloads die Grundlage entziehen. Requests garantieren eine Mindestzuteilung. Limits begrenzen das Maximum.

ymlyaml
resources:
  # Requests: minimum guaranteed resources
  # Used by scheduler to place pods on nodes
  requests:
    memory: "128Mi"
    cpu: "100m"    # 100 millicores = 0.1 CPU cores
 
  # Limits: maximum allowed resources
  # Pod is killed (OOMKill) if it exceeds memory limit
  # Pod is throttled if it exceeds CPU limit
  limits:
    memory: "256Mi"
    cpu: "500m"    # 500 millicores = 0.5 CPU cores
shbash
# Check actual resource usage vs. requests/limits
kubectl top pods -n default
 
# Example output:
# NAME                          CPU(cores)   MEMORY(bytes)
# api-server-7d5f8b6c4-abc12   45m          98Mi
# api-server-7d5f8b6c4-def34   52m          102Mi
# api-server-7d5f8b6c4-ghi56   38m          95Mi

Zu hoch angesetzte Requests verschwenden Cluster-Ressourcen. Zu niedrig angesetzte Requests führen zu Scheduling-Fehlern, sobald Nodes voll erscheinen. Beginne mit Requests auf Basis des beobachteten Verbrauchs (aus kubectl top) und setze Limits auf das Doppelte des Requests.

Health Checks, die wirklich funktionieren

Readiness- und Liveness-Probes gehören zu den am häufigsten falsch konfigurierten Einstellungen. Eine schlechte Probe-Konfiguration führt zu Kaskadenausfällen.

ymlyaml
# ❌ Common mistake: same endpoint, same timing for both probes
readinessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 5
livenessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 5
ymlyaml
# ✅ Different timing, different thresholds
readinessProbe:
  httpGet:
    path: /health/ready
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10
  failureThreshold: 3
  # Pod removed from Service after 3 consecutive failures (30s)
 
livenessProbe:
  httpGet:
    path: /health/live
    port: 3000
  initialDelaySeconds: 30
  periodSeconds: 20
  failureThreshold: 5
  # Pod restarted after 5 consecutive failures (100s)
 
startupProbe:
  httpGet:
    path: /health/live
    port: 3000
  failureThreshold: 30
  periodSeconds: 10
  # Allows up to 300s for slow-starting apps

Die Liveness-Probe sollte einen längeren initialDelaySeconds-Wert und einen höheren failureThreshold haben als die Readiness-Probe. Eine zu aggressive Liveness-Probe startet Pods neu, die nur vorübergehend überlastet sind, und erzeugt so eine Neustartschleife, die einen Ausfall noch verschlimmert.

Der /health/ready-Endpunkt der Readiness-Probe sollte nachgelagerte Abhängigkeiten prüfen (Datenbankverbindung, Erreichbarkeit des Caches). Der /health/live-Endpunkt der Liveness-Probe sollte dagegen nur eine einfache Prüfung sein, ob "der Prozess noch lebt" – Abhängigkeitsprüfungen gehören niemals in Liveness-Probes.

ConfigMaps und Secrets

Konfiguration und Secrets sollten außerhalb des Container-Images liegen und als Kubernetes-Ressourcen verwaltet werden.

ymlyaml
# ConfigMap for non-sensitive configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  LOG_LEVEL: "info"
  CACHE_TTL: "300"
  FEATURE_NEW_UI: "true"
---
# Secret for sensitive values (base64 encoded)
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  url: cG9zdGdyZXM6Ly91c2VyOnBhc3NAZGIuZXhhbXBsZS5jb206NTQzMi9teWRi
  password: c3VwZXJzZWNyZXQ=
ymlyaml
# Reference in Deployment
spec:
  containers:
    - name: api
      image: myregistry/api-server:1.4.2
      envFrom:
        - configMapRef:
            name: api-config
      env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url

Mit ConfigMaps lässt sich die Konfiguration ändern, ohne die Container-Images neu zu bauen. Eine ConfigMap zu ändern und die Pods neu zu starten, setzt eine Konfigurationsänderung innerhalb von Sekunden um.

Die wichtigsten Erkenntnisse

  1. Niemals nackte Pods betreiben: für automatischen Neustart und Rollout-Management immer Deployments verwenden
  2. Resource Requests anhand des beobachteten Verbrauchs festlegen: kubectl top liefert echte Zahlen statt Schätzungen
  3. Readiness- und Liveness-Probes trennen: unterschiedliche Endpunkte, unterschiedliche Schwellenwerte, unterschiedliche Zwecke
  4. Rolling Updates mit maxUnavailable: 0 verwenden: während Deploys die volle Kapazität aufrechterhalten
  5. Mit ClusterIP-Services beginnen: nur das extern freigeben, was wirklich externen Zugriff braucht
  6. Geheimnisse in Secrets und Konfiguration in ConfigMaps aufbewahren: niemals umgebungsspezifische Werte fest in Images einbacken
Wilfredo Rujel

Wilfredo Rujel

Full-Stack-Softwareentwickler

Diesen Beitrag teilenX