Kubernetes Patterns
Produktionsreife Kubernetes-Muster für das zuverlässige Bereitstellen, Verwalten und Debuggen von Workloads.
Wann aktivieren
- Schreiben von Kubernetes-Manifesten (Deployments, Services, Ingress, Jobs)
- Konfigurieren von Ressourcenanfragen/-limits, Liveness-/Readiness-Probes
- Einrichten von RBAC, Namespaces oder ServiceAccounts
- Verwalten von Konfigurationen und Secrets in K8s
- Debuggen von CrashLoopBackOff, OOMKilled, pending Pods oder Image-Pull-Fehlern
- Konfigurieren von HPA (Horizontal Pod Autoscaler) oder PodDisruptionBudgets
- Überprüfen von K8s-YAML auf Sicherheit oder Korrektheit
Wann verwenden
Gleiche Anlässe wie unter Wann aktivieren oben. Dieser Alias erfüllt die Konventionen des Repository-Skill-Formats. Verwenden Sie dieses Skill immer dann, wenn Sie Kubernetes-YAML und Workloads schreiben, überprüfen oder debuggen.
Wie es funktioniert
Dieses Skill bietet kopierbare, produktionsreife YAML-Muster und kubectl-Debugging-Befehle, organisiert nach Aufgaben:
- Deployment-Vorlage — Ein vollständig konfiguriertes produktionsreifes
Deploymentmit Sicherheitskontext, Rolling-Update-Strategie, allen drei Probetypen, Ressourcenlimits und Umgebungsvariablen aus ConfigMap/Secret. - Probes — Entscheidungstabelle für Startup vs. Liveness vs. Readiness mit korrekter
failureThreshold × periodSeconds-Berechnung. - Services & Ingress — ClusterIP-, LoadBalancer- und TLS-Ingress-Muster mit cert-manager-Annotationen.
- ConfigMaps & Secrets —
envFrom-, Datei-Mount- und externe Secrets-Hinweise. - Ressourcenmanagement — Daumenregeln für Requests vs. Limits nach Workload-Typ (Web-API, JVM, Worker, Sidecar).
- RBAC — Least-Privilege-ServiceAccount → Role → RoleBinding-Kette.
- HPA & PDB — Konfigurationen für Autoskalierung und Node-Drain-Sicherheit.
- Jobs & CronJobs — Muster für einmalige und geplante Workloads mit korrekter
restartPolicy. - kubectl-Spickzettel — Befehle für Logs, exec, rollback, port-forward, dry-run und häufige Fehlerdiagnosen.
- Anti-Patterns & Checkliste — Was NICHT zu tun ist, und eine Sicherheits-/Zuverlässigkeits-/Beobachtbarkeits-Checkliste.
Beispiele
Siehe die Abschnitte unten für vollständige, ausführbare Beispiele. Kurzübersicht:
| Aufgabe | Springe zu |
|---|---|
| Vollständiges produktionsreifes Deployment-YAML | Kern-Workload-Muster |
| Probe-Konfiguration | Probes |
| RBAC Least-Privilege-Setup | RBAC |
| Debuggen eines CrashLoopBackOff | kubectl Debugging-Spickzettel |
| Autoskalierung | HPA |
Kern-Workload-Muster
Deployment — Produktionsvorlage
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-namespace
labels:
app: my-app
version: "1.0.0"
spec:
replicas: 3
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Einen zusätzlichen Pod während des Updates erlauben
maxUnavailable: 0 # Nie unter die gewünschte Anzahl reduzieren
template:
metadata:
labels:
app: my-app
version: "1.0.0"
spec:
# Sicherheitskontext auf Pod-Ebene
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
# Graceful Shutdown
terminationGracePeriodSeconds: 30
containers:
- name: my-app
image: ghcr.io/org/my-app:1.0.0 # Niemals :latest verwenden
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
protocol: TCP
# Ressourcenanfragen UND -limits sind beide erforderlich
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
# Container-Sicherheitskontext
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Probes (siehe Abschnitt Probes unten)
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 0
periodSeconds: 30
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 2
# Umgebung aus ConfigMap und Secret
envFrom:
- configMapRef:
name: my-app-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-app-secrets
key: db-password
# Beschreibbares tmp-Verzeichnis bei readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
Probes — Liveness, Readiness, Startup
Das Verständnis, wann welcher Probe verwendet wird, ist entscheidend:
| Probe | Fehleraktion | Verwendung für |
|---|---|---|
startupProbe | Beendet Container bei langsamem Start | Langsam startende Apps (JVM, Python) |
livenessProbe | Startet Container neu | Erkennung von Deadlocks / hängenden Prozessen |
readinessProbe | Entfernt aus Service-Endpunkten | Temporäre Nichtverfügbarkeit (DB-Wiederverbindung) |
# Korrektes Muster: startupProbe deckt langsamen Start ab,
# dann übernehmen liveness/readiness
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30 # 30 * 5s = 150s maximale Startzeit
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 30
failureThreshold: 3 # 3 * 30s = 90s bis zum Neustart
readinessProbe:
httpGet:
path: /ready # Separater Endpunkt: prüft DB, Cache usw.
port: 8080
periodSeconds: 10
failureThreshold: 2
# FALSCH: initialDelaySeconds ohne startupProbe
# Wenn die App 60s zum Starten braucht, stattdessen eine startupProbe setzen
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60 # SCHLECHT: Willkürliches Warten, Race-Condition
Services und Ingress
Service-Typen
# ClusterIP (Standard) — nur intern
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: my-namespace
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
# LoadBalancer — externer Traffic (Cloud-Anbieter)
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: 8080
Ingress mit TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
namespace: my-namespace
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: my-app-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
ConfigMaps und Secrets
ConfigMap — Nicht-sensitive Konfiguration
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
namespace: my-namespace
data:
LOG_LEVEL: "info"
APP_ENV: "production"
MAX_CONNECTIONS: "100"
# Als Datei für komplexe Konfiguration mounten
app.yaml: |
server:
port: 8080
timeout: 30s
# ConfigMap als Datei mounten
volumes:
- name: config
configMap:
name: my-app-config
items:
- key: app.yaml
path: app.yaml
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
Secrets — Sensitive Daten
# Secret aus Literal erstellen (CLI, dann in Vault/SOPS speichern)
kubectl create secret generic my-app-secrets \
--from-literal=db-password='s3cr3t' \
--namespace=my-namespace \
--dry-run=client -o yaml | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: my-app-secrets
namespace: my-namespace
type: Opaque
# Werte sind base64-kodiert (NICHT verschlüsselt — verwenden Sie Sealed Secrets oder ESO für echte Verschlüsselung)
data:
db-password: czNjcjN0 # base64 von 's3cr3t'
Wichtig: Rohe Kubernetes-Secrets sind nur base64-kodiert, nicht im Ruhezustand verschlüsselt, es sei denn, Ihr Cluster ist entsprechend konfiguriert. Verwenden Sie Sealed Secrets oder External Secrets Operator für die Produktion.
Ressourcenanfragen und -limits
resources:
requests: # Der Scheduler verwendet dies, um den Pod zu platzieren
cpu: "100m" # 100 Millicores = 0,1 CPU
memory: "128Mi"
limits: # Container wird oberhalb dessen beendet/gedrosselt
cpu: "500m"
memory: "256Mi"
Daumenregeln:
| Workload-Typ | CPU-Anfrage | Speicheranfrage | Hinweise |
|---|---|---|---|
| Web-API | 100–250m | 128–256Mi | Limits auf 2-4x der Requests setzen |
| Worker/Consumer | 250–500m | 256–512Mi | Speicher-Limit = Request für Vorhersagbarkeit |
| JVM-App | 500m–1 | 512Mi–2Gi | Spielraum über -Xmx für JVM-Overhead lassen |
| Sidecar | 10–50m | 32–64Mi | Minimal halten |
# FALSCH: Keine Requests oder Limits — unvorhersehbare Planung, OOM-Evictions
containers:
- name: app
image: myapp:latest
# Fehlende resources: {} — das ist gefährlich in der Produktion
# FALSCH: Limits ohne Requests — Requests werden auf Limits gesetzt, überreserviert Kapazität
resources:
limits:
cpu: "2"
memory: "1Gi"
# requests fehlen — werden auf Limits-Werte gesetzt
RBAC — Rollen und ServiceAccounts
Prinzip der geringsten Privilegien
Zwei Muster, je nachdem, ob die App die Kubernetes-API aufruft:
Muster A — App benötigt KEINE Kubernetes-API (die meisten Apps)
Deaktivieren Sie das automatische Token-Mounten auf dem ServiceAccount. Die Role/RoleBinding werden nicht benötigt.
# ServiceAccount mit deaktiviertem Token — sicherster Standard
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: false # Kein K8s-API-Token in Pods injiziert
# Referenz im Deployment — kein Token, kein API-Zugriff
spec:
template:
spec:
serviceAccountName: my-app-sa
automountServiceAccountToken: false # Doppelt gemoppelt: auch auf Pod-Ebene gesetzt
Muster B — App BENÖTIGT die Kubernetes-API (Operatoren, Controller, Konfigurationsbeobachter)
Aktivieren Sie das Token und gewähren Sie nur die tatsächlich erforderlichen Berechtigungen.
# 1. ServiceAccount — Token für diesen SA aktivieren
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: true # Token erforderlich: App ruft K8s-API auf
# 2. Role — nur das gewähren, was die App benötigt (namensraumbezogen)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: my-app-role
namespace: my-namespace
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"] # Schreibgeschützt, spezifische Ressource
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["my-app-secrets"] # Auf bestimmtes Secret nach Namen beschränken
verbs: ["get"]
# 3. Role an ServiceAccount binden
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-rolebinding
namespace: my-namespace
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: my-namespace
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: my-app-role
# 4. SA im Deployment referenzieren
spec:
template:
spec:
serviceAccountName: my-app-sa
# automountServiceAccountToken ist standardmäßig true vom SA — Token wird injiziert
Horizontal Pod Autoscaler (HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
namespace: my-namespace
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2 # Mindestens 2 für Hochverfügbarkeit
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Hochskalieren, wenn durchschnittliche CPU > 70%
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
HPA erfordert, dass
resources.requestsfür alle Container gesetzt sind — es berechnet die Auslastung alscurrent / request.
PodDisruptionBudget (PDB)
Verhindert, dass zu viele Pods während Node-Drains oder Rolling-Updates ausfallen:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: my-namespace
spec:
minAvailable: 2 # ODER maxUnavailable: 1 verwenden
selector:
matchLabels:
app: my-app
Namespaces und Multi-Tenancy
# Namespace mit Ressourcenkontingenten erstellen
kubectl create namespace my-namespace
# ResourceQuota anwenden, um Namespace-Verbrauch zu begrenzen
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-namespace-quota
namespace: my-namespace
spec:
hard:
requests.cpu: "4"
requests.memory: 4Gi
limits.cpu: "8"
limits.memory: 8Gi
pods: "20"
EOF
Jobs und CronJobs
# Einmaliger Job (DB-Migration, Datenverarbeitung)
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
namespace: my-namespace
spec:
backoffLimit: 3 # Bei Fehlern bis zu 3-mal wiederholen
ttlSecondsAfterFinished: 3600 # Nach 1h automatisch löschen
template:
spec:
restartPolicy: OnFailure # Never für Jobs (nicht Always)
containers:
- name: migrate
image: ghcr.io/org/my-app:1.0.0
command: ["python", "manage.py", "migrate"]
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
# Geplanter Job (CronJob)
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
namespace: my-namespace
spec:
schedule: "0 2 * * *" # Täglich um 2 Uhr morgens
startingDeadlineSeconds: 300 # Pod muss innerhalb von 5min nach geplanter Zeit starten
successfulJobsHistoryLimit: 3 # Letzte 3 erfolgreiche Läufe behalten
failedJobsHistoryLimit: 1 # Letzten fehlgeschlagenen Lauf behalten
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: alpine:3
command: ["/bin/sh", "-c", "pg_dump db > /backup/db.sql"]
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "256Mi"
kubectl Debugging-Spickzettel
# ---- Logs ----
# Aktuelle Logs anzeigen
kubectl logs deployment/my-app -n my-namespace
# Live-Logs streamen (Tail)
kubectl logs deployment/my-app -n my-namespace -f
# Früheren Container nach Crash anzeigen
kubectl logs my-pod -n my-namespace --previous
# Alle Container in einem Pod
kubectl logs my-pod -n my-namespace -c my-app
# ---- Debuggen ----
# Shell in einem Container öffnen
kubectl exec -it my-pod -n my-namespace -- /bin/sh
# HPA-Status prüfen
kubectl describe hpa my-app-hpa -n my-namespace
# Pod-Ereignisse prüfen
kubectl describe pod my-pod -n my-namespace
# ---- Rollback ----
# Deployment-Verlauf anzeigen
kubectl rollout history deployment/my-app -n my-namespace
# Zu vorheriger Revision zurückkehren
kubectl rollout undo deployment/my-app -n my-namespace
# Zu bestimmter Revision zurückkehren
kubectl rollout undo deployment/my-app -n my-namespace --to-revision=2
# ---- Port-Forwarding ----
# Lokalen Port an Pod binden
kubectl port-forward pod/my-pod 8080:8080 -n my-namespace
# ---- Manifest-Validierung ----
# Dry-Run-Erstellung (clientseitig validieren)
kubectl apply -f manifest.yaml --dry-run=client
# Server-seitig validieren
kubectl apply -f manifest.yaml --dry-run=server
# ---- Fehlerdiagnose ----
# CrashLoopBackOff — Logs und Ereignisse prüfen
kubectl logs my-pod -n my-namespace --previous | tail -20
kubectl describe pod my-pod -n my-namespace | grep -A 20 Events
# ImagePullBackOff — Imagedetails prüfen
kubectl describe pod my-pod -n my-namespace | grep "Failed"
# OOMKilled — Speicherverbrauch prüfen
kubectl top pod my-pod -n my-namespace
# Pending Pod — Ressourcen und Knotenkapazität prüfen
kubectl describe pod my-pod -n my-namespace | grep -i "node\|resources"
kubectl describe nodes | grep -A 5 "Allocated resources"
Anti-Patterns & Checkliste
Häufige Anti-Patterns
| Anti-Pattern | Problem | Lösung |
|---|---|---|
:latest-Tag verwenden | Unvorhersehbare Versionen, Cache-Probleme | Immer explizite semantische Versionen verwenden: v1.2.3 |
| imagePullPolicy: Always | Unnötige Pulls, Verlangsamung der Bereitstellung | IfNotPresent verwenden, es sei denn, Sie brauchen Always |
| Keine Ressourcenanfragen | Unvorhersehbare Pod-Planung | Immer resources.requests setzen |
| Ressourcenlimits ohne Requests | Kapazitätsüberreservierung | Immer sowohl Requests als auch Limits setzen |
| initialDelaySeconds verwenden | Race-Condition, App wird möglicherweise neu gestartet | startupProbe mit hohem failureThreshold verwenden |
| Secrets in ConfigMaps speichern | Keine Verschlüsselung im Ruhezustand | Secrets oder externe Secrets-Lösungen verwenden |
| ServiceAccount-Token automatisch mounten | Sicherheitsrisiko, wenn die App die K8s-API nicht benötigt | Token-Mount deaktivieren mit automountServiceAccountToken: false |
| ClusterRole verwenden, wenn Role ausreicht | Übermäßige Cluster-weite Berechtigungen | Verwenden Sie Role + RoleBinding für namensraumbezogene Zugriffe |
| Pods ohne PDB ausführen | Pods können während Wartung oder Ausfällen alle gleichzeitig ausfallen | PDB für alle zustandslosen Workloads definieren |
| healthz-Endpunkt für readiness verwenden | Pod als bereit markiert, obwohl er DB noch nicht verbunden hat | Separaten /ready-Endpunkt implementieren, der Abhängigkeiten prüft |
Produktions-Checkliste
-
image: tagauf eine bestimmte Version gesetzt (nichtlatest) -
resources.requestsundresources.limitsgesetzt -
startupProbefür langsam startende Container konfiguriert -
livenessProbeundreadinessProbemit angemessenen Schwellenwerten -
readinessProbe-Endpunkt prüft tatsächliche Abhängigkeiten (DB, Cache) - Sicherheitskontext:
runAsNonRoot: true,readOnlyRootFilesystem: true -
automountServiceAccountToken: falsegesetzt, es sei denn, die App benötigt die K8s-API -
PodDisruptionBudgetfür zustandslose Workloads konfiguriert - TLS im Ingress mit cert-manager konfiguriert
- Secrets nicht fest codiert; aus Secret-Ressourcen mit Umgebungsvariablen eingebunden
-
terminationGracePeriodSecondsangemessen gesetzt (ausreichend für Drain oder DB-Transaktionen) - HPA mit
minReplicas >= 2für Hochverfügbarkeit - Jobs verwenden
restartPolicy: OnFailure, nichtAlways - Namespace ist mit
ResourceQuotakonfiguriert, um Ressourcenüberschreitungen zu verhindern
Lernen und Ressourcen
- Offizielle Kubernetes-Dokumentation
- Kubernetes-Muster von Red Hat
- Kubernetes best practices – Ressourcenanfragen und -limits
- Probleme mit der Pod-Startverzögerung beheben
Optionale Erweiterungen
Dieser Abschnitt behandelt erweiterte Szenarien, die Sie möglicherweise basierend auf Ihren Anforderungen hinzufügen möchten:
Integritätssiegel und Init-Container
# Init-Container-Funktion: Ein Container, der vor der eigentlichen App läuft
# Nützlich für Warten auf Abhängigkeiten (DB bereit, Migrationen ausführen)
spec:
template:
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command:
- sh
- -c
- |
until nc -z my-db-service 5432; do
echo "Warte auf Datenbank..."
sleep 2
done
resources:
requests:
cpu: "10m"
memory: "16Mi"
limits:
cpu: "50m"
memory: "32Mi"
Netzwerkrichtlinien (NetworkPolicies)
Standardmäßig erlauben Kubernetes-Cluster den gesamten Pod-zu-Pod-Verkehr. Für eine sicherere Multi-Tenancy Richtlinien definieren:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-app-network-policy
namespace: my-namespace
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
- podSelector:
matchLabels:
app: my-app
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
app: my-db
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
Pod-Verschlechterung und Budgets
Erweiterte PDB-Konfiguration mit benutzerdefinierten Rechten:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: my-namespace
spec:
maxUnavailable: 1
selector:
matchLabels:
app: my-app
Dieses Skill wird bereitgestellt von der Kubernetes-Patterns-Community. Verbesserungen und Pull-Requests sind willkommen.