Workshop-Wiki · Deutsch

Diagramme: Kubernetes

Dieses Dokument ist eine visuelle Referenz für deinen Kubernetes-Workshop. Es ergänzt die Konzept-, Architektur-, Best-Practices-, Kommando- und Curriculum-Dateien um erklärende Diagramme, Ablaufbilder und Troubleshooting-Flüsse.

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 / 16 Kapitel gelesen
Weitere SprachfassungEnglisch ↗Griechisch ↗

Diagramme: Kubernetes

Dieses Dokument ist eine visuelle Referenz für deinen Kubernetes-Workshop. Es ergänzt die Konzept-, Architektur-, Best-Practices-, Kommando- und Curriculum-Dateien um erklärende Diagramme, Ablaufbilder und Troubleshooting-Flüsse.

Die Diagramme sind bewusst didaktisch aufgebaut. Sie zeigen nicht jedes Implementierungsdetail, sondern die Beziehungen, Verantwortlichkeiten und Entscheidungswege, die du im Workshop erklären, diskutieren und praktisch nachvollziehen sollst.

1. Nutzungshinweise

1.1 Ziel der Diagrammdatei

Du nutzt diese Datei, um Kubernetes nicht nur textlich, sondern visuell zu verstehen. Jedes Diagramm beantwortet eine typische Workshop-Frage:

  • Welche Komponente ist wofür verantwortlich?
  • Welche Ressource erzeugt oder steuert welche andere Ressource?
  • Wo wird Desired State gespeichert?
  • Wo entsteht ein Fehlerbild?
  • Welche Stelle prüfst du zuerst?
  • Welche Verantwortung liegt bei Kubernetes, beim Add-on, beim Cloud Provider oder bei dir?

Die Diagramme eignen sich für Präsentationen, Whiteboard-Erklärungen, Gruppenübungen und Troubleshooting-Labs.

1.2 Diagrammtypen

Diagrammtyp Zweck Typische Nutzung
flowchart Komponenten, Datenflüsse, Abhängigkeiten Architektur, Netzwerk, Storage, Security
sequenceDiagram zeitliche Abläufe API Request, Rollout, DNS, Admission, kubeadm
stateDiagram-v2 Zustände und Übergänge Pod Lifecycle, Rollout, Incident Response
Entscheidungsbaum Analysepfade Troubleshooting, Designentscheidungen

1.3 Darstellungsgrenzen

Kubernetes ist erweiterbar. Ein Diagramm kann deshalb je nach Distribution anders aussehen. Managed Kubernetes, kubeadm-Cluster, OpenShift, k3s, Talos, Cilium, Calico, Istio, Argo CD oder ein Cloud-Load-Balancer ändern Details. Die Grundlogik bleibt aber gleich: API, gewünschter Zustand, Controller, Scheduler, kubelet, Runtime, Netzwerk, Storage, Security und Beobachtbarkeit greifen ineinander.

01 / 16

2. Gesamtbild und Cluster-Architektur

2.1 Gesamtüberblick: Von dir zur laufenden Anwendung

Dieses Diagramm zeigt den großen Zusammenhang. Du formulierst einen gewünschten Zustand, Kubernetes speichert ihn und mehrere Controller sowie Node-Komponenten arbeiten daran, den tatsächlichen Zustand anzupassen.

flowchart LR
    user["Du / Team"] --> intent["Gewünschter Zustand"]
    intent --> manifest["YAML, Helm, Kustomize oder GitOps"]
    manifest --> api["Kubernetes API Server"]
    api --> etcd["etcd: Cluster-Zustand"]
    api --> controllers["Controller"]
    api --> scheduler["Scheduler"]
    scheduler --> node["Worker Node"]
    controllers --> node
    node --> kubelet["kubelet"]
    kubelet --> runtime["Container Runtime"]
    runtime --> pods["Pods und Container"]
    pods --> service["Service Discovery und Netzwerk"]
    pods --> storage["Volumes und Storage"]
    pods --> signals["Logs, Metriken, Events"]
    signals --> user

Merksatz: Kubernetes startet nicht einfach Container. Kubernetes verwaltet einen gewünschten Zustand über eine API und korrigiert Abweichungen kontinuierlich.

2.2 Control Plane und Worker Nodes

Die Control Plane entscheidet und koordiniert. Worker Nodes führen Workloads aus. Der API Server ist die zentrale Schnittstelle; direkte Änderungen an Nodes umgehen das Kubernetes-Modell und sind in der Regel nicht reproduzierbar.

flowchart TD
    subgraph cp["Control Plane"]
        api["kube-apiserver"]
        etcd["etcd"]
        sched["kube-scheduler"]
        cm["kube-controller-manager"]
        ccm["cloud-controller-manager"]
    end

    subgraph n1["Worker Node A"]
        kubelet1["kubelet"]
        proxy1["kube-proxy oder eBPF-Dataplane"]
        cni1["CNI Plugin"]
        rt1["containerd oder CRI-O"]
        pods1["Pods"]
    end

    subgraph n2["Worker Node B"]
        kubelet2["kubelet"]
        proxy2["kube-proxy oder eBPF-Dataplane"]
        cni2["CNI Plugin"]
        rt2["containerd oder CRI-O"]
        pods2["Pods"]
    end

    api --> etcd
    api --> sched
    api --> cm
    api --> ccm
    api --> kubelet1
    api --> kubelet2
    kubelet1 --> rt1 --> pods1
    kubelet2 --> rt2 --> pods2
    cni1 --> pods1
    cni2 --> pods2
    proxy1 --> pods1
    proxy2 --> pods2

Workshop-Hinweis: Frage die Teilnehmenden, welche Komponente einen Pod tatsächlich startet. Die korrekte Antwort ist nicht der Scheduler, sondern kubelet über die Container Runtime. Der Scheduler weist nur einen geeigneten Node zu.

2.3 Hochverfügbare Control Plane

In produktionsnahen Umgebungen betreibst du die Control Plane redundant. Der API Server kann horizontal skaliert werden. etcd benötigt Quorum und sollte besonders geschützt, gesichert und überwacht werden.

flowchart TD
    client["kubectl, Controller, CI/CD, GitOps"] --> lb["Load Balancer für API Server"]

    lb --> api1["API Server 1"]
    lb --> api2["API Server 2"]
    lb --> api3["API Server 3"]

    api1 --> e1["etcd Member 1"]
    api2 --> e2["etcd Member 2"]
    api3 --> e3["etcd Member 3"]

    e1 <--> e2
    e2 <--> e3
    e1 <--> e3

    api1 --> sched["Scheduler mit Leader Election"]
    api2 --> cm["Controller Manager mit Leader Election"]
    api3 --> ccm["Cloud Controller Manager"]

Merksatz: API-Server-Redundanz schützt die Bedienebene. etcd-Quorum schützt den Clusterzustand. Ohne getestetes etcd-Backup hast du keinen belastbaren Disaster-Recovery-Pfad.

2.4 Stacked etcd vs. externes etcd

Bei kubeadm- und selbst betriebenen Clustern ist die etcd-Topologie eine wichtige Architekturentscheidung. Stacked etcd ist kompakter, externes etcd trennt Zuständigkeiten stärker.

flowchart LR
    subgraph stacked["Stacked Control Plane"]
        cp1["Control Plane Node 1\nAPI + etcd"]
        cp2["Control Plane Node 2\nAPI + etcd"]
        cp3["Control Plane Node 3\nAPI + etcd"]
        cp1 <--> cp2
        cp2 <--> cp3
        cp1 <--> cp3
    end

    subgraph external["Externe etcd-Topologie"]
        apiA["Control Plane Nodes\nAPI, Scheduler, Controller"]
        etcdA["dedizierter etcd Node 1"]
        etcdB["dedizierter etcd Node 2"]
        etcdC["dedizierter etcd Node 3"]
        apiA --> etcdA
        apiA --> etcdB
        apiA --> etcdC
        etcdA <--> etcdB
        etcdB <--> etcdC
        etcdA <--> etcdC
    end

Entscheidungshilfe: Nutze stacked etcd für kleinere, einfachere Setups. Nutze externes etcd, wenn du klare Trennung, eigene Betriebsprozesse und dedizierte Verantwortlichkeiten brauchst.

2.5 Innenleben eines Worker Nodes

Ein Node ist nicht nur eine VM oder ein Server. Kubernetes erwartet dort kubelet, Runtime, Netzwerkkomponenten, Pod-Sandboxen, lokale Logs und oft zusätzliche DaemonSets.

flowchart TD
    api["API Server"] --> kubelet["kubelet"]

    subgraph node["Worker Node"]
        kubelet --> cri["CRI Schnittstelle"]
        cri --> runtime["containerd oder CRI-O"]
        runtime --> sandbox["Pod Sandbox"]
        sandbox --> c1["Container app"]
        sandbox --> c2["Container sidecar"]
        kubelet --> volumes["Volume Mounts"]
        kubelet --> probes["Liveness, Readiness, Startup Probes"]
        kubelet --> logs["Container Logs"]
        proxy["kube-proxy oder eBPF"] --> dataplane["Service-Dataplane"]
        cni["CNI Plugin"] --> podnet["Pod-Netzwerk"]
        ds["DaemonSet Pods"] --> nodeagents["Monitoring, Logging, Security, CNI"]
    end

Prüffrage: Wenn ein Pod nicht startet, prüfst du nicht nur das Manifest. Du prüfst auch Node-Zustand, kubelet, Runtime, CNI, Image Pull, Volumes, Ressourcen und Events.

2.6 Control-Plane-zu-Node-Kommunikation

Der API Server spricht mit kubelet und kann über Proxy-Funktionen auf Node-, Pod- oder Service-Ziele zugreifen. Viele Debug-Befehle funktionieren nur, wenn diese Pfade erreichbar und autorisiert sind.

flowchart LR
    api["API Server"] -->|Pod-Status lesen| kubelet["kubelet"]
    api -->|Logs, exec, port-forward, debug| kubelet
    kubelet -->|Node- und Pod-Status melden| api
    api -->|Proxy-Pfad| target["Node, Pod oder Service"]
    target --> response["Antwort an API Client"]
    response --> user["kubectl"]

Troubleshooting-Hinweis: Wenn kubectl logs, kubectl exec oder kubectl port-forward ausfallen, kann das Problem im API-Zugriff, in RBAC, im kubelet-Port, im Node-Netzwerk oder im kubelet selbst liegen.

2.7 Kubernetes-Komponenten nach Verantwortungsbereich

Nicht jede Komponente löst jedes Problem. Dieses Diagramm hilft, Fehlerbilder schneller einer Schicht zuzuordnen.

