Workshop-Wiki · Deutsch

Kubernetes Workshop: Intermediate – Übungsaufgaben mit Lösungen

Bezug: 02-intermediate-DE-aktualisiert.md Schwierigkeitsgrad: Intermediate Format: Hands-on-Lab mit Musterlösungen und Walkthroughs

Workshop-Beispiele als ZIP
Lernstand in der Web-App

Kapitel als gelesen markieren, Lesefortschritt sehen und Praxismodule abhaken: Diese Funktionen stehen nach Aktivierung der optionalen Web-App zur Verfügung. Der Workshop-Fortschritt ergibt sich automatisch aus den Kapiteln aller Dokumentseiten. Deine Auswahl bleibt auf diesem Gerät; es gibt kein Konto und keinen Serverabgleich. Eine Installation ist nicht erforderlich. Beim Widerruf in den Datenschutz-Einstellungen wird der Lernstand gelöscht.

Lesefortschritt0%
0%
0 / 23 Kapitel gelesen
Weitere SprachfassungEnglisch ↗Griechisch ↗

Kubernetes Workshop: Intermediate – Übungsaufgaben mit Lösungen

Bezug: 02-intermediate-DE-aktualisiert.md
Schwierigkeitsgrad: Intermediate
Format: Hands-on-Lab mit Musterlösungen und Walkthroughs

Ziel dieses Übungsdokuments

Dieses Übungsdokument ergänzt den Intermediate-Workshop mit praxisnahen Aufgaben, Lösungen und Walkthroughs. Der Schwerpunkt liegt auf Workload-Auswahl, Init Containern, Sidecars, Ephemeral Containern, Probes, graceful Shutdown, PodDisruptionBudgets, Requests, Limits, QoS, LimitRange, ResourceQuota, Scheduling, NetworkPolicy, DNS, Ingress oder Gateway API, Storage, Helm, Kustomize und systematischem Troubleshooting.

01 / 23

Voraussetzungen

Du brauchst:

  • einen Lab-Cluster mit funktionierendem CNI
  • CoreDNS
  • kubectl
  • optional: helm
  • optional: kustomize oder kubectl kustomize
  • optional: eine StorageClass
  • optional: Ingress Controller oder Gateway Controller

Empfohlener Namespace:

bash
export NS=workshop-intermediate
kubectl create namespace "$NS"
kubectl config set-context --current --namespace="$NS"
02 / 23

Aufgabenübersicht

Nr. Thema Ergebnis
1 Workload-Auswahl Deployment, Job und CronJob fachlich abgegrenzt
2 Init Container und Sidecar Pod-Lebenszyklen nachvollzogen
3 Probes und Lifecycle Startup, Readiness und Liveness sauber getrennt
4 Resources, QoS, LimitRange, Quota Namespace-Grenzen und Scheduling-Wirkung geprüft
5 Scheduling Node Labels, nodeSelector, Taints und Tolerations getestet
6 NetworkPolicy und DNS Default Deny plus gezielte Erlaubnisse erstellt
7 Ingress oder Gateway HTTP-App extern erreichbar gemacht oder sauber eingeordnet
8 PVC und StatefulSet persistenter Zustand getestet
9 Helm und Kustomize gerenderte Manifeste verglichen
10 Troubleshooting-Parcours typische Fehler systematisch behoben

Teil A – Aufgaben

03 / 23

Aufgabe 1: Workload-Ressource auswählen

Ziel

Du entscheidest, ob Deployment, Job oder CronJob fachlich passt.

Ausgangslage

Du sollst drei Anforderungen modellieren:

  1. Ein Webserver soll dauerhaft laufen und rollend aktualisiert werden.
  2. Eine Datenmigration soll einmalig laufen und danach beendet sein.
  3. Ein Cleanup soll jede Stunde laufen.

Arbeitsauftrag

  1. Erstelle für jeden Fall ein Minimalmanifest.
  2. Wende die Manifeste an.
  3. Prüfe Status und Events.
  4. Dokumentiere, warum die gewählte Ressource passt.

04 / 23

Aufgabe 2: Init Container und Sidecar einordnen

Ziel

Du verstehst, wann ein Init Container vor der Anwendung laufen muss und wann ein zusätzlicher Container denselben Pod-Lebenszyklus teilt.

Arbeitsauftrag

