Zum Inhalt springen
sw
en

Tippe um zu suchen

Virtualisierung

Kubernetes – Container-Orchestrierung Grundlagen

Pods, Deployments, Services, kubectl, Helm und Ingress verstehen – inklusive k3s als schlanker Einstieg fuer Schweizer KMU.

13 Min Lesezeit Expert Zuletzt aktualisiert:

Warum Kubernetes und wann brauchst du es ueberhaupt?

Docker loest das Problem “Wie packe ich eine Anwendung reproduzierbar ein”. Kubernetes loest das naechste Problem: “Wie betreibe ich hunderte Container zuverlaessig, verteilt auf mehreren Maschinen, mit automatischem Neustart bei Absturz, automatischer Skalierung und Zero-Downtime-Updates”. Kubernetes (kurz K8s, weil zwischen dem K und dem s acht Buchstaben stehen) ist ein Orchestrierungssystem, das von Google entwickelt und 2014 als Open-Source-Projekt veroeffentlicht wurde. Es liegt heute bei der Cloud Native Computing Foundation (CNCF).

Die ehrliche Wahrheit vorweg: Fuer die meisten Schweizer KMU mit ein paar Dutzend virtuellen Maschinen und einer Handvoll Applikationen ist Kubernetes oft ueberdimensioniert. Ein docker compose up -d auf zwei, drei Servern erledigt 80 Prozent der Faelle problemlos. Kubernetes lohnt sich, sobald mindestens eine dieser Bedingungen zutrifft:

  • Du betreibst mehrere Dutzend Microservices, die unabhaengig skaliert werden muessen.
  • Du brauchst echte Selbstheilung: Ein Node faellt aus, die Workloads wandern automatisch auf andere Nodes.
  • Du willst deklarativ arbeiten: Der gewuenschte Zustand steht im YAML, Kubernetes sorgt selbststaendig dafuer, dass die Realitaet dem entspricht.
  • Mehrere Teams teilen sich eine Infrastruktur und brauchen Isolation via Namespaces und RBAC.

Architektur: Control Plane und Worker Nodes

Ein Kubernetes-Cluster besteht aus zwei Rollen von Maschinen (physisch, virtuell oder Cloud-Instanzen):

KomponenteRolleLaeuft auf
kube-apiserverZentrale Schnittstelle, alle Befehle (auch von kubectl) laufen hier durchControl Plane
etcdVerteilter Key-Value-Store, speichert den gesamten Cluster-ZustandControl Plane
kube-schedulerEntscheidet, auf welchem Node ein neuer Pod platziert wirdControl Plane
kube-controller-managerUeberwacht Ist- vs. Soll-Zustand, startet z.B. fehlende Pods neuControl Plane
kubeletAgent, der auf jedem Node laeuft und Pods gemaess Vorgabe startetWorker Node
kube-proxyRegelt Netzwerkweiterleitung fuer Services auf jedem NodeWorker Node
Container-RuntimeStartet die eigentlichen Container (containerd, CRI-O)Worker Node

Der Ablauf in Kurzform: Du schickst per kubectl ein YAML-Manifest an den kube-apiserver. Der Scheduler entscheidet, welcher Node genug freie Ressourcen hat. Das kubelet auf diesem Node zieht das Image und startet den Container ueber die Runtime. Der controller-manager beobachtet laufend, ob Ist- und Soll-Zustand uebereinstimmen – faellt ein Pod aus, wird automatisch ein neuer erstellt.


Pods – die kleinste Einheit

Ein Pod ist nicht dasselbe wie ein Container. Ein Pod ist eine Gruppe von einem oder mehreren Containern, die sich Netzwerk (dieselbe IP-Adresse, gleicher Loopback) und optional Storage-Volumes teilen. In der Praxis laeuft in den allermeisten Faellen genau ein Container pro Pod – Mehrfach-Container-Pods (Sidecar-Pattern) nutzt man fuer Logging-Agenten, Service-Mesh-Proxys (z.B. Envoy bei Istio) oder Init-Container fuer Vorbereitungsschritte.

# pod-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-test
  labels:
    app: nginx-test
