Zum Inhalt springen

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.

5 Min. Lesezeit
Kubernetes-Autoscaling-Architektur, die zeigt, wie das HPA Pods anhand von Metriken skaliert, das VPA Ressourcen-Requests anpasst und der Cluster Autoscaler Knoten hinzufügt, wenn Pods nicht eingeplant werden können

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.

ymlyaml
# ❌ 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
ymlyaml
# ✅ 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
tstypescript
// 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.

ymlyaml
# 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 pods

Der 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.

ymlyaml
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"]
shbash
# 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: 700Mi

VPA 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.

ymlyaml
# 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"
tstypescript
// 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.

ymlyaml
# 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 scheduled

Wichtigste 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.

Wilfredo Rujel

Wilfredo Rujel

Full-Stack-Softwareentwickler

Diesen Beitrag teilenX