flowchart TD
    platform["Plattform"] --> api["API und Zustand"]
    platform --> scheduling["Scheduling"]
    platform --> runtime["Laufzeit"]
    platform --> network["Netzwerk"]
    platform --> storage["Storage"]
    platform --> security["Security"]
    platform --> observe["Beobachtbarkeit"]

    api --> apiserver["kube-apiserver"]
    api --> etcd["etcd"]
    scheduling --> scheduler["kube-scheduler"]
    scheduling --> autoscaler["Cluster Autoscaler"]
    runtime --> kubelet["kubelet"]
    runtime --> crio["CRI Runtime"]
    network --> cni["CNI"]
    network --> service["Service-Dataplane"]
    storage --> csi["CSI"]
    storage --> pv["PV, PVC, StorageClass"]
    security --> rbac["RBAC"]
    security --> admission["Admission"]
    security --> pss["Pod Security"]
    observe --> metrics["Metriken"]
    observe --> logs["Logs"]
    observe --> traces["Traces"]
02 / 16

3. API, Ressourcenmodell und Reconciliation

3.1 API Request Lifecycle

Jede Änderung läuft über den API Server. Authentifizierung, Autorisierung, Admission und Persistenz sind getrennte Schritte. Diese Trennung ist zentral für Security und Fehlersuche.

sequenceDiagram
    participant U as Du oder Tool
    participant API as API Server
    participant AUTHN as Authentifizierung
    participant AUTHZ as Autorisierung
    participant ADM as Admission Control
    participant ETCD as etcd
    participant CTRL as Controller

    U->>API: apply, create, patch oder delete
    API->>AUTHN: Wer stellt die Anfrage?
    AUTHN-->>API: Identität
    API->>AUTHZ: Darf diese Identität das tun?
    AUTHZ-->>API: erlaubt oder verweigert
    API->>ADM: Validierung und Mutation
    ADM-->>API: akzeptiert oder abgelehnt
    API->>ETCD: Zustand speichern
    ETCD-->>API: gespeichert
    API-->>U: API-Antwort
    CTRL->>API: Änderung beobachten
    CTRL->>API: tatsächlichen Zustand anpassen

Merksatz: Ein kubectl apply ist erst dann wirksam, wenn der API Server den Request akzeptiert und den gewünschten Zustand gespeichert hat. Die Ausführung passiert danach asynchron durch Controller und kubelet.

3.2 Anatomie eines Kubernetes-Objekts

Fast jedes Kubernetes-Objekt folgt demselben mentalen Modell: Typ, Metadaten, gewünschter Zustand und tatsächlicher Zustand.

flowchart TD
    obj["Kubernetes Objekt"] --> apiVersion["apiVersion"]
    obj --> kind["kind"]
    obj --> metadata["metadata"]
    obj --> spec["spec"]
    obj --> status["status"]

    metadata --> name["name"]
    metadata --> namespace["namespace"]
    metadata --> labels["labels"]
    metadata --> annotations["annotations"]
    metadata --> owner["ownerReferences"]
    metadata --> finalizers["finalizers"]

    spec --> desired["gewünschter Zustand"]
    status --> actual["beobachteter Zustand"]
    status --> conditions["Conditions"]
    status --> observed["observedGeneration"]

Workshop-Hinweis: Lass Teilnehmende kubectl get deployment demo -o yaml ansehen und spec, status, metadata.generation sowie status.observedGeneration suchen.

3.3 Desired State, Actual State und Reconciliation

Controller arbeiten in Schleifen. Sie beobachten Ressourcen, vergleichen Soll und Ist, führen Aktionen aus und schreiben Status zurück.

flowchart LR
    desired["Desired State\nim API-Objekt"] --> compare["Controller vergleicht"]
    actual["Actual State\nim Cluster"] --> compare
    compare -->|Abweichung| act["Aktion ausführen"]
    act --> actual
    compare -->|keine Abweichung| steady["stabiler Zustand"]
    act --> status["Status aktualisieren"]
    status --> desired

Merksatz: Du sagst Kubernetes nicht primär, was es einmalig tun soll. Du beschreibst, wie der Zustand dauerhaft aussehen soll.

3.4 Controller Loop im Detail

Ein Controller ist kein magischer Hintergrundprozess. Er reagiert auf Events, nutzt lokale Caches und versucht, fehlende oder falsche Ressourcen zu korrigieren.

flowchart TD
    watch["Watch auf API-Ressourcen"] --> queue["Work Queue"]
    queue --> reconcile["Reconcile-Funktion"]
    reconcile --> read["aktuellen Zustand lesen"]
    read --> decide{Ist-Zustand korrekt?}
    decide -->|ja| status["Status prüfen oder aktualisieren"]
    decide -->|nein| change["Ressourcen erstellen, ändern oder löschen"]
    change --> requeue["erneut einreihen bei Bedarf"]
    status --> wait["auf nächste Änderung warten"]
    requeue --> queue
    wait --> watch

Betriebsrelevanz: Wenn ein Controller defekt ist, bleiben API-Objekte oft vorhanden, aber der tatsächliche Zustand ändert sich nicht wie erwartet.

3.5 Owner References und Garbage Collection

Kubernetes nutzt Owner References, um abhängige Ressourcen zuzuordnen. Dadurch kann Kubernetes nachgelagerte Objekte automatisch aufräumen.

flowchart TD
    dep["Deployment"] -->|ownerReference| rs1["ReplicaSet alte Version"]
    dep -->|ownerReference| rs2["ReplicaSet neue Version"]
    rs2 -->|ownerReference| pod1["Pod 1"]
    rs2 -->|ownerReference| pod2["Pod 2"]
    rs1 --> oldpod["alter Pod"]
    delete["Deployment wird gelöscht"] --> gc["Garbage Collector"]
    gc --> rs1
    gc --> rs2
    gc --> pod1
    gc --> pod2

Prüffrage: Warum solltest du ReplicaSets oder Pods eines Deployments nicht manuell als primären Betriebsweg ändern? Weil der Controller sie aus dem Deployment-Zustand wiederherstellt oder ersetzt.

3.6 Finalizers und kontrolliertes Löschen

Finalizers verhindern, dass ein Objekt sofort verschwindet. Ein Controller kann dadurch externe Ressourcen aufräumen, bevor das Objekt endgültig gelöscht wird.

sequenceDiagram
    participant U as Du
    participant API as API Server
    participant OBJ as Objekt mit Finalizer
    participant C as Controller
    participant EXT as Externe Ressource

    U->>API: delete objekt
    API->>OBJ: deletionTimestamp setzen
    API-->>U: Objekt ist im Löschzustand
    C->>API: Objekt mit deletionTimestamp sehen
    C->>EXT: externe Ressource aufräumen
    EXT-->>C: aufgeräumt
    C->>API: Finalizer entfernen
    API->>API: Objekt endgültig löschen

Troubleshooting-Hinweis: Wenn ein Namespace, PVC oder Custom Resource lange im Status Terminating hängt, prüfe Finalizers und den zuständigen Controller.

3.7 Conditions, Status und Events

Status und Events sind deine wichtigsten Signale im laufenden Betrieb. Sie zeigen, was Kubernetes versucht hat und warum etwas nicht funktioniert.

flowchart LR
    obj["Ressource"] --> status["status"]
    obj --> events["Events"]
    status --> conditions["Conditions"]
    conditions --> ready["Ready"]
    conditions --> available["Available"]
    conditions --> progressing["Progressing"]
    conditions --> degraded["Degraded oder Failed"]
    events --> schedule["Scheduling Events"]
    events --> image["Image Pull Events"]
    events --> volume["Volume Events"]
    events --> probe["Probe Events"]

Workshop-Kommando: kubectl describe zeigt dir häufig den schnellsten Einstieg, weil es Status, Conditions und Events zusammenführt.

03 / 16

4. Workloads und Pod-Lifecycle

4.1 Deployment, ReplicaSet und Pods

Ein Deployment verwaltet nicht direkt Container. Es erzeugt ReplicaSets, und ReplicaSets sorgen für die gewünschte Anzahl identischer Pods.

flowchart TD
    deploy["Deployment\napp=web"] --> rs["ReplicaSet\nreplicas=3"]
    rs --> pod1["Pod web-1"]
    rs --> pod2["Pod web-2"]
    rs --> pod3["Pod web-3"]

    svc["Service\nselector app=web"] --> pod1
    svc --> pod2
    svc --> pod3

Merksatz: Das Deployment beschreibt Rollout-Strategie und Pod-Template. Das ReplicaSet hält die Replikazahl. Der Service findet passende Pods über Labels.

4.2 Rolling Update eines Deployments

Beim Rolling Update erstellt Kubernetes ein neues ReplicaSet und fährt das alte schrittweise zurück. maxSurge und maxUnavailable bestimmen, wie aggressiv dieser Wechsel passiert.

sequenceDiagram
    participant U as Du
    participant API as API Server
    participant D as Deployment Controller
    participant OLD as Altes ReplicaSet
    participant NEW as Neues ReplicaSet
    participant SVC as Service

    U->>API: neues Image per apply oder set image
    API-->>D: Deployment geändert
    D->>NEW: neues ReplicaSet erstellen
    D->>NEW: neue Pods hochskalieren
    NEW-->>SVC: readiness erreicht
    D->>OLD: alte Pods herunterskalieren
    D->>API: Rollout-Status aktualisieren

Workshop-Hinweis: Zeige währenddessen kubectl rollout status, kubectl get rs und kubectl get pods -w parallel.

4.3 Rollback eines Deployments

Ein Rollback nutzt die gespeicherte Rollout-Historie. Es ist kein Ersatz für saubere Versionierung, aber ein wichtiger Betriebsmechanismus.

flowchart LR
    bad["Release v3 fehlerhaft"] --> detect["Fehler erkannt"]
    detect --> undo["kubectl rollout undo"]
    undo --> previous["ReplicaSet v2 wieder hochskalieren"]
    previous --> down["ReplicaSet v3 herunterskalieren"]
    down --> verify["Service, Logs und Metriken prüfen"]

Prüffrage: Was passiert, wenn deine ConfigMap oder externe Datenbankmigration nicht rückwärtskompatibel ist? Dann reicht ein Deployment-Rollback allein nicht aus.

4.4 Pod-Lifecycle

Ein Pod durchläuft mehrere Zustände. Viele Fehlerbilder lassen sich schneller verstehen, wenn du Phase, Container-Zustand und Events getrennt betrachtest.