Erstelle einen Pod, bei dem:

  • ein Init Container eine Datei in ein emptyDir schreibt,
  • der App Container diese Datei liest,
  • ein Sidecar Container periodisch eine Heartbeat-Datei schreibt.

Prüfe:

  1. Reihenfolge der Containerstarts,
  2. Logs aller Container,
  3. Inhalt des gemeinsamen Volumes.

05 / 23

Aufgabe 3: Probes korrekt trennen

Ziel

Du korrigierst eine fehlerhafte Probe-Konfiguration, die Neustarts verursacht.

Ausgangslage

Eine Beispiel-App startet langsam. Die Liveness Probe prüft zu früh denselben Pfad wie Readiness.

Arbeitsauftrag

  1. Erstelle ein Deployment mit absichtlich zu aggressiver Liveness Probe.
  2. Beobachte Events und Restarts.
  3. Ergänze eine Startup Probe.
  4. Trenne Readiness und Liveness fachlich.
  5. Erkläre, warum Readiness Traffic steuert und Liveness Neustarts auslöst.

06 / 23

Aufgabe 4: Resources, QoS, LimitRange und ResourceQuota

Ziel

Du setzt Requests und Limits, beobachtest QoS-Klassen und erzeugst Namespace-Grenzen.

Arbeitsauftrag

  1. Erstelle eine LimitRange mit Default Requests und Limits.
  2. Erstelle eine ResourceQuota für CPU, Memory und Pod-Anzahl.
  3. Rolle drei Pods aus:
    • ohne explizite Ressourcen,
    • mit Requests kleiner als Limits,
    • mit Requests gleich Limits.
  4. Bestimme die QoS-Klassen.
  5. Erzeuge absichtlich eine Quota-Verletzung.

07 / 23

Aufgabe 5: Scheduling mit Node Labels, nodeSelector, Taint und Toleration

Ziel

Du steuerst die Platzierung eines Pods und interpretierst Pending-Events.

Arbeitsauftrag

  1. Label einen Lab-Node mit workshop-role=apps.
  2. Erstelle einen Pod mit passendem nodeSelector.
  3. Erstelle einen Pod mit nicht passendem nodeSelector.
  4. Setze einen Taint auf den Node.
  5. Erstelle einmal einen Pod ohne Toleration und einmal mit Toleration.
  6. Dokumentiere die Events.

08 / 23

Aufgabe 6: NetworkPolicy Default Deny plus DNS-Erlaubnis

Ziel

Du erstellst eine Default-Deny-Policy und erlaubst danach gezielt DNS und App-Traffic.

Arbeitsauftrag

  1. Erstelle zwei Deployments: frontend und backend.
  2. Erstelle Services für beide.
  3. Prüfe, dass frontend backend erreichen kann.
  4. Erstelle eine Default-Deny-Policy für Ingress und Egress.
  5. Prüfe, dass Traffic blockiert ist.
  6. Erlaube DNS-Egress.
  7. Erlaube frontend zu backend auf Port 8080.
  8. Prüfe erneut.

09 / 23

Aufgabe 7: Ingress oder Gateway bereitstellen

Ziel

Du veröffentlichst eine HTTP-Anwendung über Ingress oder beschreibst alternativ die Gateway-API-Ressourcen, wenn kein Controller installiert ist.

Arbeitsauftrag

  1. Prüfe, ob ein Ingress Controller oder Gateway Controller vorhanden ist.
  2. Stelle eine HTTP-App bereit.
  3. Erstelle entweder eine Ingress-Ressource oder ein Gateway/HTTPRoute-Beispiel.
  4. Prüfe Controller-Events, Backend-Service und Routing.
  5. Dokumentiere, warum eine Ingress-Ressource ohne Controller keine Wirkung hat.

10 / 23

Aufgabe 8: PVC und StatefulSet nutzen

Ziel

Du betreibst einen kleinen stateful Workload mit PVC und prüfst, ob Daten nach Pod-Neuerstellung erhalten bleiben.

Arbeitsauftrag

  1. Prüfe vorhandene StorageClasses.
  2. Erstelle ein StatefulSet mit volumeClaimTemplates.
  3. Schreibe eine Datei in das Volume.
  4. Lösche den Pod.
  5. Prüfe nach der Neuerstellung, ob die Datei noch existiert.
  6. Dokumentiere, warum ein hostPath-Lab keine Produktionsannahme ist.

