Tutorials  /  Kubernetes

Kubernetes-Pods mit Resource Limits absichern

LLudwig · August 2026 ·9 Min. Lesezeit ·Kubernetes, Tutorial

Ein einzelner Container ohne Memory-Limit kann den Arbeitsspeicher eines kompletten Worker-Nodes belegen und dazu führen, dass das kubelet fremde Pods evictet. Requests und Limits sind die Stellschrauben, mit denen du das verhinderst. Dieses Tutorial zeigt, wie du sie pro Container setzt, welche QoS-Klasse daraus folgt und wie du ganze Namespaces per LimitRange und ResourceQuota absicherst.

Was sind Resource Limits in Kubernetes?

Resource Limits in Kubernetes sind Obergrenzen für CPU und Arbeitsspeicher, die pro Container im Feld resources.limits definiert werden. Das kubelet setzt sie über cgroups durch und beendet Container, die ihr Memory-Limit überschreiten.

Davon zu trennen ist der Request. Der Request ist die Menge, die der Scheduler beim Platzieren des Pods reserviert. Ein Node nimmt nur so lange neue Pods auf, wie die Summe der Requests in seine allocatable-Kapazität passt. Das Limit spielt beim Scheduling keine Rolle, es wirkt erst zur Laufzeit.

Aspekt requests limits
Wirkt beim Scheduling Betrieb (cgroups)
CPU-Überschreitung erlaubt, wenn frei Throttling
Memory-Überschreitung erlaubt, Eviction-Risiko OOMKill
Beeinflusst QoS-Klasse ja ja

Voraussetzungen

Lege für die Beispiele einen eigenen Namespace an:

Konsole
$ kubectl create namespace team-a
namespace/team-a created

Requests und Limits im Pod setzen

Requests und Limits werden pro Container unter spec.containers[].resources gesetzt, getrennt nach cpu, memory und optional ephemeral-storage. Speichere das folgende Manifest als /root/k8s/api-deployment.yaml:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: team-a
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: nginx:1.27-alpine
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"

Zwei Einheiten musst du sauber auseinanderhalten. CPU wird in Millicores angegeben: 250m entspricht einem Viertel eines Kerns, 1 entspricht einem vollen Kern. Arbeitsspeicher rechnet mit Binärpräfixen, wenn du Mi und Gi verwendest: 1Gi sind 1024 MiB, 1G dagegen 1000 MB. Mische die beiden Schreibweisen nicht innerhalb eines Projekts.

Konsole
$ kubectl apply -f /root/k8s/api-deployment.yaml
deployment.apps/api created
K8

Passende Infrastruktur bei centron

Vom Container zum Cluster: Managed Kubernetes mit cStack – Control Plane, Autoscaler und Traffic inklusive. Managed Kubernetes ansehen →

Was passiert, wenn ein Container sein Limit überschreitet?

Überschreitet ein Container sein CPU-Limit, drosselt der Kernel die Prozesse über die CFS-Quota; überschreitet er sein Memory-Limit, beendet der OOM-Killer den Container sofort mit Exit-Code 137. CPU ist komprimierbar, Arbeitsspeicher nicht.

graph TD
    A["Container fordert Ressourcen an"] --> B{"Limit gesetzt?"}
    B -- nein --> C["Verbrauch nur durch Node-Kapazität begrenzt"]
    B -- ja --> D{"CPU oder Arbeitsspeicher?"}
    D -- CPU --> E["Kernel drosselt auf das Limit, Latenz steigt"]
    D -- Arbeitsspeicher --> F{"Über dem Limit?"}
    F -- nein --> G["Container läuft unverändert weiter"]
    F -- ja --> H["OOMKill mit Exit-Code 137"]
    H --> I["kubelet startet den Container gemäß restartPolicy neu"]

Daraus folgt eine praktische Regel: Ein zu niedriges Memory-Limit erzeugt CrashLoops, ein zu niedriges CPU-Limit erzeugt schwer auffindbare Latenzspitzen. Für latenzkritische Dienste ist es üblich, den CPU-Request sorgfältig zu setzen und auf ein CPU-Limit zu verzichten, das Memory-Limit aber immer zu definieren.

Welche QoS-Klassen gibt es?