stateDiagram-v2
    [*] --> Pending
    Pending --> Running: Node zugewiesen und Container gestartet
    Pending --> Failed: Scheduling, Image oder Volume scheitert
    Running --> Succeeded: alle Container erfolgreich beendet
    Running --> Failed: Container oder Pod scheitert dauerhaft
    Running --> Terminating: Löschung oder Eviction
    Terminating --> [*]
    Succeeded --> [*]
    Failed --> [*]

Wichtig: CrashLoopBackOff ist kein Pod-Phase-Wert, sondern ein Container-Wartezustand. Prüfe deshalb kubectl describe pod und kubectl logs --previous.

4.5 Init Container, App Container und Sidecar-Muster

Init Container laufen vor den normalen App-Containern. Sidecars ergänzen eine App zur Laufzeit, etwa für Proxy, Logging oder Synchronisation.

flowchart TD
    start["Pod wird gestartet"] --> init1["Init Container 1"]
    init1 --> init2["Init Container 2"]
    init2 --> app["App Container"]
    init2 --> sidecar["Sidecar Container"]
    app <--> sharednet["gemeinsamer Netzwerk-Namespace"]
    sidecar <--> sharednet
    app <--> volume["gemeinsame Volumes"]
    sidecar <--> volume

Designfrage: Gehört eine Funktion wirklich in denselben Pod, oder wäre ein eigener Service sauberer? Ein Sidecar koppelt Lebenszyklus, Ressourcen und Skalierung eng an die App.

4.6 Probes und Traffic-Freigabe

Probes steuern Selbstheilung und Traffic-Routing. Readiness bestimmt, ob ein Pod Service-Traffic erhält. Liveness bestimmt, ob kubelet einen Container neu startet. Startup schützt langsame Starts.

flowchart TD
    pod["Pod"] --> startup["Startup Probe"]
    startup -->|erfolgreich| live["Liveness Probe aktiv"]
    startup -->|fehlschlägt zu oft| restart1["Container Neustart"]
    live -->|ok| ready["Readiness Probe"]
    live -->|fehlschlägt zu oft| restart2["Container Neustart"]
    ready -->|ready| endpoints["EndpointSlice enthält Pod"]
    ready -->|not ready| noTraffic["kein Service-Traffic"]

Anti-Pattern: Verwende Liveness nicht als Ersatz für Readiness. Eine falsch konfigurierte Liveness Probe kann gesunde, aber langsame Anwendungen in Neustartschleifen bringen.

4.7 Graceful Shutdown

Beim Beenden eines Pods müssen Traffic-Routing, Signalverarbeitung und terminationGracePeriodSeconds zusammenpassen.

sequenceDiagram
    participant API as API Server
    participant EP as EndpointSlice Controller
    participant K as kubelet
    participant APP as App Container
    participant SVC as Service Traffic

    API->>EP: Pod bekommt deletionTimestamp
    EP->>SVC: Endpoint aus Routing entfernen
    API->>K: Pod beenden
    K->>APP: preStop Hook ausführen, falls vorhanden
    K->>APP: SIGTERM senden
    APP->>APP: laufende Requests abschließen
    K->>APP: nach Grace Period SIGKILL, falls nötig

Workshop-Hinweis: Simuliere einen Rollout mit kurzer Grace Period und beobachte, ob Requests abbrechen.

4.8 StatefulSet und stabile Identität

StatefulSets sind für Workloads gedacht, die stabile Namen, geordnete Starts oder persistente Volume-Zuordnung brauchen.

flowchart TD
    sts["StatefulSet db"] --> pod0["db-0"]
    sts --> pod1["db-1"]
    sts --> pod2["db-2"]
    pod0 --> pvc0["data-db-0"]
    pod1 --> pvc1["data-db-1"]
    pod2 --> pvc2["data-db-2"]
    svc["Headless Service db"] --> pod0
    svc --> pod1
    svc --> pod2
    dns0["db-0.db.namespace.svc"] --> pod0
    dns1["db-1.db.namespace.svc"] --> pod1
    dns2["db-2.db.namespace.svc"] --> pod2

Merksatz: Ein StatefulSet macht eine Anwendung nicht automatisch konsistent. Replikation, Quorum, Backup und Restore bleiben Aufgabe der Anwendung oder des Operators.

4.9 DaemonSet für Node-nahe Funktionen

DaemonSets platzieren auf ausgewählten Nodes jeweils einen Pod. Sie eignen sich für Node-Agenten, nicht für normale horizontale Anwendungsskalierung.

flowchart LR
    ds["DaemonSet"] --> node1["Node A"]
    ds --> node2["Node B"]
    ds --> node3["Node C"]
    node1 --> agent1["Agent Pod"]
    node2 --> agent2["Agent Pod"]
    node3 --> agent3["Agent Pod"]
    agent1 --> use1["Logs, Metriken, CNI oder Security"]
    agent2 --> use2["Logs, Metriken, CNI oder Security"]
    agent3 --> use3["Logs, Metriken, CNI oder Security"]

Beispiele: CNI-Agent, Log Collector, Node Exporter, Runtime Security Agent oder Storage Node Plugin.

4.10 Job und CronJob

Jobs führen Aufgaben bis zum Abschluss aus. CronJobs erzeugen Jobs nach Zeitplan. Sie eignen sich nicht für dauerhaft laufende Services.

flowchart TD
    cron["CronJob\nZeitplan"] --> job1["Job 2026-06-13 01:00"]
    cron --> job2["Job 2026-06-14 01:00"]
    job1 --> pod1["Pod Versuch 1"]
    job1 --> pod2["Pod Retry, falls nötig"]
    pod1 --> success["Succeeded"]
    pod2 --> fail["Failed oder Succeeded"]
    job2 --> pod3["Pod nächste Ausführung"]

Prüfpunkte: backoffLimit, activeDeadlineSeconds, concurrencyPolicy, startingDeadlineSeconds und Aufbewahrung alter Jobs.

4.11 HPA-Skalierung eines Workloads

Der Horizontal Pod Autoscaler beobachtet Metriken und passt die gewünschte Replikazahl an. Er ersetzt keine sinnvollen Requests und keine Lasttests.

flowchart LR
    metrics["Metrics API\nCPU, Memory oder Custom Metrics"] --> hpa["Horizontal Pod Autoscaler"]
    hpa --> decision{Skalieren nötig?}
    decision -->|mehr Last| up["replicas erhöhen"]
    decision -->|weniger Last| down["replicas senken"]
    up --> deploy["Deployment oder StatefulSet scale subresource"]
    down --> deploy
    deploy --> pods["mehr oder weniger Pods"]

Betriebsrelevanz: Ohne Metrics Server oder passende Custom-Metrics-Adapter kann HPA nicht sinnvoll arbeiten.

04 / 16

5. Services, DNS, Ingress und Gateway API

5.1 Pod-Netzwerk: Jeder Pod hat eine eigene IP

Das CNI-Plugin stellt Pod-Netzwerk und Routing bereit. Kubernetes definiert das Modell, das konkrete Enforcement hängt vom CNI ab.

flowchart LR
    subgraph nodeA["Node A"]
        podA1["Pod A1\n10.244.1.10"]
        podA2["Pod A2\n10.244.1.11"]
        cniA["CNI"]
    end
    subgraph nodeB["Node B"]
        podB1["Pod B1\n10.244.2.10"]
        podB2["Pod B2\n10.244.2.11"]
        cniB["CNI"]
    end
    podA1 --> cniA
    cniA <--> cniB
    cniB --> podB1
    podA2 --> cniA
    cniB --> podB2

Merksatz: Pod-IP-Adressen sind routbar, aber nicht stabil. Für stabile Erreichbarkeit nutzt du Services.

5.2 Service, Selector und EndpointSlices

Ein Service sucht Pods über Labels und erzeugt stabile Service Discovery. EndpointSlices enthalten die tatsächlich routbaren Backends.

flowchart TD
    svc["Service\nselector app=shop"] --> selector["Label Selector"]
    selector --> pod1["Pod 1\napp=shop\nReady"]
    selector --> pod2["Pod 2\napp=shop\nReady"]
    selector --> pod3["Pod 3\napp=shop\nNotReady"]
    pod1 --> eps["EndpointSlice"]
    pod2 --> eps
    pod3 -. nicht ready .-> eps
    eps --> dataplane["kube-proxy oder eBPF-Dataplane"]
    dataplane --> client["Clusterinterner Client"]

Prüffrage: Wenn ein Service nicht funktioniert, prüfe nicht nur den Service. Prüfe Labels, Selector, Pod Readiness und EndpointSlices.

5.3 Service-Typen im Vergleich

Die Service-Typen unterscheiden sich darin, wie weit sie einen Dienst sichtbar machen.

flowchart TD
    svc["Service"] --> clusterip["ClusterIP\nintern im Cluster"]
    svc --> nodeport["NodePort\nPort auf jedem Node"]
    svc --> lb["LoadBalancer\nexterner Load Balancer"]
    svc --> external["ExternalName\nDNS-Alias"]

    clusterip --> internal["Pod-zu-Service Traffic"]
    nodeport --> node["Client erreicht Node-IP und Port"]
    lb --> cloud["Cloud oder MetalLB stellt externe Adresse bereit"]
    external --> dns["CNAME-artige Namensauflösung"]

Designregel: Nutze ClusterIP als Standard. Nutze LoadBalancer oder Gateway/Ingress für kontrollierten externen Zugriff. NodePort ist oft Infrastrukturdetail, nicht dein primäres Veröffentlichungsmodell.

5.4 DNS-Auflösung im Cluster

CoreDNS stellt Service-Namen bereit. DNS-Probleme zeigen sich oft als Anwendungsfehler, obwohl Service und Pods korrekt aussehen.

sequenceDiagram
    participant APP as App Pod
    participant DNS as CoreDNS
    participant API as API Server
    participant SVC as Service
    participant EP as EndpointSlice

    APP->>DNS: shop.demo.svc.cluster.local auflösen
    DNS->>API: Service- und Endpoint-Daten aus Cache/API
    API-->>DNS: Service-IP oder Endpoint-Informationen
    DNS-->>APP: DNS-Antwort
    APP->>SVC: Verbindung zur Service-IP
    SVC->>EP: Routing zu passendem Pod

Troubleshooting-Hinweis: Prüfe DNS mit einem Debug-Pod im gleichen Namespace und teste sowohl Kurzname als auch FQDN.

5.5 Ingress-Pfad für HTTP und HTTPS

Ingress ist ein API-Objekt, aber es funktioniert nur mit einem laufenden Ingress Controller. Der Controller übersetzt Regeln in konkrete Proxy-Konfiguration.