spec:
  containers:
    - name: nginx
      image: nginx:1.27-alpine
      ports:
        - containerPort: 80
      resources:
        requests:
          cpu: "100m"
          memory: "64Mi"
        limits:
          cpu: "250m"
          memory: "128Mi"
kubectl apply -f pod-nginx.yaml
kubectl get pods
kubectl describe pod nginx-test
kubectl logs nginx-test
kubectl delete pod nginx-test

Wichtig fuer die Ressourcenplanung: requests ist der Wert, den der Scheduler fuer die Platzierung verwendet (garantierte Mindestressourcen), limits ist die harte Obergrenze. Ueberschreitet ein Container sein Memory-Limit, wird er vom Kernel mit OOMKilled beendet. CPU-Limits fuehren dagegen zu Throttling, nicht zum Abbruch.


Deployments – Pods verwalten, skalieren, aktualisieren

Ein Deployment ist die Kontrollinstanz, die dafuer sorgt, dass immer die gewuenschte Anzahl Pod-Replikas laeuft. Es verwaltet im Hintergrund ein ReplicaSet, das wiederum die einzelnen Pods erzeugt.

# deployment-webapp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
        - name: webapp
          image: registry.example.ch/webapp:1.4.2
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
# Anwenden
kubectl apply -f deployment-webapp.yaml

# Status pruefen
kubectl get deployments
kubectl rollout status deployment/webapp

# Skalieren
kubectl scale deployment webapp --replicas=5

# Image aktualisieren (loest RollingUpdate aus)
kubectl set image deployment/webapp webapp=registry.example.ch/webapp:1.5.0

# Rollout-Historie ansehen
kubectl rollout history deployment/webapp

# Bei Problemen zurueckrollen
kubectl rollout undo deployment/webapp
kubectl rollout undo deployment/webapp --to-revision=2

Der Unterschied zwischen readinessProbe und livenessProbe ist ein haeufiger Stolperstein: Die Readiness-Probe entscheidet, ob ein Pod Traffic vom Service erhalten darf (schlaegt sie fehl, wird der Pod aus dem Load Balancing genommen, aber nicht neu gestartet). Die Liveness-Probe entscheidet, ob der Container ueberhaupt noch lebt – schlaegt sie fehl, killt Kubernetes den Container und startet ihn neu.


Services – stabile Netzwerkadressen fuer verguengliche Pods

Pods sind verguenglich: Sie bekommen bei jedem Neustart eine neue IP-Adresse. Ein Service loest das, indem er eine stabile virtuelle IP und einen DNS-Namen bereitstellt und den Traffic per Label-Selector auf die passenden Pods verteilt.

Service-TypZweckErreichbarkeit
ClusterIP (Default)Interne Kommunikation zwischen Diensten im ClusterNur innerhalb des Clusters
NodePortOeffnet einen festen Port (30000–32767) auf jedem NodeNode-IP:Port von aussen
LoadBalancerFordert einen externen Load Balancer an (bei Cloud-Providern automatisch)Oeffentliche IP
ExternalNameMappt einen Service-Namen auf einen externen DNS-NamenCNAME-artig, kein Proxying
# service-webapp.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-svc
spec:
  type: ClusterIP
  selector:
    app: webapp
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
kubectl apply -f service-webapp.yaml
kubectl get services
kubectl get endpoints webapp-svc

# Kurztest von innerhalb des Clusters
kubectl run curl-test --image=curlimages/curl -it --rm -- curl http://webapp-svc

Der Service-DNS-Name folgt dem Schema <service>.<namespace>.svc.cluster.local, innerhalb desselben Namespace reicht meist der kurze Name webapp-svc. Das ist der Grund, warum Microservices sich gegenseitig einfach ueber den Servicenamen ansprechen koennen, ohne IP-Adressen zu kennen.


Ingress – HTTP(S)-Routing und Load Balancing auf Layer 7

