Veröffentlicht am

Kubernetes Tutorial: Erster Pod, Deployment und Service

Teilen:
Authors

Kubernetes Hands-On Tutorial: Vom ersten Pod zum laufenden Service

TL;DR

  • In 30 Minuten deployen Sie einen Pod, ein Deployment mit drei Replicas und einen Service mit Load Balancing.
  • Pods sind die kleinste Einheit. Deployments sorgen fuer Selbstheilung und Rolling Updates. Services geben eine stabile Adresse.
  • Alle YAML-Beispiele sind vollstaendig und koennen direkt per kubectl apply -f angewendet werden.
  • Am Ende steht ein komplettes Beispiel mit Liveness- und Readiness-Probes zum Kopieren.

Voraussetzungen

Sie brauchen einen laufenden Kubernetes-Cluster und kubectl auf Ihrem Rechner. Fuer dieses Tutorial reicht ein lokaler Cluster voellig aus.

Option A: Docker Desktop -- Kubernetes in den Einstellungen aktivieren. Funktioniert auf macOS und Windows.

Option B: minikube -- Laeuft ueberall, braucht nur Docker oder eine VM.

# minikube installieren und starten
minikube start --cpus=2 --memory=2048

# Cluster pruefen
kubectl get nodes
# NAME       STATUS   ROLES           AGE   VERSION
# minikube   Ready    control-plane   30s   v1.29.0

Option C: Online Playground -- Killercoda oder Play with Kubernetes im Browser. Keine Installation noetig, aber zeitlich begrenzt.

Sobald kubectl get nodes einen Node im Status Ready zeigt, kann es losgehen.


Teil 1: Der erste Pod

Ein Pod ist die kleinste deploybare Einheit in Kubernetes. Er enthaelt einen oder mehrere Container, die sich einen Netzwerk-Namespace teilen. In der Praxis laeuft fast immer genau ein Container pro Pod.

# Pod mit einem nginx-Container starten
kubectl run mein-erster-pod --image=nginx:alpine

# Status pruefen
kubectl get pods
# NAME              READY   STATUS    RESTARTS   AGE
# mein-erster-pod   1/1     Running   0          5s

Der Pod laeuft. Jetzt die wichtigsten Befehle zum Untersuchen:

# Details und Events anzeigen
kubectl describe pod mein-erster-pod

# Shell im Container oeffnen
kubectl exec -it mein-erster-pod -- /bin/sh
# hostname, cat /etc/nginx/nginx.conf, exit

# Logs anschauen (live mit -f)
kubectl logs mein-erster-pod
kubectl logs -f mein-erster-pod

# Pod loeschen
kubectl delete pod mein-erster-pod

Wichtig zu verstehen: Ein einzelner Pod hat keine Selbstheilung. Wenn der Prozess im Container abstuerzt, startet Kubernetes den Container zwar neu (je nach restartPolicy). Aber wenn der Node ausfaellt oder jemand den Pod loescht, ist er weg. Fuer Produktions-Workloads braucht man Deployments.


Teil 2: Deployments fuer Hochverfuegbarkeit

Ein Deployment verwaltet eine Menge identischer Pods (Replicas). Es sorgt dafuer, dass immer die gewuenschte Anzahl laeuft, und ermoeglicht Rolling Updates ohne Downtime.

Erstellen Sie eine Datei meine-app.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: meine-app
  labels:
    app: meine-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: meine-app
  template:
    metadata:
      labels:
        app: meine-app
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: "64Mi"
              cpu: "50m"
            limits:
              memory: "128Mi"
              cpu: "100m"

Drei Dinge sind hier zentral:

  1. replicas: 3 sagt Kubernetes, dass immer drei Pods laufen sollen.
  2. selector.matchLabels definiert, welche Pods zu diesem Deployment gehoeren.
  3. resources setzt CPU- und Memory-Limits. Ohne Limits riskiert man, dass ein Pod den ganzen Node leer frisst. Mehr dazu unter OOMKilled Troubleshooting.
# Deployment anwenden
kubectl apply -f meine-app.yaml

# Status pruefen
kubectl get deployment meine-app
# NAME        READY   UP-TO-DATE   AVAILABLE   AGE
# meine-app   3/3     3            3           10s

# Alle Pods des Deployments anzeigen
kubectl get pods -l app=meine-app

Selbstheilung testen

Das ist die Eigenschaft, die Kubernetes auszeichnet:

# Einen Pod manuell loeschen
kubectl delete pod $(kubectl get pods -l app=meine-app -o name | head -1)

# Sofort danach pruefen
kubectl get pods -l app=meine-app
# -> Kubernetes startet automatisch einen neuen Pod

Der Deployment Controller bemerkt, dass nur noch zwei statt drei Pods laufen, und erstellt sofort einen neuen. Das dauert typischerweise wenige Sekunden.

Skalieren

# Hoch auf 5 Replicas
kubectl scale deployment meine-app --replicas=5

# Runter auf 2 Replicas
kubectl scale deployment meine-app --replicas=2

# Aktuellen Stand pruefen
kubectl get deployment meine-app

Teil 3: Services fuer stabilen Netzwerk-Zugriff