flowchart LR
    client["Browser oder API Client"] --> dns["DNS"]
    dns --> lb["Load Balancer"]
    lb --> controller["Ingress Controller"]
    controller --> ingress["Ingress-Regeln\nHost und Path"]
    ingress --> service["Service"]
    service --> endpoints["EndpointSlices"]
    endpoints --> pod1["Pod A"]
    endpoints --> pod2["Pod B"]
    cert["TLS Secret oder cert-manager"] --> controller

Prüfkette: DNS → Load Balancer → Ingress Controller → IngressClass → Ingress-Regel → Service → EndpointSlice → Pod Readiness.

5.6 Gateway API: Rollenorientiertes Routing

Gateway API trennt Infrastrukturverantwortung und Anwendungsrouting deutlicher als klassische Ingress-Modelle.

flowchart TD
    infra["Plattformteam"] --> gatewayClass["GatewayClass"]
    infra --> gateway["Gateway\nListener, Adresse, TLS"]
    appteam["Anwendungsteam"] --> route["HTTPRoute, GRPCRoute oder TCPRoute"]
    route --> service["Service"]
    service --> pods["Pods"]
    gatewayClass --> controller["Gateway Controller"]
    gateway --> controller
    route --> controller
    controller --> dataplane["Proxy oder Load Balancer"]
    dataplane --> service

Merksatz: Gateway API ist besonders hilfreich, wenn Plattformteam und Anwendungsteam unterschiedliche Verantwortlichkeiten haben.

5.7 NetworkPolicy: Default Deny und erlaubte Flüsse

NetworkPolicies erlauben oder verbieten Pod-Verkehr. Sie wirken nur, wenn dein Netzwerk-Plugin NetworkPolicy durchsetzt.

flowchart TD
    ns["Namespace demo"] --> defaultDeny["Default Deny Policy"]
    defaultDeny --> pod["Pod app=api"]
    client["Pod app=frontend"] -->|erlaubt TCP 8080| pod
    db["Pod app=db"] -. nicht erlaubt .-> pod
    internet["externer Traffic"] -. nicht erlaubt .-> pod
    policy["NetworkPolicy\nPodSelector app=api"] --> ingress["Ingress-Regeln"]
    ingress --> client

Anti-Pattern: Eine NetworkPolicy ohne unterstützendes CNI gibt dir Scheinsicherheit. Prüfe das Enforcement praktisch.

5.8 Egress-Kontrolle

Egress-Regeln begrenzen, wohin ein Pod Verbindungen aufbauen darf. Das ist wichtig für Datenabfluss, Supply-Chain-Schutz und kontrollierte Abhängigkeiten.

flowchart LR
    pod["App Pod"] --> policy["Egress NetworkPolicy"]
    policy -->|erlaubt| dns["CoreDNS TCP/UDP 53"]
    policy -->|erlaubt| api["interner API Service"]
    policy -. blockiert .-> internet["beliebiges Internet"]
    policy -. blockiert .-> metadata["Cloud Metadata Service"]

Workshop-Übung: Erstelle eine Default-Deny-Egress-Policy und öffne nur DNS sowie den Zielservice. Beobachte, welche Nebenwirkungen auftreten.

5.9 Service Mesh im Kontext

Ein Service Mesh ergänzt Kubernetes um Traffic-Management, mTLS, Telemetrie und Policies. Es ersetzt aber nicht Services, Deployments oder RBAC.

flowchart TD
    client["Client Pod"] --> sidecarA["Sidecar Proxy A"]
    sidecarA --> mesh["Mesh Control Plane"]
    sidecarA --> sidecarB["Sidecar Proxy B"]
    sidecarB --> server["Server Pod"]
    mesh --> certs["Zertifikate und Identitäten"]
    mesh --> routing["Traffic-Regeln"]
    mesh --> telemetry["Metriken und Traces"]
    k8s["Kubernetes API"] --> mesh

Einordnung: Ein Mesh erhöht die Plattformmacht, aber auch Komplexität. Setze es ein, wenn du die konkreten Anforderungen benennen und betreiben kannst.

05 / 16

6. Konfiguration, Secrets und Images

6.1 ConfigMaps: Konfiguration vom Image trennen

ConfigMaps entkoppeln Konfiguration vom Container-Image. Du kannst sie als Environment Variables oder als Dateien in Volumes nutzen.

flowchart TD
    cm["ConfigMap"] --> env["Environment Variables"]
    cm --> volume["Volume Mount als Datei"]
    deploy["Deployment"] --> pod["Pod"]
    pod --> env
    pod --> volume
    image["Container Image"] --> pod
    change["ConfigMap geändert"] --> effect{Wie eingebunden?}
    effect -->|Env| restart["Pod-Neustart nötig"]
    effect -->|Volume| refresh["Datei kann aktualisiert werden"]

Merksatz: Konfigurationsänderung bedeutet nicht automatisch Applikationsreload. Plane Reload-Mechanismus oder Rollout bewusst.

6.2 Secrets: Zugriffspfad und Schutzgrenzen

Kubernetes Secrets sind API-Objekte. Base64 ist nur Kodierung, keine Verschlüsselung. Der Schutz hängt von RBAC, Encryption at Rest, Node-Schutz und Secret-Management ab.

flowchart TD
    user["berechtigte Identität oder Controller"] --> api["API Server"]
    api --> rbac["RBAC Prüfung"]
    rbac --> secret["Secret Objekt"]
    secret --> etcd["etcd\noptional Encryption at Rest"]
    secret --> kubelet["kubelet auf Zielnode"]
    kubelet --> pod["Pod Volume oder Env"]
    pod --> app["App liest Secret"]
    attacker["zu breite Rechte"] -. get/list/watch secrets .-> secret

Betriebsregel: Jeder, der Secrets lesen darf, hat oft effektiven Zugriff auf Zielsysteme. Vergib Secret-Rechte deutlich restriktiver als allgemeine Leserechte.

6.3 Externe Secret-Systeme

Viele Plattformen synchronisieren Secrets aus Vault, Cloud Secret Managern oder SOPS-verschlüsselten Git-Repositories. Kubernetes bleibt dabei Ausführungsplattform, nicht zwingend autoritative Quelle.

flowchart LR
    source["Externe Secret-Quelle\nVault, Cloud Secret Manager oder SOPS"] --> controller["External Secrets Controller oder GitOps"]
    controller --> api["Kubernetes API"]
    api --> k8ssecret["Kubernetes Secret"]
    k8ssecret --> pod["Pod"]
    rotate["Rotation"] --> source
    source --> audit["Audit und Zugriffskontrolle"]

Entscheidung: Lege fest, wo das Secret autoritativ liegt, wie Rotation passiert und wer Zugriff auf Quelle, Kubernetes Secret und Pod hat.

6.4 Image Pull und Registry-Zugriff

ImagePullBackOff entsteht häufig durch falsche Image-Namen, Tags, Registry-Rechte, Netzwerkprobleme oder fehlende Pull Secrets.

sequenceDiagram
    participant K as kubelet
    participant R as Container Runtime
    participant REG as Registry
    participant P as Pod

    K->>R: Image ziehen
    R->>REG: Manifest und Layer anfordern
    REG-->>R: erlaubt oder verweigert
    R-->>K: Image vorhanden oder Fehler
    K->>P: Container starten
    P-->>K: Status melden

Prüfpunkte: Image-Name, Tag, Architektur, Registry-Erreichbarkeit, TLS, Authentifizierung, imagePullSecrets, Quotas und Rate Limits.

6.5 Image Supply Chain

Ein produktionsreifes Image entsteht nicht erst beim Deployment. Es braucht Build-Kontrolle, Scans, Signaturen und nachvollziehbare Artefakte.

flowchart LR
    code["Quellcode"] --> build["Build Pipeline"]
    build --> test["Tests"]
    test --> scan["Image- und Dependency-Scan"]
    scan --> sbom["SBOM"]
    sbom --> sign["Signatur"]
    sign --> registry["Registry"]
    registry --> admission["Admission Policy"]
    admission --> cluster["Cluster Deployment"]

Review-Frage: Welche Policy verhindert, dass ungeprüfte oder unsignierte Images in produktive Namespaces gelangen?

06 / 16

7. Storage und Datenhaltung

7.1 Storage-Grundmodell

Pods sind kurzlebig. Persistenz entsteht durch Volumes, PVCs, PVs, StorageClasses und CSI-Treiber.

flowchart TD
    pod["Pod"] --> volume["Volume Mount"]
    volume --> pvc["PersistentVolumeClaim"]
    pvc --> pv["PersistentVolume"]
    pv --> backend["Storage Backend"]
    sc["StorageClass"] --> pvc
    csi["CSI Driver"] --> pv
    csi --> backend

Merksatz: Die Anwendung fordert Speicher über einen PVC an. Die Plattform entscheidet über StorageClass, Provisionierung, Performance, Backup und Lifecycle.

7.2 Dynamische Volume-Provisionierung

Bei dynamischer Provisionierung erzeugt ein CSI-Provisioner das passende Backend-Volume, sobald ein PVC angelegt wird.

sequenceDiagram
    participant U as Du
    participant API as API Server
    participant PVC as PVC Controller
    participant CSI as CSI Provisioner
    participant BE as Storage Backend
    participant POD as Pod

    U->>API: PVC erstellen
    API-->>PVC: neuer Claim
    PVC->>CSI: Volume anfordern
    CSI->>BE: Storage Volume erzeugen
    BE-->>CSI: Volume bereitgestellt
    CSI->>API: PV erstellen und binden
    POD->>API: Pod mit PVC starten
    API-->>POD: PVC ist gebunden

Troubleshooting-Hinweis: Bei Pending-PVCs prüfst du StorageClass, Provisioner, Events, Quotas, Zonen und CSI-Controller-Logs.

7.3 Binding-Modi und Zonen

Storage kann sofort oder erst beim Scheduling gebunden werden. Das ist wichtig, wenn Storage an Availability Zones gebunden ist.

flowchart TD
    pvc["PVC"] --> mode{volumeBindingMode}
    mode --> immediate["Immediate"]
    mode --> wait["WaitForFirstConsumer"]
    immediate --> pv1["PV wird sofort erzeugt"]
    pv1 --> risk["mögliche Zone passt nicht zum späteren Node"]
    wait --> pod["Pod wird erstellt"]
    pod --> scheduler["Scheduler wählt Node und Zone"]
    scheduler --> pv2["PV passend zur Zone"]

Designregel: Für zonengebundenen Cloud-Storage ist WaitForFirstConsumer oft die robustere Wahl.

7.4 StatefulSet mit VolumeClaimTemplates

