edu@dorner-it
WerkzeugeEinsteiger25 min

Kubernetes Einstieg: Pods, Deployments & Services

Was Kubernetes wirklich macht, wie ein Cluster aufgebaut ist und welche fünf Ressourcen du im Alltag dauernd siehst. Mit kubectl-Spickzettel und einem ersten Deployment auf einem lokalen Cluster.

Zuletzt aktualisiert: 12. Mai 2026

Kubernetes (kurz k8s — acht Buchstaben zwischen K und s) ist ein Orchestrator für Container über viele Maschinen hinweg. Wer Docker Compose kennt, kennt schon 30 % der Konzepte — der Rest ist Vokabular und ein anderes Mindset: du beschreibst den Soll-Zustand, k8s sorgt für den Ist-Zustand.

Lernziele

  • Du verstehst die Cluster-Architektur (Control Plane, Nodes)
  • Du kennst die fünf wichtigsten Ressourcen: Pod, Deployment, Service, ConfigMap, Secret
  • Du beherrschst die häufigsten kubectl-Kommandos
  • Du startest dein erstes Deployment auf einem lokalen Cluster

Was ist anders als bei Compose?

Compose Kubernetes
Ein Host Beliebig viele Nodes
Du startest Container Du deklarierst Soll-Zustand
Failover = du Failover = Controller
YAML startet alles YAML beschreibt Ressourcen, Controller wenden an
flowchart LR
    Du[Du] -- "kubectl apply" --> API[API-Server]
    API --> Etcd[(etcd)]
    Controller -- "vergleicht ständig" --> API
    Controller -- "korrigiert" --> Nodes
    Nodes --> Pods[Pods laufen]

Mantra: Du sagst k8s, was du willst (3 Replicas Image v2). k8s findet heraus, wie es dahin kommt (alte stoppen, neue starten, Health prüfen, Traffic umlenken).

Cluster-Architektur in 60 Sekunden

flowchart TB
    subgraph "Control Plane"
        API[API-Server]
        Etcd[(etcd)]
        Sched[Scheduler]
        CM[Controller Manager]
    end
    subgraph "Node 1"
        K1[kubelet] --> P1[Pod A]
        K1 --> P2[Pod B]
        Proxy1[kube-proxy]
    end
    subgraph "Node 2"
        K2[kubelet] --> P3[Pod C]
        Proxy2[kube-proxy]
    end
    API <--> K1
    API <--> K2
  • API-Server — die einzige Tür zum Cluster (kubectl spricht hier)
  • etcd — die Datenbank, kennt jeden Soll-Zustand
  • Scheduler — verteilt Pods auf Nodes
  • Controller Manager — die Engine hinter „bring Ist auf Soll"
  • kubelet — Agent auf jedem Node, startet Pods
  • kube-proxy — verteilt Netzwerk-Traffic

Du als Nutzer sprichst nie direkt mit etcd, dem Scheduler oder kubelet — nur mit dem API-Server.

Die fünf Ressourcen, die du dauernd siehst

Pod

Die kleinste Einheit. Ein Pod ist ein oder mehrere Container, die sich Netzwerk und Volumes teilen — fast immer in der Praxis nur einer.

apiVersion: v1
kind: Pod
metadata:
  name: hello
spec:
  containers:
    - name: web
      image: nginx:alpine
      ports:
        - containerPort: 80

Wichtig: Pods alleine startet man fast nie. Sie sind sterblich und haben keine eingebaute Wiederherstellung — dafür gibt es Deployments.

Deployment

Ein Deployment verwaltet n Pods nach dem gleichen Bauplan und sorgt für Rollouts, Skalierung und Selbstheilung.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:alpine
          ports:
            - containerPort: 80
flowchart LR
    D[Deployment<br/>replicas: 3]
    D --> RS[ReplicaSet]
    RS --> P1[Pod]
    RS --> P2[Pod]
    RS --> P3[Pod]

Wenn ein Pod stirbt, startet das ReplicaSet einen neuen. Wenn du das Image änderst, macht das Deployment einen Rolling Update.

Service

Pods bekommen ständig neue IPs — direkt darauf zugreifen geht schief. Ein Service ist eine stabile Adresse vor einer Pod-Gruppe (per Labels ausgewählt).

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web        # alle Pods mit diesem Label
  ports:
    - port: 80
      targetPort: 80
  type: ClusterIP   # intern erreichbar (Default)
Service-Typ Wann
ClusterIP Default — nur Cluster-intern
NodePort Port auf jedem Node — schnell zum Testen, in Prod meist nicht
LoadBalancer Cloud-LB davorgeschaltet — typisch in Prod
ExternalName DNS-Alias auf einen externen Hostnamen

ConfigMap

Nicht-geheime Konfiguration als Key-Value:

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
data:
  WELCOME: "Hallo aus k8s"
  LOG_LEVEL: "info"