11 / 23

Aufgabe 9: Helm und Kustomize vergleichen

Ziel

Du renderst Manifeste, bevor du sie anwendest, und vergleichst Helm mit Kustomize.

Arbeitsauftrag

  1. Erstelle eine kleine Kustomize-Base und ein Overlay dev.
  2. Rendere das Overlay.
  3. Erstelle ein minimales Helm Chart oder nutze ein lokales Beispielchart.
  4. Rendere das Helm Chart.
  5. Vergleiche Reviewbarkeit, Parametrisierung und Fehlerrisiko.

12 / 23

Aufgabe 10: Troubleshooting-Parcours

Ziel

Du untersuchst fünf typische Fehlerbilder methodisch.

Fehlerbilder

  1. Pod bleibt Pending wegen falschem nodeSelector.
  2. Pod zeigt ImagePullBackOff wegen falschem Image-Tag.
  3. Pod zeigt CrashLoopBackOff wegen fehlerhaftem Startkommando.
  4. PVC bleibt Pending wegen falscher StorageClass.
  5. Service hat keine Endpoints wegen falschem Selector.

Arbeitsauftrag

Für jeden Fehler dokumentierst du:

  • Symptom,
  • Diagnosekommando,
  • relevante Events oder Logs,
  • Ursache,
  • Fix,
  • Validierung.

Teil B – Lösungen und Walkthroughs

13 / 23

Lösung 1: Workload-Ressource auswählen

Deployment für dauerhaft laufenden Webserver

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: workshop-intermediate
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80

Job für einmalige Migration

yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: migration
  namespace: workshop-intermediate
spec:
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: migration
          image: busybox:1.36
          command: ["sh", "-c", "echo Migration läuft; sleep 3; echo Migration fertig"]

CronJob für stündlichen Cleanup

yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: cleanup
  namespace: workshop-intermediate
spec:
  schedule: "0 * * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: cleanup
              image: busybox:1.36
              command: ["sh", "-c", "date; echo cleanup"]

Prüfung:

bash
kubectl apply -f deployment.yml
kubectl apply -f job.yml
kubectl apply -f cronjob.yml
kubectl get deployment,job,cronjob,pod
kubectl logs job/migration

Fachliche Entscheidung:

Anforderung Ressource Begründung
dauerhaft laufender HTTP-Dienst Deployment Replikate, Rollouts, Rollbacks
einmalige Migration Job Abschlusszustand ist fachlich gewünscht
stündliches Cleanup CronJob zeitgesteuerte Job-Erzeugung

14 / 23

Lösung 2: Init Container und Sidecar

Manifest:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
  namespace: workshop-intermediate
  labels:
    app: lifecycle-demo
spec:
  volumes:
    - name: shared
      emptyDir: {}
  initContainers:
    - name: init-write
      image: busybox:1.36
      command: ["sh", "-c", "echo vorbereitet > /shared/init.txt"]
      volumeMounts:
        - name: shared
          mountPath: /shared
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "while true; do cat /shared/init.txt; sleep 30; done"]
      volumeMounts:
        - name: shared
          mountPath: /shared
    - name: sidecar
      image: busybox:1.36
      command: ["sh", "-c", "while true; do date > /shared/heartbeat.txt; sleep 10; done"]
      volumeMounts:
        - name: shared
          mountPath: /shared

Prüfung:

bash
kubectl apply -f lifecycle-demo.yml
kubectl get pod lifecycle-demo
kubectl describe pod lifecycle-demo
kubectl logs lifecycle-demo -c init-write
kubectl logs lifecycle-demo -c app
kubectl exec lifecycle-demo -c app -- ls -l /shared
kubectl exec lifecycle-demo -c app -- cat /shared/heartbeat.txt

Erklärung:

  • Init Container laufen vor App Containern und müssen erfolgreich enden.
  • Sidecars laufen parallel zur Anwendung.
  • emptyDir teilt Dateien innerhalb desselben Pods, bleibt aber nicht über Pod-Löschung hinaus erhalten.

15 / 23

Lösung 3: Probes korrigieren

Robustes Beispielmanifest:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: probe-demo
  namespace: workshop-intermediate