Waehrend ein Service auf Layer 4 (TCP/UDP) arbeitet, regelt ein Ingress das HTTP/HTTPS-Routing auf Layer 7: Ein einziger Eingangspunkt kann anhand von Hostname oder Pfad auf viele verschiedene Services verteilen – inklusive TLS-Terminierung. Ingress selbst ist nur die Regel-Definition; ausgefuehrt wird sie von einem Ingress-Controller (z.B. NGINX Ingress Controller, Traefik oder HAProxy), der als Pod im Cluster laeuft.

# ingress-webapp.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webapp-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - webapp.firma-kmu.ch
      secretName: webapp-tls
  rules:
    - host: webapp.firma-kmu.ch
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: webapp-svc
                port:
                  number: 80
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 8080
# Ingress-Controller installieren (NGINX, per Helm)
helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace

kubectl apply -f ingress-webapp.yaml
kubectl get ingress

In diesem Beispiel routet webapp.firma-kmu.ch/ auf den Frontend-Service und webapp.firma-kmu.ch/api auf einen separaten API-Service – ein klassisches Muster, um mehrere Anwendungen hinter einer einzigen oeffentlichen IP und einem TLS-Zertifikat zu betreiben. Mit cert-manager (separates CNCF-Projekt) lassen sich Let’s-Encrypt-Zertifikate automatisch beziehen und erneuern.


kubectl – die wichtigsten Befehle im Alltag

# Kontext und Cluster-Info
kubectl cluster-info
kubectl config get-contexts
kubectl config use-context mein-cluster
kubectl config set-context --current --namespace=produktion

# Ressourcen anzeigen
kubectl get nodes -o wide
kubectl get pods -A                     # -A = alle Namespaces
kubectl get pods -n produktion -w       # -w = watch (live)
kubectl get all -n produktion

# Details und Fehlersuche
kubectl describe pod webapp-7d8f9c6b5-x2klm
kubectl logs webapp-7d8f9c6b5-x2klm
kubectl logs webapp-7d8f9c6b5-x2klm --previous   # Logs des vorherigen (abgestuerzten) Containers
kubectl exec -it webapp-7d8f9c6b5-x2klm -- /bin/sh

# YAML aus laufendem Cluster ziehen
kubectl get deployment webapp -o yaml > webapp-backup.yaml

# Aendern und Bereinigen
kubectl apply -f deployment-webapp.yaml
kubectl edit deployment webapp
kubectl delete -f deployment-webapp.yaml
kubectl delete pod webapp-7d8f9c6b5-x2klm    # wird vom Deployment sofort ersetzt

# Ressourcennutzung (benoetigt metrics-server)
kubectl top nodes
kubectl top pods -n produktion

# Namespaces
kubectl create namespace staging
kubectl get namespaces

Helm – der Paketmanager fuer Kubernetes

Sobald eine Anwendung aus mehreren zusammenhaengenden Ressourcen besteht (Deployment, Service, Ingress, ConfigMap, Secret, PersistentVolumeClaim), wird das Verwalten einzelner YAML-Dateien schnell unuebersichtlich. Helm buendelt diese Ressourcen in einem Chart und macht sie ueber values.yaml parametrisierbar – vergleichbar mit einem Windows-Installer, der bei der Installation nach Zielpfad und Optionen fragt.

# Helm installieren (Windows via winget)
winget install Helm.Helm

# Offizielles Repository hinzufuegen (Beispiel: Bitnami)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# Verfuegbare Charts suchen
helm search repo wordpress

# Chart installieren
helm install mein-wordpress bitnami/wordpress \
  --namespace webshop --create-namespace \
  --set wordpressUsername=admin \
  --set persistence.size=20Gi

# Installierte Releases anzeigen
helm list -A

# Konfiguration aendern und aktualisieren
helm upgrade mein-wordpress bitnami/wordpress \
  --namespace webshop \
  --set replicaCount=2

# Zuruecksetzen auf vorherige Version
helm rollback mein-wordpress 1 -n webshop

# Deinstallieren (loescht alle zugehoerigen Ressourcen)
helm uninstall mein-wordpress -n webshop

