Ressourcengrenzen im Cluster dauerhaft betreiben lassen? WZ-IT betreibt Kubernetes-Cluster mit Monitoring, regelmäßiger Kapazitätsbewertung und Updates im Wartungsfenster - auf Ihrer Infrastruktur oder im Hosting von WZ-IT in Deutschland. Managed Kubernetes ansehen
Ein Container ohne Limit kann den Arbeitsspeicher eines Nodes belegen, ein Team ohne Quota den Rest des Clusters. Kubernetes begrenzt das mit fünf Bausteinen: Requests reservieren, Limits deckeln, die QoS-Klasse ergibt sich daraus, die LimitRange setzt Defaults je Container und die ResourceQuota begrenzt die Summe je Namespace. Dieser Leitfaden zeigt jeden Baustein mit YAML, die zugehörigen Fehlermeldungen, ein Rechenbeispiel für das Namespace-Budget und die Metriken zur Überwachung. Stand Oktober 2026, geprüft gegen die Dokumentation von Kubernetes 1.37 (aktuell 1.37.1).
Inhaltsverzeichnis
- Requests, Limits und QoS-Klassen
- LimitRange: Defaults und Grenzen je Container
- ResourceQuota: das Budget je Namespace
- Scopes: BestEffort verbieten, Prioritäten trennen
- Typische Fehler und ihre Ursache
- Namespace-Budget planen: ein Rechenbeispiel
- Multi-Tenancy-Muster
- Auslastung überwachen
- Stand und Methodik
- Unser Vorgehen bei WZ-IT
- Weiterführende Guides
Requests, Limits und QoS-Klassen
Der Request ist die Menge, die der Scheduler für einen Container reserviert. Die Summe der Requests auf einem Node darf dessen Kapazität nicht überschreiten, auch wenn die tatsächliche Auslastung niedrig ist (Resource Management). Bei Konkurrenz um CPU wirkt der CPU-Request zusätzlich als Gewichtung. Das Limit ist die Obergrenze, die kubelet und Kernel durchsetzen. Es wirkt je Ressource unterschiedlich:
- CPU wird gedrosselt (Throttling). Der Container läuft weiter, bekommt aber nicht mehr Rechenzeit.
- Memory wird über OOM-Kills durchgesetzt. Überschreitet der Container sein Limit, kann der Kernel ihn beenden; das geschieht reaktiv bei Speicherdruck.
Wer nur ein Limit setzt, bekommt einen Request in gleicher Höhe, auch wenn eine LimitRange einen niedrigeren Default-Request vorsieht (Beispiel). Wer nur einen Request setzt, bekommt das Default-Limit der LimitRange, sofern sie eines vorsieht.
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
memory: 512Mi
Bei Memory zählt die Schreibweise: 400m sind 0,4 Byte, gemeint ist meist 400Mi. Aus Requests und Limits ergibt sich die QoS-Klasse des Pods (Pod-QoS):
| QoS-Klasse | Bedingung (für alle Container des Pods) | Verhalten unter Speicherdruck auf dem Node |
|---|---|---|
| Guaranteed | CPU und Memory: Request und Limit gesetzt, Request gleich Limit | wird nicht wegen des Verbrauchs anderer Pods verdrängt |
| Burstable | mindestens ein Request oder Limit, aber nicht Guaranteed | wird verdrängt, wenn der Verbrauch über dem Request liegt |
| BestEffort | weder Requests noch Limits für CPU oder Memory | Request ist 0, jede Nutzung liegt darüber: zuerst betroffen |
Die Klasse wird beim Anlegen festgelegt. Auch eine In-Place-Größenänderung (stabil seit 1.35) muss sie beibehalten (Resize). Die Verdrängungsreihenfolge richtet sich nach dem Verbrauch über dem Request und nach der Priority, nicht direkt nach der Klasse. Die Klasse bestimmt aber den oom_score_adj, mit dem der Kernel bei knappem Node-Speicher auswählt: Guaranteed -997, BestEffort 1000 (Node-pressure Eviction).
Unsere Betriebsregel: Memory-Request gleich Memory-Limit, damit Reservierung und Obergrenze übereinstimmen. Guaranteed wird der Pod nur, wenn das auch für CPU gilt. Memory-Request gleich Limit plus CPU-Request ohne CPU-Limit ergibt Burstable und ist für die meisten Workloads der pragmatische Standard.
LimitRange: Defaults und Grenzen je Container
Eine LimitRange wirkt beim Anlegen von Pods und PVCs im Namespace: Zuerst setzt sie Defaults für Container ohne eigene Angaben, danach prüft sie Minimum, Maximum und Limit-zu-Request-Verhältnis. Verstöße beantwortet die API mit 403 (LimitRange). Laufende Pods bleiben unverändert.
apiVersion: v1
kind: LimitRange
metadata:
name: container-defaults
namespace: team-a
spec:
limits:
- type: Container
defaultRequest:
cpu: 100m
memory: 128Mi
default:
memory: 256Mi
min:
memory: 32Mi
max:
memory: 2Gi
maxLimitRequestRatio:
memory: "4"
Jeder Container ohne eigene Angaben bekommt Request 100m CPU und 128Mi Memory sowie ein Memory-Limit von 256Mi, aber kein CPU-Limit. Zwei Stolpersteine:
- Defaults entstehen automatisch. Wer bei einem Container-Eintrag
maxsetzt, erzeugt damitdefault(das Limit) unddefaultRequest; wer nurminsetzt, erzeugtdefaultRequest. Einmax.cpuvon 800m ohnedefaultergibt ein Default-Limit und einen Default-Request von ebenfalls 800m (Beispiel in der Doku, Logik indefaults.go). Jeder Container ohne CPU-Angabe reserviert dann 800m. Defaults deshalb immer ausdrücklich setzen. - Defaults werden nicht auf Konsistenz geprüft. Liegt das Default-Limit unter dem Request des Pods, lehnt die API den Pod als ungültig ab (siehe Tabelle unten). Mehrere LimitRange-Objekte je Typ vermeiden, weil dann unbestimmt ist, welcher Default greift.
ResourceQuota: das Budget je Namespace
Eine ResourceQuota begrenzt die Summe über alle Objekte des Namespace und wirkt, sobald ein ResourceQuota-Objekt im Namespace existiert (Resource Quotas). Zwei Objekte decken Compute sowie Objektanzahl und Storage ab:
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute
namespace: team-a
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.memory: 12Gi
pods: "40"
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: objects-and-storage
namespace: team-a
spec:
hard:
count/jobs.batch: "20"
count/secrets: "50"
services.loadbalancers: "1"
services.nodeports: "0"
persistentvolumeclaims: "10"
requests.storage: 200Gi
fast-ssd.storageclass.storage.k8s.io/requests.storage: 100Gi
count/<Ressource>.<Gruppe> (für Ressourcen der Core-Gruppe count/<Ressource>) zählt beliebige Objekte einschließlich Custom Resources und schützt die Control Plane, etwa vor einem CronJob, der zu viele Jobs erzeugt. fast-ssd ist der Name einer StorageClass. Für das Verhalten gelten diese Regeln:
- Pflichtangaben. Nennt die Quota CPU oder Memory, muss jeder neue Pod den genannten Wert setzen, sonst lehnt die API ihn ab (
must specify limits.memory for: app, geprüft inpods.go). Die LimitRange aus dem vorigen Abschnitt füllt diese Lücke. - Gezählt wird der effektive Wert. Init-Container (Doku) und Pod-Overhead (Doku) fließen ein. Pods im Zustand Succeeded oder Failed zählen für
podsund die Compute-Ressourcen nicht (count/podszählt alle Pods unabhängig vom Zustand). - Überschreitung. Direkt angelegte Pods lehnt die API mit 403 und
exceeded quotaab. Bei einem Deployment gelingt das Anlegen, die Pods fehlen: Der Status zeigtReplicaFailuremit dem GrundFailedCreate(Deployment-Doku). - Nur harte Grenzen. Es gibt unter
spec.hardweder weiche Quotas noch Warnungen. - Absolute Werte. Zusätzliche Nodes erhöhen keine Quota, und eine Quota ordnet Pods keinen Nodes zu: Pods mehrerer Namespaces teilen sich weiterhin Nodes.
- Schutz vor Änderung. Die Rolle
adminhat keinen Schreibzugriff auf Quotas (RBAC); wer volle Schreibrechte im Namespace hat, kann sie löschen. Quota und LimitRange gehören dem Plattformteam, nicht dem Mandanten.
Scopes: BestEffort verbieten, Prioritäten trennen
Mit scopes gilt eine Quota nur für einen Teil der Pods. Der Scope BestEffort erlaubt als Ressource nur pods (Validierung im Quelltext). Mit pods: "0" verbietet er BestEffort-Pods im Namespace:
apiVersion: v1
kind: ResourceQuota
metadata:
name: no-best-effort
namespace: team-a
spec:
hard:
pods: "0"
scopes:
- BestEffort
Mit LimitRange-Defaults entstehen ohnehin keine BestEffort-Pods; die Quota fängt Namespaces ab, in denen die LimitRange fehlt. Über PriorityClass bekommen hochpriore Pods ein eigenes Budget; die Doku zeigt dafür drei Quotas für high, medium und low.
Typische Fehler und ihre Ursache
| Meldung oder Symptom | Ursache | Prüfen und beheben |
|---|---|---|
exceeded quota: mem-cpu-demo, requested: requests.memory=700Mi, used: requests.memory=600Mi, limited: requests.memory=1Gi |
Summe im Namespace ausgeschöpft | kubectl describe quota -n NAMESPACE; Requests senken, Quota erhöhen oder Workloads entfernen. Bei Deployments kubectl describe deployment und Bedingung ReplicaFailure prüfen |
must specify limits.memory for: app |
Quota nennt den Wert, der Container setzt ihn nicht | LimitRange mit Defaults anlegen oder den Wert im Manifest setzen |
maximum cpu usage per Container is 800m, but limit is 1500m |
max der LimitRange verletzt |
Limit senken oder max anpassen |
spec.containers[0].resources.requests: Invalid value: "700m": must be less than or equal to cpu limit of 500m |
Default-Limit der LimitRange liegt unter dem Request | Request und Limit im Manifest setzen oder Defaults angleichen |
OOMKilled, Exit Code 137, steigender Restart-Zähler |
Container überschreitet das Memory-Limit | kubectl describe pod, Abschnitt Last State; Speicherleck ausschließen, sonst Limit und Request erhöhen |
| Hohe Latenz bei niedriger CPU-Auslastung | CPU-Limit drosselt den Container | Metrik container_cpu_cfs_throttled_periods_total prüfen; Limit anheben oder entfernen |
Pod bleibt Pending, Ereignis FailedScheduling |
Request passt auf keinen Node | kubectl describe node, Abschnitt Allocated resources; Request senken oder Nodes ergänzen |
| Rollout hängt, obwohl die Quota im Dauerbetrieb reicht | Rolling Update braucht Platz für zusätzliche Pods | Quota mit Reserve planen (siehe Rechenbeispiel) |
CPU-Limits und Throttling. Der Kernel vergibt CPU-Zeit je Periode; das kubelet nutzt standardmäßig 100 ms (Kubelet-Referenz, Kernel-Doku). Ein Limit von 500m bedeutet 50 ms CPU-Zeit je Periode. Rechnen vier Threads gleichzeitig auf freien Kernen, ist das Kontingent nach 12,5 ms verbraucht (4 mal 12,5 ms gleich 50 ms). Bis zum Ende der Periode stehen alle Threads still, also 87,5 ms. Die mittlere Auslastung über eine Sekunde zeigt trotzdem nur 0,5 CPU. Durchschnittswerte im Dashboard verbergen das Problem, die Throttling-Metrik zeigt es.
Namespace-Budget planen: ein Rechenbeispiel
Annahmen: drei Worker mit je 8 vCPU und 32 GiB Memory, zusammen 24 vCPU und 96 GiB. 10 Prozent je Node bleiben für kubelet, Systemdienste und Eviction-Schwellen, allokierbar sind damit 21,6 vCPU und 86,4 GiB. Die Requests sollen den Ausfall eines Nodes überstehen, die Planungsgrundlage sind daher zwei Nodes: 14,4 vCPU und 57,6 GiB. Verteilung auf die Namespaces: 50, 20, 20 und 10 Prozent, auf ganze Einheiten gerundet, sodass die Summen unter den Grenzen bleiben.
| Namespace | Anteil | requests.cpu |
requests.memory |
limits.memory |
|---|---|---|---|---|
| production | 50 % | 7 | 28Gi | 28Gi |
| staging | 20 % | 3 | 12Gi | 24Gi |
| development | 20 % | 3 | 12Gi | 24Gi |
| platform | 10 % | 1 | 5Gi | 10Gi |
| Summe | 14 (Grenze 14,4) | 57Gi (Grenze 57,6) | 86Gi (Grenze 86,4) |
Die Regeln dahinter: Die Summe der Requests passt auch nach dem Ausfall eines Nodes auf die übrigen. Die Summe von limits.memory bleibt unter dem allokierbaren Speicher aller drei Nodes, im gesunden Cluster gibt es also kein Memory-Overcommit. CPU wird nicht über Limits gedeckelt, weil sich CPU drosseln lässt, Speicher aber nicht.
Rolling Updates brauchen Reserve. maxSurge ist standardmäßig 25 Prozent und wird aufgerundet. Ein Deployment mit 6 Replicas und je 250m CPU und 512Mi Memory belegt im Dauerbetrieb 1.500m und 3Gi, im Rollout aber 8 Pods mit 2.000m und 4Gi. Unsere Planungsregel: Dauerbetrieb höchstens 80 Prozent der Quota. Die Zahlen sind ein Rechenbeispiel mit offengelegten Annahmen, keine Empfehlung für einen konkreten Cluster. Echte Werte folgen aus Messungen über einen repräsentativen Zeitraum, der Lastspitzen einschließt.
Multi-Tenancy-Muster
- Ein Namespace je Team und Umgebung, zum Beispiel
team-a-produndteam-a-staging. Namespaces sind die Grundlage für RBAC, Quotas und Network Policies (Multi-Tenancy). - Je Namespace dasselbe Set: eine LimitRange, eine Compute-Quota, eine Quota für Objekte und Storage, optional
no-best-effort. Das Set gehört in ein Namespace-Template im Git-Repository, nicht in Handarbeit. - Mandanten erhalten die Rolle
adminper RoleBinding. Sie dürfen damit Quotas lesen, aber nicht ändern. - Grenzen der Quota: Sie begrenzt keinen Netzwerkverkehr. Die Doku nennt dafür Node-Isolation; für eine noch strengere Trennung beschreibt sie eigene Cluster je Mandant.
- Plattform-Komponenten bekommen ein eigenes Budget. Ein Beispiel sind die Scan-Jobs des Trivy Operators (Trivy für Kubernetes).
Auslastung überwachen
kubectl describe quota -n team-a-prod # Used und Hard je Ressource
kubectl top pod -n team-a-prod --containers # benötigt den metrics-server
kubectl describe node NODE # Abschnitt Allocated resources
kubectl top liest die Metrics API und setzt deshalb den metrics-server voraus. Für Alarme eignen sich diese Prometheus-Abfragen mit Metriknamen aus der Dokumentation von kube-state-metrics und cAdvisor:
# Quota-Auslastung je Ressource (1 = voll)
kube_resourcequota{type="used"} / ignoring(type) kube_resourcequota{type="hard"}
# Anteil gedrosselter CPU-Perioden je Container
sum by (namespace, pod, container) (rate(container_cpu_cfs_throttled_periods_total[5m]))
/ sum by (namespace, pod, container) (rate(container_cpu_cfs_periods_total[5m]))
# OOM-Ereignisse der letzten Stunde
increase(container_oom_events_total[1h]) > 0
Schwellen wie 90 Prozent Quota-Auslastung sind frei gewählte Beispiele. Ob die Metriken verfügbar sind, hängt vom Metrik-Setup des Clusters ab.
Stand und Methodik
Stand 28.10.2026. Grundlage sind die Kubernetes-Dokumentation zu Version 1.37, der Kubernetes-Quelltext für Fehlermeldungen und Defaulting sowie die Dokumentation von kube-state-metrics und cAdvisor. Die YAML-Manifeste wurden gegen die JSON-Schemas der Kubernetes-API 1.37.1 validiert und zusätzlich auf einem Testcluster mit Kubernetes 1.34 angewendet; dort wurden auch Defaults, QoS-Klassen und Fehlermeldungen nachvollzogen. Bei den Abfragen sind Metrik- und Label-Namen sowie die Syntax (promtool) geprüft, nicht die Ergebnisse. Betriebsregeln (Memory-Request gleich Limit, CPU-Limit nur mit Grund, Dauerbetrieb höchstens 80 Prozent der Quota) sind Regeln von WZ-IT und keine Vorgaben von Kubernetes. Das Rechenbeispiel gilt nur unter den genannten Annahmen.
Unser Vorgehen bei WZ-IT
Die Bestandsaufnahme der Workloads gehört zur Übernahme eines Clusters (Checkliste). Für die Ressourcen sind die Leitfragen: Welche Namespaces haben Quota und LimitRange, welche Workloads laufen ohne Memory-Limit oder Request, wie hoch ist die Quota-Auslastung. Im Betrieb überwachen wir Control Plane, Nodes, Kernkomponenten und Kapazität. Monitoring erfolgt 24/7, die Reaktion auf P1-Incidents erfolgt entsprechend Service Level. Der Leistungsumfang des Betriebs steht auf der Seite Managed Kubernetes. Requests und Limits der Anwendungen liegen bei den Anwendungsteams, sofern nichts anderes vereinbart ist.
Weiterführende Guides
- Kubernetes-Cluster übernehmen: Audit-Checkliste
- Trivy für Kubernetes: Images, Cluster und Operator scannen
- Longhorn vs. Rook-Ceph für die Wahl der StorageClass hinter den Storage-Quotas
- Was ist Kubernetes?
- Kubernetes Support ohne Vertrag für einzelne Aufgaben
Ressourcengrenzen und Auslastung im laufenden Betrieb prüfen lassen? Managed Kubernetes kostet ab 590 € netto je Cluster und Monat inklusive 3 Worker-Nodes, jeder weitere Worker-Node 129,90 €, monatlich kündbar mit 30 Tagen Frist zum Monatsende (Stand Oktober 2026). Einzelne Aufgaben ohne Vertrag: Kubernetes Support für 120 € je Stunde netto.