spec:
  replicas: 1
  selector:
    matchLabels:
      app: probe-demo
  template:
    metadata:
      labels:
        app: probe-demo
    spec:
      containers:
        - name: app
          image: registry.k8s.io/e2e-test-images/agnhost:2.53
          args: ["netexec", "--http-port=8080"]
          ports:
            - containerPort: 8080
          startupProbe:
            httpGet:
              path: /healthz
              port: 8080
            failureThreshold: 30
            periodSeconds: 2
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 2
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 10

Prüfung:

bash
kubectl apply -f probe-demo.yml
kubectl rollout status deployment/probe-demo
kubectl describe pod -l app=probe-demo
kubectl get pods -w

Mustererklärung:

  • Startup Probe schützt langsame Starts; solange sie nicht erfolgreich ist, werden Liveness und Readiness nicht aktiv.
  • Readiness entscheidet, ob ein Pod Endpoints für Services erhält.
  • Liveness entscheidet, ob kubelet den Container neu startet.
  • Eine zu aggressive Liveness Probe erzeugt Neustart-Schleifen.

16 / 23

Lösung 4: Resources, QoS, LimitRange und ResourceQuota

LimitRange:

yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
  namespace: workshop-intermediate
spec:
  limits:
    - type: Container
      defaultRequest:
        cpu: 100m
        memory: 128Mi
      default:
        cpu: 500m
        memory: 256Mi
      max:
        cpu: "1"
        memory: 512Mi

ResourceQuota:

yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: namespace-quota
  namespace: workshop-intermediate
spec:
  hard:
    pods: "5"
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "3"
    limits.memory: 2Gi

Prüfung:

bash
kubectl apply -f limitrange.yml
kubectl apply -f quota.yml
kubectl describe limitrange defaults
kubectl describe resourcequota namespace-quota

Pod mit Burstable QoS:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: burstable-pod
  namespace: workshop-intermediate
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]
      resources:
        requests:
          cpu: 100m
          memory: 128Mi
        limits:
          cpu: 500m
          memory: 256Mi

Pod mit Guaranteed QoS:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: guaranteed-pod
  namespace: workshop-intermediate
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]
      resources:
        requests:
          cpu: 100m
          memory: 128Mi
        limits:
          cpu: 100m
          memory: 128Mi

QoS prüfen:

bash
kubectl apply -f burstable-pod.yml
kubectl apply -f guaranteed-pod.yml
kubectl get pod burstable-pod -o jsonpath='{.status.qosClass}{"\n"}'
kubectl get pod guaranteed-pod -o jsonpath='{.status.qosClass}{"\n"}'

Quota-Verletzung erzeugen:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: too-large
  namespace: workshop-intermediate
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]
      resources:
        requests:
          cpu: "2"
          memory: 2Gi
        limits:
          cpu: "2"
          memory: 2Gi

Erwartung:

bash
kubectl apply -f too-large.yml

Der API Server lehnt die Ressource wegen ResourceQuota ab.


17 / 23

Lösung 5: Scheduling

Node auswählen:

bash
NODE=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')
kubectl label node "$NODE" workshop-role=apps
kubectl get nodes --show-labels | grep workshop-role

Pod mit passendem nodeSelector:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: scheduled-ok
  namespace: workshop-intermediate
spec:
  nodeSelector:
    workshop-role: apps
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]

Pod mit nicht passendem nodeSelector:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: scheduled-pending
  namespace: workshop-intermediate
spec:
  nodeSelector:
    workshop-role: does-not-exist
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]

Prüfung:

bash
kubectl apply -f scheduled-ok.yml
kubectl apply -f scheduled-pending.yml
kubectl get pods -o wide
kubectl describe pod scheduled-pending

Taint setzen:

bash
kubectl taint node "$NODE" dedicated=apps:NoSchedule

Pod mit Toleration:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: tolerated
  namespace: workshop-intermediate
spec:
  nodeSelector:
    workshop-role: apps
  tolerations:
    - key: dedicated
      operator: Equal
      value: apps
      effect: NoSchedule
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]

Cleanup:

bash
kubectl delete pod scheduled-ok scheduled-pending tolerated --ignore-not-found
kubectl taint node "$NODE" dedicated=apps:NoSchedule-
kubectl label node "$NODE" workshop-role-

Erklärung:

  • nodeSelector ist hart: kein passender Node bedeutet Pending.
  • Taint weist Pods ab.
  • Toleration erlaubt einem Pod, einen Taint zu tolerieren; sie erzwingt nicht allein die Platzierung.