StatefulSets erzeugen pro Pod einen eigenen Claim. Beim Löschen eines Pods bleibt das Volume typischerweise erhalten.

flowchart TD
    sts["StatefulSet postgres"] --> template["volumeClaimTemplates"]
    template --> pvc0["data-postgres-0"]
    template --> pvc1["data-postgres-1"]
    template --> pvc2["data-postgres-2"]
    pvc0 --> pv0["PV 0"]
    pvc1 --> pv1["PV 1"]
    pvc2 --> pv2["PV 2"]
    pod0["postgres-0"] --> pvc0
    pod1["postgres-1"] --> pvc1
    pod2["postgres-2"] --> pvc2

Prüffrage: Was passiert mit den PVCs, wenn du das StatefulSet löschst? Verlasse dich nicht auf Annahmen. Prüfe Retention Policy und Plattformverhalten.

7.5 CSI-Komponenten

CSI trennt Kubernetes von konkreten Storage-Backends. Controller-Komponenten verwalten Volumes, Node-Komponenten mounten sie auf Nodes.

flowchart TD
    subgraph control["CSI Controller Ebene"]
        provisioner["external-provisioner"]
        attacher["external-attacher"]
        resizer["external-resizer"]
        snapshotter["external-snapshotter"]
        csiController["CSI Controller Plugin"]
    end

    subgraph node["CSI Node Ebene"]
        kubelet["kubelet"]
        registrar["node-driver-registrar"]
        csiNode["CSI Node Plugin"]
        mount["Mount in Pod"]
    end

    provisioner --> csiController
    attacher --> csiController
    resizer --> csiController
    snapshotter --> csiController
    csiController --> backend["Storage Backend"]
    kubelet --> registrar
    kubelet --> csiNode
    csiNode --> mount
    mount --> pod["Pod"]

Betriebsrelevanz: Storage-Probleme können im PVC, PV, StorageClass, CSI Controller, CSI Node Plugin, Backend oder Node-Mount liegen.

7.6 Backup und Restore von Workloads

Kubernetes-Manifeste allein reichen für zustandsbehaftete Anwendungen nicht. Du brauchst auch Daten-Backups und Restore-Tests.

flowchart LR
    manifests["Manifeste und Helm Values"] --> git["Git oder Artefakt-Repository"]
    pvdata["Persistente Daten"] --> backup["Backup oder Snapshot"]
    etcd["Clusterzustand"] --> etcdbackup["etcd Backup"]
    git --> restore["Restore-Prozess"]
    backup --> restore
    etcdbackup --> restore
    restore --> validation["Validierung\nDaten, App, Service, Rechte"]

Merksatz: Ein Backup ist erst dann belastbar, wenn du den Restore geübt und fachlich validiert hast.

07 / 16

8. Scheduling, Ressourcen und Verfügbarkeit

8.1 Scheduling-Ablauf

Der Scheduler wählt einen Node auf Basis von Ressourcen, Constraints, Policies und aktueller Clusterlage.

sequenceDiagram
    participant API as API Server
    participant S as Scheduler
    participant N as Nodes
    participant K as kubelet

    API-->>S: Pod ohne nodeName sichtbar
    S->>N: Nodes filtern
    S->>N: Nodes bewerten
    S->>API: Pod an Node binden
    API-->>K: kubelet auf Zielnode sieht Pod
    K->>K: Container vorbereiten und starten
    K->>API: Pod-Status melden

Prüffrage: Ein Pod im Status Pending ist oft noch gar nicht beim kubelet angekommen. Beginne mit Scheduler-Events.

8.2 Requests, Limits und QoS

Requests steuern Scheduling. Limits begrenzen Laufzeitverbrauch. Daraus ergeben sich QoS-Klassen, die bei Ressourcenknappheit relevant sind.

flowchart TD
    container["Container Ressourcen"] --> requests["Requests"]
    container --> limits["Limits"]
    requests --> scheduling["Scheduler plant Kapazität"]
    limits --> runtime["Runtime begrenzt Verbrauch"]
    requests --> qos{QoS-Klasse}
    limits --> qos
    qos --> guaranteed["Guaranteed"]
    qos --> burstable["Burstable"]
    qos --> besteffort["BestEffort"]
    besteffort --> eviction["höheres Eviction-Risiko"]

Designregel: Jeder produktive Workload braucht sinnvolle Requests. Ohne Requests plant Kubernetes Kapazität blind.

8.3 Node Affinity und Pod Anti-Affinity

Affinity-Regeln steuern Platzierung. Nutze harte Regeln sparsam, sonst erzeugst du unnötige Pending-Probleme.

flowchart TD
    pod["Pod"] --> required["requiredDuringScheduling"]
    pod --> preferred["preferredDuringScheduling"]
    required --> nodeA["nur Nodes mit label disk=ssd"]
    preferred --> nodeB["bevorzugt zone=eu-central-1a"]
    anti["Pod Anti-Affinity"] --> spread["nicht neben gleichem App-Pod"]
    spread --> nodes["Verteilung über Nodes oder Zonen"]

Workshop-Hinweis: Lass Teilnehmende eine harte Affinity setzen, die kein Node erfüllt. Danach sollen sie Events interpretieren.

8.4 Taints und Tolerations

Taints stoßen Pods ab. Tolerations erlauben Pods, auf getainteten Nodes zu laufen, garantieren die Platzierung aber nicht.

flowchart LR
    node["Node mit Taint\ndedicated=gpu:NoSchedule"] --> barrier["Abweisung"]
    podA["Pod ohne Toleration"] -. wird nicht geplant .-> barrier
    podB["Pod mit passender Toleration"] --> barrier
    barrier --> scheduler["Scheduler darf Node berücksichtigen"]
    scheduler --> node

Merksatz: Toleration bedeutet „darf dort laufen“, nicht „muss dort laufen“. Für gezielte Platzierung kombinierst du Taints/Tolerations mit Node Affinity.

8.5 Topology Spread Constraints

Topology Spread Constraints verteilen Pods über Fehlerdomänen wie Nodes, Zonen oder Racks.

flowchart TD
    deploy["Deployment replicas=6"] --> tsc["Topology Spread Constraint\ntopologyKey=zone"]
    tsc --> zoneA["Zone A\n2 Pods"]
    tsc --> zoneB["Zone B\n2 Pods"]
    tsc --> zoneC["Zone C\n2 Pods"]
    zoneA --> nodeA1["Node A1"]
    zoneB --> nodeB1["Node B1"]
    zoneC --> nodeC1["Node C1"]

Designfrage: Soll Kubernetes bei ungleich verteilter Topologie trotzdem planen dürfen oder hart blockieren? Das steuerst du über whenUnsatisfiable.

8.6 PodDisruptionBudget

Ein PDB schützt gegen freiwillige Unterbrechungen, etwa Drain, Upgrade oder manuelle Evictions. Es schützt nicht vor Node-Ausfall oder Crash.

flowchart LR
    deploy["Deployment replicas=3"] --> pdb["PDB minAvailable=2"]
    drain["kubectl drain node-a"] --> eviction1["Eviction Pod 1"]
    eviction1 --> allowed{Bleiben mindestens 2 verfügbar?}
    allowed -->|ja| evict["Eviction erlaubt"]
    allowed -->|nein| block["Eviction blockiert"]
    pdb --> allowed

Betriebsrelevanz: Zu strenge PDBs können Upgrades blockieren. Zu schwache PDBs schützen die Anwendung nicht ausreichend.

8.7 Cluster Autoscaler und Pending Pods

Der Cluster Autoscaler reagiert auf Pods, die wegen fehlender Node-Kapazität nicht geplant werden können. Er löst keine falschen Affinity-, Taint- oder PVC-Probleme.

flowchart TD
    pod["Pod Pending"] --> reason{Warum Pending?}
    reason -->|zu wenig CPU oder Memory| ca["Cluster Autoscaler prüft Node-Gruppe"]
    reason -->|unerfüllbare Affinity| fix1["Manifest oder Labels korrigieren"]
    reason -->|fehlende Toleration| fix2["Toleration oder Taint-Design prüfen"]
    reason -->|Storage-Zonenkonflikt| fix3["StorageClass und Binding prüfen"]
    ca --> scale["neuen Node hinzufügen"]
    scale --> scheduler["Scheduler plant Pod"]

Merksatz: Autoscaling ersetzt kein sauberes Scheduling-Design.

8.8 Ressourcenquoten und LimitRanges

Namespaces können durch Quotas und LimitRanges begrenzt werden. Das schützt Plattformkapazität und erzwingt Mindeststandards.

flowchart TD
    ns["Namespace team-a"] --> quota["ResourceQuota"]
    ns --> limitrange["LimitRange"]
    quota --> max["maximale Summe\nCPU, Memory, Pods, PVCs"]
    limitrange --> defaults["Default Requests und Limits"]
    deploy["neuer Workload"] --> admission["Admission Prüfung"]
    max --> admission
    defaults --> admission
    admission -->|ok| create["Ressource wird erstellt"]
    admission -->|verletzt Grenze| reject["Request wird abgelehnt"]

Review-Frage: Gibt es pro Team-Namespace Quotas, Default Requests und klare Eskalationswege für mehr Kapazität?

08 / 16

9. Security und Zugriffskontrolle

9.1 Security-Kontrollkette

Security in Kubernetes ist eine Kette. Ein einzelner Mechanismus reicht nicht aus.

flowchart LR
    request["API Request"] --> authn["Authentifizierung"]
    authn --> authz["Autorisierung\nRBAC oder Webhook"]
    authz --> admission["Admission\nvalidierend und mutierend"]
    admission --> etcd["Persistenz in etcd"]
    etcd --> kubelet["Ausführung durch kubelet"]
    kubelet --> runtime["Runtime-Isolation"]
    runtime --> network["NetworkPolicy"]
    network --> observe["Audit, Logs, Runtime Detection"]

Merksatz: RBAC beantwortet „darf diese Identität die API-Aktion ausführen?“. Pod Security beantwortet „ist die Pod-Spezifikation zulässig?“. NetworkPolicy beantwortet „welcher Pod-Verkehr ist erlaubt?“.

9.2 RBAC-Beziehungsmodell

RBAC verbindet Subjekte mit Rollen. Rollen enthalten Regeln. Bindings stellen die Verbindung her.

flowchart TD
    subject["Subject\nUser, Group oder ServiceAccount"] --> binding["RoleBinding oder ClusterRoleBinding"]
    binding --> role["Role oder ClusterRole"]
    role --> rule1["apiGroups"]
    role --> rule2["resources"]
    role --> rule3["verbs"]
    rule3 --> examples["get, list, watch, create, update, patch, delete"]
    binding --> scope{Scope}
    scope --> ns["Namespace"]
    scope --> cluster["Clusterweit"]

