Ressourcenverwaltung und Autoscaling-Strategien in Kubernetes
Requests, Limits und QoS-Klassen in Kubernetes plus HPA, VPA und Cluster Autoscaling — Konfigurationen zwischen Performance, Kosten und Stabilität.

Ressourcenverwaltung in Kubernetes entscheidet darüber, ob ein Cluster effizient und reaktionsschnell läuft oder ob er entweder Geld für ungenutzte Kapazität verschwendet oder bei Traffic-Spitzen in die Knie geht. Um es richtig zu machen, muss man verstehen, wie Requests und Limits mit dem Scheduler zusammenspielen, wie Quality-of-Service-Klassen die Eviction-Reihenfolge bestimmen und wie die drei Autoscaler-Typen einander ergänzen.
Die meisten Teams konfigurieren Ressourcen entweder zu konservativ – und verschwenden so 60 % ihrer Cluster-Kapazität – oder verzichten ganz darauf und erleben dann OOM-Kills in Produktion.
Requests, Limits und QoS-Klassen
Requests teilen dem Scheduler mit, wie viel Kapazität ein Pod benötigt, und werden garantiert bereitgestellt. Limits begrenzen das Maximum, das ein Pod verbrauchen darf. Die Kombination aus beiden bestimmt die Quality-of-Service-Klasse des Pods.
# ❌ No resource specifications — worst reliability
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: api
image: api:latest
# BestEffort QoS — first to be evicted under pressure
# Scheduler doesn't know how much capacity is needed# ✅ Properly configured resources
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
template:
spec:
containers:
- name: api
image: api:v2.1.0
resources:
requests:
cpu: "250m" # 0.25 CPU cores guaranteed
memory: "256Mi" # 256 MiB guaranteed
limits:
cpu: "1000m" # Can burst to 1 core
memory: "512Mi" # Hard cap — OOMKilled if exceeded
# Burstable QoS — requests != limits
# Evicted after BestEffort pods under memory pressure
- name: sidecar-proxy
image: envoy:v1.28
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "100m"
memory: "64Mi"
# Guaranteed QoS — requests == limits
# Last to be evicted, highest priority// Decision framework for setting resources
interface ResourceStrategy {
workloadType: string;
requestGuidance: string;
limitGuidance: string;
qosTarget: "Guaranteed" | "Burstable" | "BestEffort";
}
const strategies: ResourceStrategy[] = [
{
workloadType: "Latency-sensitive API",
requestGuidance:
"Set to p95 usage from production metrics",
limitGuidance:
"CPU: 2-4x request (allow burst). " +
"Memory: 1.5-2x request (prevent OOM)",
qosTarget: "Burstable",
},
{
workloadType: "Background worker",
requestGuidance:
"Set to average usage — can tolerate scheduling delays",
limitGuidance:
"CPU: no limit (let it use idle capacity). " +
"Memory: 2x request",
qosTarget: "Burstable",
},
{
workloadType: "Database / Stateful",
requestGuidance:
"Set requests == limits for predictable performance",
limitGuidance:
"Same as requests — no bursting, no throttling",
qosTarget: "Guaranteed",
},
{
workloadType: "Batch job",
requestGuidance:
"Minimal requests — tolerates preemption",
limitGuidance:
"Memory limit only — let it use available CPU",
qosTarget: "Burstable",
},
];Horizontal Pod Autoscaler (HPA)
Der HPA passt die Anzahl der Replicas anhand von Metriken an. Die standardmäßige CPU-basierte Skalierung funktioniert gut für rechenintensive Workloads, während benutzerdefinierte Metriken den Rest abdecken.
# CPU-based HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
# Custom metric: requests per second from Prometheus
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100"
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Percent
value: 50
periodSeconds: 60
- type: Pods
value: 4
periodSeconds: 60
selectPolicy: Max # Use the policy that allows more pods
scaleDown:
stabilizationWindowSeconds: 300 # Wait 5 min before scaling down
policies:
- type: Percent
value: 10
periodSeconds: 60
selectPolicy: Min # Use the policy that removes fewer podsDer Abschnitt behavior ist entscheidend. Ohne ihn skaliert der HPA bei kurzen Traffic-Einbrüchen aggressiv herunter, was zu Kapazitätsproblemen führt, sobald der Traffic wieder ansteigt. Das Stabilisierungsfenster und eine konservative Scale-Down-Policy verhindern dieses Flattern.
Vertical Pod Autoscaler (VPA)
Der VPA passt Ressourcen-Requests anhand der tatsächlichen Nutzung an. Er ist besonders nützlich für Workloads, bei denen die richtigen Ressourcenwerte nicht offensichtlich sind – er beobachtet und gibt Empfehlungen ab.
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-server-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: "Off" # Recommendation-only mode
# "Auto" would restart pods to apply new resources
# Start with "Off" to review recommendations first
resourcePolicy:
containerPolicies:
- containerName: api
minAllowed:
cpu: "100m"
memory: "128Mi"
maxAllowed:
cpu: "2000m"
memory: "2Gi"
controlledResources: ["cpu", "memory"]# Check VPA recommendations
kubectl describe vpa api-server-vpa
# Output includes:
# Recommendation:
# Container Recommendations:
# Container Name: api
# Lower Bound: Cpu: 150m, Memory: 200Mi
# Target: Cpu: 350m, Memory: 384Mi
# Uncapped Target: Cpu: 350m, Memory: 384Mi
# Upper Bound: Cpu: 800m, Memory: 700MiVPA und HPA sollten nicht gleichzeitig die CPU desselben Deployments steuern. Das typische Muster: Der VPA verwaltet die Memory-Requests, der HPA verwaltet die Anzahl der Replicas basierend auf CPU oder benutzerdefinierten Metriken.
Konfiguration des Cluster Autoscalers
Der Cluster Autoscaler fügt Knoten hinzu oder entfernt sie, wenn Pods nicht eingeplant werden können oder Knoten nicht ausgelastet sind.
# Cluster autoscaler configuration (Helm values)
autoDiscovery:
clusterName: production
tags:
- k8s.io/cluster-autoscaler/enabled
- k8s.io/cluster-autoscaler/production
extraArgs:
# Don't scale down nodes with running pods from
# kube-system (except DaemonSets)
skip-nodes-with-system-pods: "true"
# Wait 10 minutes before considering a node for removal
scale-down-unneeded-time: "10m"
# Node must be below 50% utilization to be removed
scale-down-utilization-threshold: "0.5"
# Maximum time to wait for a node to be ready
max-node-provision-time: "15m"
# Don't scale down more than 1 node at a time
max-graceful-termination-sec: "600"
# Balance similar node groups
balance-similar-node-groups: "true"
# Handle GPU node groups separately
expander: "priority"// Node pool strategy for mixed workloads
interface NodePoolConfig {
name: string;
instanceType: string;
minNodes: number;
maxNodes: number;
taints: string[];
labels: Record<string, string>;
useCase: string;
}
const nodePools: NodePoolConfig[] = [
{
name: "general",
instanceType: "m5.xlarge",
minNodes: 3,
maxNodes: 20,
taints: [],
labels: { "workload-type": "general" },
useCase: "API servers, web frontends, background workers",
},
{
name: "memory-optimized",
instanceType: "r5.2xlarge",
minNodes: 0,
maxNodes: 10,
taints: ["workload=memory:NoSchedule"],
labels: { "workload-type": "memory" },
useCase: "In-memory caches, data processing",
},
{
name: "spot-batch",
instanceType: "c5.2xlarge",
minNodes: 0,
maxNodes: 50,
taints: ["lifecycle=spot:NoSchedule"],
labels: { "workload-type": "batch", lifecycle: "spot" },
useCase:
"Batch jobs, CI runners — tolerant of interruption",
},
];Alles zusammengeführt: der Autoscaling-Stack
Die drei Autoscaler arbeiten gemeinsam als mehrschichtiges System.
# Complete autoscaling setup for a production API
# 1. Deployment with right-sized resources (from VPA recommendations)
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-api
spec:
replicas: 3
template:
spec:
containers:
- name: payment-api
resources:
requests:
cpu: "350m"
memory: "384Mi"
limits:
cpu: "1000m"
memory: "768Mi"
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: payment-api
---
# 2. HPA scales pods based on request rate
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: payment-api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: payment-api
minReplicas: 3
maxReplicas: 30
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "80"
behavior:
scaleDown:
stabilizationWindowSeconds: 300
---
# 3. VPA tunes memory requests (recommendation mode)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: payment-api-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: payment-api
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: payment-api
controlledResources: ["memory"]
# 4. Cluster autoscaler adds nodes when HPA creates
# pods that can't be scheduledWichtigste Erkenntnisse
Ressourcen-Requests sind garantierte Zuweisungen, die der Scheduler für Platzierungsentscheidungen nutzt: Werden sie zu niedrig angesetzt, führt das zur Einplanung auf überbuchten Knoten, werden sie zu hoch angesetzt, verschwendet das Cluster-Kapazität. Quality-of-Service-Klassen (Guaranteed, Burstable, BestEffort) bestimmen die Eviction-Reihenfolge unter Memory-Druck – latenzkritische Workloads sollten daher Requests haben, die nahe an den Limits liegen, um eine stabile Performance zu gewährleisten. Die behavior-Policies des HPA verhindern Skalierungs-Flattern: Ein fünfminütiges Stabilisierungsfenster vor dem Scale-Down und konservative Abbauraten (10 % pro Minute) vermeiden Kapazitätsprobleme bei kurzen Traffic-Einbrüchen. Der VPA sollte zunächst im Empfehlungsmodus laufen, die tatsächliche Nutzung beobachten und passend dimensionierte Ressourcenwerte vorschlagen, bevor automatische Updates aktiviert werden, die Pods neu starten. Die drei Autoscaler greifen auf natürliche Weise ineinander: Der VPA dimensioniert die Ressourcen einzelner Pods richtig, der HPA passt die Anzahl der Replicas an die Last an, und der Cluster Autoscaler stellt Knoten bereit, wenn ausstehende Pods nicht eingeplant werden können. Mehrere Knotenpools mit Taints und Tolerations ermöglichen es, Workload-Typen den passenden Instanztypen zuzuordnen – Spot-Instances für Batch-Jobs, memory-optimierte Knoten für Caches und Allzweck-Knoten für APIs.