18 / 23

Lösung 6: NetworkPolicy und DNS

Beispiel-App:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: workshop-intermediate
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: registry.k8s.io/e2e-test-images/agnhost:2.53
          args: ["netexec", "--http-port=8080"]
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: backend
  namespace: workshop-intermediate
spec:
  selector:
    app: backend
  ports:
    - name: http
      port: 8080
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: workshop-intermediate
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: frontend
          image: curlimages/curl:8.8.0
          command: ["sh", "-c", "sleep 3600"]

Test vor Policy:

bash
kubectl apply -f net-demo.yml
kubectl wait --for=condition=Available deployment/backend --timeout=90s
kubectl wait --for=condition=Available deployment/frontend --timeout=90s
FRONTEND=$(kubectl get pod -l app=frontend -o jsonpath='{.items[0].metadata.name}')
kubectl exec "$FRONTEND" -- curl -s http://backend:8080/hostname

Default Deny:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: workshop-intermediate
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

DNS-Egress erlauben:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: workshop-intermediate
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

Frontend zu Backend erlauben:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: workshop-intermediate
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-egress-to-backend
  namespace: workshop-intermediate
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: backend
      ports:
        - protocol: TCP
          port: 8080

Prüfung:

bash
kubectl apply -f default-deny.yml
kubectl apply -f allow-dns-egress.yml
kubectl apply -f allow-frontend-backend.yml
kubectl exec "$FRONTEND" -- nslookup backend
kubectl exec "$FRONTEND" -- curl -s --max-time 5 http://backend:8080/hostname

Hinweis:

NetworkPolicies wirken nur, wenn das CNI-Plugin sie durchsetzt.


19 / 23

Lösung 7: Ingress oder Gateway

Controller prüfen:

bash
kubectl get ingressclass
kubectl get gatewayclass 2>/dev/null || true
kubectl get pods -A | grep -Ei 'ingress|gateway|traefik|nginx|envoy|haproxy'

Ingress-Beispiel:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  namespace: workshop-intermediate
spec:
  rules:
    - host: web.localtest.me
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend
                port:
                  number: 8080

Prüfung:

bash
kubectl apply -f ingress.yml
kubectl describe ingress web
kubectl get ingress web

Gateway-API-Beispiel, falls Gateway API installiert ist:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web
  namespace: workshop-intermediate
spec:
  parentRefs:
    - name: shared-gateway
      namespace: gateway-system
  hostnames:
    - web.localtest.me
  rules:
    - backendRefs:
        - name: backend
          port: 8080

Fachliche Erklärung:

  • Ingress ist nur eine Regelbeschreibung.
  • Ein Ingress Controller muss diese Regel in Proxy-/Load-Balancer-Konfiguration übersetzen.
  • Gateway API trennt Infrastrukturverantwortung stärker von Anwendungsrouting.

20 / 23

Lösung 8: PVC und StatefulSet

StorageClass prüfen:

bash
kubectl get storageclass

StatefulSet:

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: data-demo
  namespace: workshop-intermediate
spec:
  serviceName: data-demo
  replicas: 1
  selector:
    matchLabels:
      app: data-demo
  template:
    metadata:
      labels:
        app: data-demo
    spec:
      containers:
        - name: app
          image: busybox:1.36
          command: ["sh", "-c", "while true; do sleep 3600; done"]
          volumeMounts:
            - name: data
              mountPath: /data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1Gi

Anwenden und testen:

bash
kubectl apply -f statefulset.yml
kubectl rollout status statefulset/data-demo
kubectl exec data-demo-0 -- sh -c 'echo persistenz-test > /data/test.txt'
kubectl delete pod data-demo-0
kubectl wait --for=condition=Ready pod/data-demo-0 --timeout=120s
kubectl exec data-demo-0 -- cat /data/test.txt

Erwartung:

Die Datei ist nach Pod-Neuerstellung weiterhin vorhanden, weil der PVC wieder mit data-demo-0 verbunden wird.

Einordnung:

  • StatefulSets bieten stabile Pod-Namen und stabile PVC-Zuordnung.
  • Ein lokales hostPath-Volume im Lab ist bequem, aber nicht portabel und kein Produktionsspeicher.

21 / 23

Lösung 9: Helm und Kustomize

