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.
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"]
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.
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.
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.
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?
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.
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?
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.
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.
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.
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.
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.
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 |
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"]
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.
- Kubernetes Components
- Cluster Architecture
- Nodes
- Communication between Nodes and the Control Plane
- Services
- EndpointSlices
- Ingress
- Gateway API
- Network Policies
- Persistent Volumes
- Storage Classes
- Dynamic Volume Provisioning
- RBAC
- RBAC Good Practices
- Pod Security Standards
- Pod Security Admission
- Admission Controllers
- Operating etcd clusters for Kubernetes
- Creating Highly Available Clusters with kubeadm