Kubernetes kennt die drei QoS-Klassen Guaranteed, Burstable und BestEffort und leitet sie automatisch aus den gesetzten Requests und Limits ab. Die Klasse steht in status.qosClass und entscheidet, welche Pods das kubelet bei Speicherdruck auf dem Node zuerst evictet.

QoS-Klasse Bedingung Eviction-Reihenfolge
Guaranteed requests == limits für CPU und Memory in allen Containern zuletzt
Burstable mindestens ein Request gesetzt, aber nicht überall gleich dem Limit mittig
BestEffort keine Requests und keine Limits zuerst

Prüfe die Klasse direkt am Objekt:

Konsole
$ kubectl get pod -n team-a -l app=api -o jsonpath='{.items[*].status.qosClass}'
Burstable Burstable

Das Beispiel-Deployment landet in Burstable, weil CPU-Request und CPU-Limit unterschiedlich sind. Setzt du cpu in Request und Limit ebenfalls auf denselben Wert, wird der Pod Guaranteed. Für Datenbanken, Message-Broker und alles, was einen Neustart schlecht verkraftet, ist Guaranteed die richtige Wahl.

Standardwerte per LimitRange erzwingen

Ein LimitRange setzt Default-Werte sowie harte Ober- und Untergrenzen für jeden Container eines Namespace. Damit landet kein Pod mehr versehentlich in BestEffort, auch wenn ein Team die resources-Sektion vergisst.

yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: default-container-limits
  namespace: team-a
spec:
  limits:
    - type: Container
      default:
        cpu: "500m"
        memory: "512Mi"
      defaultRequest:
        cpu: "100m"
        memory: "128Mi"
      max:
        cpu: "2"
        memory: "2Gi"
      min:
        cpu: "50m"
        memory: "64Mi"

default füllt fehlende Limits, defaultRequest fehlende Requests. max und min lehnen Pods ab, die außerhalb des Korridors liegen. Wichtig: Ein LimitRange wirkt nur auf neu erstellte Pods. Bestehende Pods musst du neu ausrollen, damit die Defaults greifen.

Konsole
$ kubectl apply -f /root/k8s/limitrange.yaml
limitrange/default-container-limits created
$ kubectl rollout restart deployment/api -n team-a
deployment.apps/api restarted

Namespace-Budget mit ResourceQuota begrenzen

Eine ResourceQuota begrenzt die Summe aller Requests und Limits eines Namespace. Sobald eine Quota requests.cpu oder limits.memory enthält, muss jeder neue Pod den entsprechenden Wert deklarieren, sonst weist der API-Server ihn ab.

yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "8"
    requests.memory: 16Gi
    limits.cpu: "16"
    limits.memory: 32Gi
    pods: "50"

LimitRange und ResourceQuota gehören zusammen. Die Quota definiert das Budget, der LimitRange sorgt dafür, dass Pods ohne explizite Angaben trotzdem zulässige Werte bekommen und nicht an der Quota scheitern.

Passende Werte ermitteln

Die Ausgangswerte für Requests stammen aus dem realen Verbrauch, nicht aus Schätzungen. Miss über mindestens einen vollständigen Lastzyklus, typischerweise 24 Stunden inklusive Batch-Jobs und Backup-Fenstern:

Konsole
$ kubectl top pod -n team-a --containers
POD                   NAME   CPU(cores)   MEMORY(bytes)
api-7d9c5b8f4-2xkzq   api    12m          38Mi
api-7d9c5b8f4-lm4vn   api    9m           36Mi

Als Faustregel setzt du den Request auf das 95. Perzentil des beobachteten Verbrauchs und das Memory-Limit auf denselben oder einen leicht höheren Wert. Wenn du die Nodes selbst dimensionierst, plane pro Node 10 bis 15 Prozent Reserve für kubelet, Container-Runtime und System-Daemons ein. Auf skalierbaren Linux-VMs mit Shared oder Dedicated CPU lässt sich diese Reserve über die Instanzgröße gezielt einstellen, statt sie über zu knappe Requests zu erzwingen.