Ein eigenes Chart fuer eine interne Applikation legst du mit helm create meine-app an – das generiert ein Skelett mit Chart.yaml, values.yaml und templates/. In den Templates stehen die YAML-Manifeste mit Platzhaltern im Go-Template-Format, die zur Installationszeit durch Werte aus der values.yaml ersetzt werden (z.B. Image-Repository und Image-Tag als Variablen statt fest codiert):

# templates/deployment.yaml (Ausschnitt eines Helm-Charts)
containers:
  - name: meine-app
    image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

Kubernetes vs. Docker Compose – Entscheidungshilfe

Das ist die haeufigste Frage im KMU-Umfeld: reicht Docker Compose, oder braucht es Kubernetes?

KriteriumDocker ComposeKubernetes
LernkurveNiedrig, ein YAML-FileHoch, viele Konzepte (Pods, Services, RBAC, …)
Multi-Host-BetriebNein (nativ nur ein Host)Ja, das ist der Kernzweck
Automatisches FailoverNeinJa, Pods wandern auf andere Nodes
SelbstheilungEingeschraenkt (restart: always)Umfassend (Health-Checks, Auto-Restart)
Rolling UpdatesManuell/Skripting noetigEingebaut (kubectl rollout)
Horizontale SkalierungManuell, begrenzt sinnvollAutomatisch via HPA (Horizontal Pod Autoscaler)
Ressourcen-OverheadMinimalControl Plane braucht selbst CPU/RAM
BetriebsaufwandGeringSignifikant (Updates, Zertifikate, etcd-Backups)
Typischer Einsatz1–2 Server, wenige DiensteViele Services, mehrere Nodes, hohe Verfuegbarkeit

Vereinfachter Entscheidungsablauf

Brauchst du Multi-Host-Ausfallsicherheit oder Auto-Scaling?
├── Nein → Docker Compose reicht
└── Ja
    ├── Team kennt Kubernetes noch nicht / kleines Setup (1-5 Nodes)?
    │   └── Ja → k3s als Einstieg
    └── Grosses Setup, mehrere Teams, hohe Verfuegbarkeit noetig?
        └── Ja → Vollstaendiges Kubernetes (kubeadm, oder Managed: AKS/EKS/GKE)

k3s – der leichte Einstieg fuer Schweizer KMU

k3s ist eine von Rancher (heute Teil von SUSE) entwickelte, CNCF-zertifizierte Kubernetes-Distribution, die als einzelnes Binary unter 100 MB daherkommt. Sie ersetzt etcd standardmaessig durch SQLite (fuer Single-Node-Setups) und entfernt selten genutzte Alpha-Features, ist aber API-kompatibel zu Standard-Kubernetes. Genau das macht k3s zum idealen Einstiegspunkt fuer KMU: Ein Verwaltungsserver oder sogar ein Raspberry Pi reicht als erster Cluster-Node, alle gelernten kubectl- und Helm-Befehle funktionieren identisch weiter, ein spaeterer Umstieg auf einen vollwertigen Cluster ist ohne Konzeptwechsel moeglich.

# k3s Server-Node installieren (Linux, als root)
curl -sfL https://get.k3s.io | sh -

# Status pruefen
systemctl status k3s
k3s kubectl get nodes

# kubeconfig fuer externen Zugriff (z.B. von deinem Laptop) exportieren
cat /etc/rancher/k3s/k3s.yaml
# Server-IP in der Datei von "127.0.0.1" auf die echte IP des Servers anpassen,
# dann lokal als ~/.kube/config speichern

# Weiteren Node als Agent (Worker) beitreten lassen
# Token vom Server holen:
cat /var/lib/rancher/k3s/server/node-token

# Auf dem neuen Node ausfuehren:
curl -sfL https://get.k3s.io | K3S_URL=https://SERVER-IP:6443 \
  K3S_TOKEN=DAS_TOKEN_VOM_SERVER sh -

k3s bringt bereits einen Ingress-Controller (Traefik) und einen einfachen Load Balancer (ServiceLB, vergleichbar mit MetalLB) fertig mit – fuer viele KMU-Szenarien musst du gar nichts mehr nachinstallieren. Falls du lieber NGINX als Ingress-Controller nutzen willst, deaktivierst du Traefik beim Setup:

curl -sfL https://get.k3s.io | sh -s - --disable traefik

Fuer Testumgebungen auf einem Windows- oder Mac-Laptop eignet sich alternativ k3d (k3s in Docker) oder minikube – beide starten einen kompletten Kubernetes-Cluster innerhalb weniger Sekunden lokal in Containern.

# k3d: k3s-Cluster in Docker-Containern (super fuer lokales Testen)
k3d cluster create testcluster --agents 2
kubectl get nodes
k3d cluster delete testcluster

ConfigMaps und Secrets – Konfiguration von Code trennen

# configmap-webapp.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-config
data:
  LOG_LEVEL: "info"
  API_TIMEOUT_SECONDS: "30"
---
apiVersion: v1
kind: Secret
metadata:
  name: webapp-secret
type: Opaque
stringData:
  DB_PASSWORD: "wird-in-der-praxis-aus-vault-oder-sealed-secrets-bezogen"
# Einbindung im Deployment:
env:
  - name: LOG_LEVEL
    valueFrom:
      configMapKeyRef:
        name: webapp-config
        key: LOG_LEVEL
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: webapp-secret
        key: DB_PASSWORD

Namespaces und RBAC – Multi-Tenancy im KMU

Namespaces trennen logisch Umgebungen oder Teams innerhalb desselben physischen Clusters:

kubectl create namespace produktion
kubectl create namespace staging
kubectl create namespace team-buchhaltung

# Ressourcenquoten pro Namespace begrenzen (wichtig, damit sich Teams nicht gegenseitig Ressourcen wegnehmen)
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: team-buchhaltung
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "20"
EOF

Mit RBAC (Role-Based Access Control) legst du fest, wer was in welchem Namespace darf – analog zu NTFS-Berechtigungen oder Active-Directory-Gruppen, nur eben fuer Kubernetes-Ressourcen:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: team-buchhaltung
  name: pod-reader
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: buchhaltung-lesezugriff
  namespace: team-buchhaltung
subjects:
  - kind: User
    name: m.mueller@firma-kmu.ch
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Haeufige Fallstricke in der Praxis

  • CrashLoopBackOff: Der Container startet, stuerzt ab, wird neu gestartet, stuerzt wieder ab – mit exponentiell laengeren Wartezeiten. Ursache fast immer per kubectl logs oder kubectl describe pod sichtbar (fehlende Umgebungsvariable, falsches Startkommando, Health-Check zu strikt konfiguriert).
  • ImagePullBackOff: Falscher Image-Name/Tag, private Registry ohne imagePullSecrets, oder Rate-Limit bei Docker Hub erreicht.
  • Pending-Pods: Meist zu wenig freie Ressourcen auf den Nodes, oder ein nodeSelector/taint verhindert die Platzierung. kubectl describe pod zeigt im Event-Log den genauen Grund.
  • Fehlende Ressourcenlimits: Ohne resources.limits kann ein einzelner fehlerhafter Pod den ganzen Node lahmlegen (Noisy-Neighbour-Problem). In jedem produktiven Manifest Requests und Limits setzen.
  • latest-Tag in Produktion: Ohne festen Versions-Tag weisst du nie genau, welches Image gerade laeuft, und Rollbacks werden unmoeglich. Immer konkrete Versions-Tags verwenden.
  • Zu wenig Replikas fuer echte Hochverfuegbarkeit: Ein Deployment mit replicas: 1 bietet keinen Ausfallschutz – faellt der Node aus, ist der Dienst kurzzeitig weg, bis der Scheduler den Pod neu platziert.

Weiterlernen

Verwandte Themen

Videos

YouTube
Kubernetes: Eine Einfuehrung in 120 Minuten // deutsch
YouTube
Praktische Einfuehrung in Kubernetes mit kubectl - Deployments, Pods, Services deutsch

Kommentare

Frage, Verbesserungsvorschlag oder eigene Erfahrung zu diesem Artikel? Schreib einen Kommentar. Neue Beiträge erscheinen nach kurzer Moderation.

  • Lade Kommentare …
Kommentar schreiben