Pods bekommen bei jedem Neustart eine neue IP-Adresse. Das macht es unmoeglich, sie direkt anzusprechen. Ein Service loest dieses Problem: Er hat eine stabile IP, einen DNS-Namen und verteilt Traffic per Load Balancing auf die dahinter liegenden Pods.

Fuegen Sie am Ende Ihrer meine-app.yaml hinzu (mit --- als Trenner):

---
apiVersion: v1
kind: Service
metadata:
  name: meine-app-service
spec:
  selector:
    app: meine-app
  ports:
    - port: 80
      targetPort: 80
  type: ClusterIP

selector: app: meine-app verbindet den Service mit allen Pods, die dieses Label tragen. ClusterIP bedeutet, der Service ist nur innerhalb des Clusters erreichbar.

# Anwenden
kubectl apply -f meine-app.yaml

# Service pruefen
kubectl get service meine-app-service
# NAME                TYPE        CLUSTER-IP     PORT(S)   AGE
# meine-app-service   ClusterIP   10.96.45.123   80/TCP    5s

Service testen

# Temporaeren Test-Pod starten
kubectl run test-client --rm -it --image=busybox -- /bin/sh

# Im Test-Pod: Service per DNS-Name ansprechen
wget -qO- http://meine-app-service
# -> nginx-Startseite

# Voller DNS-Name funktioniert auch
wget -qO- http://meine-app-service.default.svc.cluster.local

exit

Extern erreichbar machen

Fuer lokale Tests kann man den Service-Typ auf NodePort aendern:

spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080
kubectl apply -f meine-app.yaml
curl http://localhost:30080
# -> nginx-Startseite

In Produktions-Umgebungen nutzt man stattdessen einen Ingress Controller oder einen LoadBalancer-Service. Fuer den Einstieg reicht NodePort aber voellig.


Teil 4: Rolling Updates und Rollbacks

Eine der staerksten Funktionen von Deployments: Man kann das Container-Image aendern, und Kubernetes aktualisiert die Pods nacheinander ohne Downtime.

# Image aktualisieren
kubectl set image deployment/meine-app nginx=nginx:1.25-alpine

# Update live beobachten
kubectl rollout status deployment/meine-app
# Waiting for deployment "meine-app" rollout to finish...
# deployment "meine-app" successfully rolled out

Kubernetes startet neue Pods mit dem neuen Image, wartet bis sie ready sind, und terminiert dann die alten. Zu keinem Zeitpunkt sind null Pods verfuegbar.

Wenn etwas schiefgeht:

# Update-Historie anzeigen
kubectl rollout history deployment/meine-app

# Zur vorherigen Version zurueck
kubectl rollout undo deployment/meine-app

# Oder zu einer bestimmten Revision
kubectl rollout undo deployment/meine-app --to-revision=1

Teil 5: Troubleshooting-Grundlagen

Pod bleibt im Status Pending

kubectl describe pod <pod-name>
# Events pruefen. Haeufige Ursachen:
# - Insufficient cpu/memory -> Node hat nicht genug Ressourcen
# - No nodes match selector -> nodeSelector oder Taints falsch

Mehr dazu unter Pod Pending Troubleshooting.

CrashLoopBackOff

# Logs des letzten Crash anzeigen
kubectl logs <pod-name> --previous

# Exit-Code pruefen
kubectl describe pod <pod-name> | grep -A2 "Last State"

Exit Code 137 bedeutet OOMKilled (zu wenig Memory). Exit Code 1 ist ein Anwendungsfehler. Ausfuehrlich erklaert unter CrashLoopBackOff Troubleshooting.

Service hat keine Endpoints

# Endpoints pruefen -- wenn leer, stimmen die Labels nicht
kubectl get endpoints meine-app-service
kubectl get pods --show-labels

Zusammenfassung

KonzeptBefehlZweck
Podkubectl runEinzelnen Container ausfuehren
Deploymentkubectl apply -fReplicas, Selbstheilung, Updates
Servicekubectl apply -fStabile IP und DNS, Load Balancing
Skalierungkubectl scaleReplica-Anzahl aendern
Updatekubectl set imageRolling Update ohne Downtime
Rollbackkubectl rollout undoZur vorherigen Version zurueck
Debugkubectl logs/describeFehlersuche

Komplettes Beispiel zum Kopieren

Deployment mit Liveness- und Readiness-Probes plus Service in einer Datei:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: "64Mi"
              cpu: "50m"
            limits:
              memory: "128Mi"
              cpu: "100m"
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 3
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  selector:
    app: webapp
  ports:
    - port: 80
      targetPort: 80
  type: NodePort
kubectl apply -f webapp.yaml && kubectl get deployment webapp
kubectl delete -f webapp.yaml

Die livenessProbe startet den Container bei Nicht-Antwort neu. Die readinessProbe steuert, ob der Pod Traffic empfangen soll.


Naechste Schritte

Dieses Tutorial deckt die Grundlagen ab. Fuer den naechsten Schritt empfehle ich:


Wenn Sie Unterstuetzung bei Ihren Kubernetes-Projekten brauchen, schauen Sie gerne auf /kontakt vorbei.

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