Anti-Pattern: cluster-admin für CI/CD, Entwickler oder Workloads ist fast immer zu weitreichend.

9.3 ServiceAccount und Workload-Identität

Pods nutzen ServiceAccounts, um mit der Kubernetes API zu sprechen. Die Rechte hängen an RBAC-Bindings, nicht am Pod-Namen.

flowchart LR
    pod["Pod"] --> sa["ServiceAccount"]
    sa --> token["Bound ServiceAccount Token"]
    token --> api["API Server"]
    api --> rbac["RBAC"]
    rbac --> allowed["erlaubte API-Aktionen"]
    rbac --> denied["verweigerte API-Aktionen"]

Prüfbefehl: kubectl auth can-i --as system:serviceaccount:<namespace>:<serviceaccount> <verb> <resource>.

9.4 Admission Control und Policy Enforcement

Admission Controller prüfen oder verändern Objekte, bevor sie gespeichert werden. Dadurch kannst du Plattformstandards erzwingen.

sequenceDiagram
    participant U as Du oder CI/CD
    participant API as API Server
    participant M as Mutating Admission
    participant V as Validating Admission
    participant P as Policy Engine
    participant E as etcd

    U->>API: Pod erstellen
    API->>M: Defaults oder Sidecars ergänzen
    M-->>API: mutiertes Objekt
    API->>V: Objekt validieren
    V->>P: Policy prüfen
    P-->>V: erlaubt oder verweigert
    V-->>API: Ergebnis
    API->>E: speichern, wenn erlaubt

Beispiele: Pod Security Admission, ValidatingAdmissionPolicy, Kyverno, Gatekeeper, Image Policies, Namespace-Standards.

9.5 Pod Security Standards

Pod Security Standards strukturieren Pod-Sicherheitsanforderungen in Level. Die Durchsetzung erfolgt häufig über Namespace-Labels und Pod Security Admission.

flowchart TD
    ns["Namespace"] --> labels["pod-security.kubernetes.io Labels"]
    labels --> privileged["Privileged\nsehr offen"]
    labels --> baseline["Baseline\ngrundlegende Einschränkungen"]
    labels --> restricted["Restricted\nstärker gehärtet"]
    pod["neuer Pod"] --> admission["Pod Security Admission"]
    labels --> admission
    admission -->|konform| allowed["Pod wird zugelassen"]
    admission -->|verletzt Policy| denied["Pod wird abgelehnt oder gewarnt"]

Workshop-Hinweis: Starte mit warn und audit, bevor du produktive Namespaces hart auf enforce=restricted setzt.

9.6 SecurityContext im Pod

Der SecurityContext steuert Linux- und Runtime-Sicherheitsoptionen für Pod und Container.

flowchart TD
    pod["Pod spec"] --> podctx["Pod SecurityContext"]
    pod --> containers["Container"]
    containers --> cctx["Container SecurityContext"]
    podctx --> fs["fsGroup, runAsUser, runAsNonRoot"]
    cctx --> caps["Capabilities drop/add"]
    cctx --> priv["privileged"]
    cctx --> ro["readOnlyRootFilesystem"]
    cctx --> seccomp["seccompProfile"]
    cctx --> allow["allowPrivilegeEscalation"]

Review-Regel: Standardmäßig nicht root, keine Privilege Escalation, keine unnötigen Capabilities, möglichst read-only Root-Dateisystem und ein passendes seccomp-Profil.

9.7 Mandantentrennung im Namespace-Modell

Namespaces allein sind keine vollständige Sicherheitsgrenze. Erst RBAC, Quotas, NetworkPolicies, Pod Security, Admission und separate Verantwortlichkeiten machen daraus ein brauchbares Mandantenmodell.

flowchart TD
    cluster["Cluster"] --> nsA["Namespace team-a"]
    cluster --> nsB["Namespace team-b"]
    nsA --> rbacA["RBAC team-a"]
    nsA --> quotaA["Quota und LimitRange"]
    nsA --> netA["NetworkPolicies"]
    nsA --> pssA["Pod Security"]
    nsB --> rbacB["RBAC team-b"]
    nsB --> quotaB["Quota und LimitRange"]
    nsB --> netB["NetworkPolicies"]
    nsB --> pssB["Pod Security"]
    admission["Clusterweite Admission Policies"] --> nsA
    admission --> nsB

Entscheidung: Für stark unterschiedliche Sicherheits- oder Compliance-Anforderungen kann ein separater Cluster sinnvoller sein als ein gemeinsamer Multi-Tenant-Cluster.

9.8 Audit-Log und Runtime Detection

Audit-Logs zeigen API-Aktivität. Runtime Detection zeigt Verhalten in laufenden Containern und auf Nodes.

flowchart LR
    actor["User, CI/CD oder Workload"] --> api["API Server"]
    api --> audit["Audit Log"]
    pod["laufender Pod"] --> runtime["Runtime Events"]
    node["Node"] --> syscall["System Calls und Prozessverhalten"]
    runtime --> detection["Falco, Tetragon oder EDR"]
    syscall --> detection
    audit --> siem["SIEM oder Log-Plattform"]
    detection --> siem
    siem --> alert["Security Alert"]

Merksatz: API-Audit beantwortet, wer Kubernetes geändert hat. Runtime Detection beantwortet, was im laufenden Container oder Node passiert.

09 / 16

10. Observability und Betrieb

10.1 Drei Signalarten: Metriken, Logs und Traces

Observability verbindet mehrere Signalarten. Events und Kubernetes-Status ergänzen diese Perspektive.

flowchart TD
    workload["Workload"] --> metrics["Metriken"]
    workload --> logs["Logs"]
    workload --> traces["Traces"]
    k8s["Kubernetes API"] --> status["Status und Conditions"]
    k8s --> events["Events"]
    metrics --> dash["Dashboards und Alerts"]
    logs --> search["Logsuche"]
    traces --> flow["Request-Flüsse"]
    status --> triage["Triage"]
    events --> triage

Betriebsregel: Starte Triage meist mit Status und Events, nicht mit einem blinden Log-Dump.

10.2 Prometheus-Modell im Cluster

Prometheus sammelt Metriken von Anwendungen, Nodes und Kubernetes-Komponenten. Service Discovery erfolgt typischerweise über Kubernetes-Labels und Endpoints.

flowchart TD
    prom["Prometheus"] --> sd["Kubernetes Service Discovery"]
    sd --> pods["App Pods mit /metrics"]
    sd --> nodes["Node Exporter"]
    sd --> kubelet["kubelet / cAdvisor"]
    sd --> ksm["kube-state-metrics"]
    prom --> rules["Alert Rules"]
    rules --> alertmanager["Alertmanager"]
    prom --> grafana["Grafana Dashboards"]

Review-Frage: Misst du nur Infrastrukturzustand oder auch fachliche SLO-Signale wie Fehlerrate, Latenz und Durchsatz?

10.3 Logging-Pipeline

Container schreiben Logs meist nach stdout und stderr. Node-Agenten sammeln diese Logs und leiten sie an eine zentrale Plattform weiter.

flowchart LR
    app["App Container"] --> stdout["stdout und stderr"]
    stdout --> nodefiles["Node Logdateien"]
    nodefiles --> agent["Log Agent als DaemonSet"]
    agent --> buffer["Puffer und Parsing"]
    buffer --> backend["Loki, Elasticsearch oder Cloud Logging"]
    backend --> query["Suche und Dashboards"]

Anti-Pattern: Logs nur im Container-Dateisystem abzulegen ist nicht robust. Bei Pod-Neustart oder Node-Problemen verlierst du wichtige Diagnoseinformationen.

10.4 Tracing über Services hinweg

Tracing hilft, Request-Pfade über mehrere Services sichtbar zu machen.

sequenceDiagram
    participant C as Client
    participant FE as Frontend
    participant API as Backend API
    participant DB as Datenbank
    participant OTEL as OpenTelemetry Collector

    C->>FE: Request mit Trace Context
    FE->>OTEL: Span senden
    FE->>API: Request weitergeben
    API->>OTEL: Span senden
    API->>DB: Query
    DB-->>API: Ergebnis
    API-->>FE: Antwort
    FE-->>C: Antwort
    OTEL->>OTEL: Trace zusammensetzen

Designregel: Traces sind nur nützlich, wenn Anwendungen Kontext weiterreichen und relevante Spans erzeugen.

10.5 Ereignisse als Triage-Einstieg

Events sind zeitlich geordnete Hinweise aus Kubernetes-Komponenten. Sie erklären oft, warum Scheduling, Image Pull, Volumes oder Probes scheitern.

flowchart TD
    problem["Problem sichtbar"] --> events["kubectl get events --sort-by=.lastTimestamp"]
    events --> scheduling["FailedScheduling"]
    events --> image["FailedPull oder BackOff"]
    events --> volume["FailedMount oder FailedAttachVolume"]
    events --> probe["Unhealthy Probe"]
    events --> eviction["Evicted oder NodePressure"]
    scheduling --> next1["Ressourcen und Constraints prüfen"]
    image --> next2["Registry und Pull Secret prüfen"]
    volume --> next3["PVC, PV, CSI prüfen"]
    probe --> next4["App Health und Probe-Config prüfen"]
    eviction --> next5["Node-Ressourcen prüfen"]

Merksatz: Events sind nicht dauerhaftes Logging. Sammle sie zentral, wenn du längere Rückblicke brauchst.

10 / 16

11. Plattform-Lifecycle, GitOps und Add-ons

11.1 kubeadm Bootstrap-Ablauf

kubeadm initialisiert die Control Plane und ermöglicht den Beitritt weiterer Nodes. Es provisioniert aber nicht automatisch deine Maschinen, Netzwerkumgebung oder Plattform-Add-ons.

sequenceDiagram
    participant A as Admin
    participant K as kubeadm
    participant CP as Control Plane Node
    participant API as API Server
    participant W as Worker Node

    A->>K: kubeadm init
    K->>CP: Zertifikate und Konfiguration erzeugen
    K->>CP: statische Pods für Control Plane schreiben
    CP->>API: API Server wird verfügbar
    A->>K: CNI installieren
    A->>W: kubeadm join mit Token
    W->>API: Node registrieren
    API-->>A: Cluster zeigt Node Ready nach CNI und kubelet

Prüffrage: Warum ist ein Cluster nach kubeadm init noch nicht vollständig nutzbar? Weil mindestens Netzwerk, Add-ons, Storage, Ingress, Policies und Beobachtbarkeit fehlen können.