Verwenden im Pod:

envFrom:
  - configMapRef:
      name: web-config

Secret

Wie ConfigMap, aber base64-kodiert und mit Zugriffsschutz. Achtung: base64 ist keine Verschlüsselung — Secrets schützt RBAC und (in Prod) Verschlüsselung at-rest.

apiVersion: v1
kind: Secret
metadata:
  name: db
type: Opaque
stringData:
  password: "geheim"

Mit stringData schreibst du den Klartext, k8s kodiert ihn beim Speichern.

kubectl — der Spickzettel

# Cluster-Info
kubectl cluster-info
kubectl get nodes
 
# Ressourcen ansehen
kubectl get pods -n default
kubectl get pods -A                # alle Namespaces
kubectl get pods -o wide           # mehr Spalten (Node, IP)
kubectl get deploy,svc,cm,secret   # mehrere auf einmal
 
# Details
kubectl describe pod web-abc123
kubectl logs web-abc123 -f          # follow
kubectl logs web-abc123 --previous  # vorheriger Container
 
# In den Pod
kubectl exec -it web-abc123 -- sh
 
# Apply / Delete
kubectl apply -f deployment.yaml
kubectl delete -f deployment.yaml
 
# Port-Forward zum Debuggen
kubectl port-forward svc/web 8080:80

Tipp: alias k=kubectl plus kubectl completion machen das deutlich angenehmer.

Labels & Selectors — wie alles zusammenhängt

Pods, Services, Deployments hängen über Labels zusammen. Es gibt keinen festen „Foreign Key" — jeder Selektor sagt: „Ich meine alles mit diesen Labels."

metadata:
  labels:
    app: web
    tier: frontend
    env: prod
kubectl get pods -l app=web,env=prod
kubectl get pods -l 'tier in (frontend,backend)'

Konvention: Mindestens app, oft auch tier, env, version. Konsistente Labels sind die Grundlage für Selektoren, Monitoring und Netzwerk-Policies.

Namespaces

Logische Trennung innerhalb eines Clusters — typisch für Umgebungen (dev, staging, prod) oder Teams.

kubectl create namespace dev
kubectl apply -f web.yaml -n dev
kubectl config set-context --current --namespace=dev

Ressourcen in unterschiedlichen Namespaces sind isoliert — derselbe Name kollidiert nicht.

Dein erstes Deployment — lokal

Du brauchst einen lokalen Cluster. Empfehlung: kind (Kubernetes-in-Docker) oder minikube.

# kind installieren (oder via Paketmanager)
brew install kind            # macOS
# oder
go install sigs.k8s.io/kind@latest
 
# Cluster hochziehen
kind create cluster --name lernen
kubectl cluster-info

Datei hello.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
        - name: web
          image: nginxdemos/hello:latest
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  selector:
    app: hello
  ports:
    - port: 80
      targetPort: 80
kubectl apply -f hello.yaml
kubectl get pods -w                  # warte, bis "Running"
kubectl port-forward svc/hello 8080:80
# Browser → http://localhost:8080

Lass jetzt einen Pod „sterben":

kubectl delete pod -l app=hello --field-selector status.phase=Running | head -1
kubectl get pods -w
# Innerhalb von Sekunden startet ein neuer Pod

Das ist Self-Healing in der Praxis.

Häufige Stolperfallen

„Pod ist Pending und startet nicht"

kubectl describe pod <name>

Im Events-Block stehen die häufigsten Ursachen:

  • Insufficient cpu/memory — Cluster zu klein oder Requests zu hoch
  • ImagePullBackOff — Image nicht gefunden / kein Pull-Secret
  • Unschedulable — keine passende Node (Taints, Selectors)

„CrashLoopBackOff"

Der Container startet, stürzt aber gleich wieder ab. kubectl logs <pod> zeigt warum. Klassisch: Konfig fehlt, DB nicht erreichbar, falscher CMD.

„Service hat keine Endpoints"

kubectl get endpoints web
# NAME   ENDPOINTS   AGE
# web    <none>      2m

Der selector matcht keinen Pod. Labels prüfen — meist Tippfehler.

„Apply geändert, aber nichts passiert"

Stell sicher, dass du am richtigen Cluster bist:

kubectl config current-context
kubectl config use-context <name>

Praxis-Übung

  1. Erstelle einen lokalen Cluster mit kind oder minikube.
  2. Deploye das hello-Beispiel oben.
  3. Skaliere auf 5 Replicas: kubectl scale deploy/hello --replicas=5.
  4. Aktualisiere das Image — beobachte den Rolling Update.
  5. Erstelle eine ConfigMap mit WELCOME=... und injizier sie als Env- Variable in das Deployment.
  6. Lösche den Cluster wieder: kind delete cluster --name lernen.

Weiterführendes