Der Vertical Pod Autoscaler liefert Empfehlungen, ohne einzugreifen, wenn du updateMode: "Off" setzt. Die Werte liest du dann per kubectl describe vpa <name> aus und übernimmst sie kontrolliert ins Manifest. Ab Kubernetes 1.33 ist zusätzlich In-Place Pod Resize als Beta standardmäßig aktiviert. Damit lassen sich CPU- und Memory-Ressourcen eines laufenden Pods über die resize-Subresource anpassen, ohne ihn neu zu starten.

Verifikation

Prüfe zuerst, wie stark die Nodes durch Requests belegt sind:

Konsole
$ kubectl describe node <node-name> | grep -A 6 "Allocated resources"
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                850m (21%)   1500m (37%)
  memory             768Mi (9%)   1280Mi (16%)

Die Prozentwerte beziehen sich auf allocatable, nicht auf die rohe Node-Kapazität. Requests über 100 Prozent sind nicht möglich, Limits über 100 Prozent sind zulässig und bedeuten Overcommitment.

Kontrolliere danach die Quota-Auslastung:

Konsole
$ kubectl describe quota team-a-quota -n team-a
Name:            team-a-quota
Namespace:       team-a
Resource         Used   Hard
--------         ----   ----
limits.cpu       1      16
limits.memory    512Mi  32Gi
pods             2      50
requests.cpu     500m   8
requests.memory  512Mi  16Gi

Troubleshooting

Pod startet immer wieder neu, Status OOMKilled. Der Container hat sein Memory-Limit überschritten. Den Beleg liefert der letzte Terminierungsgrund:

Konsole
$ kubectl get pod <pod-name> -n team-a -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}{"\n"}'
OOMKilled

Erhöhe das Memory-Limit schrittweise oder begrenze den Heap der Anwendung selbst. Bei der JVM ist -XX:MaxRAMPercentage=75 der saubere Weg, damit der Heap unterhalb des Container-Limits bleibt.

Pod bleibt in Pending mit 0/3 nodes are available: Insufficient cpu. Die Summe der Requests passt auf keinen Node. Verkleinere den Request, gib dem Cluster einen weiteren Node oder prüfe mit kubectl describe node, ob Taints Kapazität blockieren.

Deployment wird abgelehnt mit forbidden: failed quota. Der Pod deklariert keine Requests oder Limits, obwohl die Quota sie verlangt. Ergänze die resources-Sektion oder lege den passenden LimitRange an.

Anwendung ist langsam, obwohl die CPU-Auslastung niedrig aussieht. Das deutet auf CPU-Throttling hin. Die Metrik container_cpu_cfs_throttled_periods_total zeigt, wie oft der Kernel den Container gedrosselt hat. Steigt sie kontinuierlich, ist das CPU-Limit zu niedrig.

Fazit

Setze für jeden produktiven Container mindestens einen Memory-Request und ein Memory-Limit. Ergänze pro Namespace einen LimitRange für sinnvolle Defaults und eine ResourceQuota als Budgetgrenze. Kontrolliere die Werte anschließend mit kubectl top gegen den realen Verbrauch und korrigiere sie, statt sie einmalig zu raten.

Jetzt 200 € Guthaben sichern

Testen Sie Ihr Setup auf ccloud³

Registrieren Sie sich in der ccloud³ und erhalten Sie 200 € Startguthaben für Ihr Projekt – z. B. für eine PostgreSQL-VM mit automatischen Backups.

Ludwig Technische Redaktion

Schreibt bei centron über Linux-Administration, Container und Datenbanken – mit Fokus auf Anleitungen, die im Betrieb tatsächlich funktionieren.

Kategorie Kubernetes
Teilen
Noch offene Fragen?

Our team will help you with your specific setup - in German or English, by people who run the platform themselves.

War dieses Tutorial hilfreich?

Your answer is stored anonymously and helps us improve our tutorials.

Kommentare

No comments yet - be the first to ask a question about this tutorial.

Sign in to comment

Comments are open to centron customers. Sign in to your account to ask a question about this tutorial.

Weiterlesen

Das könnte Sie auch interessieren

Jetzt kostenlos anfangen

Melden Sie sich an und erhalten Sie in den ersten 60 Tagen ein Guthaben von 200 € bei centron.

Dieses Werbeangebot gilt nur für neue Konten. Angebot ausschließlich für Gewerbetreibende.

Jetzt loslegen Sales kontaktieren