11.2 Cluster-Upgrade mit Drain

Ein kontrolliertes Upgrade respektiert PDBs, DaemonSets, Stateful Workloads und Rollback-Strategien.

flowchart TD
    plan["Upgrade planen"] --> backup["etcd Backup und Restore-Pfad prüfen"]
    backup --> cp["Control Plane upgraden"]
    cp --> node1["Node cordon"]
    node1 --> drain["Node drain"]
    drain --> upgrade["kubelet und Runtime upgraden"]
    upgrade --> uncordon["Node uncordon"]
    uncordon --> validate["Workloads und SLOs prüfen"]
    validate --> next["nächster Node"]

Betriebsregel: Upgrade ist kein reiner Paketwechsel. Es ist ein geplanter Plattformprozess mit Validierung, Kommunikationsfenster und Rückfallpfad.

11.3 GitOps-Fluss

GitOps macht Git zur autoritativen Quelle für deklarative Konfiguration. Ein Controller synchronisiert den Cluster mit dem Repository.

flowchart LR
    dev["Du änderst Manifest oder Values"] --> pr["Pull Request"]
    pr --> review["Review und Checks"]
    review --> merge["Merge in Zielbranch"]
    merge --> repo["Git Repository"]
    repo --> gitops["Argo CD oder Flux"]
    gitops --> diff["Diff gegen Cluster"]
    diff --> sync["Synchronisation"]
    sync --> api["Kubernetes API"]
    api --> status["Health und Sync Status"]

Merksatz: GitOps ersetzt nicht Architekturentscheidungen. Es erzwingt aber Nachvollziehbarkeit, Review und reproduzierbare Änderungen.

11.4 Helm-Release-Lifecycle

Helm rendert Templates zu Kubernetes-Manifests und verwaltet Releases. Fehler können im Chart, in Values, im Render-Ergebnis oder im Kubernetes-Apply entstehen.

flowchart TD
    chart["Helm Chart"] --> render["helm template"]
    values["values.yaml"] --> render
    render --> manifests["gerenderte Manifeste"]
    manifests --> lint["lint und policy checks"]
    lint --> upgrade["helm upgrade --install"]
    upgrade --> api["Kubernetes API"]
    api --> release["Helm Release Secret"]
    api --> resources["Kubernetes Ressourcen"]

Prüfkette: Chart-Version → Values → gerendertes YAML → Server-side Validation → Rollout-Status → App-Health.

11.5 Kustomize-Overlay-Modell

Kustomize verändert Basis-Manifeste durch Overlays. Das ist hilfreich für Umgebungen, ohne Templatesprache einzuführen.

flowchart TD
    base["base\nDeployment, Service, Config"] --> dev["overlay dev"]
    base --> stage["overlay stage"]
    base --> prod["overlay prod"]
    dev --> patchDev["Patches und Images dev"]
    stage --> patchStage["Patches und Images stage"]
    prod --> patchProd["Patches und Images prod"]
    patchProd --> output["kubectl apply -k overlays/prod"]

Designregel: Halte Base stabil und Overlays klein. Große, widersprüchliche Patches machen Deployments schwer prüfbar.

11.6 Add-on-Abhängigkeiten

Cluster-Add-ons sind selbst Workloads und können voneinander abhängig sein. Fehler in einem Add-on erzeugen oft scheinbar unzusammenhängende Workload-Fehler.

flowchart TD
    cni["CNI"] --> pods["Pods erhalten Netzwerk"]
    coredns["CoreDNS"] --> discovery["Service Discovery"]
    metrics["Metrics Server"] --> hpa["HPA und kubectl top"]
    ingress["Ingress oder Gateway Controller"] --> external["externer Traffic"]
    csi["CSI"] --> volumes["Persistente Volumes"]
    cert["cert-manager"] --> tls["TLS Zertifikate"]
    externaldns["ExternalDNS"] --> dns["DNS Records"]
    observability["Monitoring und Logging"] --> ops["Betrieb und Triage"]

Merksatz: Plattform-Add-ons brauchen denselben Lifecycle wie Anwendungen: Versionierung, Monitoring, Upgrade-Plan und Rollback.

11 / 16

12. Troubleshooting-Diagramme

12.1 Allgemeiner Kubernetes-Triage-Fluss

Beginne breit, grenze dann systematisch ein. Springe nicht sofort in Container-Logs, wenn der Pod noch nicht läuft.

flowchart TD
    start["Problem melden"] --> scope["Scope klären\nNamespace, Workload, Zeitpunkt"]
    scope --> get["kubectl get relevante Ressourcen"]
    get --> describe["kubectl describe"]
    describe --> events["Events sortieren"]
    events --> status{Ressourcenstatus?}
    status -->|Pending| pending["Scheduling, Ressourcen, PVC, Taints"]
    status -->|CrashLoop| crash["Logs previous, Exit Code, Probes"]
    status -->|Running aber nicht erreichbar| network["Service, EndpointSlice, DNS, Ingress"]
    status -->|Forbidden| rbac["RBAC und ServiceAccount"]
    status -->|Node betroffen| node["Node, kubelet, Runtime, CNI"]
    pending --> fix["Korrektur und Validierung"]
    crash --> fix
    network --> fix
    rbac --> fix
    node --> fix

Regel: Dokumentiere Ursache, Änderung und Validierung. Nur Symptome zu beheben reicht für Betriebsreife nicht.

12.2 Pod Pending

Pending bedeutet, dass der Pod nicht erfolgreich auf einem Node läuft. Die Ursache kann vor oder während der Node-Zuweisung liegen.

flowchart TD
    pending["Pod Pending"] --> events["Events prüfen"]
    events --> scheduling{FailedScheduling?}
    scheduling -->|ja| resources["CPU, Memory, Pod-Quota"]
    scheduling -->|ja| constraints["Affinity, Taints, Topology"]
    scheduling -->|nein| image["Image Pull noch nicht abgeschlossen?"]
    events --> volume{Volume Events?}
    volume -->|ja| pvc["PVC, PV, StorageClass, CSI"]
    resources --> fix["Requests oder Kapazität korrigieren"]
    constraints --> fix
    pvc --> fix
    image --> fix

Kommandoeinstieg: kubectl describe pod <pod> -n <namespace> und kubectl get events -n <namespace> --sort-by=.lastTimestamp.

12.3 CrashLoopBackOff

CrashLoopBackOff entsteht, wenn ein Container wiederholt startet und beendet wird.

flowchart TD
    crash["CrashLoopBackOff"] --> previous["kubectl logs --previous"]
    previous --> exit["Exit Code prüfen"]
    exit --> config["ConfigMap, Secret, Env prüfen"]
    exit --> deps["Abhängigkeiten prüfen\nDB, API, DNS"]
    exit --> probe["Probe-Konfiguration prüfen"]
    exit --> resources["OOMKilled oder CPU-Starvation prüfen"]
    config --> fix["Fix deployen"]
    deps --> fix
    probe --> fix
    resources --> fix
    fix --> rollout["Rollout überwachen"]

Anti-Pattern: Den Restart-Backoff nur durch Pod-Löschen zu „lösen“ behebt die Ursache nicht.

12.4 ImagePullBackOff

ImagePullBackOff zeigt, dass kubelet oder Runtime das Image nicht ziehen kann.

flowchart TD
    img["ImagePullBackOff"] --> name["Image-Name und Tag prüfen"]
    name --> registry["Registry erreichbar?"]
    registry --> auth["Pull Secret korrekt?"]
    auth --> tls["TLS und Zertifikate ok?"]
    tls --> arch["Architektur passend? amd64, arm64"]
    arch --> quota["Rate Limit oder Registry Quota?"]
    quota --> fix["Manifest, Secret oder Registry korrigieren"]

Prüfbefehl: kubectl describe pod zeigt meist die exakte Registry-Fehlermeldung im Event-Abschnitt.

12.5 Service ohne Endpoints

Ein Service ohne Endpoints ist häufig ein Label-, Selector- oder Readiness-Problem.

flowchart TD
    svc["Service antwortet nicht"] --> eps["EndpointSlices prüfen"]
    eps --> empty{Keine Endpoints?}
    empty -->|ja| selector["Service Selector prüfen"]
    selector --> labels["Pod Labels prüfen"]
    labels --> readiness["Pod Readiness prüfen"]
    readiness --> ports["targetPort und containerPort prüfen"]
    empty -->|nein| dataplane["kube-proxy, eBPF oder NetworkPolicy prüfen"]
    ports --> fix["Labels, Readiness oder Ports korrigieren"]
    dataplane --> fix

Kommandoeinstieg: kubectl get svc,endpoints,endpointslices,pods -n <namespace> -o wide.

12.6 DNS-Fehler im Cluster

DNS-Fehler können aus falschen Namen, CoreDNS-Problemen, NetworkPolicies oder Node-Netzwerkproblemen entstehen.

flowchart TD
    dnsfail["DNS-Auflösung scheitert"] --> debugpod["Debug-Pod im Namespace starten"]
    debugpod --> fqdn["Kurzname und FQDN testen"]
    fqdn --> coredns["CoreDNS Pods und Logs prüfen"]
    coredns --> svc["kube-dns Service prüfen"]
    svc --> netpol["NetworkPolicy erlaubt DNS?"]
    netpol --> node["Node-Netzwerk und CNI prüfen"]
    node --> fix["Ursache korrigieren und erneut testen"]

Prüffrage: Betrifft das Problem alle Namespaces oder nur einen Namespace? Diese Antwort grenzt CoreDNS- und Policy-Probleme stark ein.

12.7 Ingress oder Gateway routet nicht

Bei externem HTTP(S)-Traffic gibt es viele Übergabepunkte. Prüfe sie der Reihe nach.

flowchart TD
    fail["Externer Request scheitert"] --> dns["DNS Record korrekt?"]
    dns --> lb["Load Balancer erreichbar?"]
    lb --> controller["Ingress oder Gateway Controller läuft?"]
    controller --> routeClass["IngressClass oder GatewayClass korrekt?"]
    routeClass --> rule["Host, Path, Listener, Route korrekt?"]
    rule --> svc["Service korrekt?"]
    svc --> eps["EndpointSlices vorhanden?"]
    eps --> pod["Pods ready und App antwortet?"]

Betriebsregel: Dokumentiere für jeden öffentlichen Dienst DNS-Name, TLS-Quelle, Controller, Routingobjekt, Service und Ownership.

12.8 PVC Pending

PVCs bleiben Pending, wenn Binding, Provisionierung oder Storage-Backend nicht passen.