Kustomize-Struktur:

text
kustomize-demo/
  base/
    deployment.yml
    service.yml
    kustomization.yml
  overlays/
    dev/
      kustomization.yml

base/kustomization.yml:

yaml
resources:
  - deployment.yml
  - service.yml

overlays/dev/kustomization.yml:

yaml
resources:
  - ../../base
namePrefix: dev-
labels:
  - pairs:
      environment: dev
images:
  - name: nginx
    newTag: "1.27"

Rendern:

bash
kubectl kustomize kustomize-demo/overlays/dev
kubectl apply -k kustomize-demo/overlays/dev --dry-run=server

Minimales Helm Chart:

bash
helm create webchart
helm template web ./webchart
helm lint ./webchart

Vergleich:

Kriterium Helm Kustomize
Modell Paket mit Templates und Values Base/Overlay ohne Template-Sprache
Stärke viele Parameter, Releases, Chart-Ökosystem YAML-nah, gut reviewbar
Risiko komplexe Templates können schwer prüfbar sein Overlays können unübersichtlich werden
Pflichtprüfung helm template, helm lint kubectl kustomize, dry-run

22 / 23

Lösung 10: Troubleshooting-Parcours

Musterdiagnose:

Fehlerbild Diagnose Ursache Fix
Pending kubectl describe pod kein Node erfüllt nodeSelector Label korrigieren oder Selector entfernen
ImagePullBackOff Events im Pod Image/Tag/Registry falsch gültiges Image setzen
CrashLoopBackOff kubectl logs --previous Prozess beendet sich fehlerhaft Startkommando/App-Konfiguration korrigieren
PVC Pending kubectl describe pvc StorageClass fehlt oder Provisioner fehlt StorageClass korrigieren oder bereitstellen
Service ohne Endpoints kubectl get endpoints, Labels prüfen Selector passt nicht Selector oder Pod-Labels korrigieren

Standardabfolge:

bash
kubectl get all
kubectl get pod -o wide
kubectl describe pod <pod>
kubectl get events --sort-by=.metadata.creationTimestamp
kubectl logs <pod>
kubectl logs <pod> --previous
kubectl get svc,endpoints,endpointslices
kubectl describe pvc <pvc>

Cleanup

bash
kubectl delete namespace workshop-intermediate

Bewertungscheckliste

Kriterium Erfüllt?
Workload-Ressourcen fachlich unterschieden ☐
Init Container, Sidecar und App Container erklärt ☐
Probes korrekt getrennt ☐
Requests, Limits, QoS, LimitRange und Quota angewendet ☐
Scheduling-Events interpretiert ☐
NetworkPolicy inklusive DNS berücksichtigt ☐
Ingress/Gateway Controller-Rolle erklärt ☐
PVC und StatefulSet getestet ☐
Helm und Kustomize gerendert ☐
Troubleshooting-Parcours dokumentiert ☐
23 / 23

K8S · Orthevs Academy

31 Module als durchgängiger Lernpfad.

Die Reihenfolge bleibt unverändert: von der Laborgrundstruktur über fachliche Grundlagen und Betriebsmodelle bis zum abschließenden Troubleshooting. Die technischen Verzeichnisnamen werden bewusst beibehalten.

Orthevs – Engineering als Grundlage für das, was als Nächstes kommt.

Orthevs als App

Orthevs installieren

Die Orthevs Web-App kann auf unterstützten Mobilgeräten und Desktop-Browsern installiert werden.

iPhone und iPad

Öffnen Sie Orthevs in Safari. Tippen Sie auf Teilen, dann auf ‚Zum Home-Bildschirm‘. Aktivieren Sie gegebenenfalls ‚Als Web-App öffnen‘ und tippen Sie auf ‚Hinzufügen‘.

Android

Für die Web-App öffnen Sie das Browsermenü und wählen ‚App installieren‘ oder ‚Zum Startbildschirm hinzufügen‘. Bestätigen Sie die Installation.

Desktop-Browser

Öffnen Sie das Browsermenü oder das Installationssymbol in der Adressleiste und wählen Sie ‚App installieren‘ beziehungsweise ‚Zum Dock hinzufügen‘.

Die Web-App-Installation hängt vom Browser ab. Falls kein Installationsdialog erscheint, nutzen Sie die angezeigten Schritte im Browser.