flowchart TD
    pvc["PVC Pending"] --> sc["StorageClass existiert?"]
    sc --> provisioner["Provisioner verfügbar?"]
    provisioner --> quota["StorageQuota oder Backend-Kapazität?"]
    quota --> binding["volumeBindingMode und Zone?"]
    binding --> access["AccessMode passend?"]
    access --> events["PVC- und CSI-Events prüfen"]
    events --> logs["CSI Controller Logs prüfen"]

Prüfbefehl: kubectl describe pvc <name> -n <namespace> ist der erste Einstieg.

12.9 Node NotReady

Node-Probleme betreffen oft mehrere Workloads. Kubelet, Runtime, Netzwerk, Disk, Memory und Zertifikate sind zentrale Prüfpunkte.

flowchart TD
    node["Node NotReady"] --> cond["Node Conditions prüfen"]
    cond --> kubelet["kubelet Status und Logs"]
    cond --> runtime["Container Runtime erreichbar?"]
    cond --> cni["CNI bereit?"]
    cond --> pressure["DiskPressure, MemoryPressure, PIDPressure"]
    cond --> cert["Zertifikate und API-Verbindung"]
    kubelet --> fix["Service oder Konfiguration korrigieren"]
    runtime --> fix
    cni --> fix
    pressure --> cleanup["Kapazität freigeben oder Node ersetzen"]
    cert --> fix

Betriebsregel: Bei Node-Problemen immer prüfen, ob Evictions, PDBs und Re-Scheduling wie erwartet funktionieren.

12.10 RBAC Forbidden

Forbidden ist meist kein Netzwerkfehler, sondern eine Autorisierungsentscheidung.

flowchart TD
    forbidden["API meldet Forbidden"] --> identity["Identität prüfen\nUser, Group, ServiceAccount"]
    identity --> verb["Verb prüfen\nget, list, create, patch"]
    verb --> resource["Resource und API Group prüfen"]
    resource --> scope["Namespace oder Cluster-Scope?"]
    scope --> binding["RoleBinding oder ClusterRoleBinding prüfen"]
    binding --> cani["kubectl auth can-i testen"]
    cani --> fix["minimale passende Rechte vergeben"]

Anti-Pattern: Nicht reflexartig ClusterRoleBinding auf cluster-admin setzen. Baue die kleinste passende Regel.

12.11 OOMKilled

OOMKilled bedeutet, dass der Container wegen Memory-Grenzen oder Node-Druck beendet wurde.

flowchart TD
    oom["OOMKilled"] --> limits["Memory Limit prüfen"]
    limits --> usage["tatsächliche Memory-Nutzung prüfen"]
    usage --> leak["Memory Leak oder Lastspitze?"]
    usage --> request["Request realistisch?"]
    request --> qos["QoS-Klasse prüfen"]
    qos --> node["Node MemoryPressure prüfen"]
    leak --> fix["App oder Limit korrigieren"]
    request --> fix
    node --> fix

Merksatz: Mehr Memory-Limit kann Symptome reduzieren, behebt aber keinen Leak und kein falsches Kapazitätsmodell.

12.12 Hängender Rollout

Ein Rollout hängt oft, weil neue Pods nicht ready werden oder alte Pods nicht entfernt werden können.

flowchart TD
    rollout["Rollout hängt"] --> status["kubectl rollout status"]
    status --> rs["ReplicaSets vergleichen"]
    rs --> newpods["neue Pods prüfen"]
    newpods --> pending["Pending?"]
    newpods --> crash["CrashLoop oder Probe Fail?"]
    newpods --> readiness["Readiness wird nie true?"]
    rs --> pdb["PDB blockiert alte Pod-Eviction?"]
    pending --> fix["Scheduling oder Storage fixen"]
    crash --> fix
    readiness --> fix
    pdb --> fix

Prüffrage: Ist das Problem im neuen ReplicaSet, im Service-Traffic oder in der Rollout-Strategie?

12.13 Incident Response im Kubernetes-Cluster

Incident Response braucht technische Eindämmung und Beweissicherung. Lösche verdächtige Ressourcen nicht vorschnell, wenn du forensische Informationen brauchst.

stateDiagram-v2
    [*] --> Erkennen
    Erkennen --> Eingrenzen: betroffene Ressourcen bestimmen
    Eingrenzen --> Eindämmen: Netzwerk, Rechte oder Node isolieren
    Eindämmen --> BeweiseSichern: Logs, Events, Images, Manifeste sichern
    BeweiseSichern --> ZugangEntziehen: Tokens, Secrets, Rollen rotieren
    ZugangEntziehen --> Bereinigen: Ursache entfernen
    Bereinigen --> Wiederherstellen: kontrollierter Restore oder Rollout
    Wiederherstellen --> Verbessern: Policies und Monitoring schärfen
    Verbessern --> [*]

Betriebsregel: Bereite Runbooks vor, bevor du einen Incident hast. Improvisation unter Zeitdruck führt zu Datenverlust und unvollständiger Ursachenanalyse.

12 / 16

13. Workshop-Übungen mit Diagrammen

13.1 Übung: Deployment visuell nachvollziehen

Ziel ist, dass du Ressourcenbeziehungen nicht nur auswendig lernst, sondern im Cluster erkennst.

flowchart LR
    task["Deployment ausrollen"] --> observe1["kubectl get deploy,rs,pod"]
    observe1 --> labels["Labels und Selector prüfen"]
    labels --> service["Service hinzufügen"]
    service --> endpoints["EndpointSlices beobachten"]
    endpoints --> rollout["Image ändern und Rollout verfolgen"]
    rollout --> explain["Diagramm mit realen Namen beschriften"]

Abnahme: Du kannst erklären, welches Objekt welches andere Objekt erzeugt und warum ein Service nach einem Rollout weiterhin funktioniert.

13.2 Übung: NetworkPolicy sichtbar machen

NetworkPolicies sind abstrakt. Diese Übung macht erlaubte und blockierte Pfade sichtbar.

flowchart TD
    frontend["frontend Pod"] --> api["api Pod"]
    api --> db["db Pod"]
    debug["debug Pod"] -. blockiert .-> db
    internet["Internet"] -. blockiert .-> api
    policy1["Policy frontend zu api"] --> frontend
    policy1 --> api
    policy2["Policy api zu db"] --> api
    policy2 --> db

Abnahme: Du kannst mit einem Debug-Pod zeigen, welcher Traffic erlaubt und welcher blockiert ist.

13.3 Übung: Storage-Fehler analysieren

Diese Übung verbindet PVC, Events, StorageClass und CSI-Komponenten.

flowchart LR
    create["PVC erstellen"] --> pending["PVC Pending erzeugen"]
    pending --> inspect["describe pvc und events"]
    inspect --> sc["StorageClass korrigieren"]
    sc --> bind["PVC Bound"]
    bind --> pod["Pod mit Mount starten"]
    pod --> verify["Datei schreiben und Pod neu erstellen"]

Abnahme: Du kannst erklären, ob der Fehler im Claim, in der StorageClass, im Provisioner oder im Backend lag.

13.4 Übung: Security-Kette prüfen

Die Übung zeigt, dass Security aus mehreren Kontrollen besteht.

flowchart TD
    sa["ServiceAccount mit minimalen Rechten"] --> cani["kubectl auth can-i"]
    cani --> pod["Pod mit SecurityContext"]
    pod --> admission["Pod Security Admission testen"]
    admission --> secret["Secret-Zugriff prüfen"]
    secret --> netpol["NetworkPolicy anwenden"]
    netpol --> audit["Audit- oder Event-Spuren ansehen"]

Abnahme: Du kannst begründen, welche Kontrolle API-Zugriff, Pod-Spezifikation, Secret-Zugriff und Netzwerkverkehr begrenzt.

13 / 16

14. Diagramm-Auswahl nach Workshop-Situation

Situation Passende Diagramme Ziel
Einstieg in Kubernetes Gesamtüberblick, Control Plane und Worker Nodes, Desired State Mentales Modell aufbauen
Erste Deployments Deployment, Rollout, Pod-Lifecycle, Service und EndpointSlices Ressourcenbeziehungen verstehen
Netzwerk-Lab Service-Typen, DNS, Ingress, Gateway API, NetworkPolicy Traffic-Pfade sichtbar machen
Storage-Lab PVC, dynamische Provisionierung, CSI, StatefulSet Persistenz und Binding verstehen
Security-Lab API Request Lifecycle, RBAC, Admission, Pod Security, Secrets Kontrollen sauber trennen
Betrieb und Triage Events, Pending, CrashLoop, Node NotReady, Rollout hängt Fehler systematisch eingrenzen
Plattformdesign HA Control Plane, GitOps, Add-ons, Upgrade, Mandantentrennung Architekturentscheidungen diskutieren
Incident Response Audit, Runtime Detection, Incident Response State Machine Reaktion strukturiert durchführen
14 / 16

15. Mermaid-Konventionen für eigene Ergänzungen

15.1 Benennung

Nutze sprechende Labels und kurze technische Bezeichnungen. Node-IDs sollten ASCII bleiben, Labels dürfen deutsche Umlaute enthalten.

flowchart LR
    good["gute Beschriftung\nService wählt Pods über Labels"] --> better["besser erklärbar im Workshop"]
    bad["svc1"] -. zu kryptisch .-> unclear["unklare Aussage"]

15.2 Diagramme nicht überladen

Ein Diagramm sollte eine Hauptaussage haben. Wenn du mehr als drei Aussageebenen brauchst, teile das Bild auf.

flowchart TD
    topic["Ein Thema"] --> diag1["Diagramm 1\nArchitektur"]
    topic --> diag2["Diagramm 2\nAblauf"]
    topic --> diag3["Diagramm 3\nTroubleshooting"]

15.3 Technische Tiefe an Zielgruppe anpassen

Für Einsteiger zeigst du Beziehungen. Für Fortgeschrittene ergänzt du Events, Status, Controller und Randbedingungen.

flowchart LR
    beginner["Fundamentals"] --> simple["Pods, Services, Deployments"]
    intermediate["Intermediate"] --> flow["Rollouts, Probes, Storage, Netzwerk"]
    professional["Professional"] --> ops["HA, Upgrades, Add-ons, Observability"]
    security["Security"] --> controls["RBAC, Admission, Policies, Incident Response"]
15 / 16

16. Quellen und fachliche Orientierung

Diese Diagrammdatei orientiert sich an den offiziellen Kubernetes-Konzepten und Referenzen. Nutze die Links als fachliche Rückversicherung, wenn du Diagramme im Workshop vertiefst oder an eine konkrete Plattform anpasst.

16 / 16

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.